From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- gfx/cairo/01-xlib-xrender-private.patch | 22 + gfx/cairo/02-cplusplus-no-register.patch | 15 + gfx/cairo/03-expose-lcd-filter.patch | 164 + gfx/cairo/04-subpixel-aa-api.patch | 234 + gfx/cairo/05-ft-font-synth-flags-api.patch | 35 + gfx/cairo/06-shared-ft-face.patch | 384 + gfx/cairo/07-ft-variations-runtime-check.patch | 207 + gfx/cairo/08-directwrite-additions.patch | 718 ++ gfx/cairo/09-quartz-surface-additions.patch | 314 + gfx/cairo/10-zero-sized-image.patch | 13 + gfx/cairo/11-quartz-surface-tags.patch | 94 + gfx/cairo/12-pdf-surface-typo.patch | 12 + gfx/cairo/13-quartz-cglayer-surface.patch | 253 + gfx/cairo/14-image-surface-oob-read.patch | 17 + gfx/cairo/15-remove-quartz-surface-for-data.patch | 138 + .../16-quartz-surface-doimage-extendpad.patch | 12 + gfx/cairo/17-active-edges-crash.patch | 23 + gfx/cairo/18-quartz-granular-ifdefs.patch | 83 + gfx/cairo/README | 257 + gfx/cairo/add-cairo_scaled_font_get_hint_metrics | 169 + gfx/cairo/cairo/AUTHORS | 115 + gfx/cairo/cairo/COPYING | 33 + gfx/cairo/cairo/COPYING-LGPL-2.1 | 510 ++ gfx/cairo/cairo/COPYING-MPL-1.1 | 470 + gfx/cairo/cairo/INSTALL | 184 + gfx/cairo/cairo/NEWS | 8174 ++++++++++++++++++ gfx/cairo/cairo/README | 198 + gfx/cairo/cairo/README.win32 | 66 + gfx/cairo/cairo/moz.yaml | 16 + 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 + .../cairo/src/cairo-analysis-surface-private.h | 74 + gfx/cairo/cairo/src/cairo-analysis-surface.c | 1034 +++ gfx/cairo/cairo/src/cairo-arc-private.h | 61 + gfx/cairo/cairo/src/cairo-arc.c | 312 + gfx/cairo/cairo/src/cairo-array-private.h | 93 + gfx/cairo/cairo/src/cairo-array.c | 540 ++ gfx/cairo/cairo/src/cairo-atomic-private.h | 424 + gfx/cairo/cairo/src/cairo-atomic.c | 120 + gfx/cairo/cairo/src/cairo-backend-private.h | 204 + gfx/cairo/cairo/src/cairo-base64-stream.c | 145 + gfx/cairo/cairo/src/cairo-base85-stream.c | 131 + .../cairo/src/cairo-bentley-ottmann-rectangular.c | 895 ++ .../cairo/src/cairo-bentley-ottmann-rectilinear.c | 600 ++ gfx/cairo/cairo/src/cairo-bentley-ottmann.c | 1915 +++++ gfx/cairo/cairo/src/cairo-beos-surface.cpp | 984 +++ gfx/cairo/cairo/src/cairo-beos.h | 60 + gfx/cairo/cairo/src/cairo-botor-scan-converter.c | 2202 +++++ 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 | 122 + gfx/cairo/cairo/src/cairo-boxes.c | 486 ++ gfx/cairo/cairo/src/cairo-cache-private.h | 145 + gfx/cairo/cairo/src/cairo-cache.c | 338 + gfx/cairo/cairo/src/cairo-cff-subset.c | 3454 ++++++++ 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 | 198 + 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 | 838 ++ gfx/cairo/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 | 193 + gfx/cairo/cairo/src/cairo-combsort-inline.h | 94 + gfx/cairo/cairo/src/cairo-compiler-private.h | 246 + .../cairo/src/cairo-composite-rectangles-private.h | 159 + gfx/cairo/cairo/src/cairo-composite-rectangles.c | 499 ++ gfx/cairo/cairo/src/cairo-compositor-private.h | 366 + gfx/cairo/cairo/src/cairo-compositor.c | 269 + 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-damage-private.h | 85 + gfx/cairo/cairo/src/cairo-damage.c | 241 + gfx/cairo/cairo/src/cairo-debug.c | 325 + .../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 | 156 + gfx/cairo/cairo/src/cairo-deprecated.h | 123 + gfx/cairo/cairo/src/cairo-device-private.h | 86 + gfx/cairo/cairo/src/cairo-device.c | 545 ++ gfx/cairo/cairo/src/cairo-directfb-surface.c | 545 ++ gfx/cairo/cairo/src/cairo-directfb.h | 67 + gfx/cairo/cairo/src/cairo-drm.h | 120 + gfx/cairo/cairo/src/cairo-egl-context.c | 317 + gfx/cairo/cairo/src/cairo-error-inline.h | 52 + gfx/cairo/cairo/src/cairo-error-private.h | 131 + gfx/cairo/cairo/src/cairo-error.c | 73 + gfx/cairo/cairo/src/cairo-fallback-compositor.c | 185 + .../cairo/src/cairo-features-uninstalled.pc.in | 7 + gfx/cairo/cairo/src/cairo-features.h | 94 + gfx/cairo/cairo/src/cairo-features.pc.in | 12 + gfx/cairo/cairo/src/cairo-fixed-private.h | 395 + gfx/cairo/cairo/src/cairo-fixed-type-private.h | 75 + gfx/cairo/cairo/src/cairo-fixed.c | 39 + gfx/cairo/cairo/src/cairo-font-face-twin-data.c | 1072 +++ gfx/cairo/cairo/src/cairo-font-face-twin.c | 752 ++ gfx/cairo/cairo/src/cairo-font-face.c | 345 + gfx/cairo/cairo/src/cairo-font-options.c | 622 ++ gfx/cairo/cairo/src/cairo-fontconfig-private.h | 78 + gfx/cairo/cairo/src/cairo-freed-pool-private.h | 139 + gfx/cairo/cairo/src/cairo-freed-pool.c | 93 + gfx/cairo/cairo/src/cairo-freelist-private.h | 139 + gfx/cairo/cairo/src/cairo-freelist-type-private.h | 54 + gfx/cairo/cairo/src/cairo-freelist.c | 191 + gfx/cairo/cairo/src/cairo-ft-font.c | 4088 +++++++++ gfx/cairo/cairo/src/cairo-ft-private.h | 61 + gfx/cairo/cairo/src/cairo-ft.h | 120 + gfx/cairo/cairo/src/cairo-gl-composite.c | 1364 +++ gfx/cairo/cairo/src/cairo-gl-device.c | 851 ++ gfx/cairo/cairo/src/cairo-gl-dispatch-private.h | 129 + gfx/cairo/cairo/src/cairo-gl-dispatch.c | 273 + gfx/cairo/cairo/src/cairo-gl-ext-def-private.h | 143 + gfx/cairo/cairo/src/cairo-gl-glyphs.c | 503 ++ gfx/cairo/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 + gfx/cairo/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 | 865 ++ gfx/cairo/cairo/src/cairo-gl-shaders.c | 1111 +++ gfx/cairo/cairo/src/cairo-gl-source.c | 113 + gfx/cairo/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 | 1550 ++++ gfx/cairo/cairo/src/cairo-gl-traps-compositor.c | 531 ++ gfx/cairo/cairo/src/cairo-gl.h | 155 + gfx/cairo/cairo/src/cairo-glx-context.c | 324 + gfx/cairo/cairo/src/cairo-gstate-private.h | 396 + gfx/cairo/cairo/src/cairo-gstate.c | 2361 ++++++ gfx/cairo/cairo/src/cairo-hash-private.h | 87 + gfx/cairo/cairo/src/cairo-hash.c | 578 ++ gfx/cairo/cairo/src/cairo-hull.c | 235 + gfx/cairo/cairo/src/cairo-image-compositor.c | 3176 +++++++ gfx/cairo/cairo/src/cairo-image-info-private.h | 68 + gfx/cairo/cairo/src/cairo-image-info.c | 421 + gfx/cairo/cairo/src/cairo-image-mask-compositor.c | 414 + gfx/cairo/cairo/src/cairo-image-source.c | 1648 ++++ gfx/cairo/cairo/src/cairo-image-surface-inline.h | 96 + gfx/cairo/cairo/src/cairo-image-surface-private.h | 239 + gfx/cairo/cairo/src/cairo-image-surface.c | 1363 +++ gfx/cairo/cairo/src/cairo-line-inline.h | 48 + gfx/cairo/cairo/src/cairo-line-private.h | 51 + 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 | 48 + gfx/cairo/cairo/src/cairo-lzw.c | 404 + gfx/cairo/cairo/src/cairo-malloc-private.h | 149 + gfx/cairo/cairo/src/cairo-mask-compositor.c | 1481 ++++ gfx/cairo/cairo/src/cairo-matrix.c | 1206 +++ 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 ++ gfx/cairo/cairo/src/cairo-misc.c | 1123 +++ gfx/cairo/cairo/src/cairo-mono-scan-converter.c | 612 ++ gfx/cairo/cairo/src/cairo-mutex-impl-private.h | 278 + gfx/cairo/cairo/src/cairo-mutex-list-private.h | 79 + gfx/cairo/cairo/src/cairo-mutex-private.h | 67 + gfx/cairo/cairo/src/cairo-mutex-type-private.h | 194 + gfx/cairo/cairo/src/cairo-mutex.c | 82 + gfx/cairo/cairo/src/cairo-no-compositor.c | 108 + gfx/cairo/cairo/src/cairo-observer.c | 52 + gfx/cairo/cairo/src/cairo-os2-private.h | 67 + gfx/cairo/cairo/src/cairo-os2-surface.c | 1416 ++++ gfx/cairo/cairo/src/cairo-os2.h | 110 + gfx/cairo/cairo/src/cairo-output-stream-private.h | 201 + gfx/cairo/cairo/src/cairo-output-stream.c | 811 ++ gfx/cairo/cairo/src/cairo-paginated-private.h | 184 + .../cairo/src/cairo-paginated-surface-private.h | 62 + gfx/cairo/cairo/src/cairo-paginated-surface.c | 803 ++ gfx/cairo/cairo/src/cairo-path-bounds.c | 230 + gfx/cairo/cairo/src/cairo-path-fill.c | 341 + gfx/cairo/cairo/src/cairo-path-fixed-private.h | 206 + gfx/cairo/cairo/src/cairo-path-fixed.c | 1589 ++++ gfx/cairo/cairo/src/cairo-path-in-fill.c | 290 + gfx/cairo/cairo/src/cairo-path-private.h | 57 + gfx/cairo/cairo/src/cairo-path-stroke-boxes.c | 711 ++ gfx/cairo/cairo/src/cairo-path-stroke-polygon.c | 1364 +++ gfx/cairo/cairo/src/cairo-path-stroke-traps.c | 1148 +++ gfx/cairo/cairo/src/cairo-path-stroke-tristrip.c | 1088 +++ gfx/cairo/cairo/src/cairo-path-stroke.c | 1485 ++++ gfx/cairo/cairo/src/cairo-path.c | 479 ++ 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 | 4791 +++++++++++ gfx/cairo/cairo/src/cairo-pdf-interchange.c | 1881 ++++ gfx/cairo/cairo/src/cairo-pdf-operators-private.h | 184 + gfx/cairo/cairo/src/cairo-pdf-operators.c | 1595 ++++ gfx/cairo/cairo/src/cairo-pdf-shading-private.h | 100 + gfx/cairo/cairo/src/cairo-pdf-shading.c | 279 + gfx/cairo/cairo/src/cairo-pdf-surface-private.h | 423 + gfx/cairo/cairo/src/cairo-pdf-surface.c | 8964 ++++++++++++++++++++ gfx/cairo/cairo/src/cairo-pdf.h | 161 + gfx/cairo/cairo/src/cairo-pen.c | 475 ++ gfx/cairo/cairo/src/cairo-pixman-private.h | 51 + gfx/cairo/cairo/src/cairo-platform.h | 69 + gfx/cairo/cairo/src/cairo-png.c | 973 +++ gfx/cairo/cairo/src/cairo-polygon-intersect.c | 1474 ++++ gfx/cairo/cairo/src/cairo-polygon-reduce.c | 1438 ++++ gfx/cairo/cairo/src/cairo-polygon.c | 608 ++ gfx/cairo/cairo/src/cairo-private.h | 64 + gfx/cairo/cairo/src/cairo-ps-surface-private.h | 125 + gfx/cairo/cairo/src/cairo-ps-surface.c | 5434 ++++++++++++ gfx/cairo/cairo/src/cairo-ps.h | 116 + gfx/cairo/cairo/src/cairo-qt-surface.cpp | 1741 ++++ gfx/cairo/cairo/src/cairo-qt.h | 85 + gfx/cairo/cairo/src/cairo-quartz-font.c | 922 ++ gfx/cairo/cairo/src/cairo-quartz-image-surface.c | 391 + gfx/cairo/cairo/src/cairo-quartz-image.h | 57 + gfx/cairo/cairo/src/cairo-quartz-private.h | 125 + gfx/cairo/cairo/src/cairo-quartz-surface.c | 3007 +++++++ gfx/cairo/cairo/src/cairo-quartz.h | 100 + gfx/cairo/cairo/src/cairo-raster-source-pattern.c | 430 + .../cairo/src/cairo-recording-surface-inline.h | 68 + .../cairo/src/cairo-recording-surface-private.h | 208 + gfx/cairo/cairo/src/cairo-recording-surface.c | 2319 +++++ gfx/cairo/cairo/src/cairo-rectangle.c | 299 + .../cairo/src/cairo-rectangular-scan-converter.c | 791 ++ .../cairo/src/cairo-reference-count-private.h | 62 + gfx/cairo/cairo/src/cairo-region-private.h | 77 + gfx/cairo/cairo/src/cairo-region.c | 943 ++ gfx/cairo/cairo/src/cairo-rename.h | 410 + gfx/cairo/cairo/src/cairo-rtree-private.h | 142 + gfx/cairo/cairo/src/cairo-rtree.c | 388 + gfx/cairo/cairo/src/cairo-scaled-font-private.h | 186 + .../cairo/src/cairo-scaled-font-subsets-private.h | 740 ++ gfx/cairo/cairo/src/cairo-scaled-font-subsets.c | 1381 +++ gfx/cairo/cairo/src/cairo-scaled-font.c | 3202 +++++++ gfx/cairo/cairo/src/cairo-script-private.h | 59 + gfx/cairo/cairo/src/cairo-script-surface.c | 4023 +++++++++ gfx/cairo/cairo/src/cairo-script.h | 98 + gfx/cairo/cairo/src/cairo-shape-mask-compositor.c | 340 + gfx/cairo/cairo/src/cairo-slope-private.h | 72 + gfx/cairo/cairo/src/cairo-slope.c | 99 + .../cairo/src/cairo-spans-compositor-private.h | 111 + gfx/cairo/cairo/src/cairo-spans-compositor.c | 1201 +++ gfx/cairo/cairo/src/cairo-spans-private.h | 210 + gfx/cairo/cairo/src/cairo-spans.c | 258 + gfx/cairo/cairo/src/cairo-spline.c | 424 + gfx/cairo/cairo/src/cairo-stroke-dash-private.h | 70 + gfx/cairo/cairo/src/cairo-stroke-dash.c | 96 + gfx/cairo/cairo/src/cairo-stroke-style.c | 354 + .../cairo/src/cairo-surface-backend-private.h | 228 + .../cairo/src/cairo-surface-clipper-private.h | 71 + gfx/cairo/cairo/src/cairo-surface-clipper.c | 196 + .../cairo/src/cairo-surface-fallback-private.h | 95 + gfx/cairo/cairo/src/cairo-surface-fallback.c | 115 + gfx/cairo/cairo/src/cairo-surface-inline.h | 60 + .../cairo/src/cairo-surface-observer-inline.h | 59 + .../cairo/src/cairo-surface-observer-private.h | 208 + gfx/cairo/cairo/src/cairo-surface-observer.c | 2105 +++++ gfx/cairo/cairo/src/cairo-surface-offset-private.h | 95 + gfx/cairo/cairo/src/cairo-surface-offset.c | 308 + gfx/cairo/cairo/src/cairo-surface-private.h | 123 + .../cairo/src/cairo-surface-snapshot-inline.h | 67 + .../cairo/src/cairo-surface-snapshot-private.h | 51 + gfx/cairo/cairo/src/cairo-surface-snapshot.c | 295 + .../cairo/src/cairo-surface-subsurface-inline.h | 72 + .../cairo/src/cairo-surface-subsurface-private.h | 55 + gfx/cairo/cairo/src/cairo-surface-subsurface.c | 589 ++ .../cairo/src/cairo-surface-wrapper-private.h | 200 + gfx/cairo/cairo/src/cairo-surface-wrapper.c | 695 ++ gfx/cairo/cairo/src/cairo-surface.c | 3130 +++++++ gfx/cairo/cairo/src/cairo-svg-surface-private.h | 83 + gfx/cairo/cairo/src/cairo-svg-surface.c | 3093 +++++++ gfx/cairo/cairo/src/cairo-svg.h | 131 + gfx/cairo/cairo/src/cairo-tag-attributes-private.h | 99 + gfx/cairo/cairo/src/cairo-tag-attributes.c | 728 ++ gfx/cairo/cairo/src/cairo-tag-stack-private.h | 110 + gfx/cairo/cairo/src/cairo-tag-stack.c | 298 + gfx/cairo/cairo/src/cairo-tee-surface-private.h | 47 + gfx/cairo/cairo/src/cairo-tee-surface.c | 603 ++ gfx/cairo/cairo/src/cairo-tee.h | 66 + gfx/cairo/cairo/src/cairo-time-private.h | 94 + gfx/cairo/cairo/src/cairo-time.c | 225 + gfx/cairo/cairo/src/cairo-tor-scan-converter.c | 1899 +++++ gfx/cairo/cairo/src/cairo-tor22-scan-converter.c | 1712 ++++ gfx/cairo/cairo/src/cairo-toy-font-face.c | 524 ++ 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 | 1123 +++ gfx/cairo/cairo/src/cairo-tristrip-private.h | 94 + gfx/cairo/cairo/src/cairo-tristrip.c | 185 + .../cairo/src/cairo-truetype-subset-private.h | 212 + gfx/cairo/cairo/src/cairo-truetype-subset.c | 1681 ++++ gfx/cairo/cairo/src/cairo-type1-fallback.c | 903 ++ gfx/cairo/cairo/src/cairo-type1-glyph-names.c | 410 + gfx/cairo/cairo/src/cairo-type1-private.h | 51 + gfx/cairo/cairo/src/cairo-type1-subset.c | 1838 ++++ .../cairo/src/cairo-type3-glyph-surface-private.h | 89 + gfx/cairo/cairo/src/cairo-type3-glyph-surface.c | 560 ++ gfx/cairo/cairo/src/cairo-types-private.h | 406 + gfx/cairo/cairo/src/cairo-unicode.c | 447 + gfx/cairo/cairo/src/cairo-uninstalled.pc.in | 8 + gfx/cairo/cairo/src/cairo-user-font-private.h | 46 + gfx/cairo/cairo/src/cairo-user-font.c | 831 ++ gfx/cairo/cairo/src/cairo-version.c | 255 + gfx/cairo/cairo/src/cairo-version.h | 8 + gfx/cairo/cairo/src/cairo-vg-surface.c | 1854 ++++ gfx/cairo/cairo/src/cairo-vg.h | 103 + gfx/cairo/cairo/src/cairo-wgl-context.c | 261 + gfx/cairo/cairo/src/cairo-wideint-private.h | 338 + gfx/cairo/cairo/src/cairo-wideint-type-private.h | 158 + gfx/cairo/cairo/src/cairo-wideint.c | 852 ++ gfx/cairo/cairo/src/cairo-win32.h | 147 + gfx/cairo/cairo/src/cairo-xcb-connection-core.c | 300 + gfx/cairo/cairo/src/cairo-xcb-connection-render.c | 299 + gfx/cairo/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 | 802 ++ 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 ++ gfx/cairo/cairo/src/cairo-xcb-surface-render.c | 4880 +++++++++++ gfx/cairo/cairo/src/cairo-xcb-surface.c | 1532 ++++ gfx/cairo/cairo/src/cairo-xcb.h | 116 + gfx/cairo/cairo/src/cairo-xlib-core-compositor.c | 653 ++ gfx/cairo/cairo/src/cairo-xlib-display.c | 667 ++ .../cairo/src/cairo-xlib-fallback-compositor.c | 248 + gfx/cairo/cairo/src/cairo-xlib-private.h | 472 ++ gfx/cairo/cairo/src/cairo-xlib-render-compositor.c | 2022 +++++ gfx/cairo/cairo/src/cairo-xlib-screen.c | 467 + gfx/cairo/cairo/src/cairo-xlib-source.c | 1171 +++ gfx/cairo/cairo/src/cairo-xlib-surface-private.h | 44 + gfx/cairo/cairo/src/cairo-xlib-surface-shm.c | 1466 ++++ gfx/cairo/cairo/src/cairo-xlib-surface.c | 2380 ++++++ gfx/cairo/cairo/src/cairo-xlib-visual.c | 194 + gfx/cairo/cairo/src/cairo-xlib-xcb-surface.c | 848 ++ gfx/cairo/cairo/src/cairo-xlib-xrender-private.h | 1178 +++ gfx/cairo/cairo/src/cairo-xlib-xrender.h | 66 + gfx/cairo/cairo/src/cairo-xlib.h | 118 + gfx/cairo/cairo/src/cairo-xml-surface.c | 1210 +++ gfx/cairo/cairo/src/cairo-xml.h | 67 + gfx/cairo/cairo/src/cairo.c | 4341 ++++++++++ gfx/cairo/cairo/src/cairo.h | 3238 +++++++ gfx/cairo/cairo/src/cairo.pc.in | 13 + gfx/cairo/cairo/src/cairoint.h | 2150 +++++ 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-has-hidden-symbols.c | 3 + gfx/cairo/cairo/src/check-headers.sh | 27 + gfx/cairo/cairo/src/check-link.c | 17 + gfx/cairo/cairo/src/check-plt.sh | 28 + gfx/cairo/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 ++ gfx/cairo/cairo/src/drm/cairo-drm-i915-glyphs.c | 564 ++ gfx/cairo/cairo/src/drm/cairo-drm-i915-private.h | 1270 +++ gfx/cairo/cairo/src/drm/cairo-drm-i915-shader.c | 2859 +++++++ gfx/cairo/cairo/src/drm/cairo-drm-i915-spans.c | 799 ++ gfx/cairo/cairo/src/drm/cairo-drm-i915-surface.c | 2942 +++++++ gfx/cairo/cairo/src/drm/cairo-drm-i965-glyphs.c | 504 ++ gfx/cairo/cairo/src/drm/cairo-drm-i965-private.h | 737 ++ gfx/cairo/cairo/src/drm/cairo-drm-i965-shader.c | 2825 ++++++ gfx/cairo/cairo/src/drm/cairo-drm-i965-spans.c | 407 + gfx/cairo/cairo/src/drm/cairo-drm-i965-surface.c | 1926 +++++ .../cairo/src/drm/cairo-drm-intel-brw-defines.h | 824 ++ .../cairo/src/drm/cairo-drm-intel-brw-eu-emit.c | 1089 +++ .../cairo/src/drm/cairo-drm-intel-brw-eu-util.c | 121 + gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.c | 250 + gfx/cairo/cairo/src/drm/cairo-drm-intel-brw-eu.h | 1044 +++ .../cairo/src/drm/cairo-drm-intel-brw-structs.h | 1329 +++ .../src/drm/cairo-drm-intel-command-private.h | 909 ++ gfx/cairo/cairo/src/drm/cairo-drm-intel-debug.c | 1209 +++ .../cairo/src/drm/cairo-drm-intel-ioctl-private.h | 45 + gfx/cairo/cairo/src/drm/cairo-drm-intel-private.h | 515 ++ gfx/cairo/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 + gfx/cairo/cairo/src/drm/cairo-drm-radeon-private.h | 103 + gfx/cairo/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/meson.build | 321 + gfx/cairo/cairo/src/moz.build | 284 + gfx/cairo/cairo/src/pixman-rename.h | 145 + gfx/cairo/cairo/src/test-base-compositor-surface.c | 824 ++ .../cairo/src/test-compositor-surface-private.h | 56 + gfx/cairo/cairo/src/test-compositor-surface.c | 265 + gfx/cairo/cairo/src/test-compositor-surface.h | 71 + gfx/cairo/cairo/src/test-null-compositor-surface.c | 487 ++ gfx/cairo/cairo/src/test-null-compositor-surface.h | 60 + gfx/cairo/cairo/src/test-paginated-surface.c | 289 + gfx/cairo/cairo/src/test-paginated-surface.h | 48 + gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp | 1424 ++++ gfx/cairo/cairo/src/win32/cairo-dwrite-private.h | 229 + gfx/cairo/cairo/src/win32/cairo-win32-debug.c | 87 + gfx/cairo/cairo/src/win32/cairo-win32-device.c | 202 + .../cairo/src/win32/cairo-win32-display-surface.c | 1147 +++ gfx/cairo/cairo/src/win32/cairo-win32-font.c | 2334 +++++ .../cairo/src/win32/cairo-win32-gdi-compositor.c | 671 ++ .../cairo/src/win32/cairo-win32-printing-surface.c | 2262 +++++ gfx/cairo/cairo/src/win32/cairo-win32-private.h | 275 + gfx/cairo/cairo/src/win32/cairo-win32-refptr.h | 178 + gfx/cairo/cairo/src/win32/cairo-win32-surface.c | 405 + gfx/cairo/cairo/src/win32/cairo-win32-system.c | 89 + gfx/cairo/glitz/src/glitz.def | 0 gfx/cairo/glitz/src/wgl/glitz-wgl.def | 0 gfx/cairo/libpixman/AUTHORS | 0 gfx/cairo/libpixman/COPYING | 42 + gfx/cairo/libpixman/INSTALL | 234 + gfx/cairo/libpixman/README | 140 + gfx/cairo/libpixman/src/dither/blue-noise-64x64.h | 77 + gfx/cairo/libpixman/src/dither/make-blue-noise.c | 679 ++ gfx/cairo/libpixman/src/loongson-mmintrin.h | 412 + gfx/cairo/libpixman/src/meson.build | 142 + gfx/cairo/libpixman/src/moz.build | 132 + gfx/cairo/libpixman/src/pixman-access-accessors.c | 3 + gfx/cairo/libpixman/src/pixman-access.c | 1559 ++++ gfx/cairo/libpixman/src/pixman-accessor.h | 25 + gfx/cairo/libpixman/src/pixman-arm-asm.h | 53 + gfx/cairo/libpixman/src/pixman-arm-common.h | 419 + .../libpixman/src/pixman-arm-neon-asm-bilinear.S | 1358 +++ gfx/cairo/libpixman/src/pixman-arm-neon-asm.S | 3633 ++++++++ gfx/cairo/libpixman/src/pixman-arm-neon-asm.h | 1186 +++ gfx/cairo/libpixman/src/pixman-arm-neon.c | 493 ++ .../libpixman/src/pixman-arm-simd-asm-scaled.S | 165 + gfx/cairo/libpixman/src/pixman-arm-simd-asm.S | 1184 +++ gfx/cairo/libpixman/src/pixman-arm-simd-asm.h | 998 +++ gfx/cairo/libpixman/src/pixman-arm-simd.c | 291 + gfx/cairo/libpixman/src/pixman-arm.c | 256 + .../src/pixman-arma64-neon-asm-bilinear.S | 1276 +++ gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S | 3704 ++++++++ gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h | 1310 +++ gfx/cairo/libpixman/src/pixman-bits-image.c | 1383 +++ gfx/cairo/libpixman/src/pixman-combine-float.c | 1158 +++ gfx/cairo/libpixman/src/pixman-combine32.c | 1189 +++ gfx/cairo/libpixman/src/pixman-combine32.h | 272 + gfx/cairo/libpixman/src/pixman-compiler.h | 253 + gfx/cairo/libpixman/src/pixman-conical-gradient.c | 220 + gfx/cairo/libpixman/src/pixman-edge-accessors.c | 4 + gfx/cairo/libpixman/src/pixman-edge-imp.h | 182 + gfx/cairo/libpixman/src/pixman-edge.c | 385 + gfx/cairo/libpixman/src/pixman-fast-path.c | 3298 +++++++ gfx/cairo/libpixman/src/pixman-filter.c | 478 ++ gfx/cairo/libpixman/src/pixman-general.c | 264 + gfx/cairo/libpixman/src/pixman-glyph.c | 676 ++ gfx/cairo/libpixman/src/pixman-gradient-walker.c | 264 + gfx/cairo/libpixman/src/pixman-image.c | 994 +++ gfx/cairo/libpixman/src/pixman-implementation.c | 417 + gfx/cairo/libpixman/src/pixman-inlines.h | 1365 +++ gfx/cairo/libpixman/src/pixman-linear-gradient.c | 292 + gfx/cairo/libpixman/src/pixman-matrix.c | 1073 +++ gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.S | 4283 ++++++++++ gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.h | 711 ++ gfx/cairo/libpixman/src/pixman-mips-dspr2.c | 459 + gfx/cairo/libpixman/src/pixman-mips-dspr2.h | 432 + gfx/cairo/libpixman/src/pixman-mips-memcpy-asm.S | 382 + gfx/cairo/libpixman/src/pixman-mips.c | 94 + gfx/cairo/libpixman/src/pixman-mmx.c | 4153 +++++++++ gfx/cairo/libpixman/src/pixman-noop.c | 161 + gfx/cairo/libpixman/src/pixman-ppc.c | 173 + gfx/cairo/libpixman/src/pixman-private.h | 1202 +++ gfx/cairo/libpixman/src/pixman-radial-gradient.c | 509 ++ gfx/cairo/libpixman/src/pixman-region.c | 2792 ++++++ gfx/cairo/libpixman/src/pixman-region16.c | 67 + gfx/cairo/libpixman/src/pixman-region32.c | 47 + gfx/cairo/libpixman/src/pixman-solid-fill.c | 67 + gfx/cairo/libpixman/src/pixman-sse2.c | 6528 ++++++++++++++ gfx/cairo/libpixman/src/pixman-ssse3.c | 351 + gfx/cairo/libpixman/src/pixman-timer.c | 66 + gfx/cairo/libpixman/src/pixman-trap.c | 711 ++ gfx/cairo/libpixman/src/pixman-utils.c | 330 + gfx/cairo/libpixman/src/pixman-version.h | 54 + gfx/cairo/libpixman/src/pixman-version.h.in | 54 + gfx/cairo/libpixman/src/pixman-vmx.c | 3159 +++++++ gfx/cairo/libpixman/src/pixman-x86.c | 253 + gfx/cairo/libpixman/src/pixman.c | 1133 +++ gfx/cairo/libpixman/src/pixman.h | 1423 ++++ gfx/cairo/libpixman/src/solaris-hwcap.mapfile | 30 + gfx/cairo/moz.build | 11 + gfx/cairo/pixman-arm32-clang.patch | 5205 ++++++++++++ gfx/cairo/pixman-arm64-clang.patch | 3756 ++++++++ gfx/cairo/pixman-clangcl.patch | 31 + gfx/cairo/pixman-export.patch | 51 + gfx/cairo/pixman-interp.patch | 31 + gfx/cairo/pixman-intrin.patch | 24 + gfx/cairo/pixman-mingw32.patch | 26 + gfx/cairo/pixman-rename.patch | 24 + 494 files changed, 336405 insertions(+) create mode 100644 gfx/cairo/01-xlib-xrender-private.patch create mode 100644 gfx/cairo/02-cplusplus-no-register.patch create mode 100644 gfx/cairo/03-expose-lcd-filter.patch create mode 100644 gfx/cairo/04-subpixel-aa-api.patch create mode 100644 gfx/cairo/05-ft-font-synth-flags-api.patch create mode 100644 gfx/cairo/06-shared-ft-face.patch create mode 100644 gfx/cairo/07-ft-variations-runtime-check.patch create mode 100644 gfx/cairo/08-directwrite-additions.patch create mode 100644 gfx/cairo/09-quartz-surface-additions.patch create mode 100644 gfx/cairo/10-zero-sized-image.patch create mode 100644 gfx/cairo/11-quartz-surface-tags.patch create mode 100644 gfx/cairo/12-pdf-surface-typo.patch create mode 100644 gfx/cairo/13-quartz-cglayer-surface.patch create mode 100644 gfx/cairo/14-image-surface-oob-read.patch create mode 100644 gfx/cairo/15-remove-quartz-surface-for-data.patch create mode 100644 gfx/cairo/16-quartz-surface-doimage-extendpad.patch create mode 100644 gfx/cairo/17-active-edges-crash.patch create mode 100644 gfx/cairo/18-quartz-granular-ifdefs.patch create mode 100644 gfx/cairo/README create mode 100644 gfx/cairo/add-cairo_scaled_font_get_hint_metrics create mode 100644 gfx/cairo/cairo/AUTHORS create mode 100644 gfx/cairo/cairo/COPYING create mode 100644 gfx/cairo/cairo/COPYING-LGPL-2.1 create mode 100644 gfx/cairo/cairo/COPYING-MPL-1.1 create mode 100644 gfx/cairo/cairo/INSTALL create mode 100644 gfx/cairo/cairo/NEWS create mode 100644 gfx/cairo/cairo/README create mode 100644 gfx/cairo/cairo/README.win32 create mode 100644 gfx/cairo/cairo/moz.yaml 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-analysis-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-analysis-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-arc-private.h create mode 100644 gfx/cairo/cairo/src/cairo-arc.c create mode 100644 gfx/cairo/cairo/src/cairo-array-private.h create mode 100644 gfx/cairo/cairo/src/cairo-array.c create mode 100644 gfx/cairo/cairo/src/cairo-atomic-private.h create mode 100644 gfx/cairo/cairo/src/cairo-atomic.c create mode 100644 gfx/cairo/cairo/src/cairo-backend-private.h create mode 100644 gfx/cairo/cairo/src/cairo-base64-stream.c create mode 100644 gfx/cairo/cairo/src/cairo-base85-stream.c create mode 100644 gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c create mode 100644 gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c create mode 100644 gfx/cairo/cairo/src/cairo-bentley-ottmann.c create mode 100644 gfx/cairo/cairo/src/cairo-beos-surface.cpp create mode 100644 gfx/cairo/cairo/src/cairo-beos.h create mode 100644 gfx/cairo/cairo/src/cairo-botor-scan-converter.c 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-boxes-private.h create mode 100644 gfx/cairo/cairo/src/cairo-boxes.c create mode 100644 gfx/cairo/cairo/src/cairo-cache-private.h create mode 100644 gfx/cairo/cairo/src/cairo-cache.c create mode 100644 gfx/cairo/cairo/src/cairo-cff-subset.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-private.h 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-clip.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 create mode 100644 gfx/cairo/cairo/src/cairo-color.c create mode 100644 gfx/cairo/cairo/src/cairo-combsort-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-compiler-private.h create mode 100644 gfx/cairo/cairo/src/cairo-composite-rectangles-private.h create mode 100644 gfx/cairo/cairo/src/cairo-composite-rectangles.c 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 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-debug.c create mode 100644 gfx/cairo/cairo/src/cairo-default-context-private.h create mode 100644 gfx/cairo/cairo/src/cairo-default-context.c create mode 100644 gfx/cairo/cairo/src/cairo-deflate-stream.c create mode 100644 gfx/cairo/cairo/src/cairo-deprecated.h create mode 100644 gfx/cairo/cairo/src/cairo-device-private.h create mode 100644 gfx/cairo/cairo/src/cairo-device.c create mode 100644 gfx/cairo/cairo/src/cairo-directfb-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-directfb.h create mode 100644 gfx/cairo/cairo/src/cairo-drm.h create mode 100644 gfx/cairo/cairo/src/cairo-egl-context.c create mode 100644 gfx/cairo/cairo/src/cairo-error-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-error-private.h 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 create mode 100644 gfx/cairo/cairo/src/cairo-features.h create mode 100644 gfx/cairo/cairo/src/cairo-features.pc.in create mode 100644 gfx/cairo/cairo/src/cairo-fixed-private.h create mode 100644 gfx/cairo/cairo/src/cairo-fixed-type-private.h create mode 100644 gfx/cairo/cairo/src/cairo-fixed.c create mode 100644 gfx/cairo/cairo/src/cairo-font-face-twin-data.c create mode 100644 gfx/cairo/cairo/src/cairo-font-face-twin.c create mode 100644 gfx/cairo/cairo/src/cairo-font-face.c create mode 100644 gfx/cairo/cairo/src/cairo-font-options.c create mode 100644 gfx/cairo/cairo/src/cairo-fontconfig-private.h create mode 100644 gfx/cairo/cairo/src/cairo-freed-pool-private.h create mode 100644 gfx/cairo/cairo/src/cairo-freed-pool.c create mode 100644 gfx/cairo/cairo/src/cairo-freelist-private.h create mode 100644 gfx/cairo/cairo/src/cairo-freelist-type-private.h create mode 100644 gfx/cairo/cairo/src/cairo-freelist.c create mode 100644 gfx/cairo/cairo/src/cairo-ft-font.c create mode 100644 gfx/cairo/cairo/src/cairo-ft-private.h create mode 100644 gfx/cairo/cairo/src/cairo-ft.h 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-glyphs.c 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-private.h create mode 100644 gfx/cairo/cairo/src/cairo-gl-shaders.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-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-gl-traps-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-gl.h create mode 100644 gfx/cairo/cairo/src/cairo-glx-context.c create mode 100644 gfx/cairo/cairo/src/cairo-gstate-private.h create mode 100644 gfx/cairo/cairo/src/cairo-gstate.c create mode 100644 gfx/cairo/cairo/src/cairo-hash-private.h create mode 100644 gfx/cairo/cairo/src/cairo-hash.c create mode 100644 gfx/cairo/cairo/src/cairo-hull.c create mode 100644 gfx/cairo/cairo/src/cairo-image-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-image-info-private.h create mode 100644 gfx/cairo/cairo/src/cairo-image-info.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-image-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-line-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-line-private.h 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-list-private.h create mode 100644 gfx/cairo/cairo/src/cairo-lzw.c create mode 100644 gfx/cairo/cairo/src/cairo-malloc-private.h create mode 100644 gfx/cairo/cairo/src/cairo-mask-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-matrix.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 create mode 100644 gfx/cairo/cairo/src/cairo-misc.c create mode 100644 gfx/cairo/cairo/src/cairo-mono-scan-converter.c create mode 100644 gfx/cairo/cairo/src/cairo-mutex-impl-private.h create mode 100644 gfx/cairo/cairo/src/cairo-mutex-list-private.h create mode 100644 gfx/cairo/cairo/src/cairo-mutex-private.h create mode 100644 gfx/cairo/cairo/src/cairo-mutex-type-private.h create mode 100644 gfx/cairo/cairo/src/cairo-mutex.c create mode 100644 gfx/cairo/cairo/src/cairo-no-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-observer.c create mode 100644 gfx/cairo/cairo/src/cairo-os2-private.h create mode 100644 gfx/cairo/cairo/src/cairo-os2-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-os2.h create mode 100644 gfx/cairo/cairo/src/cairo-output-stream-private.h create mode 100644 gfx/cairo/cairo/src/cairo-output-stream.c create mode 100644 gfx/cairo/cairo/src/cairo-paginated-private.h create mode 100644 gfx/cairo/cairo/src/cairo-paginated-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-paginated-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-path-bounds.c create mode 100644 gfx/cairo/cairo/src/cairo-path-fill.c create mode 100644 gfx/cairo/cairo/src/cairo-path-fixed-private.h create mode 100644 gfx/cairo/cairo/src/cairo-path-fixed.c create mode 100644 gfx/cairo/cairo/src/cairo-path-in-fill.c create mode 100644 gfx/cairo/cairo/src/cairo-path-private.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-path-stroke.c create mode 100644 gfx/cairo/cairo/src/cairo-path.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-pattern.c create mode 100644 gfx/cairo/cairo/src/cairo-pdf-interchange.c create mode 100644 gfx/cairo/cairo/src/cairo-pdf-operators-private.h create mode 100644 gfx/cairo/cairo/src/cairo-pdf-operators.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-pdf-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-pdf-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-pdf.h create mode 100644 gfx/cairo/cairo/src/cairo-pen.c create mode 100644 gfx/cairo/cairo/src/cairo-pixman-private.h create mode 100644 gfx/cairo/cairo/src/cairo-platform.h create mode 100644 gfx/cairo/cairo/src/cairo-png.c 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-polygon.c create mode 100644 gfx/cairo/cairo/src/cairo-private.h create mode 100644 gfx/cairo/cairo/src/cairo-ps-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-ps-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-ps.h create mode 100644 gfx/cairo/cairo/src/cairo-qt-surface.cpp create mode 100644 gfx/cairo/cairo/src/cairo-qt.h create mode 100644 gfx/cairo/cairo/src/cairo-quartz-font.c create mode 100644 gfx/cairo/cairo/src/cairo-quartz-image-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-quartz-image.h create mode 100644 gfx/cairo/cairo/src/cairo-quartz-private.h create mode 100644 gfx/cairo/cairo/src/cairo-quartz-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-quartz.h create mode 100644 gfx/cairo/cairo/src/cairo-raster-source-pattern.c create mode 100644 gfx/cairo/cairo/src/cairo-recording-surface-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-recording-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-recording-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-rectangle.c create mode 100644 gfx/cairo/cairo/src/cairo-rectangular-scan-converter.c create mode 100644 gfx/cairo/cairo/src/cairo-reference-count-private.h create mode 100644 gfx/cairo/cairo/src/cairo-region-private.h create mode 100644 gfx/cairo/cairo/src/cairo-region.c create mode 100644 gfx/cairo/cairo/src/cairo-rename.h create mode 100644 gfx/cairo/cairo/src/cairo-rtree-private.h create mode 100644 gfx/cairo/cairo/src/cairo-rtree.c create mode 100644 gfx/cairo/cairo/src/cairo-scaled-font-private.h create mode 100644 gfx/cairo/cairo/src/cairo-scaled-font-subsets-private.h create mode 100644 gfx/cairo/cairo/src/cairo-scaled-font-subsets.c create mode 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-script-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-script.h create mode 100644 gfx/cairo/cairo/src/cairo-shape-mask-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-slope-private.h create mode 100644 gfx/cairo/cairo/src/cairo-slope.c create mode 100644 gfx/cairo/cairo/src/cairo-spans-compositor-private.h create mode 100644 gfx/cairo/cairo/src/cairo-spans-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-spans-private.h create mode 100644 gfx/cairo/cairo/src/cairo-spans.c create mode 100644 gfx/cairo/cairo/src/cairo-spline.c create mode 100644 gfx/cairo/cairo/src/cairo-stroke-dash-private.h create mode 100644 gfx/cairo/cairo/src/cairo-stroke-dash.c create mode 100644 gfx/cairo/cairo/src/cairo-stroke-style.c create mode 100644 gfx/cairo/cairo/src/cairo-surface-backend-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-clipper-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-clipper.c create mode 100644 gfx/cairo/cairo/src/cairo-surface-fallback-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-fallback.c create mode 100644 gfx/cairo/cairo/src/cairo-surface-inline.h 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-offset-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-offset.c create mode 100644 gfx/cairo/cairo/src/cairo-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-snapshot-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-snapshot-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-snapshot.c create mode 100644 gfx/cairo/cairo/src/cairo-surface-subsurface-inline.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-subsurface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-subsurface.c create mode 100644 gfx/cairo/cairo/src/cairo-surface-wrapper-private.h create mode 100644 gfx/cairo/cairo/src/cairo-surface-wrapper.c create mode 100644 gfx/cairo/cairo/src/cairo-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-svg-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-svg-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-svg.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-tee-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-tee-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-tee.h 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-tor-scan-converter.c create mode 100644 gfx/cairo/cairo/src/cairo-tor22-scan-converter.c create mode 100644 gfx/cairo/cairo/src/cairo-toy-font-face.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-traps.c 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-truetype-subset-private.h create mode 100644 gfx/cairo/cairo/src/cairo-truetype-subset.c create mode 100644 gfx/cairo/cairo/src/cairo-type1-fallback.c create mode 100644 gfx/cairo/cairo/src/cairo-type1-glyph-names.c create mode 100644 gfx/cairo/cairo/src/cairo-type1-private.h create mode 100644 gfx/cairo/cairo/src/cairo-type1-subset.c create mode 100644 gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-type3-glyph-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-types-private.h create mode 100644 gfx/cairo/cairo/src/cairo-unicode.c create mode 100644 gfx/cairo/cairo/src/cairo-uninstalled.pc.in create mode 100644 gfx/cairo/cairo/src/cairo-user-font-private.h create mode 100644 gfx/cairo/cairo/src/cairo-user-font.c create mode 100644 gfx/cairo/cairo/src/cairo-version.c create mode 100644 gfx/cairo/cairo/src/cairo-version.h create mode 100644 gfx/cairo/cairo/src/cairo-vg-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-vg.h create mode 100644 gfx/cairo/cairo/src/cairo-wgl-context.c create mode 100644 gfx/cairo/cairo/src/cairo-wideint-private.h create mode 100644 gfx/cairo/cairo/src/cairo-wideint-type-private.h create mode 100644 gfx/cairo/cairo/src/cairo-wideint.c create mode 100644 gfx/cairo/cairo/src/cairo-win32.h 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-xcb-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-xcb.h create mode 100644 gfx/cairo/cairo/src/cairo-xlib-core-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-display.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-fallback-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-private.h create mode 100644 gfx/cairo/cairo/src/cairo-xlib-render-compositor.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-screen.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-source.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-surface-private.h create mode 100644 gfx/cairo/cairo/src/cairo-xlib-surface-shm.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-visual.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-xcb-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-xlib-xrender-private.h create mode 100644 gfx/cairo/cairo/src/cairo-xlib-xrender.h create mode 100644 gfx/cairo/cairo/src/cairo-xlib.h create mode 100644 gfx/cairo/cairo/src/cairo-xml-surface.c create mode 100644 gfx/cairo/cairo/src/cairo-xml.h create mode 100644 gfx/cairo/cairo/src/cairo.c create mode 100644 gfx/cairo/cairo/src/cairo.h create mode 100644 gfx/cairo/cairo/src/cairo.pc.in create mode 100644 gfx/cairo/cairo/src/cairoint.h 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 100644 gfx/cairo/cairo/src/check-has-hidden-symbols.c create mode 100755 gfx/cairo/cairo/src/check-headers.sh create mode 100644 gfx/cairo/cairo/src/check-link.c 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 create mode 100644 gfx/cairo/cairo/src/drm/cairo-drm-intel-ioctl-private.h 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 create mode 100644 gfx/cairo/cairo/src/meson.build create mode 100755 gfx/cairo/cairo/src/moz.build create mode 100644 gfx/cairo/cairo/src/pixman-rename.h create mode 100644 gfx/cairo/cairo/src/test-base-compositor-surface.c create mode 100644 gfx/cairo/cairo/src/test-compositor-surface-private.h create mode 100644 gfx/cairo/cairo/src/test-compositor-surface.c create mode 100644 gfx/cairo/cairo/src/test-compositor-surface.h create mode 100644 gfx/cairo/cairo/src/test-null-compositor-surface.c create mode 100644 gfx/cairo/cairo/src/test-null-compositor-surface.h create mode 100644 gfx/cairo/cairo/src/test-paginated-surface.c create mode 100644 gfx/cairo/cairo/src/test-paginated-surface.h create mode 100644 gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp create mode 100644 gfx/cairo/cairo/src/win32/cairo-dwrite-private.h 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 create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-font.c create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-gdi-compositor.c create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-private.h create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-refptr.h create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-surface.c create mode 100644 gfx/cairo/cairo/src/win32/cairo-win32-system.c create mode 100644 gfx/cairo/glitz/src/glitz.def create mode 100644 gfx/cairo/glitz/src/wgl/glitz-wgl.def create mode 100644 gfx/cairo/libpixman/AUTHORS create mode 100644 gfx/cairo/libpixman/COPYING create mode 100644 gfx/cairo/libpixman/INSTALL create mode 100644 gfx/cairo/libpixman/README create mode 100644 gfx/cairo/libpixman/src/dither/blue-noise-64x64.h create mode 100644 gfx/cairo/libpixman/src/dither/make-blue-noise.c create mode 100644 gfx/cairo/libpixman/src/loongson-mmintrin.h create mode 100644 gfx/cairo/libpixman/src/meson.build create mode 100644 gfx/cairo/libpixman/src/moz.build create mode 100644 gfx/cairo/libpixman/src/pixman-access-accessors.c create mode 100644 gfx/cairo/libpixman/src/pixman-access.c create mode 100644 gfx/cairo/libpixman/src/pixman-accessor.h create mode 100644 gfx/cairo/libpixman/src/pixman-arm-asm.h create mode 100644 gfx/cairo/libpixman/src/pixman-arm-common.h create mode 100644 gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S create mode 100644 gfx/cairo/libpixman/src/pixman-arm-neon-asm.S create mode 100644 gfx/cairo/libpixman/src/pixman-arm-neon-asm.h create mode 100644 gfx/cairo/libpixman/src/pixman-arm-neon.c create mode 100644 gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S create mode 100644 gfx/cairo/libpixman/src/pixman-arm-simd-asm.S create mode 100644 gfx/cairo/libpixman/src/pixman-arm-simd-asm.h create mode 100644 gfx/cairo/libpixman/src/pixman-arm-simd.c create mode 100644 gfx/cairo/libpixman/src/pixman-arm.c create mode 100644 gfx/cairo/libpixman/src/pixman-arma64-neon-asm-bilinear.S create mode 100644 gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S create mode 100644 gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h create mode 100644 gfx/cairo/libpixman/src/pixman-bits-image.c create mode 100644 gfx/cairo/libpixman/src/pixman-combine-float.c create mode 100644 gfx/cairo/libpixman/src/pixman-combine32.c create mode 100644 gfx/cairo/libpixman/src/pixman-combine32.h create mode 100644 gfx/cairo/libpixman/src/pixman-compiler.h create mode 100644 gfx/cairo/libpixman/src/pixman-conical-gradient.c create mode 100644 gfx/cairo/libpixman/src/pixman-edge-accessors.c create mode 100644 gfx/cairo/libpixman/src/pixman-edge-imp.h create mode 100644 gfx/cairo/libpixman/src/pixman-edge.c create mode 100644 gfx/cairo/libpixman/src/pixman-fast-path.c create mode 100644 gfx/cairo/libpixman/src/pixman-filter.c create mode 100644 gfx/cairo/libpixman/src/pixman-general.c create mode 100644 gfx/cairo/libpixman/src/pixman-glyph.c create mode 100644 gfx/cairo/libpixman/src/pixman-gradient-walker.c create mode 100644 gfx/cairo/libpixman/src/pixman-image.c create mode 100644 gfx/cairo/libpixman/src/pixman-implementation.c create mode 100644 gfx/cairo/libpixman/src/pixman-inlines.h create mode 100644 gfx/cairo/libpixman/src/pixman-linear-gradient.c create mode 100644 gfx/cairo/libpixman/src/pixman-matrix.c create mode 100644 gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.S create mode 100644 gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.h create mode 100644 gfx/cairo/libpixman/src/pixman-mips-dspr2.c create mode 100644 gfx/cairo/libpixman/src/pixman-mips-dspr2.h create mode 100644 gfx/cairo/libpixman/src/pixman-mips-memcpy-asm.S create mode 100644 gfx/cairo/libpixman/src/pixman-mips.c create mode 100644 gfx/cairo/libpixman/src/pixman-mmx.c create mode 100644 gfx/cairo/libpixman/src/pixman-noop.c create mode 100644 gfx/cairo/libpixman/src/pixman-ppc.c create mode 100644 gfx/cairo/libpixman/src/pixman-private.h create mode 100644 gfx/cairo/libpixman/src/pixman-radial-gradient.c create mode 100644 gfx/cairo/libpixman/src/pixman-region.c create mode 100644 gfx/cairo/libpixman/src/pixman-region16.c create mode 100644 gfx/cairo/libpixman/src/pixman-region32.c create mode 100644 gfx/cairo/libpixman/src/pixman-solid-fill.c create mode 100644 gfx/cairo/libpixman/src/pixman-sse2.c create mode 100644 gfx/cairo/libpixman/src/pixman-ssse3.c create mode 100644 gfx/cairo/libpixman/src/pixman-timer.c create mode 100644 gfx/cairo/libpixman/src/pixman-trap.c create mode 100644 gfx/cairo/libpixman/src/pixman-utils.c create mode 100644 gfx/cairo/libpixman/src/pixman-version.h create mode 100644 gfx/cairo/libpixman/src/pixman-version.h.in create mode 100644 gfx/cairo/libpixman/src/pixman-vmx.c create mode 100644 gfx/cairo/libpixman/src/pixman-x86.c create mode 100644 gfx/cairo/libpixman/src/pixman.c create mode 100644 gfx/cairo/libpixman/src/pixman.h create mode 100644 gfx/cairo/libpixman/src/solaris-hwcap.mapfile create mode 100644 gfx/cairo/moz.build create mode 100644 gfx/cairo/pixman-arm32-clang.patch create mode 100644 gfx/cairo/pixman-arm64-clang.patch create mode 100644 gfx/cairo/pixman-clangcl.patch create mode 100644 gfx/cairo/pixman-export.patch create mode 100644 gfx/cairo/pixman-interp.patch create mode 100644 gfx/cairo/pixman-intrin.patch create mode 100644 gfx/cairo/pixman-mingw32.patch create mode 100644 gfx/cairo/pixman-rename.patch (limited to 'gfx/cairo') diff --git a/gfx/cairo/01-xlib-xrender-private.patch b/gfx/cairo/01-xlib-xrender-private.patch new file mode 100644 index 0000000000..ed5aa68987 --- /dev/null +++ b/gfx/cairo/01-xlib-xrender-private.patch @@ -0,0 +1,22 @@ +diff --git a/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h b/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h +--- a/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h ++++ b/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h +@@ -96,6 +96,10 @@ + #define PictOpBlendMaximum 0x3e + #endif + ++/* The mozilla build doesn't set up all the following HAVE_* symbols, ++ so we cheat by just checking the version number for now. */ ++#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 ++ + #if !HAVE_XRENDERCREATESOLIDFILL + #define XRenderCreateSolidFill _int_consume + #endif +@@ -132,6 +136,7 @@ typedef struct _XConicalGradient { + } XConicalGradient; + #endif + ++#endif /* RENDER_MAJOR == 0 && RENDER_MINOR < 10 */ + + #else /* !CAIRO_HAS_XLIB_XRENDER_SURFACE */ + diff --git a/gfx/cairo/02-cplusplus-no-register.patch b/gfx/cairo/02-cplusplus-no-register.patch new file mode 100644 index 0000000000..30de2a2dc6 --- /dev/null +++ b/gfx/cairo/02-cplusplus-no-register.patch @@ -0,0 +1,15 @@ +diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h +--- a/gfx/cairo/cairo/src/cairoint.h ++++ b/gfx/cairo/cairo/src/cairoint.h +@@ -190,7 +190,11 @@ static inline int cairo_const + #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + return __builtin_popcount (mask); + #else ++#ifdef __cplusplus ++ int y; ++#else + register int y; ++#endif + + y = (mask >> 1) &033333333333; + y = mask - y - ((y >>1) & 033333333333); diff --git a/gfx/cairo/03-expose-lcd-filter.patch b/gfx/cairo/03-expose-lcd-filter.patch new file mode 100644 index 0000000000..30c4a7817b --- /dev/null +++ b/gfx/cairo/03-expose-lcd-filter.patch @@ -0,0 +1,164 @@ +diff --git a/gfx/cairo/cairo/src/cairo-font-options.c b/gfx/cairo/cairo/src/cairo-font-options.c +--- a/gfx/cairo/cairo/src/cairo-font-options.c ++++ b/gfx/cairo/cairo/src/cairo-font-options.c +@@ -412,7 +412,7 @@ cairo_font_options_get_subpixel_order (c + } + + /** +- * _cairo_font_options_set_lcd_filter: ++ * cairo_font_options_set_lcd_filter: + * @options: a #cairo_font_options_t + * @lcd_filter: the new LCD filter + * +@@ -422,8 +422,8 @@ cairo_font_options_get_subpixel_order (c + * #cairo_lcd_filter_t for full details. + **/ + void +-_cairo_font_options_set_lcd_filter (cairo_font_options_t *options, +- cairo_lcd_filter_t lcd_filter) ++cairo_font_options_set_lcd_filter (cairo_font_options_t *options, ++ cairo_lcd_filter_t lcd_filter) + { + if (cairo_font_options_status (options)) + return; +@@ -432,7 +432,7 @@ void + } + + /** +- * _cairo_font_options_get_lcd_filter: ++ * cairo_font_options_get_lcd_filter: + * @options: a #cairo_font_options_t + * + * Gets the LCD filter for the font options object. +@@ -441,7 +441,7 @@ void + * Return value: the LCD filter for the font options object + **/ + cairo_lcd_filter_t +-_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options) ++cairo_font_options_get_lcd_filter (const cairo_font_options_t *options) + { + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_LCD_FILTER_DEFAULT; +diff --git a/gfx/cairo/cairo/src/cairo-types-private.h b/gfx/cairo/cairo/src/cairo-types-private.h +--- a/gfx/cairo/cairo/src/cairo-types-private.h ++++ b/gfx/cairo/cairo/src/cairo-types-private.h +@@ -157,30 +157,6 @@ struct _cairo_array { + 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, +diff --git a/gfx/cairo/cairo/src/cairo-xcb-screen.c b/gfx/cairo/cairo/src/cairo-xcb-screen.c +--- a/gfx/cairo/cairo/src/cairo-xcb-screen.c ++++ b/gfx/cairo/cairo/src/cairo-xcb-screen.c +@@ -121,7 +121,7 @@ static void + 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_lcd_filter (&screen->font_options, lcd_filter); + cairo_font_options_set_hint_metrics (&screen->font_options, CAIRO_HINT_METRICS_ON); + } + +diff --git a/gfx/cairo/cairo/src/cairo-xlib-screen.c b/gfx/cairo/cairo/src/cairo-xlib-screen.c +--- a/gfx/cairo/cairo/src/cairo-xlib-screen.c ++++ b/gfx/cairo/cairo/src/cairo-xlib-screen.c +@@ -265,7 +265,7 @@ static void + 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); + } + +diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h +--- a/gfx/cairo/cairo/src/cairo.h ++++ b/gfx/cairo/cairo/src/cairo.h +@@ -1365,6 +1365,30 @@ typedef enum _cairo_hint_metrics { + } cairo_hint_metrics_t; + + /** ++ * _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; ++ ++/** + * cairo_font_options_t: + * + * An opaque structure holding all options that are used when +@@ -1470,6 +1494,13 @@ cairo_get_font_options (cairo_t + 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_public cairo_lcd_filter_t ++cairo_font_options_get_lcd_filter (const cairo_font_options_t *options); ++ ++cairo_public void + cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face); + + cairo_public cairo_font_face_t * +diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h +--- a/gfx/cairo/cairo/src/cairoint.h ++++ b/gfx/cairo/cairo/src/cairoint.h +@@ -891,13 +891,6 @@ 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); +- +-cairo_private void + _cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, + cairo_round_glyph_positions_t round); + diff --git a/gfx/cairo/04-subpixel-aa-api.patch b/gfx/cairo/04-subpixel-aa-api.patch new file mode 100644 index 0000000000..c4763638eb --- /dev/null +++ b/gfx/cairo/04-subpixel-aa-api.patch @@ -0,0 +1,234 @@ +diff --git a/gfx/cairo/cairo/src/cairo-compositor-private.h b/gfx/cairo/cairo/src/cairo-compositor-private.h +--- a/gfx/cairo/cairo/src/cairo-compositor-private.h ++++ b/gfx/cairo/cairo/src/cairo-compositor-private.h +@@ -85,7 +85,8 @@ struct cairo_compositor { + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, +- cairo_bool_t overlap); ++ cairo_bool_t overlap, ++ cairo_bool_t permit_subpixel_antialiasing); + }; + + struct cairo_mask_compositor { +diff --git a/gfx/cairo/cairo/src/cairo-compositor.c b/gfx/cairo/cairo/src/cairo-compositor.c +--- a/gfx/cairo/cairo/src/cairo-compositor.c ++++ b/gfx/cairo/cairo/src/cairo-compositor.c +@@ -248,7 +248,8 @@ cairo_int_status_t + compositor = compositor->delegate; + + status = compositor->glyphs (compositor, &extents, +- scaled_font, glyphs, num_glyphs, overlap); ++ scaled_font, glyphs, num_glyphs, overlap, ++ surface->permit_subpixel_antialiasing); + + compositor = compositor->delegate; + } while (status == CAIRO_INT_STATUS_UNSUPPORTED); +diff --git a/gfx/cairo/cairo/src/cairo-no-compositor.c b/gfx/cairo/cairo/src/cairo-no-compositor.c +--- a/gfx/cairo/cairo/src/cairo-no-compositor.c ++++ b/gfx/cairo/cairo/src/cairo-no-compositor.c +@@ -91,7 +91,8 @@ static cairo_int_status_t + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, +- cairo_bool_t overlap) ++ cairo_bool_t overlap, ++ cairo_bool_t permit_subpixel_antialiasing) + { + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_NOTHING_TO_DO; +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -1947,7 +1947,8 @@ static cairo_int_status_t + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, +- cairo_bool_t overlap) ++ cairo_bool_t overlap, ++ cairo_bool_t permit_subpixel_antialiasing) + { + CGAffineTransform textTransform, invTextTransform; + CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; +@@ -1963,6 +1964,7 @@ static cairo_int_status_t + CGFontRef cgfref = NULL; + + cairo_bool_t didForceFontSmoothing = FALSE; ++ cairo_antialias_t effective_antialiasing; + + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) + return CAIRO_INT_STATUS_UNSUPPORTED; +@@ -1983,7 +1985,12 @@ static cairo_int_status_t + CGContextSetFont (state.cgMaskContext, cgfref); + CGContextSetFontSize (state.cgMaskContext, 1.0); + +- switch (scaled_font->options.antialias) { ++ effective_antialiasing = scaled_font->options.antialias; ++ if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL && ++ !permit_subpixel_antialiasing) { ++ effective_antialiasing = CAIRO_ANTIALIAS_GRAY; ++ } ++ switch (effective_antialiasing) { + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: + CGContextSetShouldAntialias (state.cgMaskContext, TRUE); +diff --git a/gfx/cairo/cairo/src/cairo-surface-private.h b/gfx/cairo/cairo/src/cairo-surface-private.h +--- a/gfx/cairo/cairo/src/cairo-surface-private.h ++++ b/gfx/cairo/cairo/src/cairo-surface-private.h +@@ -71,6 +71,7 @@ struct _cairo_surface { + unsigned has_font_options : 1; + unsigned owns_device : 1; + unsigned is_vector : 1; ++ unsigned permit_subpixel_antialiasing : 1; + + cairo_user_data_array_t user_data; + cairo_user_data_array_t mime_data; +diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c +--- a/gfx/cairo/cairo/src/cairo-surface.c ++++ b/gfx/cairo/cairo/src/cairo-surface.c +@@ -115,6 +115,7 @@ const cairo_surface_t name = { \ + FALSE, /* has_font_options */ \ + FALSE, /* owns_device */ \ + FALSE, /* is_vector */ \ ++ FALSE, /* permit_subpixel_antialiasing */ \ + { 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 */ \ +@@ -421,6 +422,7 @@ void + surface->serial = 0; + surface->damage = NULL; + surface->owns_device = (device != NULL); ++ surface->permit_subpixel_antialiasing = TRUE; + + _cairo_user_data_array_init (&surface->user_data); + _cairo_user_data_array_init (&surface->mime_data); +@@ -452,6 +454,8 @@ static void + _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); +@@ -1619,6 +1623,51 @@ cairo_surface_get_font_options (cairo_su + } + slim_hidden_def (cairo_surface_get_font_options); + ++/** ++ * 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. ++ **/ ++void ++cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, ++ cairo_subpixel_antialiasing_t enabled) ++{ ++ if (surface->status) ++ return; ++ ++ if (surface->finished) { ++ _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); ++ return; ++ } ++ ++ surface->permit_subpixel_antialiasing = ++ enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; ++} ++ ++/** ++ * 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. ++ **/ ++cairo_subpixel_antialiasing_t ++cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface) ++{ ++ if (surface->status) ++ return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; ++ ++ return surface->permit_subpixel_antialiasing ? ++ CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; ++} ++ + cairo_status_t + _cairo_surface_flush (cairo_surface_t *surface, unsigned flags) + { +diff --git a/gfx/cairo/cairo/src/cairo-xcb-private.h b/gfx/cairo/cairo/src/cairo-xcb-private.h +--- a/gfx/cairo/cairo/src/cairo-xcb-private.h ++++ b/gfx/cairo/cairo/src/cairo-xcb-private.h +@@ -453,7 +453,8 @@ cairo_private cairo_int_status_t + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, +- cairo_bool_t overlap); ++ cairo_bool_t overlap, ++ cairo_bool_t permit_subpixel_antialiasing); + cairo_private void + _cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +diff --git a/gfx/cairo/cairo/src/cairo-xcb-surface-render.c b/gfx/cairo/cairo/src/cairo-xcb-surface-render.c +--- a/gfx/cairo/cairo/src/cairo-xcb-surface-render.c ++++ b/gfx/cairo/cairo/src/cairo-xcb-surface-render.c +@@ -4816,7 +4816,8 @@ cairo_int_status_t + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, +- cairo_bool_t overlap) ++ cairo_bool_t overlap, ++ cairo_bool_t permit_subpixel_antialiasing) + { + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface; + cairo_operator_t op = composite->op; +diff --git a/gfx/cairo/cairo/src/cairo-xcb-surface.c b/gfx/cairo/cairo/src/cairo-xcb-surface.c +--- a/gfx/cairo/cairo/src/cairo-xcb-surface.c ++++ b/gfx/cairo/cairo/src/cairo-xcb-surface.c +@@ -912,7 +912,8 @@ static cairo_int_status_t + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, +- cairo_bool_t overlap) ++ cairo_bool_t overlap, ++ cairo_bool_t permit_subpixel_antialiasing) + { + cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface; + cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents); +diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h +--- a/gfx/cairo/cairo/src/cairo.h ++++ b/gfx/cairo/cairo/src/cairo.h +@@ -2573,6 +2573,26 @@ cairo_surface_show_page (cairo_surface_t + 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_public cairo_surface_t * diff --git a/gfx/cairo/05-ft-font-synth-flags-api.patch b/gfx/cairo/05-ft-font-synth-flags-api.patch new file mode 100644 index 0000000000..a234872034 --- /dev/null +++ b/gfx/cairo/05-ft-font-synth-flags-api.patch @@ -0,0 +1,35 @@ +diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-font.c +--- a/gfx/cairo/cairo/src/cairo-ft-font.c ++++ b/gfx/cairo/cairo/src/cairo-ft-font.c +@@ -3736,7 +3736,8 @@ cairo_ft_font_face_create_for_pattern (F + **/ + cairo_font_face_t * + cairo_ft_font_face_create_for_ft_face (FT_Face face, +- int load_flags) ++ int load_flags, ++ unsigned int synth_flags) + { + cairo_ft_unscaled_font_t *unscaled; + cairo_font_face_t *font_face; +@@ -3748,7 +3749,7 @@ cairo_ft_font_face_create_for_ft_face (F + return (cairo_font_face_t *)&_cairo_font_face_nil; + + ft_options.load_flags = load_flags; +- ft_options.synth_flags = 0; ++ ft_options.synth_flags = synth_flags; + _cairo_font_options_init_default (&ft_options.base); + + font_face = _cairo_ft_font_face_create (unscaled, &ft_options); +diff --git a/gfx/cairo/cairo/src/cairo-ft.h b/gfx/cairo/cairo/src/cairo-ft.h +--- a/gfx/cairo/cairo/src/cairo-ft.h ++++ b/gfx/cairo/cairo/src/cairo-ft.h +@@ -54,7 +54,8 @@ CAIRO_BEGIN_DECLS + + cairo_public cairo_font_face_t * + cairo_ft_font_face_create_for_ft_face (FT_Face face, +- int load_flags); ++ int load_flags, ++ unsigned int synth_flags); + + /** + * cairo_ft_synthesize_t: diff --git a/gfx/cairo/06-shared-ft-face.patch b/gfx/cairo/06-shared-ft-face.patch new file mode 100644 index 0000000000..f98714db18 --- /dev/null +++ b/gfx/cairo/06-shared-ft-face.patch @@ -0,0 +1,384 @@ +diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-font.c +--- a/gfx/cairo/cairo/src/cairo-ft-font.c ++++ b/gfx/cairo/cairo/src/cairo-ft-font.c +@@ -101,6 +101,24 @@ + */ + #define MAX_OPEN_FACES 10 + ++extern void mozilla_AddRefSharedFTFace(void* aContext); ++extern void mozilla_ReleaseSharedFTFace(void* aContext, void* aOwner); ++/* Returns true if the face's state has been modified 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 aLibrary); ++extern void mozilla_UnlockFTLibrary(FT_Library aLibrary); ++ ++#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 + * @Title: FreeType Fonts +@@ -154,6 +172,7 @@ 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; +@@ -336,7 +355,9 @@ static void + _cairo_hash_table_remove (font_map->hash_table, + &unscaled->base.hash_entry); + +- if (! unscaled->from_face) ++ if (unscaled->from_face) ++ mozilla_ReleaseSharedFTFace (unscaled->face_context, unscaled); ++ else + _font_map_release_face_lock_held (font_map, unscaled); + + _cairo_ft_unscaled_font_fini (unscaled); +@@ -395,7 +416,8 @@ static void + cairo_bool_t from_face, + char *filename, + int id, +- FT_Face face) ++ FT_Face face, ++ void *face_context) + { + unsigned long hash; + +@@ -403,6 +425,7 @@ static void + 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 */ +@@ -438,7 +461,8 @@ static cairo_status_t + cairo_bool_t from_face, + const char *filename, + int id, +- FT_Face face) ++ FT_Face face, ++ void *face_context) + { + _cairo_unscaled_font_init (&unscaled->base, + &cairo_ft_unscaled_font_backend); +@@ -447,7 +471,7 @@ static cairo_status_t + + if (from_face) { + unscaled->from_face = TRUE; +- _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, id, face); ++ _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, id, face, face_context); + + + unscaled->have_color = FT_HAS_COLOR (face) != 0; +@@ -474,12 +498,13 @@ static cairo_status_t + + 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); ++ _cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL, NULL); + + unscaled->have_color_set = FALSE; + } +@@ -528,7 +553,8 @@ static int + unscaled_a->from_face == unscaled_b->from_face) + { + if (unscaled_a->from_face) +- return unscaled_a->face == unscaled_b->face; ++ return unscaled_a->face == unscaled_b->face && ++ unscaled_a->face_context == unscaled_b->face_context; + + if (unscaled_a->filename == NULL && unscaled_b->filename == NULL) + return TRUE; +@@ -549,6 +575,7 @@ static cairo_status_t + char *filename, + int id, + FT_Face font_face, ++ void *face_context, + cairo_ft_unscaled_font_t **out) + { + cairo_ft_unscaled_font_t key, *unscaled; +@@ -559,7 +586,7 @@ static cairo_status_t + 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); ++ _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face, face_context); + + /* Return existing unscaled font if it exists in the hash table. */ + unscaled = _cairo_hash_table_lookup (font_map->hash_table, +@@ -576,7 +603,7 @@ static cairo_status_t + goto UNWIND_FONT_MAP_LOCK; + } + +- status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face); ++ status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face, face_context); + if (unlikely (status)) + goto UNWIND_UNSCALED_MALLOC; + +@@ -586,6 +613,8 @@ static cairo_status_t + if (unlikely (status)) + goto UNWIND_UNSCALED_FONT_INIT; + ++ mozilla_AddRefSharedFTFace (face_context); ++ + DONE: + _cairo_ft_unscaled_font_map_unlock (); + *out = unscaled; +@@ -638,16 +667,17 @@ static cairo_status_t + + DONE: + return _cairo_ft_unscaled_font_create_internal (font_face != NULL, +- filename, id, font_face, ++ filename, id, font_face, NULL, + 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, face->face_index, face, out); ++ return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, face->face_index, face, face_context, out); + } + + static cairo_bool_t +@@ -675,12 +705,16 @@ static cairo_bool_t + */ + if (unscaled->faces && unscaled->faces->unscaled == NULL) { + assert (unscaled->faces->next == NULL); ++ 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 (); + +@@ -709,7 +743,13 @@ static cairo_warn FT_Face + FT_Face face = NULL; + FT_Error error; + +- CAIRO_MUTEX_LOCK (unscaled->mutex); ++ if (unscaled->face_context) { ++ if (!mozilla_LockSharedFTFace (unscaled->face_context, unscaled)) { ++ unscaled->have_scale = FALSE; ++ } ++ } else { ++ CAIRO_FT_LOCK (unscaled); ++ } + unscaled->lock_count++; + + if (unscaled->face) +@@ -744,7 +784,7 @@ static cairo_warn FT_Face + if (error) + { + unscaled->lock_count--; +- CAIRO_MUTEX_UNLOCK (unscaled->mutex); ++ CAIRO_FT_UNLOCK (unscaled); + _cairo_error_throw (_ft_to_cairo_error (error)); + return NULL; + } +@@ -769,7 +809,7 @@ static void + + unscaled->lock_count--; + +- CAIRO_MUTEX_UNLOCK (unscaled->mutex); ++ CAIRO_FT_UNLOCK (unscaled); + } + + +@@ -3164,19 +3204,21 @@ static cairo_bool_t + * 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_unscaled_font_destroy (&font_face->unscaled->base); +- font_face->unscaled = NULL; +- +- return FALSE; +- } +- + if (font_face->unscaled) { ++ CAIRO_FT_LOCK (font_face->unscaled); ++ ++ 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_FT_UNLOCK (font_face->unscaled); ++ _cairo_unscaled_font_destroy (&font_face->unscaled->base); ++ font_face->unscaled = NULL; ++ ++ return FALSE; ++ } ++ + cairo_ft_font_face_t *tmp_face = NULL; + cairo_ft_font_face_t *last_face = NULL; + +@@ -3195,6 +3237,7 @@ static cairo_bool_t + last_face = tmp_face; + } + ++ CAIRO_FT_UNLOCK (font_face->unscaled); + _cairo_unscaled_font_destroy (&font_face->unscaled->base); + font_face->unscaled = NULL; + } +@@ -3268,6 +3311,24 @@ static cairo_font_face_t * + 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 +@@ -3277,7 +3338,11 @@ const cairo_font_face_backend_t _cairo_f + #endif + _cairo_ft_font_face_destroy, + _cairo_ft_font_face_scaled_font_create, +- _cairo_ft_font_face_get_implementation ++ _cairo_ft_font_face_get_implementation, ++/* ++ _cairo_ft_font_face_lock, ++ _cairo_ft_font_face_unlock ++*/ + }; + + #if CAIRO_HAS_FC_FONT +@@ -3320,6 +3385,8 @@ static cairo_font_face_t * + { + 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; +@@ -3341,15 +3408,19 @@ static cairo_font_face_t * + * from owner to ownee. */ + font_face->unscaled = unscaled; + _cairo_unscaled_font_reference (&unscaled->base); +- return &font_face->base; +- } else +- return cairo_font_face_reference (&font_face->base); ++ } else { ++ cairo_font_face_reference (&font_face->base); ++ } ++ ++ CAIRO_FT_UNLOCK (unscaled); ++ return &font_face->base; + } + } + + /* No match found, create a new one */ + 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; + } +@@ -3376,6 +3447,7 @@ static cairo_font_face_t * + + _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); + ++ CAIRO_FT_UNLOCK (unscaled); + return &font_face->base; + } + +@@ -3737,14 +3809,16 @@ cairo_ft_font_face_create_for_pattern (F + cairo_font_face_t * + cairo_ft_font_face_create_for_ft_face (FT_Face face, + int load_flags, +- unsigned int synth_flags) ++ unsigned int synth_flags, ++ void *face_context) + { + 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, &unscaled); ++ status = _cairo_ft_unscaled_font_create_from_face (face, face_context, ++ &unscaled); + if (unlikely (status)) + return (cairo_font_face_t *)&_cairo_font_face_nil; + +@@ -3896,7 +3970,7 @@ cairo_ft_scaled_font_lock_face (cairo_sc + * opportunity for creating deadlock. This is obviously unsafe, + * but as documented, the user must add manual locking when using + * this function. */ +- CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex); ++ CAIRO_FT_UNLOCK (scaled_font->unscaled); + + return face; + } +@@ -3929,7 +4003,7 @@ cairo_ft_scaled_font_unlock_face (cairo_ + * 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_MUTEX_LOCK (scaled_font->unscaled->mutex); ++ CAIRO_FT_LOCK (scaled_font->unscaled); + + _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); + } +diff --git a/gfx/cairo/cairo/src/cairo-ft.h b/gfx/cairo/cairo/src/cairo-ft.h +--- a/gfx/cairo/cairo/src/cairo-ft.h ++++ b/gfx/cairo/cairo/src/cairo-ft.h +@@ -55,7 +55,8 @@ CAIRO_BEGIN_DECLS + cairo_public cairo_font_face_t * + cairo_ft_font_face_create_for_ft_face (FT_Face face, + int load_flags, +- unsigned int synth_flags); ++ unsigned int synth_flags, ++ void *face_context); + + /** + * cairo_ft_synthesize_t: diff --git a/gfx/cairo/07-ft-variations-runtime-check.patch b/gfx/cairo/07-ft-variations-runtime-check.patch new file mode 100644 index 0000000000..a7bb4e9ad0 --- /dev/null +++ b/gfx/cairo/07-ft-variations-runtime-check.patch @@ -0,0 +1,207 @@ +diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-font.c +--- a/gfx/cairo/cairo/src/cairo-ft-font.c ++++ b/gfx/cairo/cairo/src/cairo-ft-font.c +@@ -72,6 +72,7 @@ + #else + #define access(p, m) 0 + #endif ++#include + + /* Fontconfig version older than 2.6 didn't have these options */ + #ifndef FC_LCD_FILTER +@@ -120,6 +121,16 @@ extern void mozilla_UnlockFTLibrary(FT_L + : (void)CAIRO_MUTEX_UNLOCK((unscaled)->mutex)) + + /** ++ * Function types for FreeType symbols we'll look up at runtime, rather than ++ * relying on build-time checks for availability. ++ */ ++typedef FT_Error (*GetVarFunc) (FT_Face, FT_MM_Var**); ++typedef FT_Error (*DoneVarFunc) (FT_Library, FT_MM_Var*); ++typedef FT_Error (*GetVarDesignCoordsFunc) (FT_Face, FT_UInt, FT_Fixed*); ++typedef FT_Error (*SetVarDesignCoordsFunc) (FT_Face, FT_UInt, FT_Fixed*); ++typedef FT_Error (*GetVarBlendCoordsFunc) (FT_Face, FT_UInt, FT_Fixed*); ++ ++/** + * SECTION:cairo-ft + * @Title: FreeType Fonts + * @Short_Description: Font support for FreeType +@@ -477,22 +488,31 @@ static cairo_status_t + unscaled->have_color = FT_HAS_COLOR (face) != 0; + unscaled->have_color_set = TRUE; + +-#ifdef HAVE_FT_GET_VAR_DESIGN_COORDINATES +- { ++ static GetVarFunc getVar; ++ static DoneVarFunc doneVar; ++ static GetVarDesignCoordsFunc getVarDesignCoords; ++ ++ static int firstTime = 1; ++ if (firstTime) { ++ getVar = (GetVarFunc) dlsym (RTLD_DEFAULT, "FT_Get_MM_Var"); ++ doneVar = (DoneVarFunc) dlsym (RTLD_DEFAULT, "FT_Done_MM_Var"); ++ getVarDesignCoords = (GetVarDesignCoordsFunc) dlsym (RTLD_DEFAULT, "FT_Get_Var_Design_Coordinates"); ++ firstTime = 0; ++ } ++ ++ if (getVar && getVarDesignCoords) { + FT_MM_Var *ft_mm_var; +- if (0 == FT_Get_MM_Var (face, &ft_mm_var)) ++ if (0 == (*getVar) (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 ++ (*getVarDesignCoords) (face, ft_mm_var->num_axis, unscaled->variations); ++ if (doneVar) ++ (*doneVar) (face->glyph->library, ft_mm_var); ++ else ++ free (ft_mm_var); + } + } +-#endif + } else { + char *filename_copy; + +@@ -2366,7 +2386,24 @@ cairo_ft_apply_variations (FT_Face + FT_Error ret; + unsigned int instance_id = scaled_font->unscaled->id >> 16; + +- ret = FT_Get_MM_Var (face, &ft_mm_var); ++ static GetVarFunc getVar; ++ static DoneVarFunc doneVar; ++ static GetVarDesignCoordsFunc getVarDesignCoords; ++ static SetVarDesignCoordsFunc setVarDesignCoords; ++ ++ static int firstTime = 1; ++ if (firstTime) { ++ getVar = (GetVarFunc) dlsym (RTLD_DEFAULT, "FT_Get_MM_Var"); ++ doneVar = (DoneVarFunc) dlsym (RTLD_DEFAULT, "FT_Done_MM_Var"); ++ getVarDesignCoords = (GetVarDesignCoordsFunc) dlsym (RTLD_DEFAULT, "FT_Get_Var_Design_Coordinates"); ++ setVarDesignCoords = (SetVarDesignCoordsFunc) dlsym (RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates"); ++ firstTime = 0; ++ } ++ ++ if (!getVar || !setVarDesignCoords) ++ return; ++ ++ ret = (*getVar) (face, &ft_mm_var); + if (ret == 0) { + FT_Fixed *current_coords; + FT_Fixed *coords; +@@ -2431,27 +2468,28 @@ skip: + } + + 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 (getVarDesignCoords) { ++ ret = (*getVarDesignCoords) (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; + } +- if (i == ft_mm_var->num_axis) +- goto done; + } +-#endif +- +- FT_Set_Var_Design_Coordinates (face, ft_mm_var->num_axis, coords); ++ ++ (*setVarDesignCoords) (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 ++ ++ if (doneVar) ++ (*doneVar) (face->glyph->library, ft_mm_var); ++ else ++ free (ft_mm_var); + } + } + +@@ -2877,6 +2915,18 @@ static cairo_int_status_t + FT_Face face; + FT_Error error; + ++ static GetVarFunc getVar; ++ static DoneVarFunc doneVar; ++ static GetVarBlendCoordsFunc getVarBlendCoords; ++ ++ static int firstTime = 1; ++ if (firstTime) { ++ getVar = (GetVarFunc) dlsym (RTLD_DEFAULT, "FT_Get_MM_Var"); ++ doneVar = (DoneVarFunc) dlsym (RTLD_DEFAULT, "FT_Done_MM_Var"); ++ getVarBlendCoords = (GetVarBlendCoordsFunc) dlsym (RTLD_DEFAULT, "FT_Get_Var_Blend_Coordinates"); ++ firstTime = 0; ++ } ++ + if (scaled_font->ft_options.synth_flags != 0) { + *is_synthetic = TRUE; + return status; +@@ -2896,7 +2946,7 @@ static cairo_int_status_t + * are the same as the font tables */ + *is_synthetic = TRUE; + +- error = FT_Get_MM_Var (face, &mm_var); ++ error = getVar ? (*getVar) (face, &mm_var) : -1; + if (error) { + status = _cairo_error (_ft_to_cairo_error (error)); + goto cleanup; +@@ -2909,15 +2959,14 @@ static cairo_int_status_t + 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. + */ +- { ++ if (getVarBlendCoords) { + int i; + +- FT_Get_Var_Blend_Coordinates (face, num_axis, coords); ++ (*getVarBlendCoords) (face, num_axis, coords); + *is_synthetic = FALSE; + for (i = 0; i < num_axis; i++) { + if (coords[i]) { +@@ -2926,15 +2975,13 @@ static cairo_int_status_t + } + } + } +-#endif + + cleanup: + free (coords); +-#if HAVE_FT_DONE_MM_VAR +- FT_Done_MM_Var (face->glyph->library, mm_var); +-#else +- free (mm_var); +-#endif ++ if (doneVar) ++ (*doneVar) (face->glyph->library, mm_var); ++ else ++ free (mm_var); + } + + _cairo_ft_unscaled_font_unlock_face (unscaled); diff --git a/gfx/cairo/08-directwrite-additions.patch b/gfx/cairo/08-directwrite-additions.patch new file mode 100644 index 0000000000..b6521c4aec --- /dev/null +++ b/gfx/cairo/08-directwrite-additions.patch @@ -0,0 +1,718 @@ +diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h +--- a/gfx/cairo/cairo/src/cairo-win32.h ++++ b/gfx/cairo/cairo/src/cairo-win32.h +@@ -69,9 +69,15 @@ cairo_win32_surface_create_with_dib (cai + 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_get_size (const cairo_surface_t *surface, int *width, int *height); ++ + #if CAIRO_HAS_WIN32_FONT + + /* +@@ -105,8 +111,33 @@ cairo_public void + cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *device_to_logical); + ++cairo_public BYTE ++cairo_win32_get_system_text_quality (void); ++ + #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_set_force_GDI_classic (cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed); ++ ++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 */ ++ + CAIRO_END_DECLS + + #else /* CAIRO_HAS_WIN32_SURFACE */ +diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h +--- a/gfx/cairo/cairo/src/cairo.h ++++ b/gfx/cairo/cairo/src/cairo.h +@@ -1609,7 +1609,8 @@ typedef enum _cairo_font_type { + CAIRO_FONT_TYPE_FT, + CAIRO_FONT_TYPE_WIN32, + CAIRO_FONT_TYPE_QUARTZ, +- CAIRO_FONT_TYPE_USER ++ CAIRO_FONT_TYPE_USER, ++ CAIRO_FONT_TYPE_DWRITE + } cairo_font_type_t; + + cairo_public cairo_font_type_t +diff --git a/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp +--- a/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp ++++ b/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp +@@ -37,7 +37,9 @@ + #include "cairoint.h" + + #include "cairo-win32-private.h" ++#include "cairo-pattern-private.h" + #include "cairo-surface-private.h" ++#include "cairo-image-surface-private.h" + #include "cairo-clip-private.h" + #include "cairo-win32-refptr.h" + +@@ -136,7 +138,7 @@ ID2D1DCRenderTarget *D2DFactory::mRender + static cairo_status_t + _cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); +-static void ++static cairo_bool_t + _cairo_dwrite_font_face_destroy (void *font_face); + + static cairo_status_t +@@ -162,22 +164,6 @@ static cairo_warn cairo_int_status_t + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info); + +-cairo_warn cairo_int_status_t +-_cairo_dwrite_scaled_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); +- + cairo_int_status_t + _cairo_dwrite_load_truetype_table(void *scaled_font, + unsigned long tag, +@@ -193,33 +179,18 @@ const cairo_scaled_font_backend_t _cairo + CAIRO_FONT_TYPE_DWRITE, + _cairo_dwrite_scaled_font_fini, + _cairo_dwrite_scaled_glyph_init, +- NULL, ++ NULL, /* text_to_glyphs */ + _cairo_dwrite_ucs4_to_index, +- _cairo_dwrite_scaled_show_glyphs, + _cairo_dwrite_load_truetype_table, +- NULL, ++ NULL, /* index_to_ucs4 */ ++ NULL, /* is_synthetic */ ++ NULL, /* index_to_glyph_name */ ++ NULL, /* load_type1_data */ ++ NULL, /* has_color_glyphs */ + }; + + /* Helper conversion functions */ + +-/** +- * Get a D2D matrix from a cairo matrix. Note that D2D uses row vectors where cairo +- * uses column vectors. Hence the transposition. +- * +- * \param Cairo matrix +- * \return D2D matrix +- */ +-static D2D1::Matrix3x2F +-_cairo_d2d_matrix_from_matrix(const cairo_matrix_t *matrix) +-{ +- return D2D1::Matrix3x2F((FLOAT)matrix->xx, +- (FLOAT)matrix->yx, +- (FLOAT)matrix->xy, +- (FLOAT)matrix->yy, +- (FLOAT)matrix->x0, +- (FLOAT)matrix->y0); +-} +- + + /** + * Get a DirectWrite matrix from a cairo matrix. Note that DirectWrite uses row +@@ -316,7 +287,7 @@ static cairo_status_t + return CAIRO_STATUS_SUCCESS; + } + +-static void ++static cairo_bool_t + _cairo_dwrite_font_face_destroy (void *font_face) + { + cairo_dwrite_font_face_t *dwrite_font_face = static_cast(font_face); +@@ -324,6 +295,7 @@ static void + dwrite_font_face->dwriteface->Release(); + if (dwrite_font_face->font) + dwrite_font_face->font->Release(); ++ return TRUE; + } + + +@@ -507,7 +479,6 @@ static cairo_status_t + dwriteFont->antialias_mode = options->antialias; + } + +- dwriteFont->manual_show_glyphs_allowed = TRUE; + dwriteFont->rendering_mode = + default_quality == CAIRO_ANTIALIAS_SUBPIXEL ? + cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL : cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE; +@@ -562,162 +533,6 @@ unsigned long + return index; + } + +-cairo_warn cairo_int_status_t +-_cairo_dwrite_scaled_show_glyphs(void *scaled_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_surface_t *surface = (cairo_win32_surface_t *)generic_surface; +- cairo_int_status_t status; +- +- if (width == 0 || height == 0) +- return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; +- +- if (_cairo_surface_is_win32 (generic_surface) && +- surface->format == CAIRO_FORMAT_RGB24 && +- op == CAIRO_OPERATOR_OVER) { +- +- //XXX: we need to set the clip region here +- +- status = (cairo_int_status_t)_cairo_dwrite_show_glyphs_on_surface (surface, op, pattern, +- glyphs, num_glyphs, +- (cairo_scaled_font_t*)scaled_font, NULL); +- +- return status; +- } else { +- cairo_dwrite_scaled_font_t *dwritesf = +- static_cast(scaled_font); +- BOOL transform = FALSE; +- +- AutoDWriteGlyphRun run; +- run.allocate(num_glyphs); +- UINT16 *indices = const_cast(run.glyphIndices); +- FLOAT *advances = const_cast(run.glyphAdvances); +- DWRITE_GLYPH_OFFSET *offsets = const_cast(run.glyphOffsets); +- +- run.bidiLevel = 0; +- run.fontFace = ((cairo_dwrite_font_face_t*)dwritesf->base.font_face)->dwriteface; +- run.isSideways = FALSE; +- IDWriteGlyphRunAnalysis *analysis; +- +- if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 && +- dwritesf->mat.xx == dwritesf->base.font_matrix.xx && +- dwritesf->mat.yy == dwritesf->base.font_matrix.yy) { +- +- for (int i = 0; i < num_glyphs; i++) { +- indices[i] = (WORD) glyphs[i].index; +- // Since we will multiply by our ctm matrix later for rotation effects +- // and such, adjust positions by the inverse matrix now. +- offsets[i].ascenderOffset = (FLOAT)dest_y - (FLOAT)glyphs[i].y; +- offsets[i].advanceOffset = (FLOAT)glyphs[i].x - dest_x; +- advances[i] = 0.0; +- } +- run.fontEmSize = (FLOAT)dwritesf->base.font_matrix.yy; +- } else { +- transform = TRUE; +- +- for (int i = 0; i < num_glyphs; i++) { +- indices[i] = (WORD) glyphs[i].index; +- double x = glyphs[i].x - dest_x; +- double y = glyphs[i].y - dest_y; +- cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); +- // Since we will multiply by our ctm matrix later for rotation effects +- // and such, adjust positions by the inverse matrix now. +- offsets[i].ascenderOffset = -(FLOAT)y; +- offsets[i].advanceOffset = (FLOAT)x; +- advances[i] = 0.0; +- } +- run.fontEmSize = 1.0f; +- } +- +- HRESULT hr; +- if (!transform) { +- hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, +- 1.0f, +- NULL, +- DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, +- DWRITE_MEASURING_MODE_NATURAL, +- 0, +- 0, +- &analysis); +- } else { +- DWRITE_MATRIX dwmatrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); +- hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, +- 1.0f, +- &dwmatrix, +- DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, +- DWRITE_MEASURING_MODE_NATURAL, +- 0, +- 0, +- &analysis); +- } +- +- if (FAILED(hr) || !analysis) { +- return CAIRO_INT_STATUS_UNSUPPORTED; +- } +- +- RECT r; +- r.left = 0; +- r.top = 0; +- r.right = width; +- r.bottom = height; +- +- BYTE *surface = new BYTE[width * height * 3]; +- +- analysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &r, surface, width * height * 3); +- +- cairo_image_surface_t *mask_surface = +- (cairo_image_surface_t*)cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); +- +- cairo_surface_flush(&mask_surface->base); +- +- for (unsigned int y = 0; y < height; y++) { +- for (unsigned int x = 0; x < width; x++) { +- mask_surface->data[y * mask_surface->stride + x * 4] = surface[y * width * 3 + x * 3 + 1]; +- mask_surface->data[y * mask_surface->stride + x * 4 + 1] = surface[y * width * 3 + x * 3 + 1]; +- mask_surface->data[y * mask_surface->stride + x * 4 + 2] = surface[y * width * 3 + x * 3 + 1]; +- mask_surface->data[y * mask_surface->stride + x * 4 + 3] = surface[y * width * 3 + x * 3 + 1]; +- } +- } +- cairo_surface_mark_dirty(&mask_surface->base); +- +- pixman_image_set_component_alpha(mask_surface->pixman_image, 1); +- +- cairo_surface_pattern_t mask; +- _cairo_pattern_init_for_surface (&mask, &mask_surface->base); +- +- status = (cairo_int_status_t)_cairo_surface_composite (op, pattern, +- &mask.base, +- generic_surface, +- source_x, source_y, +- 0, 0, +- dest_x, dest_y, +- width, height, +- clip_region); +- +- _cairo_pattern_fini (&mask.base); +- +- analysis->Release(); +- delete [] surface; +- +- cairo_surface_destroy (&mask_surface->base); +- *remaining_glyphs = 0; +- +- return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; +- } +-} +- + /* cairo_dwrite_scaled_glyph_init helper function bodies */ + cairo_int_status_t + _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_font, +@@ -906,37 +721,40 @@ cairo_int_status_t + return CAIRO_INT_STATUS_SUCCESS; + } + +-/* Helper function also stolen from cairo-win32-font.c */ ++/* Helper function adapted from _compute_mask in cairo-win32-font.c */ + + /* Compute an alpha-mask from a monochrome RGB24 image + */ + static cairo_surface_t * +-_compute_a8_mask (cairo_win32_surface_t *mask_surface) ++_compute_a8_mask (cairo_surface_t *surface) + { +- cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; +- cairo_image_surface_t *image8; ++ cairo_image_surface_t *glyph; ++ cairo_image_surface_t *mask; + int i, j; + +- if (image24->base.status) +- return cairo_surface_reference (&image24->base); ++ glyph = (cairo_image_surface_t *)cairo_surface_map_to_image (surface, NULL); ++ if (unlikely (glyph->base.status)) ++ return &glyph->base; + +- image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, +- image24->width, image24->height); +- if (image8->base.status) +- return &image8->base; ++ /* No quality param, just use the non-ClearType path */ + +- 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); ++ /* Compute an alpha-mask by using the green channel of a (presumed monochrome) ++ * RGB24 image. ++ */ ++ mask = (cairo_image_surface_t *) ++ cairo_image_surface_create (CAIRO_FORMAT_A8, glyph->width, glyph->height); ++ if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { ++ for (i = 0; i < glyph->height; i++) { ++ uint32_t *p = (uint32_t *) (glyph->data + i * glyph->stride); ++ uint8_t *q = (uint8_t *) (mask->data + i * mask->stride); + +- for (j = 0; j < image24->width; j++) { +- *q = 255 - ((*p & 0x0000ff00) >> 8); +- p++; +- q++; +- } ++ for (j = 0; j < glyph->width; j++) ++ *q++ = 255 - ((*p++ & 0x0000ff00) >> 8); ++ } + } + +- return &image8->base; ++ cairo_surface_unmap_image (surface, &glyph->base); ++ return &mask->base; + } + + cairo_int_status_t +@@ -1017,7 +835,7 @@ cairo_int_status_t + + GdiFlush(); + +- image = _compute_a8_mask (surface); ++ image = _compute_a8_mask (&surface->base); + status = (cairo_int_status_t)image->status; + if (status) + goto FAIL; +@@ -1091,13 +909,6 @@ cairo_dwrite_font_face_create_for_dwrite + } + + void +-cairo_dwrite_scaled_font_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed) +-{ +- cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); +- font->manual_show_glyphs_allowed = allowed; +-} +- +-void + cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force) + { + cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); +@@ -1299,20 +1110,11 @@ cairo_int_status_t + + /* If we have a fallback mask clip set on the dst, we have + * to go through the fallback path */ +- if (clip != NULL) { +- if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { +- cairo_region_t *clip_region; +- cairo_int_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 (dst, clip_region); +- } +- } else { +- _cairo_win32_surface_set_clip_region (surface, NULL); ++ if (!_cairo_surface_is_win32_printing (&dst->base)) { ++ if (clip != NULL) ++ _cairo_win32_display_surface_set_clip (to_win32_display_surface (dst), clip); ++ else ++ _cairo_win32_display_surface_unset_clip (to_win32_display_surface (dst)); + } + + /* It is vital that dx values for dxy_buf are calculated from the delta of +diff --git a/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h b/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h +--- a/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h ++++ b/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h +@@ -50,7 +50,6 @@ struct _cairo_dwrite_scaled_font { + cairo_matrix_t mat_inverse; + cairo_antialias_t antialias_mode; + DWRITE_MEASURING_MODE measuring_mode; +- cairo_bool_t manual_show_glyphs_allowed; + enum TextRenderingState { + TEXT_RENDERING_UNINITIALIZED, + TEXT_RENDERING_NO_CLEARTYPE, +diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-font.c b/gfx/cairo/cairo/src/win32/cairo-win32-font.c +--- a/gfx/cairo/cairo/src/win32/cairo-win32-font.c ++++ b/gfx/cairo/cairo/src/win32/cairo-win32-font.c +@@ -286,8 +286,8 @@ static cairo_bool_t + version_info.dwMinorVersion >= 1)); /* XP or newer */ + } + +-static BYTE +-_get_system_quality (void) ++BYTE ++cairo_win32_get_system_text_quality (void) + { + BOOL font_smoothing; + UINT smoothing_type; +@@ -354,7 +354,7 @@ static cairo_status_t + * here is the hint_metrics options. + */ + if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) +- f->quality = _get_system_quality (); ++ f->quality = cairo_win32_get_system_text_quality (); + else { + switch (options->antialias) { + case CAIRO_ANTIALIAS_NONE: +diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c +--- a/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c ++++ b/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c +@@ -244,7 +244,7 @@ static cairo_status_t + case CAIRO_PATTERN_TYPE_MESH: + default: + ASSERT_NOT_REACHED; +- break; ++ return CAIRO_INT_STATUS_UNSUPPORTED; + } + + _cairo_pattern_init_for_surface (image_pattern, &image->base); +@@ -1808,6 +1808,7 @@ static cairo_int_status_t + cairo_solid_pattern_t clear; + cairo_composite_rectangles_t extents; + cairo_bool_t overlap; ++ cairo_scaled_font_t *local_scaled_font = NULL; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &surface->win32.base, +@@ -1851,6 +1852,13 @@ static cairo_int_status_t + } + #endif + ++#if CAIRO_HAS_DWRITE_FONT ++ if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { ++ status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); ++ goto cleanup_composite; ++ } ++#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 +@@ -1890,6 +1898,23 @@ static cairo_int_status_t + 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) +@@ -1948,6 +1973,10 @@ static cairo_int_status_t + + cleanup_composite: + _cairo_composite_rectangles_fini (&extents); ++ ++ if (local_scaled_font) ++ cairo_scaled_font_destroy (local_scaled_font); ++ + return status; + } + +@@ -2181,6 +2210,12 @@ cairo_win32_printing_surface_create (HDC + return paginated; + } + ++cairo_bool_t ++_cairo_surface_is_win32_printing (const cairo_surface_t *surface) ++{ ++ return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING; ++} ++ + static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { + CAIRO_SURFACE_TYPE_WIN32_PRINTING, + _cairo_win32_printing_surface_finish, +diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-private.h b/gfx/cairo/cairo/src/win32/cairo-win32-private.h +--- a/gfx/cairo/cairo/src/win32/cairo-win32-private.h ++++ b/gfx/cairo/cairo/src/win32/cairo-win32-private.h +@@ -53,6 +53,8 @@ + + #define WIN32_FONT_LOGICAL_SCALE 32 + ++CAIRO_BEGIN_DECLS ++ + /* Surface DC flag values */ + enum { + /* If this is a surface created for printing or not */ +@@ -199,6 +201,12 @@ const cairo_compositor_t * + cairo_status_t + _cairo_win32_print_gdi_error (const char *context); + ++cairo_bool_t ++_cairo_surface_is_win32 (const cairo_surface_t *surface); ++ ++cairo_bool_t ++_cairo_surface_is_win32_printing (const cairo_surface_t *surface); ++ + cairo_private void + _cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface); + +@@ -245,4 +253,23 @@ cairo_bool_t + 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/win32/cairo-win32-surface.c b/gfx/cairo/cairo/src/win32/cairo-win32-surface.c +--- a/gfx/cairo/cairo/src/win32/cairo-win32-surface.c ++++ b/gfx/cairo/cairo/src/win32/cairo-win32-surface.c +@@ -48,6 +48,7 @@ + + #include "cairoint.h" + ++#include "cairo-backend-private.h" + #include "cairo-default-context-private.h" + #include "cairo-error-private.h" + #include "cairo-image-surface-private.h" +@@ -170,6 +171,48 @@ cairo_win32_surface_get_dc (cairo_surfac + return NULL; + } + ++HDC ++cairo_win32_get_dc_with_clip (cairo_t *cr) ++{ ++ cairo_surface_t *surface = cairo_get_target (cr); ++ if (cr->backend->type == CAIRO_TYPE_DEFAULT) { ++ cairo_default_context_t *c = (cairo_default_context_t *) cr; ++ cairo_clip_t *clip = _cairo_clip_copy (_cairo_gstate_get_clip (c->gstate)); ++ if (_cairo_surface_is_win32 (surface)) { ++ cairo_win32_display_surface_t *winsurf = (cairo_win32_display_surface_t *) surface; ++ ++ _cairo_win32_display_surface_set_clip (winsurf, clip); ++ ++ _cairo_clip_destroy (clip); ++ return winsurf->win32.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_printing_surface_t *psurf = (cairo_win32_printing_surface_t *) target; ++ ++ status = _cairo_surface_clipper_set_clip (&psurf->clipper, clip); ++ ++ _cairo_clip_destroy (clip); ++ ++ if (status) ++ return NULL; ++ ++ return psurf->win32.dc; ++ } ++#endif ++ } ++ _cairo_clip_destroy (clip); ++ } ++ return NULL; ++} ++ + /** + * _cairo_surface_is_win32: + * @surface: a #cairo_surface_t +@@ -178,7 +221,7 @@ cairo_win32_surface_get_dc (cairo_surfac + * + * Return value: %TRUE if the surface is an win32 surface + **/ +-static inline cairo_bool_t ++cairo_bool_t + _cairo_surface_is_win32 (const cairo_surface_t *surface) + { + /* _cairo_surface_nil sets a NULL backend so be safe */ +@@ -219,6 +262,16 @@ cairo_int_status_t + cairo_scaled_font_t *scaled_font, + cairo_bool_t glyph_indexing) + { ++#if CAIRO_HAS_DWRITE_FONT ++ if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { ++ if (!glyph_indexing) return CAIRO_INT_STATUS_UNSUPPORTED; ++ ++ // FIXME: fake values for params that aren't currently passed in here ++ cairo_operator_t op = CAIRO_OPERATOR_SOURCE; ++ cairo_clip_t *clip = NULL; ++ return _cairo_dwrite_show_glyphs_on_surface (dst, op, source, glyphs, num_glyphs, scaled_font, clip /* , glyph_indexing */ ); ++ } ++#endif + #if CAIRO_HAS_WIN32_FONT + WORD glyph_buf_stack[STACK_GLYPH_SIZE]; + WORD *glyph_buf = glyph_buf_stack; +@@ -335,3 +388,18 @@ cairo_int_status_t + #endif + } + #undef STACK_GLYPH_SIZE ++ ++cairo_status_t ++cairo_win32_surface_get_size (const cairo_surface_t *surface, int *width, int *height) ++{ ++ if (surface->type != CAIRO_SURFACE_TYPE_WIN32) ++ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; ++ ++ const cairo_win32_surface_t *winsurface = (const cairo_win32_surface_t *) surface; ++ ++ *width = winsurface->extents.width; ++ *height = winsurface->extents.height; ++ ++ return CAIRO_STATUS_SUCCESS; ++} ++ diff --git a/gfx/cairo/09-quartz-surface-additions.patch b/gfx/cairo/09-quartz-surface-additions.patch new file mode 100644 index 0000000000..104d453f4f --- /dev/null +++ b/gfx/cairo/09-quartz-surface-additions.patch @@ -0,0 +1,314 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h +--- a/gfx/cairo/cairo/src/cairo-quartz-private.h ++++ b/gfx/cairo/cairo/src/cairo-quartz-private.h +@@ -71,8 +71,11 @@ typedef struct cairo_quartz_surface { + cairo_surface_t *imageSurfaceEquiv; + + cairo_surface_clipper_t clipper; ++ + cairo_rectangle_int_t extents; + cairo_rectangle_int_t virtual_extents; ++ ++ cairo_bool_t ownsData; + } cairo_quartz_surface_t; + + typedef struct cairo_quartz_image_surface { +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -1520,6 +1520,103 @@ static cairo_int_status_t + + + /* ++ * get source/dest image implementation ++ */ ++ ++/* 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) ++{ ++ unsigned char *imageData; ++ cairo_image_surface_t *isurf; ++ ++ 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) { ++ CGContextFlush(surface->cgContext); ++ *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv); ++ return CAIRO_STATUS_SUCCESS; ++ } ++ ++ 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 + */ + +@@ -1542,11 +1639,14 @@ static cairo_status_t + surface->cgContext = 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; +@@ -2298,6 +2398,8 @@ cairo_quartz_surface_t * + surface->cgContext = cgContext; + surface->cgContextBaseCTM = CGContextGetCTM (cgContext); + ++ surface->ownsData = TRUE; ++ + return surface; + } + +@@ -2452,10 +2554,124 @@ cairo_quartz_surface_create (cairo_forma + surf->imageData = imageData; + surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride); + ++ // We created this data, so we can delete it. ++ surf->ownsData = TRUE; ++ + return &surf->base; + } + + /** ++ * 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 [Mozilla addition] ++ **/ ++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)); ++ } ++ ++ cgc = CGBitmapContextCreate (imageData, ++ width, ++ height, ++ bitsPerComponent, ++ stride, ++ cgColorspace, ++ bitinfo); ++ CGColorSpaceRelease (cgColorspace); ++ ++ if (!cgc) { ++ free (imageData); ++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); ++ } ++ ++ /* flip the Y axis */ ++ CGContextTranslateCTM (cgc, 0.0, height); ++ CGContextScaleCTM (cgc, 1.0, -1.0); ++ ++ surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format), ++ width, height); ++ if (surf->base.status) { ++ CGContextRelease (cgc); ++ free (imageData); ++ // create_internal will have set an error ++ return (cairo_surface_t*) surf; ++ } ++ ++ surf->imageData = imageData; ++ ++ 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; ++} ++ ++/** + * cairo_quartz_surface_get_cg_context: + * @surface: the Cairo Quartz surface + * +@@ -2497,6 +2713,18 @@ cairo_bool_t + 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 +diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h +--- a/gfx/cairo/cairo/src/cairo-quartz.h ++++ b/gfx/cairo/cairo/src/cairo-quartz.h +@@ -54,9 +54,19 @@ cairo_quartz_surface_create_for_cg_conte + unsigned int width, + unsigned int height); + ++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 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 + + /* diff --git a/gfx/cairo/10-zero-sized-image.patch b/gfx/cairo/10-zero-sized-image.patch new file mode 100644 index 0000000000..887341566a --- /dev/null +++ b/gfx/cairo/10-zero-sized-image.patch @@ -0,0 +1,13 @@ +diff --git a/gfx/cairo/cairo/src/cairo-image-surface.c b/gfx/cairo/cairo/src/cairo-image-surface.c +--- a/gfx/cairo/cairo/src/cairo-image-surface.c ++++ b/gfx/cairo/cairo/src/cairo-image-surface.c +@@ -1243,6 +1243,9 @@ static cairo_image_color_t + int x, y; + cairo_image_color_t color; + ++ if (image->width == 0 || image->height == 0) ++ return CAIRO_IMAGE_IS_MONOCHROME; ++ + if (image->format == CAIRO_FORMAT_A1) + return CAIRO_IMAGE_IS_MONOCHROME; + diff --git a/gfx/cairo/11-quartz-surface-tags.patch b/gfx/cairo/11-quartz-surface-tags.patch new file mode 100644 index 0000000000..7f73d388ea --- /dev/null +++ b/gfx/cairo/11-quartz-surface-tags.patch @@ -0,0 +1,94 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -48,6 +48,7 @@ + #include "cairo-surface-backend-private.h" + #include "cairo-surface-clipper-private.h" + #include "cairo-recording-surface-private.h" ++#include "cairo-tag-attributes-private.h" + + #include + +@@ -2313,6 +2314,70 @@ static cairo_status_t + return CAIRO_STATUS_SUCCESS; + } + ++static cairo_int_status_t ++_cairo_quartz_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_link_attrs_t link_attrs; ++ cairo_int_status_t status = CAIRO_STATUS_SUCCESS; ++ int i, num_rects; ++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; ++ ++ /* Currently the only tag we support is "Link" */ ++ if (strcmp (tag_name, "Link")) ++ return CAIRO_INT_STATUS_UNSUPPORTED; ++ ++ /* We only process the 'begin' tag, and expect a rect attribute; ++ using the extents of the drawing operations enclosed by the begin/end ++ link tags to define the clickable area is not implemented. */ ++ if (!begin) ++ return status; ++ ++ status = _cairo_tag_parse_link_attributes (attributes, &link_attrs); ++ if (unlikely (status)) ++ return status; ++ ++ num_rects = _cairo_array_num_elements (&link_attrs.rects); ++ if (num_rects > 0) { ++ CFURLRef url = CFURLCreateWithBytes (NULL, ++ (const UInt8 *) link_attrs.uri, ++ strlen (link_attrs.uri), ++ kCFStringEncodingUTF8, ++ NULL); ++ ++ for (i = 0; i < num_rects; i++) { ++ CGRect link_rect; ++ cairo_rectangle_t rectf; ++ ++ _cairo_array_copy_element (&link_attrs.rects, i, &rectf); ++ ++ link_rect = ++ CGRectMake (rectf.x, ++ surface->extents.height - rectf.y - rectf.height, ++ rectf.width, ++ rectf.height); ++ ++ CGPDFContextSetURLForRect (surface->cgContext, url, link_rect); ++ } ++ ++ CFRelease (url); ++ } ++ ++ _cairo_array_fini (&link_attrs.rects); ++ free (link_attrs.dest); ++ free (link_attrs.uri); ++ free (link_attrs.file); ++ ++ return status; ++} ++ + // XXXtodo implement show_page; need to figure out how to handle begin/end + + static const struct _cairo_surface_backend cairo_quartz_surface_backend = { +@@ -2346,6 +2411,11 @@ static const struct _cairo_surface_backe + _cairo_quartz_surface_fill, + NULL, /* fill-stroke */ + _cairo_quartz_surface_glyphs, ++ ++ NULL, /* has_show_text_glyphs */ ++ NULL, /* show_text_glyphs */ ++ NULL, /* get_supported_mime_types */ ++ _cairo_quartz_surface_tag /* tag */ + }; + + cairo_quartz_surface_t * diff --git a/gfx/cairo/12-pdf-surface-typo.patch b/gfx/cairo/12-pdf-surface-typo.patch new file mode 100644 index 0000000000..53bb47b967 --- /dev/null +++ b/gfx/cairo/12-pdf-surface-typo.patch @@ -0,0 +1,12 @@ +diff --git a/gfx/cairo/cairo/src/cairo-pdf-surface.c b/gfx/cairo/cairo/src/cairo-pdf-surface.c +--- a/gfx/cairo/cairo/src/cairo-pdf-surface.c ++++ b/gfx/cairo/cairo/src/cairo-pdf-surface.c +@@ -7560,7 +7560,7 @@ static cairo_int_status_t + if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) { + if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x || + _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y || +- _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width || ++ _cairo_fixed_integer_floor(box.p2.x) > rec_extents.x + rec_extents.width || + _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height) + { + return CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/gfx/cairo/13-quartz-cglayer-surface.patch b/gfx/cairo/13-quartz-cglayer-surface.patch new file mode 100644 index 0000000000..f4edc5f364 --- /dev/null +++ b/gfx/cairo/13-quartz-cglayer-surface.patch @@ -0,0 +1,253 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h +--- a/gfx/cairo/cairo/src/cairo-quartz-private.h ++++ b/gfx/cairo/cairo/src/cairo-quartz-private.h +@@ -55,7 +55,8 @@ typedef enum { + DO_DIRECT, + DO_SHADING, + DO_IMAGE, +- DO_TILED_IMAGE ++ DO_TILED_IMAGE, ++ DO_LAYER + } cairo_quartz_action_t; + + /* define CTFontRef for pre-10.5 SDKs */ +@@ -72,6 +73,11 @@ typedef struct cairo_quartz_surface { + + cairo_surface_clipper_t clipper; + ++ /** ++ * If non-null, this is the CGLayer for the surface. ++ */ ++ CGLayerRef cgLayer; ++ + cairo_rectangle_int_t extents; + cairo_rectangle_int_t virtual_extents; + +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -503,6 +503,7 @@ static CGBlendMode + default: + ASSERT_NOT_REACHED; + } ++ return kCGBlendModeNormal; /* just to silence clang warning [-Wreturn-type] */ + } + + static cairo_int_status_t +@@ -1065,7 +1066,7 @@ typedef struct { + /* Destination rect */ + CGRect rect; + +- /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */ ++ /* Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE, DO_LAYER */ + CGAffineTransform transform; + + /* Used with DO_IMAGE and DO_TILED_IMAGE */ +@@ -1077,6 +1078,11 @@ typedef struct { + /* Temporary destination for unbounded operations */ + CGLayerRef layer; + CGRect clipRect; ++ ++ /* Source layer to be rendered when using DO_LAYER. ++ Unlike 'layer' above, this is not owned by the drawing state ++ but by the source surface. */ ++ CGLayerRef sourceLayer; + } cairo_quartz_drawing_state_t; + + /* +@@ -1253,7 +1259,9 @@ static cairo_int_status_t + } + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && +- (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))) ++ (source->extend == CAIRO_EXTEND_NONE || ++ source->extend == CAIRO_EXTEND_PAD || ++ (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; +@@ -1265,6 +1273,20 @@ static cairo_int_status_t + cairo_fixed_t fw, fh; + cairo_bool_t is_bounded; + ++ /* Draw nonrepeating CGLayer surface using DO_LAYER */ ++ if (source->extend != CAIRO_EXTEND_REPEAT && ++ cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) { ++ cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf; ++ if (quartz_surf->cgLayer) { ++ cairo_matrix_invert(&m); ++ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform); ++ state->rect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height); ++ state->sourceLayer = quartz_surf->cgLayer; ++ state->action = DO_LAYER; ++ return CAIRO_STATUS_SUCCESS; ++ } ++ } ++ + _cairo_surface_get_extents (composite->surface, &extents); + status = _cairo_surface_to_cgimage (pat_surf, &extents, format, + &m, clip, &img); +@@ -1426,7 +1448,14 @@ static void + CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height); + CGContextScaleCTM (state->cgDrawContext, 1, -1); + +- if (state->action == DO_IMAGE) { ++ 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. ++ */ ++ assert (state->sourceLayer); ++ CGContextDrawLayerAtPoint (state->cgDrawContext, state->rect.origin, ++ state->sourceLayer); ++ } else if (state->action == DO_IMAGE) { + CGContextDrawImage (state->cgDrawContext, state->rect, state->image); + if (op == CAIRO_OPERATOR_SOURCE && + state->cgDrawContext == state->cgMaskContext) +@@ -1655,6 +1684,10 @@ static cairo_status_t + + surface->imageData = NULL; + ++ if (surface->cgLayer) { ++ CGLayerRelease (surface->cgLayer); ++ } ++ + return CAIRO_STATUS_SUCCESS; + } + +@@ -1693,9 +1726,14 @@ static cairo_surface_t * + int width, + int height) + { +- cairo_quartz_surface_t *surface, *similar_quartz; ++ cairo_quartz_surface_t *similar_quartz; + cairo_surface_t *similar; + cairo_format_t format; ++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; ++ ++ if (surface->cgLayer) ++ return cairo_quartz_surface_create_cg_layer (abstract_surface, content, ++ width, height); + + if (content == CAIRO_CONTENT_COLOR_ALPHA) + format = CAIRO_FORMAT_ARGB32; +@@ -2068,7 +2106,6 @@ static cairo_int_status_t + cairo_quartz_drawing_state_t state; + cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; + int i; +- CGFontRef cgfref = NULL; + + cairo_bool_t didForceFontSmoothing = FALSE; + cairo_antialias_t effective_antialiasing; +@@ -2087,10 +2124,12 @@ static cairo_int_status_t + CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip); + } + +- /* this doesn't addref */ +- cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); +- CGContextSetFont (state.cgMaskContext, cgfref); +- CGContextSetFontSize (state.cgMaskContext, 1.0); ++ if (!CTFontDrawGlyphsPtr) { ++ /* this doesn't addref */ ++ CGFontRef cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); ++ CGContextSetFont (state.cgMaskContext, cgfref); ++ CGContextSetFontSize (state.cgMaskContext, 1.0); ++ } + + effective_antialiasing = scaled_font->options.antialias; + if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL && +@@ -2625,6 +2664,79 @@ cairo_quartz_surface_create_for_cg_conte + } + + /** ++ * cairo_quartz_surface_create_cg_layer ++ * @surface: The returned surface can be efficiently drawn into this ++ * destination surface (if tiling is not used)." ++ * @content: the content type of the surface ++ * @width: width of the surface, in pixels ++ * @height: height of the surface, in pixels ++ * ++ * Creates a Quartz surface backed by a CGLayer, if the given surface ++ * is a Quartz surface; the CGLayer is created to match the surface's ++ * Quartz context. Otherwise just calls cairo_surface_create_similar. ++ * The returned surface can be efficiently blitted to the given surface, ++ * but tiling and 'extend' modes other than NONE are not so efficient. ++ * ++ * Return value: the newly created surface. ++ * ++ * Since: 1.10 ++ **/ ++cairo_surface_t * ++cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, ++ cairo_content_t content, ++ unsigned int width, ++ unsigned int height) ++{ ++ cairo_quartz_surface_t *surf; ++ CGLayerRef layer; ++ CGContextRef ctx; ++ CGContextRef cgContext; ++ ++ cgContext = cairo_quartz_surface_get_cg_context (surface); ++ if (!cgContext) ++ return cairo_surface_create_similar (surface, content, ++ width, height); ++ ++ if (!_cairo_quartz_verify_surface_size(width, height)) ++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); ++ ++ /* If we pass zero width or height into CGLayerCreateWithContext below, ++ * it will fail. ++ */ ++ if (width == 0 || height == 0) { ++ return (cairo_surface_t*) ++ _cairo_quartz_surface_create_internal (NULL, content, ++ width, height); ++ } ++ ++ layer = CGLayerCreateWithContext (cgContext, ++ CGSizeMake (width, height), ++ NULL); ++ if (!layer) ++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); ++ ++ ctx = CGLayerGetContext (layer); ++ /* Flip it when we draw into it, so that when we finally composite it ++ * to a flipped target, the directions match and Quartz will optimize ++ * the composition properly ++ */ ++ CGContextTranslateCTM (ctx, 0, height); ++ CGContextScaleCTM (ctx, 1, -1); ++ ++ CGContextRetain (ctx); ++ surf = _cairo_quartz_surface_create_internal (ctx, content, ++ width, height); ++ if (surf->base.status) { ++ CGLayerRelease (layer); ++ // create_internal will have set an error ++ return (cairo_surface_t*) surf; ++ } ++ surf->cgLayer = layer; ++ ++ return (cairo_surface_t *) surf; ++} ++ ++/** + * cairo_quartz_surface_create: + * @format: format of pixels in the surface to create + * @width: width of the surface, in pixels +diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h +--- a/gfx/cairo/cairo/src/cairo-quartz.h ++++ b/gfx/cairo/cairo/src/cairo-quartz.h +@@ -61,6 +61,12 @@ cairo_quartz_surface_create_for_data (un + unsigned int height, + unsigned int stride); + ++cairo_public cairo_surface_t * ++cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, ++ cairo_content_t content, ++ unsigned int width, ++ unsigned int height); ++ + cairo_public CGContextRef + cairo_quartz_surface_get_cg_context (cairo_surface_t *surface); + diff --git a/gfx/cairo/14-image-surface-oob-read.patch b/gfx/cairo/14-image-surface-oob-read.patch new file mode 100644 index 0000000000..6aa4c1b678 --- /dev/null +++ b/gfx/cairo/14-image-surface-oob-read.patch @@ -0,0 +1,17 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -873,8 +873,12 @@ static cairo_status_t + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + ++ // The last row of data may have less than stride bytes so make sure we ++ // only copy the minimum amount required from that row. + memcpy (image_data, image_surface->data, +- image_surface->height * image_surface->stride); ++ (image_surface->height - 1) * image_surface->stride + ++ cairo_format_stride_for_width (image_surface->format, ++ image_surface->width)); + *image_out = CairoQuartzCreateCGImage (image_surface->format, + image_surface->width, + image_surface->height, diff --git a/gfx/cairo/15-remove-quartz-surface-for-data.patch b/gfx/cairo/15-remove-quartz-surface-for-data.patch new file mode 100644 index 0000000000..a82d4aab21 --- /dev/null +++ b/gfx/cairo/15-remove-quartz-surface-for-data.patch @@ -0,0 +1,138 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -2856,117 +2856,6 @@ cairo_quartz_surface_create (cairo_forma + } + + /** +- * 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 [Mozilla addition] +- **/ +-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)); +- } +- +- cgc = CGBitmapContextCreate (imageData, +- width, +- height, +- bitsPerComponent, +- stride, +- cgColorspace, +- bitinfo); +- CGColorSpaceRelease (cgColorspace); +- +- if (!cgc) { +- free (imageData); +- return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); +- } +- +- /* flip the Y axis */ +- CGContextTranslateCTM (cgc, 0.0, height); +- CGContextScaleCTM (cgc, 1.0, -1.0); +- +- surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format), +- width, height); +- if (surf->base.status) { +- CGContextRelease (cgc); +- free (imageData); +- // create_internal will have set an error +- return (cairo_surface_t*) surf; +- } +- +- surf->imageData = imageData; +- +- 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; +-} +- +-/** + * cairo_quartz_surface_get_cg_context: + * @surface: the Cairo Quartz surface + * +diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h +--- a/gfx/cairo/cairo/src/cairo-quartz.h ++++ b/gfx/cairo/cairo/src/cairo-quartz.h +@@ -54,13 +54,6 @@ cairo_quartz_surface_create_for_cg_conte + unsigned int width, + unsigned int height); + +-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_cg_layer (cairo_surface_t *surface, + cairo_content_t content, diff --git a/gfx/cairo/16-quartz-surface-doimage-extendpad.patch b/gfx/cairo/16-quartz-surface-doimage-extendpad.patch new file mode 100644 index 0000000000..eaf67b42a4 --- /dev/null +++ b/gfx/cairo/16-quartz-surface-doimage-extendpad.patch @@ -0,0 +1,12 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -1315,7 +1315,7 @@ static cairo_int_status_t + + srcRect = CGRectMake (0, 0, extents.width, extents.height); + +- if (source->extend == CAIRO_EXTEND_NONE) { ++ if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD) { + int x, y; + if (op == CAIRO_OPERATOR_SOURCE && + (pat_surf->content == CAIRO_CONTENT_ALPHA || diff --git a/gfx/cairo/17-active-edges-crash.patch b/gfx/cairo/17-active-edges-crash.patch new file mode 100644 index 0000000000..809d07df65 --- /dev/null +++ b/gfx/cairo/17-active-edges-crash.patch @@ -0,0 +1,23 @@ +diff --git a/gfx/cairo/cairo/src/cairo-polygon-intersect.c b/gfx/cairo/cairo/src/cairo-polygon-intersect.c +--- a/gfx/cairo/cairo/src/cairo-polygon-intersect.c ++++ b/gfx/cairo/cairo/src/cairo-polygon-intersect.c +@@ -1167,7 +1167,7 @@ active_edges (cairo_bo_edge_t *left, + } while (1); + + right = left->next; +- do { ++ while (right) { + if unlikely ((right->deferred.other)) + edges_end (right, top, polygon); + +@@ -1179,7 +1179,9 @@ active_edges (cairo_bo_edge_t *left, + } + + right = right->next; +- } while (1); ++ }; ++ if (! right) ++ return; + + edges_start_or_continue (left, right, top, polygon); + diff --git a/gfx/cairo/18-quartz-granular-ifdefs.patch b/gfx/cairo/18-quartz-granular-ifdefs.patch new file mode 100644 index 0000000000..47d50762e2 --- /dev/null +++ b/gfx/cairo/18-quartz-granular-ifdefs.patch @@ -0,0 +1,83 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-font.c b/gfx/cairo/cairo/src/cairo-quartz-font.c +index 48f79d1680920..740ca108e7d19 100644 +--- a/gfx/cairo/cairo/src/cairo-quartz-font.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-font.c +@@ -102,8 +102,10 @@ static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL; + static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL; + static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL; + ++#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID + /* Not public anymore in 64-bits nor in 10.7 */ +-static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL; ++static ATSFontRef (*FMGetATSFontRefFromFontPtr) (ATSUFontID iFont) = NULL; ++#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */ + + static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE; + static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE; +@@ -164,7 +166,9 @@ quartz_font_ensure_symbols(void) + CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); + CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); + ++#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID + FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); ++#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */ + + if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && + CGFontGetGlyphBBoxesPtr && +@@ -870,6 +874,7 @@ _cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font) + /* + * compat with old ATSUI backend + */ ++#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID + + /** + * cairo_quartz_font_face_create_for_atsu_font_id: +@@ -913,3 +918,5 @@ 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 /* CAIRO_HAS_QUARTZ_ATSUFONTID */ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-image.h b/gfx/cairo/cairo/src/cairo-quartz-image.h +index 0dd5abb4fd2d2..9e8409c1169b3 100644 +--- a/gfx/cairo/cairo/src/cairo-quartz-image.h ++++ b/gfx/cairo/cairo/src/cairo-quartz-image.h +@@ -40,8 +40,6 @@ + + #if CAIRO_HAS_QUARTZ_IMAGE_SURFACE + +-#include +- + CAIRO_BEGIN_DECLS + + cairo_public cairo_surface_t * +diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h +index 20c86423b25de..2118d8ff9c842 100644 +--- a/gfx/cairo/cairo/src/cairo-quartz.h ++++ b/gfx/cairo/cairo/src/cairo-quartz.h +@@ -40,7 +40,14 @@ + + #if CAIRO_HAS_QUARTZ_SURFACE + ++#if CAIRO_HAS_QUARTZ_CORE_GRAPHICS ++#include ++#include ++#endif ++ ++#if CAIRO_HAS_QUARTZ_APPLICATION_SERVICES + #include ++#endif + + CAIRO_BEGIN_DECLS + +@@ -75,8 +82,10 @@ 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 CAIRO_HAS_QUARTZ_ATSUFONTID + cairo_public cairo_font_face_t * + cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id); ++#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */ + + #endif /* CAIRO_HAS_QUARTZ_FONT */ + diff --git a/gfx/cairo/README b/gfx/cairo/README new file mode 100644 index 0000000000..27f2cc14ca --- /dev/null +++ b/gfx/cairo/README @@ -0,0 +1,257 @@ +Snapshots of cairo and glitz for mozilla usage. + +We only include the relevant parts of each release (generally, src/*.[ch]), +as we have Makefile.in's that integrate into the Mozilla build system. For +documentation and similar, please see the official tarballs at +http://www.cairographics.org/. + +VERSIONS: + + cairo (277a1daec80cb6cf7bfb0e200cf78e7842cb2f82) + pixman (0.42.2) + +==== Patches ==== + +Some specific things: + +max-font-size.patch: Clamp freetype font size to 1000 to avoid overflow issues + +win32-logical-font-scale.patch: set CAIRO_WIN32_LOGICAL_FONT_SCALE to 1 + +nonfatal-assertions.patch: Make assertions non-fatal + +buggy-repeat.patch: Unconditionally turn on buggy-repeat handling to bandaid bug 413583. + +cairo-version-fixes.patch: fix up cairo-version.c/cairo-version.h for in-place builds + +win32-ddb-dib.patch: fix for bug 455513; not upstream yet pending feebdack + +win32-vertically-offset-glyph.patch: bug 454098; vertical positioning errors when drawing glyph runs including delta-y offsets on screen via GDI + +ignore-rank0.patch: bug 474886; Not redrawing the background when changing page on flickr + +win32-canvas-glyph-position.patch: bug 475092; horizontal positioning errors when drawing glyph runs with delta-y offsets to canvas through win32-font + +win32-cleartype-clipping.patch: bug 445087; some glyphs are clipped, mainly on right-hand edge, when ClearType is enabled and drawing to RGBA canvas + +on-edge.patch: reverts the in-fill semantic change. + +wrap-source_image.patch: make sure we don't free the source image until we're done with it. + +zero-sized.patch: deal with zero sized surface in ways less likely to crash. + +text-path-filling-threshold.patch: use path filling instead of platform glyph rasterization at a smaller size threshold of 256 device pixels, if the backend supports native filling (which we assume will be fast). + +zombie-face.patch: bug 486974; leak and possible crash with @font-face{src:url()}. Upstream commit: 0238fe2cafea2e1ed19bb222117bd73ee6898d4d + +win32-raster.patch: bug 498689; use scanline rasterizer on win32 + +quartz-falback.patch: try to fix Quartz fallback-to-pixman path; possiby incorrect and obsoleted by Andrea Canciani patch + +quartz-repeating-radial-gradients.patch: use Quartz to render repeating radial gradients instead of falling back + +quartz-const-globals.patch: make some Quartz color function data const globals instead of local variables + +quartz-minimze-gradient-repeat.patch: reduce the number of gradient stop repetitions we use, to improve quality of Quartz's gradient rendering + +quartz-first-stop.patch: return the first stop for negative positions on the gradient line of a nonrepeating linear gradient + +quartz-glyph-extents.patch: bug 534260; work around incorrect glyph extents returned by quartz for anomalous empty glyphs + +quartz-state.patch: bug 522859; refactor cairo-quartz-surface so that state local to a drawing operation is stored in a cairo_quartz_drawing_state_t instead of the surface + +quartz-cache-CGImageRef.patch: cache CGImageRef for a CGBitmapContext; when we reuse it, Quartz will cache stuff, improving performance + +quartz-remove-snapshot.patch: remove broken implementation of backend snapshot + +quartz-cglayers.patch: add support for cairo surfaces backed by CGLayers + +quartz-cglayers-fix-fallback.patch: Bug 572912; fix bug in fallback code in previous patch + +quartz-get-image.patch: Bug 575521; add a way to get the image surface associated with a surface + +quartz-create-for-data.patch: Bug 575521; add a way to create quartz surfaces backed with application-provided data + +premultiply-alpha-solid-gradients.patch: bug 539165; multiply the solid color by the alpha component before using it for a solid surface + +xlib-initialize-members.path: bug 548793; initialize XRender version if the server doesn't have the extension + +remove-comma: remove a comma from enum + +d2d.patch: add d2d support + +fix-zero-len-graident.patch: fix zero length gradients + +fix-clip-copy.patch: fix clip copying + +fix-clip-region-simplification.patch: fixes a bug in clip region simplifications + +expand-in-stroke-limits.patch: expand the in-stroke limits to avoid a bug + +d2d-dwrite.patch: update the d2d/dwrite stuff + +add-a-stash-of-cairo_t-s.patch: use the stash to avoid malloc/freeing cairo_t's + +bgr.patch: fix image wrapping + +disable-server-graidents.patch: disable server-side gradients + +clip-invariant.patch: make rasterization closer to being clip invariant + +fix-unnecessary-fallback.patch: avoid unnecessary fallback + +handle-a1-upload.patch: handle a1 image uploads through converter + +surface-clipper.patch: remove an incorrect optimization + +fix-win32-show-glyphs-clipping.patch: fix a clipping bug + +native-clipping.patch: Add support for a native clipping api + +quartz-is-clear.patch: Propagate the quartz is_clear flag. + +cairo-qt-compile.patch: Fix compile error, return not reached, and clone_similar interface + +dwrite-glyph-extents.patch: Add padding to extents of antialiased glyphs, to avoid unwanted clipping. (bug 568191) + +fix-ps-output.patch: PS: Add missing 'q' when resetting clip path (42b5cac7668625c9761113ff72b47af5cfd10377) + +ensure-text-flushed.patch: PDF-operators: ensure text operations flushed before emitting clip (42b5cac7668625c9761113ff72b47af5cfd10377) + +fix-xcopyarea-with-clips.patch: 5d07307b691afccccbb15f773d5231669ba44f5a + +cairo-x-visual.patch: make valid visua for cairo_xlib_surface_create_with_xrender_format (55037bfb2454a671332d961e061c712ab5471580) + +win32-transparent-surface.patch: add API so we can create a win32 surface for an HDC and indicate the surface has an alpha channel + +cairo_qt_glyphs.patch: Drop X surface from Qt surface, add support for new qt glyphs api + +empty-clip-rectangles.patch: f2fa15680ec3ac95cb68d4957557f06561a7dc55 + +empty-clip-extents.patch: b79ea8a6cab8bd28aebecf6e1e8229d5ac017264 + +clip-rects-surface-extents.patch: 108b1c7825116ed3f93aa57384bbd3290cdc9181 + +disable-previous-scaled-font-cache.patch: Disable the previous-scaled-font-cache until we figure out our ctm handling (#583035) + +copyarea-with-alpha.patch: support simple overlapping self copies in (some) color_alpha xlib surfaces. https://bugs.freedesktop.org/show_bug.cgi?id=29250 + +fix-clip-test.patch: Use y 498c10032ea3f8631a928cd7df96766f2c8ddca4 + +quartz-refactor-surface-setup.patch: Extract the surface-source setup chunk of _cairo_quartz_setup_state into its own function + +quartz-fix-PAD.patch: Treat PAD like NONE instead of REPEAT + +quartz-mask-non-OVER.patch: Don't use CGContextSetAlpha to optimize alpha masking for non-OVER operators + +quartz-layers-content.patch: Store cairo content type in CGLayer surfaces + +quartz-optimize-OVER.patch: Optimize OVER to SOURCE for opaque patterns + +quartz-check-imageSurfaceEquiv.patch: Drop cairo_quartz_surface_t's "imageSurfaceEquiv" member variable if we have problems creating it + +disable-subpixel-antialiasing.patch: Add API to disable subpixel antialiasing completely for a target surface + +tee-surfaces-pointwise.patch: Composite tee subsurfaces pointwise if possible + +pattern_get_surface-no-error.patch: Don't put a pattern into error if cairo_pattern_get_surface fails + +missing-cairo-clip-init.diff: Missing cairo_clip_init call in cairo_gstate_show_text_glyphs lead to crash + +fix-cairo-win32-print-gdi-error.diff: Don't use fwprintf with char* format. Flush stderr so that all error messages appears before exit. + +pixman-image-transform.patch: Reset the transform on pixman images when using them as destinations. + +fix-cairo-surface-wrapper-flush-build-warning.patch: Ensures that _cairo_surface_wrapper_flush always returns a status, to silence the build warning + +fixup-unbounded.patch: Hack to work around bad assumption. + +quartz-get-image-performance: Make cairo_quartz_get_image faster in the failure case by not flushing unless we are going to succeed. + +lround-c99-only.patch: Only use lround in C99 programs. + +unicode-printing.patch: Print as unicode (bug 454532) + +quartz-mark-dirty.patch: Add a quartz implementation of mark_dirty_rectangle (bug 715704) + +expose-snapshot.patch: Make functions to add snapshots public, as well as allow creating null surfaces publically. (bug 715658) + +fix-build-with-Werror=return-type.patch: Fix builds with -Werror=return-type (bug 737909) + +avoid-extend-none.patch: Avoid incorrectly using EXTEND_NONE (bug 751668) + +win32-ExtCreatePen-zero-size.patch: Don't pass zero width or dash lengths to ExtCreatePen (bug 768348) + +d2d-repeating-gradients.patch: Minimize number of gradient stops added to handle repeating with path fills (bug 768775) + +xlib-glyph-clip-region.patch: bug 709477, addressed upstream by be1ff2f45fdbc69537e513834fcffa0435e63073 + +gdi-RGB24-ARGB32.patch: bug 788794 + +dwrite-font-printing.patch: bug 468568; don't substitute a GDI font for a DWrite font if the name tables aren't equal + +d2d-gradient-ensure-stops.patch: bug 792903, ensure we don't set num_stops to 0 + +setlcdfilter_in_tree.patch: bug 790139; force cairo to use FT_Library_SetLcdFilter from our in tree library rather than picking it up from the system + +dwrite-font-match-robustness.patch: bug 717178, don't crash when _name_tables_match is passed a nil scaled-font + +handle-multi-path-clip.patch: bug 813124, handle multiple clip paths correctly + +win32-gdi-font-cache.patch: Bug 717178, cache GDI font faces to reduce usage of GDI resources + +win32-gdi-font-cache-no-HFONT.patch: Bug 717178, don't cache GDI font faces when an HFONT belonging to the caller is passed in + +fix-win32-font-assertion.patch: Bug 838617, fix assertion from bug 717178 that was in the wrong place + +xlib-flush-glyphs.patch: bug 839745, flush glyphs when necessary + +dasharray-zero-gap.patch: bug 885585, ensure strokes get painted when the gaps in a dash array are all zero length + +cairo-mask-extends-bug.patch: bug 918671, sometimes when building a mask we wouldn't clear it properly. This is fixed in cairo 1.12 + +ft-no-subpixel-if-surface-disables.patch: bug 929451, don't use subpixel aa for ft fonts on surfaces that don't support it + +win32-printing-axis-swap.patch: bug 1205854, workaround for Windows printer drivers that can't handle swapped X and Y axes + +no-pixman-image-reuse-across-threads.patch: bug 1273701, picked from 71e8a4c23019b01aa43b334fcb2784c70daae9b5 + +==== pixman patches ==== + +pixman-clangcl.patch: clang-cl compilation fix + +pixman-export.patch: make sure pixman symbols are not exported in libxul + +pixman-interp.patch: use lower quality interpolation by default on mobile + +pixman-intrin.patch: include intrin.h on Windows to fix bustage + +pixman-mingw32.patch: include xmmintrin.h on MINGW32 builds to avoid redefinition + +pixman-rename.patch: include pixman-rename.h for renaming of external symbols + +pixman-arm32-clang.patch: don't use -no-integrated-as for arm32 + +pixman-arm64-clang.patch: don't use -no-integrated-as for aarch64 + +quartz-support-color-emoji-font.patch: support Apple Color Emoji font in cairo-quartz backend + +use-show-text-glyphs-if-glyph-path-fails.patch: fall back to show_text_glyphs even at huge sizes if scaled_font_glyph_path didn't work + +win32-d3dsurface9.patch: Create a win32 d3d9 surface to support LockRect + +win32-avoid-extend-pad-fallback: Avoid falling back to pixman when using EXTEND_PAD + +support-new-style-atomic-primitives.patch: Support the __atomic_* primitives for atomic operations + +clang-cl-popcount.patch: Use __builtin_popcount for Clang (including clang-cl) + +==== disable printing patch ==== + +disable-printing.patch: allows us to use NS_PRINTING to disable printing. + +==== cairo clamp bounday patch ==== +cairo-clamp-boundary.patch: don't call pixman_fill with negative starts or negative sizes + +==== cairo zero subpath bounds patch ==== +zero-subpath-bounds.patch: include zero length paths in bounds diff --git a/gfx/cairo/add-cairo_scaled_font_get_hint_metrics b/gfx/cairo/add-cairo_scaled_font_get_hint_metrics new file mode 100644 index 0000000000..4c48c46d91 --- /dev/null +++ b/gfx/cairo/add-cairo_scaled_font_get_hint_metrics @@ -0,0 +1,169 @@ +# HG changeset patch +# User Mats Palmgren +# Parent f3483af8ecf997453064201c49c48a682c7f3c29 +Bug 1377257 part 1 - Add cairo_scaled_font_get_hint_metrics to avoid malloc/free that is required to use cairo_scaled_font_get_font_options. r=jfkthame + +diff --git a/gfx/cairo/add-cairo_scaled_font_get_hint_metrics b/gfx/cairo/add-cairo_scaled_font_get_hint_metrics +new file mode 100644 +--- /dev/null ++++ b/gfx/cairo/add-cairo_scaled_font_get_hint_metrics +@@ -0,0 +1,82 @@ ++# HG changeset patch ++# User Mats Palmgren ++# Parent f3483af8ecf997453064201c49c48a682c7f3c29 ++Bug 1377257 part 1 - Add cairo_scaled_font_get_hint_metrics to avoid malloc/free that is required to use cairo_scaled_font_get_font_options. r=jfkthame ++ ++diff --git a/gfx/cairo/cairo/src/cairo-rename.h b/gfx/cairo/cairo/src/cairo-rename.h ++--- a/gfx/cairo/cairo/src/cairo-rename.h +++++ b/gfx/cairo/cairo/src/cairo-rename.h ++@@ -262,16 +262,17 @@ ++ #define cairo_scale _moz_cairo_scale ++ #define cairo_scaled_font_create _moz_cairo_scaled_font_create ++ #define cairo_scaled_font_destroy _moz_cairo_scaled_font_destroy ++ #define cairo_scaled_font_extents _moz_cairo_scaled_font_extents ++ #define cairo_scaled_font_get_ctm _moz_cairo_scaled_font_get_ctm ++ #define cairo_scaled_font_get_font_face _moz_cairo_scaled_font_get_font_face ++ #define cairo_scaled_font_get_font_matrix _moz_cairo_scaled_font_get_font_matrix ++ #define cairo_scaled_font_get_font_options _moz_cairo_scaled_font_get_font_options +++#define cairo_scaled_font_get_hint_metrics _moz_cairo_scaled_font_get_hint_metrics ++ #define cairo_scaled_font_get_reference_count _moz_cairo_scaled_font_get_reference_count ++ #define cairo_scaled_font_get_scale_matrix _moz_cairo_scaled_font_get_scale_matrix ++ #define cairo_scaled_font_get_type _moz_cairo_scaled_font_get_type ++ #define cairo_scaled_font_get_user_data _moz_cairo_scaled_font_get_user_data ++ #define cairo_scaled_font_glyph_extents _moz_cairo_scaled_font_glyph_extents ++ #define cairo_scaled_font_reference _moz_cairo_scaled_font_reference ++ #define cairo_scaled_font_set_user_data _moz_cairo_scaled_font_set_user_data ++ #define cairo_scaled_font_status _moz_cairo_scaled_font_status ++diff --git a/gfx/cairo/cairo/src/cairo-scaled-font.c b/gfx/cairo/cairo/src/cairo-scaled-font.c ++--- a/gfx/cairo/cairo/src/cairo-scaled-font.c +++++ b/gfx/cairo/cairo/src/cairo-scaled-font.c ++@@ -2983,8 +2983,28 @@ cairo_scaled_font_get_font_options (cair ++ if (scaled_font->status) { ++ _cairo_font_options_init_default (options); ++ return; ++ } ++ ++ _cairo_font_options_init_copy (options, &scaled_font->options); ++ } ++ 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_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; +++} +++slim_hidden_def (cairo_scaled_font_get_hint_metrics); ++diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h ++--- a/gfx/cairo/cairo/src/cairo.h +++++ b/gfx/cairo/cairo/src/cairo.h ++@@ -1493,16 +1493,21 @@ cairo_scaled_font_get_ctm (cairo_scaled_ ++ cairo_public void ++ cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, ++ cairo_matrix_t *scale_matrix); ++ ++ 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 */ ++ ++ cairo_public cairo_font_face_t * ++ cairo_toy_font_face_create (const char *family, ++ cairo_font_slant_t slant, ++ cairo_font_weight_t weight); ++ +diff --git a/gfx/cairo/cairo/src/cairo-rename.h b/gfx/cairo/cairo/src/cairo-rename.h +--- a/gfx/cairo/cairo/src/cairo-rename.h ++++ b/gfx/cairo/cairo/src/cairo-rename.h +@@ -262,16 +262,17 @@ + #define cairo_scale _moz_cairo_scale + #define cairo_scaled_font_create _moz_cairo_scaled_font_create + #define cairo_scaled_font_destroy _moz_cairo_scaled_font_destroy + #define cairo_scaled_font_extents _moz_cairo_scaled_font_extents + #define cairo_scaled_font_get_ctm _moz_cairo_scaled_font_get_ctm + #define cairo_scaled_font_get_font_face _moz_cairo_scaled_font_get_font_face + #define cairo_scaled_font_get_font_matrix _moz_cairo_scaled_font_get_font_matrix + #define cairo_scaled_font_get_font_options _moz_cairo_scaled_font_get_font_options ++#define cairo_scaled_font_get_hint_metrics _moz_cairo_scaled_font_get_hint_metrics + #define cairo_scaled_font_get_reference_count _moz_cairo_scaled_font_get_reference_count + #define cairo_scaled_font_get_scale_matrix _moz_cairo_scaled_font_get_scale_matrix + #define cairo_scaled_font_get_type _moz_cairo_scaled_font_get_type + #define cairo_scaled_font_get_user_data _moz_cairo_scaled_font_get_user_data + #define cairo_scaled_font_glyph_extents _moz_cairo_scaled_font_glyph_extents + #define cairo_scaled_font_reference _moz_cairo_scaled_font_reference + #define cairo_scaled_font_set_user_data _moz_cairo_scaled_font_set_user_data + #define cairo_scaled_font_status _moz_cairo_scaled_font_status +diff --git a/gfx/cairo/cairo/src/cairo-scaled-font.c b/gfx/cairo/cairo/src/cairo-scaled-font.c +--- a/gfx/cairo/cairo/src/cairo-scaled-font.c ++++ b/gfx/cairo/cairo/src/cairo-scaled-font.c +@@ -2983,8 +2983,28 @@ cairo_scaled_font_get_font_options (cair + if (scaled_font->status) { + _cairo_font_options_init_default (options); + return; + } + + _cairo_font_options_init_copy (options, &scaled_font->options); + } + 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_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; ++} ++slim_hidden_def (cairo_scaled_font_get_hint_metrics); +diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h +--- a/gfx/cairo/cairo/src/cairo.h ++++ b/gfx/cairo/cairo/src/cairo.h +@@ -1493,16 +1493,19 @@ cairo_scaled_font_get_ctm (cairo_scaled_ + cairo_public void + cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *scale_matrix); + + cairo_public void + cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, + cairo_font_options_t *options); + ++cairo_public cairo_hint_metrics_t ++cairo_scaled_font_get_hint_metrics (cairo_scaled_font_t *scaled_font); ++ + + /* Toy fonts */ + + cairo_public cairo_font_face_t * + cairo_toy_font_face_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + diff --git a/gfx/cairo/cairo/AUTHORS b/gfx/cairo/cairo/AUTHORS new file mode 100644 index 0000000000..fe5a883e8b --- /dev/null +++ b/gfx/cairo/cairo/AUTHORS @@ -0,0 +1,115 @@ +Josh Aas Memory leak fix for quartz backend +Daniel Amelang Many (magic) floating-point optimizations +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 +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, application testing +Adrian Johnson PDF backend improvement +Michael Johnson Bug fix for pre-C99 compilers +Jonathon Jongsma Fix documentation typos +Ø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 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, OpenGL backend fixups, random fixes +Mike Owens Bug fixes +Emmanuel Pacaud SVG backend +Keith Packard Original concept, polygon tessellation, dashing, font metrics rewrite +Stuart Parmenter Original GDI+ backend, win32 fixes +Alfred Peng Fixes for Sun compilers and for a memory leak +Christof Petig Build fixes related to freetype +Joonas Pihlaja Huge improvements to the tessellator performance +Mart Raudsepp Build fixes +David Reveman New pattern API, glitz backend +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, 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 +Kalle Vahlman Allow perf reports to be compared across different platforms +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 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 +Kent Worsnop Fix PDF dashing bug +Dave Yeo Build fix for win32 + +(please let us know if we have missed anyone) diff --git a/gfx/cairo/cairo/COPYING b/gfx/cairo/cairo/COPYING new file mode 100644 index 0000000000..f54969f1c9 --- /dev/null +++ b/gfx/cairo/cairo/COPYING @@ -0,0 +1,33 @@ +Cairo is free software. + +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 +liberal terms, but we believe that in all cases, each file may be used +under either the LGPL or the MPL. + +See the following files in this directory for the precise terms and +conditions of either license: + + COPYING-LGPL-2.1 + COPYING-MPL-1.1 + +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 new file mode 100644 index 0000000000..f1ed6182c5 --- /dev/null +++ b/gfx/cairo/cairo/COPYING-LGPL-2.1 @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/gfx/cairo/cairo/COPYING-MPL-1.1 b/gfx/cairo/cairo/COPYING-MPL-1.1 new file mode 100644 index 0000000000..7714141d15 --- /dev/null +++ b/gfx/cairo/cairo/COPYING-MPL-1.1 @@ -0,0 +1,470 @@ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``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/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + diff --git a/gfx/cairo/cairo/INSTALL b/gfx/cairo/cairo/INSTALL new file mode 100644 index 0000000000..dfdc2139ed --- /dev/null +++ b/gfx/cairo/cairo/INSTALL @@ -0,0 +1,184 @@ +Quick-start build instructions +------------------------------ +1) Configure the package: + + ./configure + +2) Compile it: + + make + +3) Install it: + + make install + +This final step may require temporary root access (eg. with sudo) if +you don't have write permission to the directory in which cairo will +be installed. + +NOTE: If you are working with source from git/cvs rather than from a tar +file, then you should use ./autogen.sh in place of ./configure +anywhere it is mentioned in these instructions. + +More detailed build instructions +-------------------------------- +1) Configure the package + + The first step in building cairo is to configure the package by + running the configure script. [Note: if you don't have a configure + script, skip down below to the Extremely detailed build + instructions.] + + The configure script attempts to automatically detect as much as + possible about your system. So, you should primarily just accept + its defaults by running: + + ./configure + + The configure script does accept a large number of options for + fine-tuning its behavior. See "./configure --help" for a complete + list. The most commonly used options are discussed here. + + --prefix=PREFIX + + This option specifies the directory under which the software + should be installed. By default configure will choose a + directory such as /usr/local. If you would like to install + cairo to some other location, pass the director to configure + with the --prefix option. For example: + + ./configure --prefix=/opt/cairo + + would install cairo into the /opt/cairo directory. You could + also choose a prefix directory within your home directory if + you don't have write access to any system-wide directory. + + After installing into a custom prefix, you will need to set + some environment variables to allow the software to be + found. Assuming the /opt/cairo prefix and assuming you are + using the bash shell, the following environment variables + should be set: + + PKG_CONFIG_PATH=/opt/cairo/lib/pkgconfig + LD_LIBRARY_PATH=/opt/cairo/lib + export PKG_CONFIG_PATH LD_LIBRARY_PATH + + (NOTE: On Mac OS X, at least, use DYLD_LIBRARY_PATH in place + of LD_LIBRARY_PATH above.) + + --enable-XYZ + --enable-XYZ=yes + --enable-XYZ=auto + --enable-XYZ=no + --disable-XYZ + + 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: + + * 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. + + * 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: + + This step is very simple. Just: + + make + + The Makefiles included with cairo are designed to work on as many + different systems as possible. + + When cairo is compiled, you can also run some automated tests of + cairo with: + + make check + + NOTE: Some versions of X servers will cause the -xlib tests to + report failures in make check even when cairo is working just + fine. If you see failures in nothing but -xlib tests, please + examine the corresponding -xlib-out.png images and compare them to + the -ref.png reference images (the -xlib-diff.png images might also + be useful). If the results seem "close enough" please do not report + a bug against cairo as the "failures" you are seeing are just due + to subtle variations in X server implementations. + +3) Install the package: + + The final step is to install the package with: + + make install + + If you are installing to a system-wide location you may need to + temporarily acquire root access in order to perform this + operation. A good way to do this is to use the sudo program: + + sudo make install + +Extremely detailed build instructions +------------------------------------- +So you want to build cairo but it didn't come with a configure +script. This is probably because you have checked out the latest +in-development code via git. If you need to be on the bleeding edge, +(for example, because you're wanting to develop some aspect of cairo +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: + + https://cairographics.org/releases + +or perhaps the latest (unstable) development snapshot: + + 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. + +But you're still reading, so you're someone that loves to +learn. Excellent! We hope you'll learn enough to make some excellent +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 + autoconf + 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 +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 + +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 +provide). So go back up to step (1) above and see what additional +arguments you might want to pass, (such as prefix). Then continue with +the instructions, simply using ./autogen.sh in place of ./configure. + +Happy hacking! diff --git a/gfx/cairo/cairo/NEWS b/gfx/cairo/cairo/NEWS new file mode 100644 index 0000000000..3eeb1c389e --- /dev/null +++ b/gfx/cairo/cairo/NEWS @@ -0,0 +1,8174 @@ +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 +release of the cairo graphics library. This release reverts the xlib +locking change introduced in 1.6.4, (and the application crashes that +it caused). The community would be glad to sack its current release +manager and is accepting applications for someone who could do the job +with more discipline. + +Revert 'add missing locking in cairo-xlib' +------------------------------------------ +This change was introduced in cairo 1.6.2, but also introduced a bug +which causes many cairo-xlib applications to crash, (with a +segmentation fault inside of XSetClipMask). Instead of attempting +another fix for the broken fix, the change in 1.6.2 has been +reverted. The original bug which the change was addressing has been +present since at least cairo 1.4, so it is not expected that leaving +this bug unfixed will cause any new problems for applications moving +from cairo 1.4 to cairo 1.6. + +At this point, the code of cairo 1.6.4 differs from cairo 1.6.0 only +in the fix for the PostScript-printer crashes. + +Tweak build to avoid linking with g++ +------------------------------------- +Cairo 1.6.4 avoids a quirk in automake that was causing the cairo +library to be linked with g++ and linked against libstdc++ even when +only C source files were compiled for the library. + +Release 1.6.2 (2008-04-11 Carl Worth ) +========================================================= +The cairo community is pleased (but somewhat sheepish) to announce the +1.6.2 release of the cairo graphics library. This is an update to +yesterday's 1.6.0 release with an important fix to prevent cairo's +PostScript output from crashing some printers. This release also +includes a locking fix for cairo's xlib backend to improve thread +safety. There are no changes beyond these two fixes. + +Fix for PostScript printer crash +-------------------------------- +Adrian Johnson discovered that cairo 1.6.0 was being a bit hard on +PostScript printers, by changing the font matrix very frequently. This +causes some PostScript interpreters to allocate new font objects every +few glyphs, eventually exhausting available resources. The fix +involves leaving translational components of the font matrix as zero, +so that the PostScript interpreter sees an identical font matrix +repeatedly, and can more easily share internal font object resources. + +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 + https://bugs.freedesktop.org/show_bug.cgi?id=15348 + + Cairo-generated postscript blocks Dell 5100cn + https://bugs.freedesktop.org/show_bug.cgi?id=15445 + +Add missing locking in cairo-xlib +--------------------------------- +Chris Wilson noticed that cairo 1.6.0 was manipulating an internal +cache of GC object within cairo's Xlib backend without proper +locking. The missing locking could cause failures for multi-threaded +applications. He fixed this in 1.6.2 by adding the missing locks. + +Release 1.6.0 (2008-04-10 Carl Worth ) +========================================================= +The cairo community is quite pleased to announce the 1.6.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 cairo 1.4, 1.2, or 1.0. We recommend that +anybody using a previous version of cairo upgrade to cairo 1.6.0. + +The most significant new features in this release are dramatically +improved PDF and PostScript[*] output, support for arbitrary X server +visuals (including PseudoColor), a new Quartz backend, and and a new +"win32 printing" backend. See below for more details on these and +other new features. + +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: + + 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 +required for other backends). This is the same code that was +previously included as part of cairo itself, but is now an external +library so that it can be shared by both cairo and by the X server, +(which is where the code originated). + +Improved PDF, PostScript, and SVG output (Thanks, Adrian!) +---------------------------------------------------------- +Users of the cairo-pdf, cairo-ps, and cairo-svg should see a dramatic +improvement from cairo 1.2/1.4 to 1.6. With this release there are now +almost no operations that will result in unnecessary rasterization in +the PDF and PostScript. Rasterized "image fallbacks" are restricted +only to minimal portions of the document where something is being +drawn with cairo that is beyond the native capabilities of the +document, (this is rare for PDF or SVG, but occurs when blending +translucent objects for PostScript). + +This means that the final output will be of higher quality, and will +also be much smaller, and therefore will print more quickly. The +machinery for doing analysis and minimal fallbacks also benefits the +win32-printing surface described below. + +In addition to doing less rasterization, the PostScript and PDF output +also has several other improvements to make the output more efficient +and more compatible with specifications. + +[*] Note: Just before this release, a bug has been reported that the +PostScript output from cairo can crash some printers, (so far the +following models have been reported as problematic Xerox Workcentre +7228 or 7328 and Dell 5100cn). We will implement a workaround as soon +as we can learn exactly what in cairo's output these printers object +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 + https://bugs.freedesktop.org/show_bug.cgi?id=15348 + +New support for arbitrary X server visuals (Thanks, Keith and Behdad!) +---------------------------------------------------------------------- +As of cairo 1.6, cairo should now work with an arbitrary TrueColor or +8-bit PseudoColor X server visual. Previous versions of cairo did not +support these X servers and refused to draw anything. We're pleased to +announce that this limitation has been lifted and people stuck with +ancient display systems need no longer be stuck with ancient software +just because of cairo. + +New, supported Quartz backend for Mac OS X (Thanks, Brian and Vladimir!) +------------------------------------------------------------------------ +As of cairo 1.6, the cairo-quartz backend is now marked as "supported" +rather than "experimental" as in previous cairo releases. Its API now +has guarantees of API stability into future cairo releases, and its +output quality is comparable to other backends. There have been +significant improvements to cairo-quartz since 1.4. It now uses many +fewer image fallbacks, (meaning better performance), and has greatly +improved text rendering. + +New, "win32 printing" backend (Thanks, Adrian and Vladimir!) +------------------------------------------------------------ +A new win32-printing surface has been added with an interface very +similar to the original win32 surface, (both accept an HDC +parameter). But this new surface should only be called with a printing +DC, and will result in all drawing commands being stored into a +meta-surface and emitted after each page is complete. This allows +cairo to analyze the contents, (as it does with PDF, PostScript, and +SVG backends), and to do minimal image-based fallbacks as +necessary. The analysis keeps things as efficient as possible, while +the presence of fallbacks, (when necessary), ensure the consistent, +high-quality output expected from cairo. + +Robustness fixes (Thanks, Chris!) +--------------------------------- +There has been a tremendous number of improvements to cairo's +robustness. Areas that have been improved include: + + * Proper reporting of errors + + * Responding correctly to invalid input + + * Avoiding integer overflows + + * Avoiding memory leaks on error-recovery paths + + * Making reference counting thread safe + + * Exhaustive testing of memory allocation points + +Other fixes (Thanks, everybody!) +-------------------------------- +Cairo's internal fixed-point representation has been changed from +16.16 to 24.8. This has a direct impact on applications as it allows +much larger objects to be drawn before internal limits in cairo make +the drawing not work. + +The CAIRO_EXTEND_PAD mode is now fully supported by surface +patterns. This mode allows applications to use cairo_rectangle and +cairo_fill to draw scaled images with high-quality bilinear filtering +for the internal of the image, but without any objectionably blurry +edges, (as would happen with the default EXTEND_NONE and cairo_paint). + +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 https://cairographics.org/manual now documents +100% of the functions and types in cairo's public API. + +API additions +------------- +Several small features have been added to cairo with new API functions: + +cairo_format_stride_for_width + + Must be called to compute a properly aligned stride value before + calling cairo_image_surface_create_for_data. + +cairo_has_current_point + + Allows querying if there is a current point defined for the + current path. + +cairo_path_extents + + Allows querying for path extents, (independent of any fill or + stroke parameters). + +cairo_surface_copy_page +cairo_surface_show_page + + Allow beginning a new document page without requiring a cairo_t + object. + +cairo_ps_surface_restrict_to_level +cairo_ps_get_levels +cairo_ps_level_to_string +cairo_ps_surface_set_eps + + Allow controlling the Post PostScript level, (2 or 3), to + target, as well as to generate Encapsulated PostScript (EPS). + +cairo_quartz_font_face_create_for_cgfont + + Create a quartz-specific cairo_font_face_t from a CGFontRef. + +cairo_win32_font_face_create_for_logfontw_hfont + + Create a win32-specific cairo_font_face from a LOGFONTW and an + HFONT together. + +Thanks, Everyone! +----------------- +I've accounted for 32 distinct people with attributed code added to +cairo between 1.4.14 and 1.6.0, (their names are below). That's an +impressive number, but there are certainly dozens more that +contributed with testing, suggestions, clarifying questions, and +encouragement. I'm grateful for the friendships that have developed as +we have worked on cairo together. Thanks to everyone for making this +all so much fun! + +Adrian Johnson, Alp Toker, Antoine Azar, Behdad Esfahbod, +Benjamin Otte, Bernardo Innocenti, Bertram Felgenhauer, +Boying Lu, Brian Ewins, Carl Worth, Chris Heath, Chris Wilson, +Claudio Ciccani, Emmanuel Pacaud, Jeff Muizelaar, Jeremy Huddleston, +Jim Meyering, Jinghua Luo, Jody Goldberg, Jonathan Gramain, +Keith Packard, Ken Herron, Kouhei Sutou, Kristian Høgsberg, +Larry Ewing, Martin Ejdestig, Nis Martensen, Peter Weilbacher, +Richard Hult, Shailendra Jain, Søren Sandmann Pedersen, +Vladimir Vukicevic + +Snapshot 1.5.20 (2008-04-04 Carl Worth ) +=========================================================== +This is the tenth snapshot in cairo's unstable 1.5 series. It comes +just two days (and only one working day) after the 1.5.18 +snapshot. The quick snapshot is due to two embarrassing bugs (both +affecting cairo-xlib) that had been introduced in the 1.5.18 +snapshot. The fixes for these are described below along with a few +other fixes, (which hopefully aren't introducing new bugs this time). + +cairo-xlib +---------- +Revert fix from 1.5.18 to allow pattern expansion based on the filter +mode. This fix seemed so boring, (the use case it addresses is almost +never used in practice), that it didn't even get mentioned in the +1.5.18 release notes. However, the "fix" happened to break rendering +that is always used resulting in corrupt image rendering in mozilla, +evolution, and probably everything else that uses cairo. + +Fix to avoid BadMatch errors in cairo_surface_create_similar. These +were introduced, (inadvertently, of course), as part of the fix in +1.5.18 for creating similar surfaces without the Render +extension. Again, thanks to mozilla, (and Vladimir Vukicevic in +particular), for noticing our mistake. + +general +------- +Correctly handle an in-error surface in +cairo_surface_write_to_png. Previously this function would cause an +assertion failure if you gave it a finished surface. Now it cleanly +returns a CAIRO_STATUS_SURFACE_FINISHED result instead. + +Avoid potentially infinite wandering through memory inside +_cairo_hull_prev_valid. Thanks to Jonathan Watt for noticing this +problem: + + https://bugzilla.mozilla.org/show_bug.cgi?id=306649#c21 + +cairo-pdf +--------- +Fix generation of "soft" masks made by drawing to a similar surface +and then calling cairo_mask_surface() with it. + +cairo-svg +--------- +Fix for code that uses cairo_mask() on an intermediate surface which +is later passed to cairo_mask_surface(). + +Snapshot 1.5.18 (2008-04-05 Carl Worth ) +=========================================================== +This is the ninth snapshot in cairo's unstable 1.5 series. It comes +just 4 days after the 1.5.16 snapshot. We had hoped to not need +another snapshot before the final 1.6.0 release, but several critical +bugs were found and fixed in the last few days, so we thought it +important to let people test the fixes with this snapshot. See below +for details. + +documentation +------------- +The README now lists necessary dependencies. + +Various graphics state defaults are now documented, (source pattern is +opaque black, line width is 2.0, line join is miter, line cap is butt, +miter limit is 10.0, etc.). + +general +------- +Several cleanups have been made along many error-path returns, +(carefully propagating up the original error status values, cleaning +up memory leaks during error recovery, etc.). This is yet another in +Chris "ickle" Wilson's long series of error-handling cleanups during +the 1.5 series. + +Avoid undesired clipping when drawing scaled surface patterns with +bilinear filtering. + +cairo-pdf +--------- +Fix emission of 1-bit alpha masks in PDF output. + +Fix a bug that would cause glyphs to be misplaced along the Y axis: + + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%23474136 + + Originally, an issue about a crash, but later leading to the + misplaced glyphs issue being discovered. + +cairo-ps +-------- +Fix misplaced glyphs in cairo's PostScript output. + + This issue occurs when consecutive glyphs are placed far + apart. This case is exercised by the new ft-show-glyphs-table test + case, which was originally inspired by the Debian bug #23474136 + mentioned above. + +Fix more misplaced glyphs in cairo's PostScript output: + + The issue here showed up under very particular circumstance, (when + converting a PDF file with a CFF font with CID Identity-H encoding + and using glyph 0, (defined by the CFF specification as .notdef) + as a space instead). More concretely, this problem appeared when + converting the UbuntuDesktop.pdf file mentioned in this bug + report: + + https://bugs.freedesktop.org/show_bug.cgi?id=15348#c3 + + As usual with arcane font-encoding-specific bugs like this, many + thanks to Adrian Johnson for his magical ability to dive into + specifications and emerge almost instantaneously with fixes. And + thanks to Sebastien Bacher for bringing the bug to our attention. + +cairo-xlib +---------- +Fix serious failure on X servers without the Render extension. + + Since the 1.5.14 snapshot (with support for PseudoColor visuals), + any application attempting to create a "similar" xlib surface would + fail on an X server without the Render extension. Thanks to + Frederic Crozat for pointing out that cairo's test suite was + entirely failing when run against Xvfb. + +Avoid crashing cairo-xlib applications for too-large glyphs + + Naively sending glyphs of any size to the X server will eventually + violate the X limit on maximum request sizes. We now properly + detect when a glyph would be too large and use existing fallbacks + to render the glyph rather than trying to send it to the X server. + +Enable the buggy_repeat workaround for Xorg servers < 1.4 + + We have determined that Xorg 1.3.0 (as packaged in Fedora 8 at + least) has a bug that can result in an X server crash when cairo + uses certain X Render repeat operations, (as exercised by cairo's + extend-reflect test). We avoid this crash by using fallbacks + whenever a repeating surface is needed for any Xorg server with a + version less than 1.4. This is slower, but should prevent the + crash. + + (Meanwhile, there appears to be a separate bug where some X + servers or specific X-server drivers will use random pixmap data + when asked to draw a repeating surface. The buggy_repeat + workaround would also avoid those problems, but we have not yet + characterized whether the new "version < 1.4" is a good + characterization of those problems or not.) + +cairo-quartz-font +----------------- +Implement cairo_font_extents for this backend. + +The cairo-quartz-font implementation added in the 1.5.14 snapshot was +entirely missing support for the cairo_font_extents function. Thanks to +Richard Hult for pointing out this obvious shortcoming, (and obvious +lack of coverage in our test suite): + + CGFont backend returns 0 font extents + https://bugs.freedesktop.org/show_bug.cgi?id=15319 + +Snapshot 1.5.16 (2008-04-01 Carl Worth ) +=========================================================== +This is the eighth snapshot in cairo's unstable 1.5 series. It comes +less than two weeks after the 1.5.14 snapshot and it really is a +legitimate snapshot, (in spite of sharing this date with that of many +bogus announcements). The major change in this snapshot is that the +cairo-quartz backend is now officially "supported", including new API +to construct a font face from a CGFontRef . Also several bug fixes +have been fixed in many backends. See below for details. + +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: + + 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 +1.5.12 snapshot. + +paginated (all of ps, pdf, svg, and win32-printing) +--------------------------------------------------- +Fix assertion failure when some drawing elements are outside the page +boundaries, (this bug was noticed when using Inkscape to print a +drawing with landscape orientation to a portrait-oriented piece of +paper). + +cairo-ps +-------- +Fix of bug causing incorrect glyph positioning. + +Fix handling of CAIRO_OPERATOR_SOURCE. + +cairo-pdf +--------- +More reduction of unnecessary digits of precision in PDF output. + +Fix handling of CAIRO_OPERATOR_SOURCE. + +cairo-svg +--------- +Fix bug in usage of libpng that was preventing cairo_mask from working +with the svg backend. + +Fix transformation of source pattern for cairo_stroke(). + +cairo-win32-printing +-------------------- +Fix fallback resolution, (thanks again to inkscape users/developers +for helping us find this one). + +cairo-quartz +------------ +Mark the cairo-quartz backend as "supported" rather than +"experimental". This means the following: + + * The backend will now be built by default (if possible). + + * We are committing that the backend-specific API (as published in + cairo-quartz.h) are stable and will be supported in all future + cairo 1.x releases. + + * We are committing that the output quality of this backend + compares favorably with other cairo backends, (and that quality + is ensured by good results from the cairo test suite). + + * We recommend that distributions build and distribute this + backend when possible. + +Note that the cairo_quartz_image API (in cairo-quartz-image.h) is +still experimental, will not build by default, (pass +--enable-quartz-image to configure to build it), and may see API +changes before it is marked as "supported" in a future release. + +Put the CAIRO_FONT_TYPE_ATSUI name back into +cairo-deprecated.h. Without this, the cairo 1.5.14 snapshot broke all +builds for applications using the C++ cairomm bindings (and perhaps +others) which have the CAIRO_FONT_TYPE_ATSUI name in their header +files. This breakage happened even for applications not using +cairo-quartz at all. + + Note: Even though the CAIRO_FONT_TYPE_ATSUI name is provided to + avoid this build breakage, we still recommend that bindings and + applications move to the new, and more accurate, + CAIRO_FONT_TYPE_QUARTZ name. + +Replace the implementation of cairo-quartz-font to use CFFont instead +of ATSUI. The CGFont API is a better fit than ATSUI, and this new +implementation is also more correct than the old one as well. + +This also adds the following new API call: + + cairo_public cairo_font_face_t * + cairo_quartz_font_face_create_for_cgfont (CGFontRef font); + +The previous cairo_quartz_font_face_create_for_atsu_font_id function +continues to exist and is part of the supported API going +forward. (However, the old name of that same function, which was +cairo_atsui_font_face_create_for_atsu_font_id is officially +deprecated. Any source code using the old name should be updated to +use the new name.) + +Fix transformation of source pattern for cairo_stroke(). + +cairo-win32 +----------- +Avoid crash in create_similar is cairo_win32_surface_create fails. + +Snapshot 1.5.14 (2008-03-20 Carl Worth ) +=========================================================== +This is the seventh snapshot in cairo's unstable 1.5 series. It comes +3 weeks after the 1.5.12 snapshot. This snapshot includes support for +arbitrary X server visuals, (including PseudoColor), which was the +final remaining cairo-specific item on the cairo 1.6 roadmap. It also +includes a huge number of improvements to the cairo-quartz backend. So +this is effectively a cairo 1.6 release candidate. We expect very few +changes from now until 1.6 and only for specific bug fixes. + +API Change +---------- +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_FONT_TYPE_ATSUI -> CAIRO_FONT_TYPE_QUARTZ + + cairo_atsui_font_face_create_for_atsu_font_id -> + cairo_quartz_font_font_create_for_atsu_font_id + +This API change is justified by the cairo-quartz backend still be +marked as "experimental" rather than "supported", (though this is one +step toward making the change to "supported" before 1.6). Cairo will +still provide ABI compatibility with the old symbol name, however. + +paginated (all of ps, pdf, svg, and win32-printing) +--------------------------------------------------- +Optimize by not analyzing an image surface for transparency more than +once, (previously all images were analyzed twice). + +cairo-ps and cairo-pdf +---------------------- +Avoiding emitting a matrix into the stroke output when unnecessary, +(making output size more efficient). + +Reduce rounding error of path shapes by factoring large scale factors +out of the path matrix, (ensuring that a fixed-number of printed +digits for path coordinates contains as much information as possible). + +Reduce excess digits for text position coordinates. This makes the +output file size much smaller without making the result any less +correct. + +cairo-ps +-------- +Eliminate bug causing extraneous text repetition on Linux PostScript +output in some cases. + + See: Mozilla Bug 419917 – Printed page contents are reflected + inside bordered tables (Linux-only) + + https://bugzilla.mozilla.org/show_bug.cgi?id=419917 + +Optimize output when EXTEND_PAD is used. + +cairo-pdf +--------- +Fix to not use fill-stroke operator with transparent fill, (else PDF +output doesn't match the cairo-defined correct result). See: + + https://bugs.launchpad.net/inkscape/+bug/202096 + +cairo-svg +--------- +Fix stroke of path with a non-solid-color source pattern: + + https://bugs.freedesktop.org/show_bug.cgi?id=14556 + +cairo-quartz +------------ +Fix text rendering with gradient or image source pattern. + +Handling antialiasing correctly for cairo_stroke(), cairo_clip(), and +cairo_show_text()/cairo_show_glyphs(). + +Correctly handle gradients with non-identity transformations: + + Fixes https://bugs.freedesktop.org/show_bug.cgi?id=14248 + +Add native implementation of REPEAT and REFLECT extend modes for +gradients. + +Fix implementation for the "unbounded" operators, (CAIRO_OPERATOR_OUT, +_IN, _DEST_IN, and _DEST_ATOP). + +Correctly handle endiannees in multi-architecture compiles on Mac OS +X. + +Avoid behavior which would cause Core Graphics to print warnings to +the console in some cases. + +cairo-win32 +----------- +Fix handling of miter limit. + +cairo-win32-printing +-------------------- +Fix to not use a 1bpp temporary surface in some cases while printing, +(so grayscale data is preserved rather than just becoming black and +white). + +cairo-xlib +---------- +Add support for rendering to arbitrary TrueColor X server +visuals. This fixes at least the following bugs: + + cairo doesn't support 8-bit truecolor visuals + https://bugs.freedesktop.org/show_bug.cgi?id=7735 + + cairo doesn't support 655 xlib format + https://bugs.freedesktop.org/show_bug.cgi?id=9719 + +Add support for rendering to 8-bit PseudoColor X server visuals. This +fixes the following bug: + + Cairo doesn't support 8-bit pseudocolor visuals + https://bugs.freedesktop.org/show_bug.cgi?id=4945 + +Snapshot 1.5.12 (2008-02-28 Carl Worth ) +=========================================================== +This is the sixth snapshot in cairo's unstable 1.5 series. It comes 1 +week after the 1.5.10 snapshot. This snapshot includes the +long-awaited change from 16.16 to 24.8 fixed-point values, (see below +for why you should care). It also includes several backend-specific +bug fixes. + +24.8 fixed-point format +----------------------- +Cairo has always converted path coordinates to a fixed-point +representation very early in its processing. Historically, this has +been a 32-bit representation with 16 bits of integer for the +device-pixel grid and 16 bits of sub-pixel positioning. The choice of +16 bits for the integer coordinate space was based on the 16-bit limit +for X Window drawables. + +This 16-bit limit has proven problematic for many applications. It's +an especially vexing problem when targeting non-X backends that don't +have any 16-bit restriction. But even when targeting cairo-xlib, it's +often desirable to draw a large shape, (say a background rectangle), +that extends beyond the surface bounds and expect it to fill the +surface completely, (rather than overflowing and triggering random +behavior). + +Meanwhile, nobody has ever really needed 16 bits of sub-pixel +precision. + +With this snapshot, the fixed-point system is still in place and is +still using a 32-bit representation, (future versions of cairo might +move entirely to floating-point when targeting PDF output for +example). But the representation now provides 24 bits of pixel +addressing and only 8 bits of sub-pixel positioning. This should give +a much less stifling space to many applications. + +However, the underlying pixman library still has 16-bit limitations in +many places, (it has its roots in the X server as well). Until those +are also fixed, applications targeting cairo image surfaces, or +hitting software fallbacks when targeting other surfaces will still +encounter problems with device-space values needing more than 16 +integer bits. + +generic fixes +------------- +Add a few tests to the test suite to increase coverage. + +Cleanup a few error-handling paths, (propagate error correctly). + +cairo-ft +-------- +Fix handling of font sizes smaller than 1 device pixel. + +cairo-pdf +--------- +Fix to properly save/restore clip when analyzing meta-surface +patterns, (fixing a couple of test-suite failures). + +Implement native support for CAIRO_OPERATOR_SOURCE when the source +pattern is opaque. + +Emit rectangles as PDF rectangles ("re" operator) rather than as +general paths. + +cairo-ps +-------- +Fix to work properly with the 16.16->24.8 change. + +cairo-svg +--------- +Fix CAIRO_EXTEND_REFLECT by using an image fallback, (there's no +direct SVG support for reflected patterns). + +Fix the use of alpha-only masks, (such as CAIRO_FORMAT_A8). + +cairo-quartz +------------ +Add new API for efficiently using image data as a source: + + cairo_surface_t * + cairo_quartz_image_surface_create (cairo_surface_t *image_surface); + + cairo_surface_t * + cairo_quartz_image_surface_get_image (cairo_surface_t *surface); + +For full documentation, see: + + https://cairographics.org/manual/cairo-Quartz-Surfaces.html#cairo-quartz-image-surface-create + +Several fixes for cairo_mask(). + +cairo-atsui +----------- +Change default from from Monaco to Helvetica to be more consistent +with other font backends. + +Snapshot 1.5.10 (2008-02-20 Carl Worth ) +=========================================================== +This is the fifth snapshot in cairo's unstable 1.5 series. It comes 3 +weeks after the 1.5.8 snapshot. This snapshot adds one new API +function, (cairo_has_current_point), and the usual mix of +improvements, (more efficient PostScript/PDF output, optimized +stroking), and fixes (more robust error-handling, etc.). See below for +details. + +New API +------- +Add a new function to query if there is a current point: + + cairo_bool_t + cairo_has_current_point (cairo_t *cr); + +There is no current point immediately after cairo_create(), nor after +cairo_new_path() or cairo_new_sub_path(). There is a current point +after any of the path-creation functions, (cairo_move_to, +cairo_line_to, cairo_curve_to, etc.). + +With this new function, we also revert the change of the return type +of cairo_get_current_point from cairo 1.5.8, (it's now a void function +again). + +Optimizations +------------- +Optimize stroking code to avoid repeated calculation of redundant +values, (particularly significant for very large, offscreen paths). + +General fixes +------------- +Patch a few more potential buffer overruns, (due to integer +overflow). + +Many fixes and improvements to cairo's error-handling, (ensure that +correct error values are returned, clean up memory leaks on +error-handling paths, etc.). + +Fix a potential infinite loop when stroking a spline with a pen that +has been transformed to a line segment. + +Remove treating NULL as a synonym for a valid cairo_font_options_t* +with default values, (a change that had been introduced as of cairo +1.5.8). + +Remove the altered handling of tolerance and fallback-resolution that +had been introduced as of cairo 1.5.4. + +cairo-xlib +---------- +Pass the original Drawable, (as opposed to the root window), to +XCreatePixmap when creating a similar surface. This gives the X server +more information so that it can be clever and efficient. + +cairo-pdf +--------- +Fix the rendering of repeating and reflecting patterns. + +Ensure miter limit is always >= 1, (smaller limits are not meaningful, +but they can cause some PDF viewers to fail to display pages). + +Generate more efficient output when the same path is used for both +fill and stroke. + +cairo-ps +-------- +Start sharing much of the cairo-pdf code rather than implementing very +similar code in cairo-ps. + +Implement native support for repeating and reflecting linear +gradients. + +Implement reflected surface patterns. + +Ensure miter limit is always >= 1, (smaller limits are not meaningful, +but they can cause some PostScript viewers to crash). + +Generate PostScript that will perform more efficiently and use less +memory on printers, (use currentfile instead of a giant string array +for image data, and avoid using PostScript patterns for paint() and +fill() when possible). + +cairo-svg +--------- +Avoid unnecessary rasterization when copying a "similar" surface to +another svg surface, (allow the SOURCE operator to be implemented with +all-vector operations if there are no underlying objects). + +cairo-atsui +----------- +Eliminate infinite loop when attempting to render an empty string. + +Snapshot 1.5.8 (2008-01-30 Carl Worth ) +========================================================== +This is the fourth snapshot in cairo's unstable 1.5 series. It comes 2 +weeks after the 1.5.6 snapshot. It adds a few new API functions. Most +notably all callers of cairo_image_surface_create_for_data should now +be calling cairo_format_stride_for_width to compute a legal stride +value. See below for more details. + +New API in cairo 1.5.8 +---------------------- +We've added a new function that should be called to compute a legal +stride value before allocating data to be used with +cairo_image_surface_create_for_data: + + int + cairo_format_stride_for_width (cairo_format_t format, + int width); + +We've also added a new cairo_path_extents function that can be used to +compute a bounding box for geometry such as a single line segment, +(contrast with cairo_path_extents and cairo_stroke_extents): + + void + cairo_path_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +And finally, we've added a function to allow for querying the +XRenderPictFormat of a cairo-xlib surface: + + XRenderPictFormat * + cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface); + +API changes +----------- +Fix return types of cairo_surface_show_page and +cairo_surface_copy_page. This is an API change to functions that are +new in the 1.5 series, so not an API break compared to any stable +cairo release, (1.0.x, 1.2.x, 1.4.x). + +Change the return type of cairo_get_current_point() from void to +cairo_status_t. This allows the caller to receive a +CAIRO_STATUS_NO_CURRENT_POINT value to distinguish the a current point +at the origin from no current point existing. + +Performance improvement +----------------------- +Improve performance of clipping by using an optimized code path +internally, (with the ADD operator instead of IN). + +General bug fixes +----------------- +Fix various cairo_*_extents functions to initialize the return-value +variables even in the case of a cairo_t in error. + +Treat NULL as a legitimate value for cairo_font_options_t*. [NOTE: +On discussion afterwards, we decided against this change so it has +been removed as of cairo 1.5.10.] + +Fix rendering with CAIRO_ANTIALIAS_NONE to be more predictable, (that +is, to avoid seams appearing when geometry and imagery share an +identical edge). Portions of this fix are in the pixman library and +will appear in a future release of that library. + +Avoid triggering an error for a font size of 0. + +Miscellaneous changes +--------------------- +Require pixman >= 0.9.6. + +There has been a tremendous amount improvement to cairo's +documentation. We're delighted that 100% of the public API has at +least some documentation in the API reference manual. Many thanks to +Behdad Esfahbod and Nis Martensen for leading this effort. + +cairo-pdf and cairo-ps +---------------------- +Eliminate failure when a Type 1 font is embedded with an explicit +glyph 0. + +cairo-pdf +--------- +Implement a more correct and more efficient approach for patterns with +an extend mode of CAIRO_EXTEND_REFLECT. + +cairo-ps +-------- +Fix image masks to properly pack and pad mask bits. + +cairo-quartz +------------ +Take care to only use DrawTiledImage for integer-aligned images, (and +use slower paths to get the correct result in other cases). + +cairo-win32 +----------- +Fix for older versions of mingw. + +Improve the handling of the clipping with the win32 and win32-printing +surfaces. + +Fix rendering of non black/white text. + +Snapshot 1.5.6 (2008-01-15 Carl Worth ) +========================================================== +This is the third snapshot in cairo's unstable 1.5 series. It comes +about 6 weeks after the 1.5.4 snapshot. The only API addition compared +to 1.5.4 is very minor, (a new value CAIRO_STATUS_TEMP_FILE_ERROR). +The remainder of the changes are the usual accumulation of bug fixes +and improvements. See below for details. + +General bug fixes +----------------- +Fix handling of fonts that contain a mixture of outline and bitmapped +glyphs. There was a change in this handling in 1.5.4 that improved +some cases and also regressed other cases. Now, all cases should be +handled quite well. + +Fix alignment issues that were causing SIGBUS failures on SPARC. + +Fix a regression (which first appeared in 1.5.2) where stroking under +a large scale would sometimes incorrectly replace a miter join with a +bevel join. (Thanks to Keith Packard.) + +Fix reporting of zero-sized extents to be {0,0} rather than +{INT_MAX,INT_MIN}. This avoids several integer overflow and +allocations of massive regions in some cases. + +Fix failures of gradients with no stops, (quartz, ps, and pdf). + +Fix handling of Type 1 fonts on Windows platforms. + +Fix handling of Type 1 fonts with no specific family name in the font +itself, (generate a CairoFont-x-y name). + +Handle NULL string values in cairo_show_text, cairo_show_glyphs, and +friends. + +Many robustness improvements along error-handling paths, (thanks as +always, to Chris "ickle" Wilson). + +Various other minor fixes. + +Paginated backends (PDF/PostScript/win32-printing) +-------------------------------------------------- +Avoid unnecessary rasterization when using a paginated surface as a +source, (such as drawing from one pdf surface to another). + +Fix replaying of paginated surface with more than one level of push/pop +group. + +cairo-xlib +---------- +Fix xlib backend to not consider recent X server release as having a +buggy repeat implementation in the Render extension. + +cairo-pdf +--------- +Fix PDF output to avoid triggering very slow rendering in PDF viewers, +(avoid starting and stopping the content stream for each pattern +emission). + +Support CAIRO_OPERATOR_SOURCE in cases where there is nothing below +the object being drawn. + +Fix to avoid seams appearing between multiple fallback regions. + +cairo-ps (PostScript) +--------------------- +Use correct bounding box in Type 3 fonts. + +Fix several bugs in cairo's PostScript output. These include making +the PostScript output more compatible with recent versions of +ghostscript that are more strict about Type 3 fonts, for +example. + +Fix for win32 to not attempt to create temporary files in the root +directory, (where the user may not have write permission). + +Avoid generating Level 3 PostScript if Level 2 is sufficient. Also, +add code in output documents to alert the user if Level 3 PostScript +is handed to a device that cannot handle PostScript beyond Level +2. + +cairo-directfb +-------------- +Various performance optimizations. + +Fixed support for small surfaces (less than 8x8). + +Provide support for environment variables CAIRO_DIRECTFB_NO_ACCEL to +disable acceleration and CAIRO_DIRECTFB_ARGB_FONT to enable ARGB fonts +instead of A8. + +cairo-os2 +--------- +Allow OS/2 APIs instead of C library allocation functions. + +Snapshot 1.5.4 (2007-12-05 Carl Worth ) +========================================================== +This is the second snapshot in cairo's unstable 1.5 series. It comes +just over 1 month after the 1.5.2 snapshot. There are no API changes +or additions in 1.5.4 compared to 1.5.2, but there are several bug +fixes, and some optimizations. Most of these apply to particular +backends. See below for details. + +General improvements +-------------------- +Use less memory for spline approximation calculations. + +Change how the tolerance value is interpreted with regard to +fallback-resolution. [Note: On further discussion, we decided against +this change for now. It is removed as of cairo 1.5.10.] + +Fix precision of floating-point values in vector-output backends to +avoid rounding errors with very small numbers. + +Xlib improvements +----------------- +Fix bug in glyph rendering with xlib, (due to everything being clipped +out). This was a regression in the 1.5.2 snapshot that was visible in +the GIMP, for example. See: + + cairo 1.5.2 causes font problems in GIMP 2.4 status bar and evolution 2.12.1 + https://bugs.freedesktop.org/show_bug.cgi?id=13084 + +PostScript improvements +----------------------- +Fix bug leading to invalid PostScript files when rendering +text, (need "0 0 xyshow" instead of "0 xyshow"). + +Fix many issues with Type 3 fonts, including making the resulting text +extractable. + +Quartz improvements +------------------- +Fix font metrics height value for ATSUI, (helps webkit on GTK+ OS X +layout nicely). + +Fix gradients. + +Fix EXTEND_NONE mode for patterns. + +Fix cairo_quartz_surface_create to properly clear the new surface +in cairo_quartz_surface_create. + +Fix to correctly handle 0x0 sized surfaces. + +Optimize drawing of EXTEND_REPEAT patterns for OS X 10.5. + +Snapshot 1.5.2 (2007-10-30 Carl Worth ) +========================================================== +This is the first snapshot in cairo's unstable 1.5 series. It comes 4 +months after the 1.4.10 release. This snapshot includes significant +improvements to PDF and PostScript output, which is one of the things +in which we're most interested in getting feedback. There are a couple +of minor API additions, and several optimizations, (primarily in the +"print/vector" backends). And there are dozens of bug fixes and +robustness improvements. + +New dependency on external pixman library +----------------------------------------- +A significant change in this snapshot compared to all previous cairo +releases is that cairo now depends on an external "pixman" library for +its software rendering. Previously this same code was compiled +internally as part of cairo, but now the code is separate so that both +cairo and the X server can now share common code, (thanks very much to +Søren Sandmann for his work on separating pixman and maintaining it). + +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: + + https://cairographics.org/releases/pixman-0.9.6.tar.gz + + which can be verified with: + + https://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1 + 66f01a682c64403a3d7a855ba5aa609ed93bcb9e pixman-0.9.6.tar.gz + + https://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1.asc + (signed by Carl Worth) + +Major PDF/PostScript improvements +--------------------------------- +Adrian Johnson has done some long-awaited work to make cairo's PDF and +PostScript output more interesting than ever before. First, many +operations that previously triggered image fallbacks will now be +rendered as native vectors. These operations include: + + PDF: cairo_push_group, cairo_surface_create_similar, + cairo_mask, A8/A1 surface sources, repeating/reflecting linear + gradients. + + PostScript: cairo_push_group, cairo_surface_create_similar, + gradients, bilevel alpha masks, (for example, all values either 0 or + 255 for an A8 mask). + +Not only that, but when an image fallback is required, it will now be +limited to only the necessary region. For example, a tiny translucent +image overlaying a small portion of text would previously caused an +entire PostScript page to be rendered as a giant image. Now, the +majority of that page will be nice text, and there will only be a tiny +image in the output. + +Additionally, the PostScript output now carefully encodes text so that +if it is subsequently converted to PDF, the text will be +selectable. + +This is very exciting progress, and we're hoping to hear from users +during the 1.5 series about how things have improved, (for example, +inkscape users doing cairo-based PDF export: please let us know how +things look). And feel free to pass your thanks along to Adrian for his excellent work. + +NOTE: This much improved PDF output makes more sophisticated use of +functionality in the PDF specification. This means that cairo's output +will sometimes expose bugs in some free software PDF viewers, (evince, +poppler, and xpdf, for example), that are not yet ready for such PDF +files. We're working with the poppler maintainers to get these bugs +fixed as quickly as possible. In the meantime, please double-check +with other PDF viewers if cairo-generated PDF files are not being +rendered correctly. It may be due to a bug in the viewer rather than +in the PDF file that cairo has created. + +Robustness improvements +----------------------- +Chris Wilson has made the largest contribution by far to cairo 1.5.2, +(in number of commits). His more than 150 commits include a huge +number of fixes to increase cairo's robustness. These fixes make cairo +more robust against invalid and degenerate input, (NaN, empty path, +etc.), against size-0 malloc calls, against memory leaks on +error-recovery paths, and against other failures during error +handling. He also implemented atomic operations to cairo, and used +them to fix cairo's previously non-thread-safe reference counting, +again improving robustness. + +Chris has put a tremendous amount of time and effort into writing +analysis tools for this work, and in running those tools and fixing +the problems they report. We're very grateful for this work, and hope +that all cairo users appreciate the more robust implementation that +results from it. + +This work is largely thankless, so it might make sense to notice +sometime that cairo has been running quite smoothly for you, and when +you do, send a quick "thank you" off to Chris Wilson, since it +is all definitely running smoother thanks to his work. + +New API +------- +There are no major additions to cairo's core API. The only new, +generic functions are: + + void + cairo_surface_copy_page (cairo_surface_t *surface); + + void + cairo_surface_show_page (cairo_surface_t *surface); + +which can now be used much more conveniently than the existing +cairo_copy_page and cairo_show_page functions in some +situations. These functions act identically, but require only a +cairo_surface_t* and not a cairo_t*. + +All other API additions are specific to particular backends. + +New cairo-win32 API (new font face function and "win32 printing" surface) +------------------------------------------------------------------------- +There is a new function for creating a win32 font face for both a +logfontw and an hfont together. This complements the existing +functions for creating a font face from one or the other: + + cairo_font_face_t * + cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, + HFONT font); + +There is also a new "win32 printing" surface: + + cairo_surface_t * + cairo_win32_printing_surface_create (HDC hdc); + +This interface looks identical to the original +cairo_win32_surface_create, (both accept and HDC), but the behavior of +this new surface is very different. It should only be called with a +printing DC, and will result in all drawing commands being stored into +a meta-surface and emitted after each page is complete, with analysis +to do as minimal image-based fallbacks as necessary. The behavior and +implementation shares much with the PDF and PostScript backends. + +New cairo-ps API (EPS and PostScript level control) +--------------------------------------------------- +An often requested feature has been the ability to generate +Encapsulated PostScript (EPS) with cairo. We have that now with the +following very simple API. Just do cairo_ps_surface_create as usual +then call this function with a true value: + + void + cairo_ps_surface_set_eps (cairo_surface_t *surface, + cairo_bool_t eps); + +[NOTE: As always with snapshots, it's possible---though not very +likely---that the API could still be modified before a final +release. For example, this is the first public cairo function that +accepts a Boolean parameter. I'm generally opposed to Boolean +parameters, but this is probably the one case where I'm willing to +accept one, (namely a "set" function that accepts a single Boolean).] + +Also, it is now possible to control what PostScript level to target, +(either level 2 or level 3), with the following new API: + + typedef enum _cairo_ps_level { + CAIRO_PS_LEVEL_2, + CAIRO_PS_LEVEL_3 + } cairo_ps_level_t; + + void + cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, + cairo_ps_level_t level); + + void + cairo_ps_get_levels (cairo_ps_level_t const **levels, + int *num_levels); + + const char * + cairo_ps_level_to_string (cairo_ps_level_t level); + +Improvement for cairo-quartz +---------------------------- +Brian Ewins had contributed several improvements to cairo-quartz. These +include an implementation of EXTEND_NONE for linear and radial +gradients, (so this extend mode will no longer trigger image fallbacks +for these gradients), as well as native surface-mask clipping, (only +on OS X 10.4+ where the CGContextClipToMask function is available). + +He also fixed a semantic mismatch between cairo and quartz for dashing +with an odd number of entries in the dash array. + +We're grateful for Brian since not many quartz-specific improvements +to cairo would be happening without him. + +Optimizations +------------- +Optimize SVG output for when the same path is both filled and stroked, +and avoid unnecessary identity matrix in SVG output. (Emmanuel Pacaud). + +Optimize PS output to take less space (Ken Herron). + +Make PS output more compliant with DSC recommendations (avoid initclip +and copy_page) (Adrian Johnson). + +Make PDF output more compact (Adrian Johnson). + +Release glyph surfaces after uploading them to the X server, (should +save some memory for many xlib-using cairo application). (Behdad +Esfahbod). + +Optimize cairo-win32 to use fewer GDI objects (Vladimir Vukicevic). + +win32-printing: Avoid falling back to images when alpha == 255 +everywhere. (Adrian Johnson). + +win32-printing: Avoid falling back for cairo_push_group and +cairo_surface_create_similar. (Adrian Johnson) + +Bug fixes +--------- +Avoid potential integer overflows when allocating large buffers +(Vladimir Vukicevic). + +Preparations to allow the 16.16 fixed-point format to change to +24.8 (Vladimir Vukicevic). + +Fix bugs for unsupported X server visuals (rgb565, rgb555, bgr888, and +abgr8888). (Carl Worth and Vladimir Vukicevic) + +Fix bugs in PDF gradients (Adrian Johnson). + +Fix cairo-xlib to build without requiring Xrender header +files (Behdad Esfahbod). + +Make cairo more resilient in the case of glyphs not being available in +the current font. (Behdad Esfahbod) + +Prevent crashes when both atsui and ft font backends are compiled in +(Brian Ewins). + +Make font subsetting code more robust against fonts that don't include +optional tables (Adrian Johnson). + +Fix CFF subsetting bug, (which manifested by generating PDF files that +Apple's Preview viewer could not read) (Adrian Johnson). + +Fixed error handling for quartz and ATSUI backends (Brian Ewins). + +Avoid rounding problems by pre-transforming to avoid integer-only +restrictions on transformation in GDI (Adrian Johnson). + +Fixed an obscure bug (#7245) computing extents for some stroked +paths (Carl Worth). + +Fix crashes due to extreme transformation of the pen, (seems to show +up in many .swf files for some reason) (Carl Worth). + +Release 1.4.10 (2007-06-27 Carl Worth ) +========================================================== +This is the fifth update in cairo's stable 1.4 series. It comes +roughly three weeks after the 1.4.8 release. The most significant +change in this release is a fix to avoid an X error in certain cases, +(that were causing OpenOffice.org to crash in Fedora). There is also a +semantic change to include child window contents when using an xlib +surface as a source, an optimization when drawing many rectangles, and +several minor fixes. + +Eliminate X errors that were killing OO.o (Chris Wilson) +-------------------------------------------------------- +Cairo is fixed to avoid the X errors propagated when cleaning up +Render Pictures after the application had already destroyed the +Drawable they reference. (It would be nice if the X server wouldn't +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 + +Use IncludeInferiors when using xlib surface as a source (Ryan Lortie) +---------------------------------------------------------------------- +When an xlib surface is used as the source of a draw operation the +contents of child windows are now included in the source data. The +semantics of drawing to xlib surfaces are unchanged (ie: draws are +still clipped by child windows overlapping the destination window). + +Optimize drawing of many rectangles (Vladimir Vukicevic) +-------------------------------------------------------- +Avoid O(N*N) loop when filling many axis-aligned rectangles, (either +many rectangles as separate sub-paths or due to dashing). + +Miscellaneous fixes +------------------- +Fix cairo-perf on Solaris by linking to librt. (Behdad Esfahbod) + +Fix make check for systems that require executable files to have a +particular extension. (Behdad Esfahbod) + +Eliminate some warnings in cairo-quartz. (Brian Ewins) + +Fix build-breaking typo for cairo-directfb. (Chris Wilson) + +Release 1.4.8 (2007-06-07 Carl Worth ) +========================================================= +This is the fourth update in cairo's stable 1.4 series. It comes just +over five weeks after the 1.4.6 release. This release includes a +thread-safe surface-cache for solid patterns which significantly +improves text rendering with the xlib backend. Also, dozens of error +paths in cairo have been fixed thanks to extensive fault-injection +testing by Chris Wilson. + +Surface cache for solid patterns +-------------------------------- +Originally written by Jorn Baayen, the introduction of a small cache +for surfaces created for solid patterns improves performance +dramatically. For example, this reduces the volume of X requests +during text rendering to the same level as Xft. + +This cache first made its appearance in a 1.3.x snapshot, but was +removed before appearing in any previous major release due to +complications with multi-threaded programs. For example, programs like +evince that would carefully restrict usage of cairo-xlib to a single +thread were unpleasantly surprised to find that using cairo-image in a +separate thread could trigger X requests. + +Behdad Esfahbod designed a fix which was implemented by Chris +Wilson. Now, the necessary X requests are queued up until the next +time the application directly operates on an xlib surface. + +Improved error handling paths +------------------------------ +Chris Wilson continued the excellent work he started in cairo 1.4.4 to +make cairo much more robust against out-of-memory and other errors. He +applied his memory allocation fault injection cairo's main test suite, +(previously he had applied it to cairo's performance suite). + +Chris's testing found dozens of bugs which he fixed. Many of these +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: + + 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! + +Other fixes +----------- +Cleanup of mutex declarations (Behdad Esfahbod) + +Remove unnecessary clip region from SVG output (Emmanuel Pacaud) + +Remove Xsun from the buggy_repeat blacklist (Elaine Xiong) + +ATSUI: Fix glyph measurement: faster and more correct (Brian Ewins) + +Quartz: fixed 'extend' behaviour for patterns, improved pattern performance, +and a few smaller correctness fixes. (Brian Ewins, Vladimir Vukicevic) + +Release 1.4.6 (2007-05-01 Carl Worth ) +========================================================= +This is the third update in cairo's stable 1.4 series. It comes a +little less than three weeks since the 1.4.4 release. This release +fixes the broken mutex initialization that made cairo 1.4.4 unusable +on win32, OS/2, and BeOS systems. This release also adds significant +improvements to cairo's PDF backend, (native gradients!), and a couple +of performance optimizations, (one of which is very significant for +users of the xlib backend). See below for more details. + +Repaired mutex initialization +----------------------------- +We apologize that cairo 1.4.4 did little more than crash on many +platforms which are less-frequently used by the most regular cairo +maintainers, (win32, OS/2, and BeOS). The mutex initialization +problems that caused those crashes should be fixed now. And to avoid +similar problems in the future, we've now started posting pre-release +snapshots to get better testing, (subscribe to cairo@cairographics.org +if you're interested in getting notified of those and testing them). + +PDF Improvements +---------------- +Thanks to Adrian Johnson, (cairo PDF hacker extraordinaire), we have +several improvements to cairo's PDF backend to announce: + +Native gradients: + + As of cairo 1.4.6, cairo will now generate native PDF gradients in + many cases, (previously, the presence of a gradient on any page + would force rasterized output for that page). Currently, only + gradients with extend types of PAD (the default) or NONE will + generate native PDF gradients---others will still trigger + rasterization, (but look for support for other extend modes in a + future release). Many thanks to Miklós Erdélyi as well, who did the + initial work for this support. + +Better compatibility with PDF viewers: + + The PDF output from cairo should now be displayed correctly by a + wider range of PDF viewers. Adrian tested cairo's PDF output against + many PDF viewers, identified a common bug in many of those viewers + (ignoring the CTM matrix in some cases), and modified cairo's output + to avoid triggering that bugs (pre-transforming coordinates and + using an identity matrix). + +Better OpenType/CFF subsetting: + + Cairo will now embed CFF and TrueType fonts as CID fonts. + +Performance optimizations +------------------------- +Faster cairo_paint_with_alpha: + + The cairo_paint_with_alpha call is used to apply a uniform alpha + mask to a pattern. For example, it can be used to gradually fade an + image out or in. Jeff Muizelaar fixed some missing/broken + optimizations within the implementation of this function resulting + in cairo_paint_with_alpha being up to 4 times faster when using + cairo's image backend. + +Optimize rendering of "off-screen" geometry: + + Something that applications often do is to ask cairo to render + things that are either partially or wholly outside the current clip + region. Since 1.4.0 the image backend has been fixed to not waste + too much time in this case. But other backends have still been + suffering. + + In particular, the xlib backend has often performed quite badly in + this situation. This is due to a bug in the implementation of + trapezoid rasterization in many X servers. + + Now, in cairo 1.4.6 there is a higher-level fix for this + situation. Cairo now eliminates or clips trapezoids that are wholly + or partially outside the clip region before handing the trapezoids + to the backend. This means that the X server's performance bug is + avoided in almost all cases. + + The net result is that doing an extreme zoom-in of vector-based + objects drawn with cairo might have previously brought the X server + to its knees as it allocated buffers large enough to fit all of the + geometry, (whether visible or not). But now the memory usage should + be bounded and performance should be dramatically better. + +Miscellaneous +------------- +Behdad contributed an impressively long series of changes that +organizes cairo's internals in several ways that will be very +beneficial to cairo developers. Thanks, Behdad! + +Behdad has also provided a utility for generating malloc statistics, +(which was used during the great malloc purges of 1.4.2 and +1.4.4). This utility isn't specific to cairo so may be of benefit to +others. It is found in cairo/util/malloc-stats.c and here are Behdad's +notes on using it: + + To build, do: + + make malloc-stats.so + + inside util/, and to use, run: + + 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 + +Finally, the cairo-perf-diff-files utility was enhanced to allow for +generating performance reports from several runs of the same backend +while some system variables were changed. For example, this is now +being used to allow cairo-perf to measure the performance of various +different acceleration architectures and configuration options of the +X.org X server. + +Release 1.4.4 (2007-04-13 Carl Worth ) +========================================================= +This is the second update release in cairo's stable 1.4 series. It +comes just less than a month after 1.4.2. The changes since 1.4.2 +consist primarily of bug fixes, but also include at least one +optimization. See below for details. + +Of all the work that went into the 1.4.4 release + +There have been lots of individuals doing lots of great work on cairo, +but two efforts during the 1.4.4 series deserve particular mention: + +Internal cleanup of error handling, (Chris Wilson) +-------------------------------------------------- +Chris contributed a tremendous series of patches (74 patches!) to +improve cairo's handling of out-of-memory and other errors. He began +by adding gcc's warn_unused_attribute to as many functions as +possible, and then launched into the ambitious efforts of adding +correct code to quiet the dozens of resulting warnings. + +Chris also wrote a custom valgrind skin to systematically inject +malloc failures into cairo, and did all the work necessary to verify +that cairo's performance test suite runs to completion without +crashing. + +The end result is a much more robust implementation. Previously, many +error conditions would have gone unnoticed and would have led to +assertion failures, segmentation faults, or other harder-to-diagnose +problems. Now, more than ever, cairo should cleanly let the user know +of problems through cairo_status and other similar status +functions. Well done, Chris! + +More malloc reduction, (Mathias Hasselmann) +------------------------------------------- +After 1.4.0, Behdad launched an effort to chase down excessive calls +to malloc within the implementation of cairo. He fixed a lot of +malloc-happy objects for 1.4.2, but one of the worst offenders, +(pixman regions), was left around. Mathias contributed an excellent +series of 15 patches to finish off this effort. + +The end result is a cairo that calls malloc much less often than it +did before. Compared to 1.4.2, 55% of the calls to malloc have been +eliminate, (and 60% have been eliminated compared to 1.4.0). Well +done, Mathias! + +Other improvements since 1.4.2 +------------------------------ +• Centralize mutex declarations (will reduce future build breaks), + (Mathias Hasselmann) + +• Reduce malloc by caching recently freed pattern objects (Chris + Wilson) + +• Fix some broken composite operations (David Reveman) + https://bugs.freedesktop.org/show_bug.cgi?id=5777 + +Backend-specific fixes +---------------------- +PDF: + • Use TJ operator for more compact representation of glyphs (Adrian + Johnson) + + • Fix glyph positioning bug when glyphs are not horizontal + https://lists.freedesktop.org/archives/cairo/2007-April/010337.html + +win32: + • Fix crash when rendering with bitmap fonts (Carl Worth) + https://bugzilla.mozilla.org/show_bug.cgi?id=376498 + +xlib: + • Turn metrics-hinting on by default (Behdad Esfahbod) + + • Fix edge-effect problem with transformed images drawn to xlib + (Behdad Esfahbod) + https://bugs.freedesktop.org/show_bug.cgi?id=10508 + + • Avoid dereferencing a NULL screen. (Chris Wilson) + https://bugs.freedesktop.org/show_bug.cgi?id=10517 + +Quartz/ATSUI: + • Fix scaling of glyph surfaces + (Brian Ewins) + https://bugs.freedesktop.org/show_bug.cgi?id=9568 + + • Fix compilation failure when both xlib and quartz enabled + (Brian Ewins) + + • Fix rounding bug leading to incorrectly positioned glyphs + (Robert O'Callahan) + https://bugs.freedesktop.org/show_bug.cgi?id=10531 + +Release 1.4.2 (2007-03-19 Carl Worth ) +========================================================= +This is the first update release in cairo's stable 1.4 series. It +comes just less than 2 weeks after 1.4.0. We hadn't anticipated an +update this early, but we've managed to collect some important fixes +that we wanted to get out to cairo users as soon as possible, (6 fixes +for crashes, 1 case where graphical elements would not be drawn at +all, a handful of backend-specific bugs, and several important build +fixes). + +There's almost nothing but bug fixes in this release, (see below one +optimization that Behdad did sneak in), so we recommend that everyone +upgrade to this release when possible. + +Thanks to the many people that worked to fix these bugs, and those +that did the work to report them and to test the fixes, (wherever +possible both names are credited below). + +Critical fixes +-------------- +• Fix a crash due to a LOCK vs. UNLOCK typo (M. Drochner fixing Carl + Worth's embarrassing typo). + + 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 + Brabec, and sangu). + + https://bugs.freedesktop.org/show_bug.cgi?id=10267 + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=232576 + +• Fix a crash in cairo_stroke_extents or cairo_in_stroke when line + width is 0.0. (Carl Worth and Sebastien Bacher) + + https://bugs.freedesktop.org/show_bug.cgi?id=10231 + +• Fix a crash on certain combinations of X server/video drivers (Carl + Worth and Tomas Carnecky). + + https://bugs.freedesktop.org/show_bug.cgi?id=10250 + +• Fix a crash due to mishandling of invalid user input (Carl Worth and + Alexander Darovsky). + + https://bugs.freedesktop.org/show_bug.cgi?id=9844 + +• xlib: Cleanup server-side glyph caches on XCloseDisplay. This + eliminated a crash detected by the perf suite, (and that + applications could have run into as well). (Chris Wilson) + +Other bug fixes +--------------- +• Fix for some geometry which simply disappeared under some + transformations---a stroked line with an extreme skew in X, for + example (Carl Worth and Jonathan Watt). + + https://bugzilla.mozilla.org/show_bug.cgi?id=373632 + +• SVG: Fix radial gradients for CAIRO_EXTEND_REFLECT and when r0 > r1 + (Emmanuel Pacaud). + +• PDF: Set page group color space to DeviceRGB. + + This fixes incorrect (muddy) transparent colors when rendering cairo + PDF output in some viewers. (Adrian Johnson, Adam Goode, and + MenTaLguY). + + 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). + +• quartz: Fix glyph interfaces to correctly return user-space rather + than device-space coordinates (Brian Ewins). + + https://bugs.freedesktop.org/show_bug.cgi?id=9568 + +• xcb: Fix parameter-order confusion with xcb_create_pixmap, which now + makes all tests that pass with xlib now pass with xcb (Carl Worth, + Jamey Sharp). + +• Fix some memory leaks in the perf suite (Chris Wilson). + +• Fix perf suite to consider changes in pixman/src (Mathias + Hasselmann). + +Build fixes +----------- +• Don't include pre-generated cairo-features.h file. This was causing + build failures when building with the directfb backend enabled + (Behdad Esfahbod). + + https://bugs.freedesktop.org/show_bug.cgi?id=10189 + +• Eliminate use of maintainer mode from cairo's automake/configure + script. This means that updates to files such as Makefile.am will + take effect, (by rerunning automake and friends as necessary) when + invoking make rather than being silently ignored. (Behdad Esfahbod) + +• Don't compile cairo-deflate-stream.c, which depends on zlib, unless + building the pdf backend which requires it. (Carl Worth, Tor + Lillqvist) + + https://bugs.freedesktop.org/show_bug.cgi?id=10202 + +• Don't make the ps backend link against zlib anymore, since it + doesn't require it (Carl Worth). + +• Use "find !" rather than "find -not" for better portability (Thomas + Klausner). + + https://bugs.freedesktop.org/show_bug.cgi?id=10226 + +• Don't use unsupported visibility attribute "hidden" on Solaris + (Gilles Dauphin, Thomas Klausner). + + https://bugs.freedesktop.org/show_bug.cgi?id=10227 + +Optimization +------------ +• It was Behdad that suggested we focus strictly on bug fixes now that + we shipped so many performance improvements in 1.4.0, but it was + also Behdad that got distracted by the chance to remove a lot of + mallocs from cairo. Paths, gstates, trapezoids, splines, polygons, + and gradient color stops will now use small, stack-allocated buffers + in the most common cases rather than calling malloc as + often. (Behdad Esfahbod). And look for more from Mathias Hasselmann + soon. + +Release 1.4.0 (2007-03-06 Carl Worth ) +========================================================= +The many people[*] who have been working hard on cairo are very +pleased to announce the long-awaited release of cairo 1.4. This +release comes 4 months after the last stable update release (1.2.6) +and 9 months since the initial release of 1.2.0. + +The release notes below are intended to capture the highlights of the +changes that have occurred from the 1.2 series to the new 1.4.0 +release. + +Performance improvements +------------------------ +Within the cairo project, the last 6 months or so has seen an intense +effort focusing on the performance of cairo itself. That effort has +paid off considerably, as can be seen in the following highlights of +some of the performance differences from cairo 1.2.6 to cairo 1.4.0. + +(Note: The performance results reported here were measured on an x86 +laptop. Many of the improvements in 1.4---particular those involving +text rendering---are even more dramatic on embedded platforms without +hardware floating-point units. Such devices played an important part +of many of the optimizations that found their way into cairo over the +last few months.) + +• Dramatic improvement when drawing objects that are mostly off-screen + with the image backend (with the xlib backend this case is still + slow due to an X server bug): + + image-rgba long-lines-uncropped-100 479.64 -> 4.98: 96.24x speedup + ███████████████████████████████████████████████▋ + +• Dramatic improvement when copying a small fraction of an image + surface to an xlib surface: + + xlib-rgba subimage_copy-512 3.93 -> 0.07: 54.52x speedup + ██████████████████████████▊ + +• Dramatic improvement to tessellation speed for complex objects: + + image-rgb tessellate-256-100 874.16 -> 34.79: 25.13x speedup + ████████████▏ + xlib-rgba zrusin_another_fill-415 148.40 -> 13.85: 10.72x speedup + ████▉ + xlib-rgb world_map-800 680.20 -> 345.54: 1.97x speedup + ▌ + +• Dramatic improvement to the speed of stroking rectilinear shapes, + (such as the outline of a rectangle or "box"): + + image-rgb box-outline-stroke-100 0.18 -> 0.01: 24.22x speedup + ███████████▋ + xlib-rgb box-outline-stroke-100 0.46 -> 0.06: 8.05x speedup + ███▌ + + +• Dramatic improvements to text rendering speeds: + + xlib-rgba text_image_rgba_over-256 63.12 -> 9.61: 6.57x speedup + ██▊ + +• 3x improvements to floating-point to fixed-point conversion speeds: + + image-rgba pattern_create_radial-16 9.29 -> 3.44: 2.70x speedup + ▉ + +• 2x improvements to linear gradient computation: + + image-rgb paint_linear_rgb_source-512 26.22 -> 11.61: 2.26x speedup + ▋ + +• 2x improvement to a case common in PDF rendering: + + image-rgb unaligned_clip-100 0.10 -> 0.06: 1.81x speedup + ▍ + +• 1.3x improvement to rectangle filling speed (note: this improvement + is new since 1.3.16---previously this test case was a 1.3x slowdown + compared to 1.2.6): + + image-rgba rectangles-512 6.19 -> 4.37: 1.42x speedup + ▎ + xlib-rgba rectangles-512 7.48 -> 5.58: 1.34x speedup + ▏ + +NOTE: In spite of our best efforts, there are some measurable +performance regressions in 1.4 compared to 1.2. It appears that the +primary problem is the increased overhead of the new tessellator when +drawing many, very simple shapes. The following test cases capture +some of that slowdown: + + image-rgba mosaic_tessellate_lines-800 11.03 -> 14.29: 1.30x slowdown + ▏ + image-rgba box-outline-fill-100 0.01 -> 0.01: 1.26x slowdown + ▏ + image-rgba fill_solid_rgb_over-64 0.20 -> 0.22: 1.12x slowdown + + image-rgba fill_image_rgba_over-64 0.23 -> 0.25: 1.10x slowdown + + xlib-rgb paint_image_rgba_source-256 3.24 -> 3.47: 1.07x slowdown + +We did put some special effort into eliminating this slowdown for the +very common case of drawing axis-aligned rectangles with an identity +matrix (see the box-outline-stroke and rectangles speedup numbers +above). Eliminating the rest of this slowdown will be a worthwhile +project going forward. + +Also note that the "box-outline-fill" case is a slowdown while +"box-outline-stroke" is a (huge) speedup. These two test cases +resulted from the fact that some GTK+ theme authors were filling +between two rectangles to avoid slow performance from the more natural +means of achieving the same shape by stroking a single rectangle. With +1.4 that workaround should definitely be eliminated as it will now +cause things to perform more slowly. + +Greatly improved PDF output +--------------------------- +We are very happy to be able to announce that cairo-generated PDF +output will now have text that can be selected, cut-and-pasted, and +searched with most capable PDF viewer applications. This is something +that was not ever possible with cairo 1.2. + +Also, the PDF output now has much more compact encoding of text than +before. Cairo is now much more careful to not embed multiple copies of +the same font at different sizes. It also compresses text and font +streams within the PDF output. + +API additions +------------- +There are several new functions available in 1.4 that were not +available in 1.2. Curiously, almost all of the new functions simply +allow the user to query state that has been set in cairo (many new +"get" functions) rather than providing any fundamentally new +operations. The new functionality is: + +• Getting information about the current clip region + + cairo_clip_extents + cairo_copy_clip_rectangle_list + cairo_rectangle_list_destroy + +• Getting information about the current dash setting + + cairo_get_dash_count + cairo_get_dash + +• Getting information from a pattern + + cairo_pattern_get_rgba + cairo_pattern_get_surface + cairo_pattern_get_color_stop_rgba + cairo_pattern_get_color_stop_count + cairo_pattern_get_linear_points + cairo_pattern_get_radial_circles + +• Getting the current scaled font + + cairo_get_scaled_font + +• Getting reference counts + + cairo_get_reference_count + cairo_surface_get_reference_count + cairo_pattern_get_reference_count + cairo_font_face_get_reference_count + cairo_scaled_font_get_reference_count + +• Setting/getting user data on objects + + cairo_set_user_data + cairo_get_user_data + cairo_pattern_set_user_data + cairo_pattern_get_user_data + cairo_scaled_font_set_user_data + cairo_scaled_font_get_user_data + +• New cairo-win32 functions: + + cairo_win32_surface_create_with_ddb + cairo_win32_surface_get_image + cairo_win32_scaled_font_get_logical_to_device + cairo_win32_scaled_font_get_device_to_logical + +API deprecation +--------------- +The CAIRO_FORMAT_RGB16_565 enum value has been deprecated. It never +worked as a format value for cairo_image_surface_create, and it wasn't +necessary for supporting 16-bit 565 X server visuals. + +A sampling of bug fixes in cairo 1.4 +------------------------------------ + • Fixed radial gradients + • Fixed dashing (degenerate and "leaky" cases) + • Fixed transformed images in PDF/PS output (eliminate bogus repeating) + • Eliminate errors from CAIRO_EXTEND_REFLECT and CAIRO_EXTEND_PAD + • cairo_show_page no longer needed for single-page output + • SVG: Fix bug preventing text from appearing in many viewers + • cairo-ft: Return correct metrics when hinting is off + • Eliminate crash in cairo_create_similar if nil surface is returned + • Eliminate crash after INVALID_RESTORE error + • Fix many bugs related to multi-threaded use and locking + • Fix for glyph spacing 32 times larger than desired (cairo-win32) + • Fixed several problems in cairo-atsui (assertion failures) + • Fix PDF output to avoid problems when printing from Acrobat Reader + • Fix segfault on Mac OS X (measuring a zero-length string) + • Fix text extents to not include the size of non-inked characters + • Fix for glyph cache race condition in glitz backend (Jinghua Luo) + • Fix make check to work on OPD platforms (IA64 or PPC64) + • Fix compilation problems of cairo "wideint" code on some platforms + • Many, many others... + +Experimental backends (quartz, XCB, OS/2, BeOS, directfb) +--------------------------------------------------------- +None of cairo's experimental backends are graduating to "supported" +status with 1.4.0, but two of them in particular (quartz and xcb), are +very close. + +The quartz baceknd has been entirely rewritten and is now much more +efficient. The XCB backend has been updated to track the latest XCB +API (which recently had a 1.0 release). + +We hope to see these backends become supported in a future release, +(once they are passing all the tests in cairo's test suite). + +The experimental OS/2 backend is new in cairo 1.4 compared to cairo +1.2. + +Documentation improvements +-------------------------- +We have added documentation for several functions and types that +were previously undocumented, and improved documentation on other +ones. As of this release, there remain only two undocumented +symbols: cairo_filter_t and cairo_operator_t. + +[*]Thanks to everyone +--------------------- +I've accounted for 41 distinct people with attributed code added to +cairo between 1.2.6 and 1.4.0, (their names are below). That's an +impressive number, but there are certainly dozens more that +contributed with testing, suggestions, clarifying questions, and +encouragement. I'm grateful for the friendships that have developed as +we have worked on cairo together. Thanks to everyone for making this +all so much fun! + +Adrian Johnson, Alfred Peng, Alp Toker, Behdad Esfahbod, +Benjamin Otte, Brian Ewins, Carl Worth, Christian Biesinger, +Christopher (Monty) Montgomery, Daniel Amelang, Dan Williams, +Dave Yeo, David Turner, Emmanuel Pacaud, Eugeniy Meshcheryakov, +Frederic Crozat, Hans Breuer, Ian Osgood, Jamey Sharp, Jeff Muizelaar, +Jeff Smith, Jinghua Luo, Jonathan Watt, Joonas Pihlaja, Jorn Baayen, +Kalle Vahlman, Kjartan Maraas, Kristian Høgsberg, M Joonas Pihlaja, +Mathias Hasselmann, Mathieu Lacage, Michael Emmel, Nicholas Miell, +Pavel Roskin, Peter Weilbacher, Robert O'Callahan, +Soren Sandmann Pedersen, Stuart Parmenter, T Rowley, +Vladimir Vukicevic + +Snapshot 1.3.16 (2007-03-02 Carl Worth ) +=========================================================== +New API functions +----------------- +A few new public functions have been added to the cairo API since the +1.3.14 snapshot. These include a function to query the current scaled +font: + + cairo_get_scaled_font + +New functions to query the reference count of all cairo objects: + + cairo_get_reference_count + + cairo_surface_get_reference_count + cairo_pattern_get_reference_count + + cairo_font_face_get_reference_count + cairo_scaled_font_get_reference_count + +And new functions to allow the use of user_data with any cairo object, +(previously these were only available on cairo_surface_t and +cairo_font_face_t objects): + + cairo_set_user_data + cairo_get_user_data + + cairo_pattern_set_user_data + cairo_pattern_get_user_data + + cairo_scaled_font_set_user_data + cairo_scaled_font_get_user_data + +Usability improvement for PDF/PS/SVG generation +----------------------------------------------- +In previous versions of cairo, generating single-page output with the +cairo-pdf, cairo-ps, or cairo-svg backends required a final call to +cairo_show_page. This was often quite confusing as people would port +functional code from a non-paginated backend and be totally mystified +as to why the output was blank until they learned to add this call. + +Now that call to cairo_show_page is optional, (it will be generated +implicitly if the user does not call it). So cairo_show_page is only +needed to explicitly separate multiple pages. + +Greatly improved PDF output +--------------------------- +We are very happy to be able to announce that cairo-generated PDF +output will now have text that can be selected, cut-and-paste, and +searched with most capable PDF viewer applications. This is something +that was not ever possible with cairo 1.2. + +Also, the PDF output now has much more compact encoding of text than +before. Cairo is now much more careful to not embed multiple copies of +the same font at different sizes. It also compresses text and font +streams within the PDF output. + +Major bug fixes +--------------- + • Fixed radial gradients + + The rendering of radial gradients has been greatly improved. In + the cairo 1.2 series, there was a serious regression affecting + radial gradients---results would be very incorrect unless one of + the gradient circles had a radius of 0.0 and a center point within + the other circle. These bugs have now been fixed. + + • Fixed dashing + + Several fixes have been made to the implementation of dashed + stroking. Previously, some dashed, stroked rectangles would + mis-render and fill half of the rectangle with a large triangular + shape. This bug has now been fixed. + + • Fixed transformed images in PDF/PS output + + In previous versions of cairo, painting with an image-based source + surface pattern to the PDF or PS backends would cause many kinds + of incorrect results. One of the most common problems was that an + image would be repeated many times even when the user had + explicitly requested no repetition with CAIRO_EXTEND_NONE. These + bugs have now been fixed. + + • Eliminate errors from CAIRO_EXTEND_REFLECT and CAIRO_EXTEND_PAD + + In the 1.2 version of cairo any use of CAIRO_EXTEND_REFLECT or + CAIRO_EXTEND_PAD with a surface-based pattern resulted in an + error, (cairo would stop rendering). This bug has now been + fixed. + + Now, CAIRO_EXTEND_REFLECT should work properly with surface + patterns. + + CAIRO_EXTEND_PAD is still not working correctly, but it will now + simply behave as CAIRO_EXTEND_NONE rather than triggering the + error. + +New rewrite of quartz backend (still experimental) +-------------------------------------------------- +Cairo's quartz backend has been entirely rewritten and is now much +more efficient. This backend is still marked as experimental, not +supported, but it is now much closer to becoming an officially +supported backend. (For people that used the experimental nquartz +backend in previous snapshots, that implementation has now been +renamed from "nquartz" to "quartz" and has replaced the old quartz +backend.) + +Documentation improvements +-------------------------- +We have added documentation for several functions and types that +were previously undocumented, and improved documentation on other +ones. As of this release, there remain only two undocumented +symbols: cairo_filter_t and cairo_operator_t. + +Other bug fixes +--------------- + • cairo-svg: Fix bug that was preventing text from appearing in many + viewers + + • cairo-ft: Return correct metrics when hinting is off + + • Cairo 1.3.14 deadlocks in cairo_scaled_font_glyph_extents or + _cairo_ft_unscaled_font_lock_face + + https://bugs.freedesktop.org/show_bug.cgi?id=10035 + + • cairo crashes in cairo_create_similar if nil surface returned by + other->backend->create_similar + + https://bugs.freedesktop.org/show_bug.cgi?id=9844 + + • evolution crash in _cairo_gstate_backend_to_user() + https://bugs.freedesktop.org/show_bug.cgi?id=9906 + + • Fix memory leak in rectilinear stroking code + +Things not in this release +-------------------------- + • Solid-surface-pattern cache: This patch had been applied during + the 1.3.x series, but it was reverted due to some inter-thread + problems it caused. The patch is interesting since it made a big + benefit for text rendering performance---so we'll work to bring a + corrected version of this patch back as soon as possible. + +Snapshot 1.3.14 (2006-02-13 Carl Worth ) +=========================================================== +This is the seventh development snapshot in the 1.3 series, (and there +likely won't be many more before the 1.4.0 release). It comes just +over 3 weeks after the 1.3.12 snapshot. + +Since we're so close to the 1.4.0 release, there are not a lot of new +features nor even a lot of new performance improvements in this +snapshot. Instead, there are a great number of bug fixes. Some are +long-standing bugs that we're glad to say goodbye to, and several are +fixes for regressions that were introduced as part of the optimization +efforts during the 1.3.x series. + +PDF text selection fixed +------------------------ +The inability to correctly select text in cairo-generated PDF has been +a defect ever since the initial support for the PDF backend in the +cairo 1.2.0 release. With the 1.3.14 snapshot, in most situations, and +with most PDF viewer applications, the PDF generated by cairo will +allow text to be correctly selected for copy-and-paste, (as well as +searching). + +We're very excited about this new functionality, (and very grateful to +Adrian Johnson, Behdad Esfahbod, and others that have put a lot of +work into this lately). Please test this new ability and give feedback +on the cairo@cairographics.org list. + +Many thread-safety issues fixed +------------------------------- +We've discovered that no release of cairo has ever provided safe text +rendering from a multi-threaded application. With the 1.3.14 snapshot +a huge number of the bugs in this area have been fixed, and multiple +application dvelopers have now reported success at writing +multi-threaded applications with cairo. + +Other fixes +----------- +Fixed a bug that was causing glyph spacing to be 32 times larger than +desired when using cairo-win32. + +Fixed a regression in the rendering of linear gradients that had been +present since the 1.3.8 snapshot. + +Fixed several problems in cairo-atsui that were leading to assertion +failures when rendering text. + +Fix corrupted results when rendering a transformed source image +surface to an xlib surface. This was a regression that had been +present since the 1.3.2 snapshot. + +Fixed PDF output to prevent problems printing from some versions of +Acrobat Reader, (a single glyph was being substituted for every +glyph). + +And many other fixes as well, (see the logs for details). + +Snapshot 1.3.12 (2007-01-20 Carl Worth ) +=========================================================== +The relentless march toward the cairo 1.4 release continues, (even if +slightly late out of the starting blocks in 2007). This is the sixth +development snapshot in the 1.3 series. It comes 4 weeks after the +1.3.10 snapshot. + +Performance +----------- +As usual, this snapshot has some fun performance improvements to show +off: + +image-rgba long-lines-uncropped-100 470.08 -> 4.95: 94.91x speedup +███████████████████████████████████████████████ +image-rgb long-lines-uncropped-100 461.60 -> 4.96: 93.02x speedup +██████████████████████████████████████████████ + +This 100x improvement, (and yes, that's 100x, not 100%), in the image +backend occurs when drawing large shapes where only a fraction of the +shape actually appears in the final result, (the rest being outside +the bounds of the destination surface). Many applications should see +speedups here, and the actual amount of speedup depends on the ratio +of non-visible to visible portions of geometry. + +[Note: There remains a similar performance bug when drawing mostly +non-visible objects with the xlib backend. This is due to a similar +bug in the X server itself, but we hope a future cairo snapshot will +workaround that bug to get a similar speedup with the xlib backend.] + +image-rgba unaligned_clip-100 0.09 -> 0.06: 1.67x speedup +▍ +image-rgb unaligned_clip-100 0.09 -> 0.06: 1.66x speedup +▍ + +This speedup is due to further MMX optimization by Soeren Sandmann for +a case commonly hit when rendering PDF files, (and thanks to Jeff +Muizelaar for writing code to extract the test case for us). + +There's another MMX optimization in this snapshot (without a fancy +speedup chart) by Dan Williams which improves compositing performance +specifically for the OLPC machine. + +Thanks to Adrian Johnson, cairo's PDF output is now much more +efficient in the way it encodes text output. By reducing redundant +information and adding compression to text output streams, Adrian +achieved a ~25x improvement in the efficiency of encoding text in PDF +files, (was ~45 bytes per glyph and is now ~1.6 bytes per glyph). + +Bug fixes +--------- +In addition to those performance improvements, this snapshot includes +several bug fixes: + + * A huge number of bug fixes for cairo-atsui text rendering, (for mac + OS X). These bugs affect font selection, glyph positioning, glyph + rendering, etc. One noteworthy bug fixes is that + cairo_select_font_face will no longer arbitrarily select bold nor + italic when not requested, (at least not when using a standard CSS2 + font family name such as "serif", "sans-serif", "monospace", etc.). + All these fixes are thanks to Brian Ewins who continues to do a + great job as the new cairo-atsui maintainer. + + * Fix PDF output so that images that are scaled down no longer + mysteriously repeat (Carl Worth). + + * Fix segfault on Mac OS X dues to attempt to measure extents of a + zero-length string (Behdad Esfahbod). + + * Fix text extents to not include the size of initial/trailing + non-inked characters (Behdad Esfahbod). + +API tweaks +---------- +Three functions have had API changes to improve consistency. Note that +the API functions being changed here are all functions that were +introduced as new functions during these 1.3.x snapshots. As always, +there will not be any API changes to functions included in a major +release (1.2.x, 1.4.x, etc.) of cairo. + +The changes are as follows: + + * Rename of cairo_copy_clip_rectangles to cairo_copy_clip_rectangle_list. + + * Change cairo_get_dash_count to return an int rather than accepting a + pointer to an int for the return value. + + * Change cairo_get_dash to have a void return type rather than + returning cairo_status_t. + +It's possible there will be one more round of changes to these +functions, (and perhaps cairo_get_color_stop as well), as we seek to +establish a unifying convention for returning lists of values. + +Snapshot 1.3.10 (2006-12-23 Carl Worth ) +=========================================================== +Santa Claus is coming just a little bit early this year, and he's +bringing a shiny new cairo snapshot for all the good little boys and +girls to play with. + +This is the fifth development snapshot in the 1.3 series. It comes 9 +days after the 1.3.8 snapshot, and still well within our goal of +having a new snapshot every week, (though don't expect one next +week---we'll all be too stuffed with sugar plums). + +Speaking of sugar plums, there's a sweet treat waiting in this cairo +snapshot---greatly improved performance for stroking rectilinear +shapes, like the ever common rectangle: + +image-rgb box-outline-stroke-100 0.18 -> 0.01: 25.58x speedup +████████████████████████▋ +image-rgba box-outline-stroke-100 0.18 -> 0.01: 25.57x speedup +████████████████████████▋ +xlib-rgb box-outline-stroke-100 0.49 -> 0.06: 8.67x speedup +███████▋ +xlib-rgba box-outline-stroke-100 0.22 -> 0.04: 5.39x speedup +████▍ + +In past releases of cairo, some people had noticed that using +cairo_stroke to draw rectilinear shapes could be awfully slow. Many +people had worked around this by using cairo_fill with a more complex +path and gotten a 5-15x performance benefit from that. + +If you're one of those people, please rip that workaround out, as now +the more natural use of cairo_stroke should be 1.2-2x faster than the +unnatural use of cairo_fill. + +And if you hadn't ever implemented that workaround, then you just +might get to see your stroked rectangles now get drawn 5-25x faster. + +Beyond that performance fix, there are a handful of bug fixes in this +snapshot: + + * Fix for glyph cache race condition in glitz backend (Jinghua Luo) + + * Many fixes for ATSUI text rendering (Brian Ewins) + + * Un-break recent optimization-triggered regression in rendering text + with a translation in the font matrix (Behdad Esfahbod) + + * Fix make check to work on OPD platforms (IA64 or PPC64) + (Frederic Crozat) + + * Fix a couple of character spacing issues on Windows + (Jonathan Watt) + +Have fun with that, everybody, and we'll be back for more in the new +year, (with a plan to add the last of our performance improvements in +this round, fix a few bad, lingering bugs, and then finish off a nice, +stable 1.4 release before the end of January). + +-Carl + +Snapshot 1.3.8 (2006-12-14 Carl Worth ) +========================================================== +This is the fourth development snapshot in the 1.3 series. It comes +just slightly more than one week after the 1.3.6 snapshot. + +After the bug fixes in 1.3.6, we're back to our original program of +weekly snapshots, each one faster than the one from the week +before. Cairo 1.3.8 brings a 2x improvement in the speed of rendering +linear gradients (thanks to David Turner), and a significant reduction +in X traffic when rendering text (thanks to Xan Lopez and Behdad +Esfahbod), making cairo behave very much like Xft does. + +A few other things in the 1.3.8 snapshot worth noting include a more +forgiving image comparator in the test suite, (using the "perceptual +diff" metric and GPL implementation by Hector Yee[*]), a bug fix for +broken linking on x86_64 (thanks to M Joonas Pihlaja) and an even +better implementation of _cairo_lround, (not faster, but supporting a +more complete input range), from Daniel Amelang. + +[*] http://pdiff.sourceforge.net/ + +Snapshot 1.3.6 (2006-12-06 Carl Worth ) +========================================================== +This is the third development snapshot in the 1.3 series. It comes two +weeks after the 1.3.4 snapshot. + +We don't have fancy performance charts this week as the primary +changes in this snapshot are bug fixes. The performance work continues +and the next snapshot (planned for one week from today) should include +several improvements. The bug fixes in this snapshot include: + + * Fix undesirable rounding in glyph positioning (Dan Amelang) + + This bug was noticed by several users, most commonly by seeing + improper text spacing or scrambled glyphs as drawn by nautilus. For + example: + + Update to cairo-1.3.4 worsen font rendering + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=217819 + + * Fix reduced range of valid input coordinates to tessellator + (M Joonas Pihlaja) + + This bug was causing lots of assertion failures in mozilla as + mentioned here: + + CAIRO_BO_GUARD_BITS and coordinate space? + https://lists.freedesktop.org/archives/cairo/2006-December/008743.html + + * Fix several regressions in new tessellator (M Joonas Pihlaja) + + Joonas just had a good eye for detail here. I don't think any + external cairo users had noticed any of these bugs yet. + + * Fix compilation problems of cairo "wideint" code on some platforms + (Mathieu Lacage) + + * Fix failed configure due to broken grep (Dan Amelang) + + This bug was reported here: + + AX_C_FLOAT_WORDS_BIGENDIAN doesn't work because grep doesn't + work with binary file + https://bugs.freedesktop.org/show_bug.cgi?id=9124 + + * Remove the pkg-config minimum version requirement (Behdad Esfahbod) + + Some systems ship with pkg-config 0.15 and there was really no good + reason for cairo to insist on having version 0.19 before it would + build. + +There is also one new (but inert) feature in this snapshot. There's a +new option that can be passed to cairo's configure script: + + --disable-some-floating-point + + Disable certain code paths that rely heavily on double precision + floating-point calculation. This option can improve + performance on systems without a double precision floating-point + unit, but might degrade performance on those that do. + +As of this snapshot, this option does not make any change to cairo, +but it is possible that future versions of cairo will respect this +option and change the implementation of various functions as +appropriate. + +Snapshot 1.3.4 (2006-11-22 Carl Worth ) +========================================================== +This is the second development snapshot in the 1.3 series. It comes +one week after the 1.3.2 snapshot. + +This snapshot has a couple of significant performance improvements, +and also adds new support for producing multi-page SVG output, (when +targeting SVG 1.2)---thanks to Emmanuel Pacaud. The details of the +performance improvements are as follows: + +1. The long-awaited "new tessellator". + + The credit for this being an improvement goes to Joonas Pihlaja. He + took my really slow code and really put it through its paces to get + the dramatic performance improvement seen below (up to 38x faster + on realistic cases, and more than 10x faster for the zrusin_another + test). + + 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: + + 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 + email---see "Generating fewer trapezoids"). + +2. More floating-point improvements + + Daniel Amelang continues to work the magic he began in the 1.3.2 + snapshot. This time he short-circuits floating-point + transformations by identity matrices and applies the earlier + floating-to-fixed-point technique to the problem of rounding. + + The improvements here will primarily benefit text performance, and + will benefit platforms without hardware floating-point more than + those that have it, (some text tests show 20% improvement on an x86 + machine and closer to 80% improvement on arm). + +The performance chart comparing 1.3.2 to 1.3.4 really speaks for +itself, (this is on an x86 laptop). This is quite a lot of progress +for one week: + + xlib-rgb stroke_similar_rgba_over-256 74.99 1.45% -> 2.03 68.38%: 36.86x speedup +███████████████████████████████████▉ + xlib-rgb stroke_similar_rgba_source-256 78.23 1.43% -> 3.30 67.05%: 23.71x speedup +██████████████████████▊ + xlib-rgba tessellate-256-100 820.42 0.15% -> 35.06 2.84%: 23.40x speedup +██████████████████████▍ +image-rgba tessellate-256-100 819.55 0.32% -> 35.04 3.56%: 23.39x speedup +██████████████████████▍ + xlib-rgb stroke_image_rgba_over-256 78.10 1.43% -> 4.33 65.56%: 18.04x speedup +█████████████████ + xlib-rgb stroke_image_rgba_source-256 80.11 1.63% -> 5.75 63.99%: 13.94x speedup +█████████████ + xlib-rgba zrusin_another_tessellate-415 89.22 0.35% -> 8.38 5.23%: 10.65x speedup +█████████▋ +image-rgba zrusin_another_tessellate-415 87.38 0.89% -> 8.37 5.22%: 10.44x speedup +█████████▍ +image-rgba zrusin_another_fill-415 117.67 1.34% -> 12.88 2.77%: 9.14x speedup +████████▏ + xlib-rgba zrusin_another_fill-415 140.52 1.57% -> 15.79 2.88%: 8.90x speedup +███████▉ +image-rgba tessellate-64-100 9.68 3.42% -> 1.42 0.60%: 6.82x speedup +█████▉ + xlib-rgba tessellate-64-100 9.78 4.35% -> 1.45 0.83%: 6.72x speedup +█████▊ + xlib-rgb stroke_linear_rgba_over-256 46.01 2.44% -> 7.74 54.51%: 5.94x speedup +█████ + xlib-rgb stroke_linear_rgba_source-256 48.09 2.15% -> 9.14 53.00%: 5.26x speedup +████▎ + xlib-rgb stroke_radial_rgba_over-256 50.96 2.34% -> 12.46 47.99%: 4.09x speedup +███▏ + xlib-rgb stroke_radial_rgba_source-256 53.06 1.57% -> 13.96 46.57%: 3.80x speedup +██▊ +image-rgba paint_similar_rgba_source-256 0.12 1.57% -> 0.08 9.92%: 1.42x speedup +▍ +image-rgba paint_image_rgba_source-256 0.12 2.49% -> 0.08 10.70%: 1.41x speedup +▍ +image-rgba world_map-800 356.28 0.46% -> 275.72 1.15%: 1.29x speedup +▎ + xlib-rgba world_map-800 456.81 0.39% -> 357.95 1.39%: 1.28x speedup +▎ +image-rgb tessellate-16-100 0.09 0.57% -> 0.07 3.43%: 1.23x speedup +▎ +image-rgba tessellate-16-100 0.09 0.06% -> 0.07 2.46%: 1.23x speedup +▎ +image-rgba text_solid_rgb_over-256 5.39 4.01% -> 4.47 0.70%: 1.21x speedup +▎ +image-rgba text_solid_rgba_over-256 5.37 0.82% -> 4.45 0.75%: 1.21x speedup +▎ +image-rgba text_image_rgb_over-64 0.78 0.10% -> 0.65 0.74%: 1.20x speedup +▎ +image-rgba text_image_rgba_over-64 0.78 0.29% -> 0.65 0.68%: 1.19x speedup +▎ +image-rgb text_solid_rgb_over-64 0.76 2.45% -> 0.63 0.81%: 1.19x speedup +▎ +image-rgba text_solid_rgba_over-64 0.76 0.33% -> 0.64 0.66%: 1.19x speedup +▎ +image-rgba text_similar_rgba_over-256 5.99 4.72% -> 5.04 1.09%: 1.19x speedup +▎ + +We should point out that there is some potential for slowdown in this +snapshot. The following are the worst slowdowns reported by the cairo +performance suite when comparing 1.3.2 to 1.3.4: + +image-rgba subimage_copy-256 0.01 0.87% -> 0.01 3.61%: 1.45x slowdown +▌ + xlib-rgb paint_solid_rgb_over-256 0.31 10.23% -> 0.38 0.33%: 1.26x slowdown +▎ +image-rgba box-outline-fill-100 0.01 0.30% -> 0.01 2.52%: 1.21x slowdown +▎ +image-rgba fill_solid_rgb_over-64 0.20 1.22% -> 0.22 1.59%: 1.12x slowdown +▏ +image-rgb fill_similar_rgb_over-64 0.21 1.04% -> 0.24 1.06%: 1.11x slowdown +▏ +image-rgba fill_image_rgb_over-64 0.21 1.19% -> 0.24 0.72%: 1.11x slowdown +▏ +image-rgba fill_similar_rgb_over-64 0.21 0.18% -> 0.24 0.30%: 1.11x slowdown +▏ +image-rgb fill_solid_rgba_over-64 0.22 1.66% -> 0.24 1.15%: 1.11x slowdown +▏ +image-rgb fill_image_rgb_over-64 0.21 0.14% -> 0.24 0.80%: 1.11x slowdown +▏ +image-rgba fill_image_rgba_over-64 0.22 1.34% -> 0.25 0.20%: 1.11x slowdown +▏ +image-rgba fill_solid_rgba_over-64 0.22 1.48% -> 0.24 0.95%: 1.11x slowdown +▏ +image-rgb fill_similar_rgba_over-64 0.22 1.13% -> 0.25 1.25%: 1.10x slowdown +▏ + +The 45% slowdown for subimage_copy is an extreme case. It's unlikely +to hit many applications unless they often use cairo_rectangle; +cairo_fill to copy a single pixel at a time. In any case, it shows a +worst-case impact of the overhead of the new tessellator. The other +slowdowns (~ 10%) are probably more realistic, and still very +concerning. + +We will work to ensure that performance regressions like these are not +present from one major release of cairo to the next, (for example, +from 1.2 to 1.4). + +But we're putting this 1.3.4 snapshot out there now, even with this +potential slowdown so that people can experiment with it. If you've +got complex geometry, we hope you will see some benefit from the new +tessellator. If you've got primarily simple geometry, we hope things +won't slowdown too much, but please let us know what slowdown you see, +if any, so we can calibrate our performance suite against real-world +impacts. + +Thanks, and have fun with cairo! + +Snapshot 1.3.2 (2006-11-14 Carl Worth ) +========================================================== +This is the first development snapshot since the 1.2 stable series +branched off shortly after the 1.2.4 release in August 2006. + +This snapshot includes all the bug fixes from the 1.2.6 release, +(since they originated here on the 1.3 branch first and were +cherry-picked over to 1.2). But more importantly, it contains some new +API in preparation for a future 1.4 release, and most importantly, it +contains several performance improvements. + +The bug fixes will not be reviewed here, as most of them are already +described in the 1.2.6 release notes. But details for the new API and +some performance improvements are included here. + +As with all snapshots, this is experimental code, and the new API +added here is still experimental and is not guaranteed to appear +unchanged in any future release of cairo. + +API additions +------------- +Several new API additions are available in this release. There is a +common theme among all the additions in that they allow cairo to +advertise information about its state that it was refusing to +volunteer earlier. So this isn't groundbreaking new functionality, but +it is essential for easily achieving several tasks. + +The new functions can be divided into three categories: + + Getting information about the current clip region + ------------------------------------------------- + cairo_clip_extents + cairo_copy_clip_rectangles + cairo_rectangle_list_destroy + + Getting information about the current dash setting + -------------------------------------------------- + cairo_get_dash_count + cairo_get_dash + + Getting information from a pattern + ---------------------------------- + cairo_pattern_get_rgba + cairo_pattern_get_surface + cairo_pattern_get_color_stop_rgba + cairo_pattern_get_color_stop_count + cairo_pattern_get_linear_points + cairo_pattern_get_radial_circles + +In each of these areas, we have new API for providing a list of +uniform values from cairo. The closest thing we had to this before was +cairo_copy_path, (which is rather unique in providing a list of +non-uniform data). + +The copy_clip_rectangles/rectangle_list_destroy functions follow a +style similar to that of cairo_copy_path. Meanwhile, the dash and +pattern color stop functions introduce a new style in which there is a +single call to return the number of elements available (get_dash_count +and get_color_stop_count) and then a function to be called once to get +each element (get_dash and get_color_stop_rgba). + +I'm interested in hearing feedback from users of these new API +functions, particularly from people writing language bindings. One +open question is whether the clip "getter" functionality should adopt +a style similar to that of the new dash and color_stop interfaces. + +API deprecation +--------------- +The CAIRO_FORMAT_RGB16_565 enum value has been deprecated. It never +worked as a format value for cairo_image_surface_create, and it wasn't +necessary for supporting 16-bit 565 X server visuals. + +XCB backend changes +------------------- +The XCB backend has been updated to track the latest XCB API (which +recently had a 1.0 release). + +New quartz backend +------------------ +Vladimir Vukicevic has written a new "native quartz" backend which +will eventually replace the current "image-surface wrapping" quartz +backend. For now, both backends are available, (the old one is +"quartz" and the new one is "nquartz"). But it is anticipated that the +new backend will replace the old one and take on the "quartz" name +before this backend is marked as supported in a release of cairo. + +New OS/2 backend +---------------- +Doodle and Peter Weilbacher have contributed a new, experimental +backend for using cairo on OS/2 systems. + +Performance improvements +------------------------ +Here are some highlights from cairo's performance suite showing +improvements from cairo 1.2.6 to cairo 1.3.2. The command used to +generate this data is: + + ./cairo-perf-diff 1.2.6 HEAD + +available in the perf/ directory of a recent checkout of cairo's +source, (the cairo-perf-diff script does require a git checkout and +will not work from a tar file---though ./cairo-perf can still be used +to generate a single report there and ./cairo-perf-diff-files can be +used to compare two reports). + +Results are described below both for an x86 laptop (with an old Radeon +video card, recent X.org build, XAA, free software drivers), as well +as for a Nokia 770. First the x86 results with comments on each, (all +times are reported in milliseconds). + +Copying subsets of an image surface to an xlib surface (much faster) +-------------------------------------------------------------------- + xlib-rgba subimage_copy-512 10.50 -> : 53.97x speedup +█████████████████████████████████████████████████████ + +Thanks to Christopher (Monty) Montgomery for this big performance +improvement. Any application which has a large image surface and is +copying small pieces of it at a time to an xlib surface, (imagine an +application that loads a single image containing all the "sprites" for +that application), will benefit from this fix. The larger the ratio of +the image surface to the portion being copied, the larger the benefit. + +Floating-point conversion (3x faster) +------------------------------------- + xlib-rgba pattern_create_radial-16 27.75 -> 3.93 : 2.94x speedup +██ +image-rgb pattern_create_radial-16 26.06 -> 3.74 : 2.90x speedup +█▉ + +Thanks to Daniel Amelang, (and others who had contributed the idea +earlier), for this nice improvement in the speed of converting +floating-point values to fixed-point. + +Text rendering (1.3 - 2x faster) +------------------------------ + xlib-rgba text_image_rgba_source-256 319.73 -> 62.40 : 2.13x speedup +█▏ +image-rgb text_solid_rgba_over-64 2.85 -> 0.88 : 1.35x speedup +▍ + +I don't think we've ever set out to improve text performance +specifically, but we did it a bit anyway. I believe the extra +improvement in the xlib backend is due to Monty's image copying fix +above, and the rest is due to the floating-point conversion speedup. + +Thin stroke improvements (1.5x faster) +--------------------------------------------- +image-rgb world_map-800 1641.09 -> 414.77 : 1.65x speedup +▋ + xlib-rgba world_map-800 1939.66 -> 529.94 : 1.52x speedup +▌ + +The most modest stuff to announce in this release is the 50% +improvement I made in the world_map case. This is in improvement that +should help basically anything that is doing strokes with many +straight line segments, (and the thinner the better, since that makes +tessellation dominate rasterization). The fixes here are to use a +custom quadrilateral tessellator rather than the generic tessellator +for straight line segments and the miter joins. + +Performance results from the Nokia 770 +-------------------------------------- + xlib-rgba subimage_copy-512 55.88 -> 2.04 : 27.34x speedup +██████████████████████████▍ + xlib-rgb text_image_rgb_over-256 1487.58 -> 294.43 : 5.05x speedup +████ +image-rgb pattern_create_radial-16 187.13 -> 91.86 : 2.04x speedup +█ + xlib-rgba world_map-800 21261.41 -> 15628.02 : 1.36x speedup +▍ + +Here we see that the subimage_copy improvement was only about half as +large as the corresponding improvement on my laptop, (27x faster +compared to 54x) and the floating-point conversion fix also was quite +as significant, (2x compared to 3x). Oddly the improvement to text +rendering performance was more than twice as good (5x compared to +2x). I don't know what the reason for that is, but I don't think it's +anything anybody should complain about. + +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 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. + +Various improvements have been made in the testing infrastructure to prevent +false positives, and to make sure the generated cairo shared object behaves as +expected in terms of exported symbols and relocations. + +There were a total of 89 changes since 1.2.4. The following list the most +important ones: + +Common fixes +------------ +- Avoid unsigned loop control variable to eliminate infinite, + memory-scribbling loop. (#7593) +- Fix cairo_image_surface_create to report INVALID_FORMAT errors. + Previously the detected error was being lost and a nil surface was + returned that erroneously reported CAIRO_STATUS_NO_MEMORY. +- Change _cairo_color_compute_shorts to not rely on any particular + floating-point epsilon value. (#7497) +- Fix infinite-join test case (bug #8379) +- Pass correct surface to create_similar in _cairo_clip_init_deep_copy(). + +PS/PDF fixes +------------ +- Fix Type 1 embedding in PDF. +- Correct the value of /LastChar in the PDF Type 1 font dictionary. +- Improve error checking in TrueType subsetting. +- Compute right index when looking up left side bearing. (bug #8180) +- Correct an unsigned to signed conversion problem in truetype subsetting + bbox. +- Type1 subsetting: Don't put .notdef in Encoding when there are 256 glyphs. +- Add cairo version to PS header / PDF document info dictionary. +- Set CTM before path construction. + +Win32 fixes +----------- +- Get correct unhinted outlines on win32. (bug 7603) +- Make cairo as a win32 static library possible. +- Use CAIRO_FORMAT_RGB24 for BITSPIXEL==32 surfaces too. + +Build system fixes +------------------ +- Define WINVER if it's not defined. (bug 6456) +- Fix the AMD64 final link by removing SLIM from pixman. +- Misc win32 compilation fixes. +- Add Sun Pro C definition of pixman_private. +- Use pixman_private consistently as prefix not suffix. +- Added three tests check-plt.sh, check-def.sh, and check-header.sh that check + that the shared object, the .def file, and the public headers agree about + the exported symbols. +- Require pkg-config 0.19. (#8686) + + +Release 1.2.4 (2006-08-18 Carl Worth ) +========================================================= +This is the second bug fix release in the 1.2 series, coming less than +two weeks after the 1.2.2 release made on August 8. + +The big motivation for a quick release was that there were a log of +build system snags that people ran into with the 1.2.2 release. But, +by the time we got those all done, we found that we had a bunch of +fixes for cairo's rendering as well. So there's a lot of goodness in +here for such a short time period. + +Rendering fixes +--------------- +Fix image surfaces to not be clipped when used as a source (Vladimir Vukicevic) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=72e25648c4c4bc82ddd938aa4e05887a293f0d8b + +Fix a couple of corner cases in dashing degenerate paths (Jeff Muizelaar) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=fbb1758ba8384650157b2bbbc93d161b0c2a05f0 + +Fix support for type1 fonts on win32 (Adrian Johnson) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=da1019c9138695cb838a54f8b871bbfd0e8996d7 + +Fix assertion failure when rotating bitmap fonts (Carl Worth) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=0bfa6d4f33b8ddb5dc55bbe419c15df4af856ff9 + +Fix assertion failure when calling cairo_text_path with bitmap fonts (Carl Worth) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=9878a033531e6b96b5f27e69e10e90dee7440cd9 + +Fix mis-handling of cairo_close_path in some situations (Tim Rowley, Carl Worth) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=53f74e59faf1af78f2f0741ccf1f23aa5dad4efc + +Respect font_matrix translation in _cairo_gstate_glyph_path (Behdad Esfahbod) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=f183b835b111d23e838889178aa8106ec84663b3 + +Fix vertical metrics adjustment to work with non-identity shapes (Behdad Esfahbod) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7bc263842a798d657a95e539e1693372448837f + +[PS] Set correct ImageMatrix in _cairo_ps_surface_emit_bitmap_glyph_data (Behdad Esfahbod) +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) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=0e78e7144353703cbd28aae6a67cd9ca261f1d68 + +Fix typos causing win32 build problem with PS,PDF, and SVG backends (Behdad Esfahbod) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=aea83b908d020e26732753830bb3056e6702a774 + +Fix configure cache to not use stale results (Behdad Esfahbod) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=6d0e3260444a2d5b6fb0cb223ac79f1c0e7b3a6e + +Fix to not pass unsupported warning options to the compiler (Jens Granseuer) +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) +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) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=6122cc85c8f71b1ba2df3ab86907768edebe1781 + +Fix test suite to not require signal.h to be present (Behdad Esfahbod) +https://gitweb.freedesktop.org/?p=cairo;a=commit;h=6f8cf53b1e1ccdbe1ab6a275656b19c6e5120e40 + +Code cleanups +------------- +Many useful warnings cleanups from sparse, valgrind, and careful eyes +(Kjartan Maraas, Pavel Roskin) + +Release 1.2.2 (2006-08-08 Carl Worth ) +========================================================= +This is the first bug fix release in the 1.2 series since the original +1.2.0 release made six weeks ago. + +There were some very serious bugs in the 1.2.0 release, (see below), +so everybody is encouraged to upgrade from 1.2.0 to 1.2.2. The 1.2.2 +release maintains source and binary compatibility with 1.2.0 and does +not make any API additions. + +Fix crashes with BGR X servers +------------------------------ +With cairo 1.2.0 many people reported problems with all cairo-using +programs, (including all GTK+ programs with GTK+ >= 2.8) immediately +crashing with a complaint about an unsupported image format. This bug +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: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=9ae66174e774b57f16ad791452ed44efc2770a59 + +Fix the "disappearing text" bug +------------------------------- +With cairo 1.2.0 many people reported that text would disappear from +applications, sometimes reappearing with mouse motion or +selection. The text would disappear after the first space in a string +of text. This bug was caused by an underlying bug in (very common) X +servers, and only affected text rendered without antialiasing, (either +a bitmap font or a vector font with antialiasing disabled). The bug +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: 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 + +Fix broken image fallback scaling (aka. "broken printing") +---------------------------------------------------------- +The various "print" backends, (pdf, ps, and svg), sometimes fallback +to using image-based rendering for some operations. In cairo 1.2.0 +these image fallbacks were scaled improperly. Applications using cairo +can influence the resolution of the image fallbacks with +cairo_surface_set_fallback_resolution. With the bug, any value other +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: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=1feb4291cf7813494355459bb547eec604c54ffb + +Fix inadvertent semantic change of font matrix translation (Behdad Esfahbod) +---------------------------------------------------------------------------- +The 1.2.0 release introduced an inadvertent change to how the +translation components of a font matrix are interpreted. In the 1.0 +series, font matrix translation could be used to offset the glyph +origin, (though glyph metrics were reported incorrectly in +1.0). However in 1.2.0, the translation was applied to the advance +values between each glyph. The 1.2.0 behavior is fairly useless in +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: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=84840e6bba6e72aa88fad7a0ee929e8955ba9051 + +Fix create_similar to preserve fallback resolution and font options (Behdad Esfahbod) +------------------------------------------------------------------------------------- +There has been a long-standing issue with cairo_surface_create_similar +such that font options and other settings from the original +destination surface would not be preserved to the intermediate +"similar" surface. This could result in incorrect rendering +(particularly with respect to text hinting/antialiasing) with +fallbacks, for example. + +report: https://bugs.freedesktop.org/show_bug.cgi?id=4106 +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) +---------------------------------------------------------------------------- +Several people noticed that upgrading from cairo 1.0 to cairo 1.2.0 +caused a significant performance regression when using the xlib +backend. This performance regression was particularly noticeable when +doing lots of text rendering and when using a high-latency connection +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: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7191885c88068dad57d68ced69a752d1162b12c + +PDF: Eliminate dependency on FreeType library dependency (Adrian Johnson) +------------------------------------------------------------------------- +The cairo 1.2 series adds a supported pdf backend to cairo. In cairo +1.2.0 this backend required the freetype library, which was an +undesirable dependency on systems such as win32, (cairo is designed to +always prefer the "native" font system). As of cairo 1.2.2 the +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: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=a0989f427be87c60415963dd6822b3c5c3781691 + +PDF: Fix broken output on amd64 (Adrian Johnson) +------------------------------------------------ +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: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=067d97eb1793a6b0d0dddfbd0b54117844511a94 + +PDF: Fix so that dashing doesn't get stuck on (Kent Worsnop) +------------------------------------------------------------ +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: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=778c4730a86296bf0a71080cf7008d7291792256 + +Fix memory leaks in failure paths in gradient creation (Alfred Peng) +-------------------------------------------------------------------- +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: 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: 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: https://gitweb.freedesktop.org/?p=cairo;a=commit;h=e0ad1aa995bcec4246c0b8ab0d5a5a79871ce235 + +Various memory leak fixes +------------------------- +Fix memory leak in _cairo_surface_show_glyphs (bug 7766) +Fix file handle leak in failure path (bug 7616) +Fix some memory leaks in the test cases. +Fix some memory leaks in font subsetting code used in print backends. + +Documentation improvements (Behdad Esfahbod) +-------------------------------------------- +Added new documentation for several functions (cairo_show_page, +cairo_copy_page, cairo_in_stroke, cairo_in_fill). + +Fixed some syntax errors that were preventing some existing +documentation from being published. + +Fixed several minor typographical errors. + +Added an index for new symbols in 1.2. + +Release 1.2.0 (2006-06-27 Carl Worth ) +========================================================= +This is the culmination of the work that has gone on within the 1.1 +branch of cairo. + +There has been one API addition since the cairo 1.1.10 snapshot: + + cairo_xlib_surface_get_width + cairo_xlib_surface_get_height + +There's also a new feature without any API change: + + Dots can now be drawn by using CAIRO_LINE_CAP_ROUND with + degenerate sub-paths, (cairo_move_to() followed by either + cairo_close_path() or a cairo_line_to() to the same location). + +And at least the following bugs have been fixed: + + 6759 fontconfig option AntiAlias doesn't work in cairo 1.1.2 + 6955 Some characters aren't displayed when using xlib (cache u... + 7268 positive device_offset values don't work as source + * PDF emit_glyph function needs to support bitmapped glyphs + * PS emit_glyph function needs to support bitmapped glyphs + * SVG emit_glyph function needs to support bitmapped glyphs + * PDF: minefield page one is falling back unnecessarily + * 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 + https://bugzilla.gnome.org/show_bug.cgi?id=332266 + * PS: Fix source surfaces with transformations + * 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 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 +been a part of it. Congratulations to all contributors to cairo! + +Snapshot 1.1.10 (2006-06-16 Carl Worth ) +=========================================================== +This is the fifth in a series of snapshots working toward the 1.2 +release of cairo. + +The primary motivation for this snapshot is to fix a long-standing bug +that had long been silent, but as of the 1.1.8 snapshot started +causing crashes when run against 16-bit depth X servers, (often Xvnc +or Xnest). The fix for this adds a new CAIRO_FORMAT_RGB16_565 to the +API. + +This snapshot also includes a rewrite of cairo's SVG backend to +eliminate the dependency on libxml2. With this in place, cairo 1.2 +will not depend on any libraries that cairo 1.0 did not. + +As usual, there are also a few fixes for minor bugs. + +Snapshot 1.1.8 (2006-06-14 Carl Worth ) +========================================================== +This is the fourth in a series of snapshots working toward the 1.2 +release of cairo. At this point, all major features of the 1.2 release +are in place, leaving just a few bug fixes left. + +In particular, there well be no additional API changes between this +1.1.8 snapshot and the 1.2 release. + +The announcement for 1.1.6 mentioned several API changes being +considered. Only one of these changes was actually implemented +(set_dpi -> fallback_resolution). This change does introduce one +source-level incompatibility with respect to previous 1.1.x snapshots, +so see below for details. + +Here is an abbreviated summary of changes since the 1.1.6 snapshot: + +** API Change ** +---------------- +According to the plan mentioned in the 1.1.6 notes, one source-level +incompatible change has been implemented. The following three +functions have been removed from cairo's API: + + cairo_pdf_surface_set_dpi + cairo_ps_surface_set_dpi + cairo_svg_surface_set_dpi + +and in their place the following function has been added: + + cairo_surface_set_fallback_resolution + +The signature and semantics of the function remains the same, so it is +a simple matter of changing the name of the function when calling +it. As a transition mechanism, this snapshot will (on many systems) +build to include the old symbols so that code previously compiled will +still run. However, all source code using the old names must be +updated before it will compile. And the upcoming 1.2 release is not +anticipated to include the old symbols. + +Finally, it should be pointed out that the old symbols never existed +in the supported API of any stable release of cairo. (In the stable +1.0 releases the PDF, PS, and SVG backends were advertised as +experimental and unstable.) + +And, as always, cairo continues to maintain source and binary +compatibility between major releases. So applications compiled against +supported backends in a stable release of cairo (1.0.4 say) will +continue to compile and run without modification against new major +releases (1.2.0 say) without modification. + +API additions +------------- +The following new functions have been added to cairo's API: + + cairo_surface_get_content + cairo_debug_reset_static_data + cairo_image_surface_get_data + cairo_image_surface_get_format + cairo_image_surface_get_stride + cairo_win32_font_face_create_for_hfont + +New, backend-specific pkg-config files +-------------------------------------- +In addition to the original cairo.pc file, cairo will also now install +a pkg-config files for each configured backend, (for example +cairo-pdf.pc, cairo-svg.pc, cairo-xlib.pc, cairo-win32.pc, etc.) this +also includes optional font backends (such as cairo-ft.pc) and the +optional png functionality (cairo-png.pc). + +These new pkg-config files should be very convenient for allowing +cairo-using code to easily check for the existing of optional +functionality in cairo without having to write complex rules to grub +through cairo header files or the compiled library looking for +symbols. + +Printing backend (PS, PDF, and SVG) +----------------------------------- +Improving the quality of the "printing" backends has been a priority +of the development between cairo 1.1.6 and cairo 1.1.8. + +The big improvement here is in the area of text output. Previously, at +best, text was output as paths without taking advantage of any font +support available in the output file format. + +Now, at the minimum text paths will be shared by using type3 fonts +(for PS and PDF---and similarly, defs for SVG). Also, if possible, +type3 and truetype fonts will be embedded in PostScript and PDF +output. There are still some known bugs with this, (for example, +selecting text in a cairo-generated PDF file with an embedded truetype +font does not work). So there will be some more changes in this area +before cairo 1.2, but do try test this feature out as it exists so +far. + +Many thanks to Kristian Høgsberg for the truetype and type1 font +embedding. + +win32 backend +------------- +Performance improvements by preferring GDI over pixman rendering when possible. +Fixes for text rendering. + +xlib backend +------------ +Fix potentially big performance bug by making xlib's create_similar +try harder to create a pixmap of a depth matching that of the screen. + +Bug fixes +--------- +Among various other fixes, the following bugs listed in bugzilla have +been fixed: + + Bug 2488: Patch to fix pixman samping location bug (#2488). + https://bugs.freedesktop.org/show_bug.cgi?id=2488 + + Bug 4196: undef MIN an MAX before defining to avoid duplicate definition + https://bugs.freedesktop.org/show_bug.cgi?id=4196 + + Bug 4723: configure.in: Fix m4 quoting when examining pkg-config version + https://bugs.freedesktop.org/show_bug.cgi?id=4723 + + Bug 4882: Flag Sun's X server has having buggy_repeat. + https://bugs.freedesktop.org/show_bug.cgi?id=4882 + + Bug 5306: test/pdf2png: Add missing include of stdio.h + https://bugs.freedesktop.org/show_bug.cgi?id=5306 + + Bug 7075: Fix make clean to remove cairo.def + https://bugs.freedesktop.org/show_bug.cgi?id=7075 + +(Many thanks to Behdad Esfahbod for helping us track down and fix many +of these.) + +Snapshot 1.1.6 (2006-05-04 Carl Worth ) +========================================================== +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: + + 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 +performance optimization (X server gradients) and a list of bug +fixes. This gives us a fair amount of freedom to cut the 1.2 release +at almost any point by deciding to defer remaining bug fixes to +subsequent maintenance releases such as 1.2.2 and 1.2.4. + +Before we will do that, we must first be wiling to commit to all the +new API additions. As a heads-up, there are a couple of potential API +changes being considered. (Note that these are changes to new API +introduced during 1.1 so these will not introduce API +incompatibilities compared to the stable 1.0 series). The changes +being considered are: + + cairo_get_group_target: may acquire x and y offset return + parameters. May also be eliminated in favor of + cairo_get_target assuming its role + + cairo_pdf_surface_set_dpi: + cairo_ps_surface_set_dpi: + cairo_svg_surface_set_dpi: These functions may be removed in favor + of a new cairo_surface_set_fallback_resolution + +Additionally there is the possibility of a slight change in the +semantics of cairo_set_line_width. We believe the current behavior of the sequence: + + cairo_set_line_width; ... change CTM ...; cairo_stroke; + +is buggy. It is currently behaving the same as: + + ... change CTM ...; cairo_set_line_width; cairo_stroke; + +We are considering fixing this bug before 1.2 with the hope that +nobody is already relying on the buggy behavior described here. Do +shout if you suspect you might be in that position. + +The items included in this snapshot (since the 1.1.4 snapshot) are +described below. + +API additions +------------- +The long-awaited group-rendering support is now available with the +following function calls: + + cairo_push_group + cairo_push_group_with_content + cairo_pop_group + cairo_pop_group_to_source + cairo_get_group_target + +This API provides a much more convenient mechanism for doing rendering +to an intermediate surface without the need to manually create a +temporary cairo_surface_t and a temporary cairo_t and clean them up +afterwards. + +Add the following missing get function to complement +cairo_surface_set_device_offset: + + cairo_surface_get_device_offset + +PDF backend (API addition) +-------------------------- +The PDF backend now provides for per-page size changes, (similar to +what the PostScript backend got in the 1.1.4 snapshot). The new API +is: + + cairo_pdf_surface_set_size + +Xlib backend (API additions) +---------------------------- +The following functions have been added to allow the extraction of +Xlib surface: + + cairo_xlib_surface_get_display + cairo_xlib_surface_get_drawable + cairo_xlib_surface_get_screen + cairo_xlib_surface_get_visual + cairo_xlib_surface_get_depth + +XCB backend (experimental) +-------------------------- +Update backend so that it now compiles with the recent XCB 0.9 release. + +Bug fixes and memory leak cleanup +--------------------------------- +Various little things, nothing too significant though. + +Snapshot 1.1.4 (2006-05-03 Carl Worth ) +========================================================== +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: + + https://cairographics.org/ROADMAP + +The items included in this snapshot (since the 1.1.2 snapshot) are +described below. + +PostScript backend: new printing-oriented API +--------------------------------------------- +We anticipate that with cairo 1.2, toolkits will begin to use cairo +for printing on systems that use PostScript as the spool format. To +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 + +These functions allow variation of the page size/orientation from one +page to the next in the PostScript output. They also allow the toolkit +to provide per-document and per-page printer control options in a +device-independent way, (for example, by using PPD options and +emitting them as DSC comments into the PostScript output). This should +allow toolkits to provide very fine-grained control of many options +available in printers, (media size, media type, tray selection, etc.). + +SVG backend: builds by default, version control +----------------------------------------------- +The SVG backend continues to see major improvements. It is expected +that the SVG backend will be a supported backend in the 1.2 +release. This backend will now be built by default if its dependencies +(freetype and libxml2) are met. + +Additionally, the SVG backend now has flexibility with regard to what +version of SVG it targets. It will target SVG 1.1 by default, which +will require image fallbacks for some of the "fancier" cairo +compositing operators. Or with the following new function calls: + + cairo_svg_surface_restrict_to_version + cairo_svg_get_versions + cairo_svg_version_to_string + +it can be made to target SVG 1.2 in which there is native support for +these compositing operators. + +Bug fixes +--------- +At least the following bugs have been fixed since the 1.1.2 snapshot: + +crash at XRenderAddGlyphs +https://bugs.freedesktop.org/show_bug.cgi?id=4705 + +Can't build cairo-1.1.2 on opensolaris due to " void function cannot return value" +https://bugs.freedesktop.org/show_bug.cgi?id=6792 + +Missing out-of-memory check at gfx/cairo/cairo/src/cairo-atsui-font.c:185 +https://bugzilla.mozilla.org/show_bug.cgi?id=336129 + +A couple of memory leaks. + +Snapshot 1.1.2 (2006-04-25 Carl Worth ) +========================================================== +This is the first in a series of snapshots working toward the upcoming +1.2 release of cairo. (Subsequent snapshot will use successive even +numbers for the third digit, 1.1.4, 1.1.6, etc.) This snapshot is +backwards-compatible with the 1.0 series---it makes a few API +additions but does not remove any API. + +PostScript and PDF backends are no longer "experimental" +-------------------------------------------------------- +The major theme of the 1.2 release is improved PostScript and PDF +backends for cairo. Unlike the 1.0 series, in the 1.2 series these +backends will not be marked as experimental and will be enabled by +default. We encourage people to test this snapshot and the PS/PDF +backends in particular as much as possible. + +The PostScript and PDF output is not yet ideal. + + * One major problem with the PostScript output is that image + fallbacks are used more often than strictly necessary, and the + image fallbacks are at a lower resolution than desired, (the + cairo_ps_surface_set_dpi call is ignored). + + * The major drawback of the current PDF backend implementation is + its text support. Every glyph is represented by a filled path in + the PDF file. The causes file sizes to be much larger and + rendering to be much slower than desired. + +It is anticipated that both of these shortcomings will see some +improvements before the final 1.2 release. + +In spite of those shortcomings, we hope that the PS and PDF backends +will yield faithful results for pretty much any cairo operations you +can throw at them. Please let us know if you are getting obviously +"different" results from the PS/PDF backends than from the image or +xlib backends. + +Other new experimental backends +------------------------------- +This snapshot includes three new backends that did not exist in the +1.0 series: + + * beos backend + + * directfb backend + + * svg backend + +These are all currently marked "experimental" and are disabled by +default. But the SVG backend in particular has seen a lot of recent +development and is very close to passing the entire cairo test +suite. It is possible that this backend will become a fully supported +backend by the time of the cairo 1.2 release. + +Public API additions +-------------------- +There have been a few new API functions added to cairo, including: + +New get_type functions for querying sub-types of object: + + cairo_surface_get_type + cairo_pattern_get_type + cairo_font_face_get_type + cairo_scaled_font_get_type + +More convenience in working with cairo_scaled_font_t with new getter +functions: + + cairo_scaled_font_get_font_face + cairo_scaled_font_get_font_matrix + cairo_scaled_font_get_ctm + cairo_scaled_font_get_font_options + +As well as a convenience function for setting a scaled font into a +cairo context: + + cairo_set_scaled_font + +and a function to allow text extents to be queried directly from a +scaled font, (without requiring a cairo_surface_t or a cairo_t): + + cairo_scaled_font_text_extents + +These new scaled font functions were motivated by the needs of the +pango library. + +Finally, a new path-construction function was added which clears the +current point in preparation for a new sub path. This makes cairo_arc +easier to use in some situations: + + cairo_new_sub_path + +Before the 1.2 release is final we do still plan a few more API +additions specifically motivated by the needs of Mozilla/Firefox. + +Optimizations and bug fixes +--------------------------- +Shortly after the 1.0 maintenance series branched off the mainline +there was a major rework of the cairo font internals. This should +provide some good performance benefits, but it's also another area +people should look at closely for potential regressions. + +There has not yet been any widespread, systematic optimization of +cairo, but various performance improvements have been made, (and some +of them are fairly significant). So if some things seem faster than +1.0 then things are good. If there are any performance regressions +compared to 1.0 then there is a real problem and we would like to hear +about that. + +There has been a huge number of bug fixes---too many to mention in +detail. Again, things should be better, and never worse compared to +1.0. Please let us know if your testing shows otherwise. + +Release 1.0.2 (2005-10-03 Carl Worth ) +========================================================= +For each bug number XXXX below, see: + + https://bugs.freedesktop.org/show_bug.cgi?id=XXXX + +for more details. + +General bug fixes +----------------- + * 4408 - Add support for dashing of stroked curves + (Carl Worth) + + * 4409 - Fix dashing so that each dash is capped on both ends + (Carl Worth) + + * 4414 - Prevent SIGILL failures (proper use of -mmmx and -msse flags) + (Sebastien Bacher, Billy Biggs) + + * 4299 - Fix crashes with text display in multi-threaded program + (Alexey Shabalin, Carl Worth) + + * 4401 - Do not use sincos function since it is buggy on some platforms) + (Tim Mooney, Carl Worth) + + * 4245 - Fix several bugs in the test suite exposed by amd64 systems + (Seemant Kulleen, Carl Worth) + + * 4321 - Add missing byteswapping on GetImage/PutImage + (Sjoerd Simons, Owen Taylor) + + * 4220 - Make the check for rectangular trapezoids simpler and more accurate + (Richard Stellingwerff, Owen Taylor) + + * 4260 - Add missing channel-order swapping for antialised fonts + (Barbie LeVile, Owen Taylor) + + * 4283 - Fix compilation failure with aggressive inlining (gcc -O3) + (Marco Manfredini, Owen Taylor) + + * 4208 - Fix some warnings from sparse + (Kjartan Maraas, Billy Biggs) + + * 4269 - Fix to not crash when compiled with -fomit-frame-pointer + (Ronald Wahl, Owen Taylor) + + * 4263 - Improve performance for vertical gradients + (Richard Stellingwerff, Owen Taylor) + + * 4231 + * 4298 - Accommodate gentoo and Mandriva versions in X server vendor string check + (Billy Biggs, Frederic Crozat, Owen Taylor) + +win32-specific fixes +-------------------- + * 4599 - Fix "missing wedges" on some stroked paths (win32) + (Tim Rowley, Jonathan Watt, Bertram Felgenhauer, Carl Worth, Keith Packard) + + * 4612 - Fix disappearing text if first character out of surface (win32) + (Tim Rowley) + + * 4602 - Fix shutdown of cairo from failing intermediate, size-0 bitmaps (win32) + Aka. the "white rectangles" bug from mozilla-svg testing + (Tim Rowley) + + * Various portability improvements for win32 + (Hans Breuer, Owen Taylor, Carl Worth) + + * 4593 - Fix font sizes to match user expectations (win32) + (Tor Lillqvist, Owen Taylor) + + * 3927 - Fix to report metrics of size 0 for glyph-not-available (win32) + (Hans Breuer, Owen Taylor, Tor Lillqvist) + + * Add locking primitives for win32 + (Hans Breuer) + +xlib-specific fixes +------------------- + * Fix crash from size-0 pixmap due to empty clip region (xlib) + (Radek Doulík, Carl Worth) + +Release 1.0.0 (2005-08-24 Carl Worth ) +========================================================= +Experimental backends +--------------------- + * The Glitz, PS, PDF, Quartz, and XCB backends have been declared + experimental, and are not part of the API guarantees that accompany + this release. They are not built by default, even when the required + libraries are available, and must be enabled explicitly with + --enable-ps, --enable-pdf, --enable-quartz or --enable-xcb. + + It is very painful for us to be pushing out a major release without + these backends enabled. There has been a tremendous amount of work + put into each one and all are quite functional to some + extent. However, each also has some limitations. And none of these + backends have been tested to the level of completeness and + correctness that we expect from cairo backends. + + We do encourage people to experiment with these backends and report + success, failure, or means of improving them. + +Operator behavior +----------------- + * Prior to 0.9.0 the SOURCE, CLEAR and a number of other operators + behaved in an inconsistent and buggy fashion and could affect areas + outside the clip mask. In 0.9.0, these six "unbounded" operators + were fixed to consistently clear areas outside the shape but within + the clip mask. This is useful behavior for an operator such as IN, + but not what was expected for SOURCE and CLEAR. So, in this release + the behavior of SOURCE and CLEAR has been changed again. They now + affect areas only within both the source and shape. We can write + the new operators as: + + SOURCE: dest' = (mask IN clip) ? source : dest + CLEAR: dest' = (mask IN clip) ? 0 : dest + +Behavior and API changes +------------------------ + * Setting the filter on a gradient pattern would change the + interpolation between color stops away from the normal linear + interpolation. This dubious behavior has been removed. + + * The CAIRO_CONTENT_VALID() and CAIRO_FORMAT_VALID() macros -- + implementation details that leaked into cairo.h -- have been moved + into an internal header. + + * The cairo_show_text function now advances the current point + according to the total advance values of the string. + +API additions +------------- + * cairo_set_dash can now detect error and can set + CAIRO_STATUS_INVALID_DASH. + +Features +-------- + * When compiled against recent versions of fontconfig and FreeType, + artificial bold fonts can now be turned on from fonts.conf using + the FC_EMBOLDEN fontconfig key. + +Optimization +------------ + * The compositing code from the 'xserver' code tree has now been + completely merged into libpixman. This includes MMX optimization of + common operations. + + * The image transformation code in libpixman has been improved and + now performs significantly faster. + +Bug fixes +--------- + * Several crashes related to corruption in the font caches have been + fixed. + + * All test cases now match pixel-for-pixel on x86 and PPC; this + required fixing bugs in the compositing, stroking, and pattern + rendering code. + + * Negative dash offsets have been fixed to work correctly. + + * 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. + + * Lots of other bug fixes, but we're too tired to describe them in + more detail here. + +Release 0.9.2 (2005-08-13 Carl Worth ) +========================================================= +Release numbering +----------------- + * You will notice that this release jumped from 0.9.0 to 0.9.2. We've + decided to use an odd micro version number (eg. 0.9.1) to indicate + in-progress development between releases. As soon as 0.9.2 is + tagged, the version will be incremented in CVS to 0.9.3 where it + will stay until just before 0.9.4 is built, uploaded, and tagged. + + So, even-micro == a released version, odd-micro == something in-between. + +Libpixman dependency dropped +---------------------------- + * As of this release, the dependency on an external libpixman has + been dropped. Instead, the code from libpixman needed for cairo has + been incorporated into the cairo source tree. The motivation for + this change is that while cairo's API is stable and ready to be + maintained after the 1.0 release, libpixman's API is not, so we do + not want to expose it at this time. + + Also, the incorporation of libpixman into cairo also renames all + previously-public libpixman symbols in order to avoid any conflict + with a future release of libpixman + +API additions +------------- + * Macros and functions have been added so that the version of cairo + can be queried at either compile-time or at run-time. The version + is made available as both a human-readable string and as a single + integer: + + CAIRO_VERSION_STRING eg. "0.9.2" + CAIRO_VERSION eg. 000902 + + const char* + cairo_version_string (void); /* eg. "0.9.2" */ + + int + cairo_version (void); /* eg. 000902 */ + + A macro is provided to convert a three-part component version into + the encoded single-integer form: + + CAIRO_VERSION_ENCODE(X,Y,Z) + + For example, the CAIRO_VERSION value of 000902 is obtained as + CAIRO_VERSION_ENCODE(0,9,2). The intent is to make version + comparisons easy, either at compile-time: + + #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(0,9,2) + ... + #endif + + Or at run-time: + + if (cairo_version() >= CAIRO_VERSION_ENCODE(0,9,2)) { /* ... */ } + +Thread safety +------------- + * This release adds pthread-based locking (when available) to make + the caches used by cairo safe for threaded programs. Some may + remember a failed experiment with this locking between the 0.5.1 + and 0.5.2 snapshots, (where even single-threaded programs that + linked with -lpthread would deadlock). We believe that that problem + has been fixed, so we are looking forward to testing and reports + from users with threaded applications. + +Bug fixes +--------- + * The XCB and Quartz backends failed to compiled in the 0.9.0 release + due to minor syntax errors. These have now been fixed. + + * Various crashes in glitz and pixman due to size 0 glyphs have been + fixed. + +Release 0.9.0 (2005-08-08 Carl Worth ) +========================================================= +Soname change +------------- + * In all prior snapshots, the libtool library versioning was set to + 1:0:0. As this release is intended to mark the beginning of + backwards-compatible releases, the versioning has been incremented + to 2:0:0. You will notice that the numeric extension on the + installed library filename will change similarly. + + This change will also require all cairo-using applications to be + recompiled. We recognize that this may cause some frustration since + this release is backwards-compatible with 0.6.0 and in that sense + "shouldn't" require re-compilation. However, since all historical + snapshots have used the same 1:0:0 version in spite of incompatible + API changes between them, it was essential that the upcoming 1.0 + release series have distinct library versioning. + + All future releases will use the library versioning to properly + indicate compatibility between releases. So, any application + re-compiled now to work with the 0.9.0 will not need to be + recompiled when a compatible 1.0 release of cairo is made in the + future. + +API additions +------------- + * Add new function calls to set/get the current antialiasing mode in + the graphics state: + + cairo_set_antialias + cairo_get_antialias + + This call accepts the same modes recently added for font options + (NONE or GRAY) but affects the rendering of geometry other than + text. The intent of this call is to enable more precise control of + which pixels are affected by each operation, for example to allow + for full-scene antialiasing for seam-free rendering. It is not + expected that non-antialiased rendering will perform better than + anti-aliased rendering. + + * Three new functions were added to provide support for mixed cairo- + and non-cairo drawing to the same surface: + + cairo_surface_mark_dirty + cairo_surface_mark_dirty_rectangle + cairo_surface_flush + + * The return type of the several "reference" functions was change, + (API compatibly), from void to the same type as the argument. The + affected functions are: + + cairo_font_face_reference + cairo_scaled_font_reference + cairo_pattern_reference + cairo_surface_reference + cairo_reference + + This allows a convenient way to assign and reference in a single + statement. + +Semantic changes +---------------- + * The behavior of cairo_set_source with a pattern with a non-identity + matrix was previously not well-defined. The new behavior is as + follows: + + The pattern's transformation matrix will be locked to the + user space in effect at the time of cairo_set_source(). This means + that further modifications of the CTM will not affect the source + pattern. + +cairo-win32 +----------- + * Some portability improvements, (eg. workaround for missing stdint.h). + +cairo-ft +-------- + * Updated to allow compilation with older versions of freetype. + +Bug fixes +--------- + * Fix the unbounded operators to actually produce a correct result, + (previously the results were artificially restricted to the + bounding box of whatever shape was being drawn rather than + extending out infinitely). The fixed operators are: + + CAIRO_OPERATOR_CLEAR + CAIRO_OPERATOR_SOURCE + CAIRO_OPERATOR_OUT + CAIRO_OPERATOR_IN + CAIRO_OPERATOR_DEST_IN + CAIRO_OPERATOR_DEST_ATOP + + * Fix cairo_mask and cairo_mask_surface to transform the mask by the + current transformation matrix (CTM). + + * Fix cairo_set_source to lock the CTM used to transform the pattern. + + * Workaround for X server Render bug involving repeating patterns + with a general transformation matrix. + + * cairo_get_font_face fixed to return a "nil" font face object rather + than NULL on error. + + * cairo_set_font_face fixed to not crash if given a NULL font face, + (which is the documented interface for restoring the default font + face). + + * Fix xlib glyphset caching to not try to free a NULL glyph. + +Snapshot 0.6.0 (2005-07-28 Carl Worth ) +========================================================== +API changes +----------- +* The prototypes of the following functions have changed: + + cairo_xlib_surface_create_with_xrender_format + cairo_xlib_surface_create_for_bitmap + + A Screen* parameter has been added to each. This allows the cairo + xlib backend to work correctly with multi-head X servers. + +* The following function has been modified: + + cairo_scaled_font_create + + to accept a cairo_font_options_t*. See below fore more details. + +* All opaque, reference-counted cairo objects have now been moved to a + standard error-handling scheme. The new objects to receive this + treatment are cairo_font_face_t, cairo_scaled_font_t, and + cairo_surface_t. (Previous snapshots already provided this scheme + for cairo_t, cairo_path_t, and cairo_pattern_t.) + + This changes two functions to have a return type of void rather than + cairo_status_t: + + cairo_scaled_font_extent + cairo_surface_finish + + And significantly, none of the create functions for any of the + objects listed above will return NULL. The pointer returned from any + function will now always be a valid pointer and should always be + passed to the corresponding destroy function when finished + + The simplest strategy for porting code is to switch from: + + object = cairo__create (); + if (object == NULL) + goto BAILOUT; + + /* act on object */ + + cairo__destroy (object); + + to: + + object = cairo__create (); + if (cairo__status (object)) + goto BAILOUT; + + /* act on object */ + + cairo__destroy (object); + + But significantly, it is not required to check for an error status + before the "act on object" portions of the code above. All + operations on an object with an error status are, by definition, + no-ops without side effect. So new code might be written in an + easier-to-read style of: + + object = cairo__create (); + + /* act on object */ + + cairo__destroy (object); + + with cairo__status checks placed only at strategic + locations. For example, passing an error object to another object, + (eg. cairo_set_source with an in-error pattern), will propagate the + error to the subsequent object (eg. the cairo_t). This means that + error checking can often be deferred even beyond the destruction of + a temporary object. + +API additions +------------- +* New functions for checking the status of objects that have been + switched to the common error-handling scheme: + + cairo_font_face_status + cairo_scaled_font_status + cairo_surface_status + +* The _cairo_error function which was added in 0.5.1 has now been made + much more useful. In 0.5.1 only errors on cairo_t objects passed + through _cairo_error. Now, an error on any object should pass + through _cairo_error making it much more reliable as a debugging + mechanism for finding when an error first occurs. + +* Added new font options support with a myriad of functions: + + cairo_font_options_create + cairo_font_options_copy + cairo_font_options_destroy + + cairo_font_options_status + + cairo_font_options_merge + cairo_font_options_equal + cairo_font_options_hash + + cairo_font_options_set_antialias + cairo_font_options_get_antialias + cairo_font_options_set_subpixel_order + cairo_font_options_get_subpixel_order + cairo_font_options_set_hint_style + cairo_font_options_get_hint_style + cairo_font_options_set_hint_metrics + cairo_font_options_get_hint_metrics + + cairo_surface_get_font_options + + cairo_ft_font_options_substitute + + cairo_set_font_options + cairo_get_font_options + + This new font options support allows the application to have much + more fine-grained control over how fonts are rendered. + Significantly, it also allows surface backends to have some + influence over the process. For example, the xlib backend now + queries existing Xft properties to set font option defaults. + +* New function: + + cairo_xlib_surface_set_drawable + + which allows the target drawable for an xlib cairo_surface_t to be + changed to another with the same format, screen, and display. This + is necessary in certain double-buffering techniques. + +New features +------------ +* Sub-pixel text antialiasing is now supported. + +Bug fixes +--------- +* Fixed assertion failure in cairo_surface_create_similar when + application commits an error by passing a cairo_format_t rather than + a cairo_content_t. + +* Avoid division by zero in various places (cairo-ft). + +* Fix infinite loop when using non-default visuals (cairo-xlib). + +* Eliminate segfault in cairo_image_surface_create_from_png_stream. + +* Prevent errant sign-extension of masks on 64-bit architectures + (cairo-xlib and cairo-xcb). + +* Other miscellaneous fixes. + +Snapshot 0.5.2 (2005-07-18 Carl Worth ) +========================================================== +API changes +----------- +* New functions for creating patterns of a single color: + + cairo_pattern_create_rgb + cairo_pattern_create_rgba + +* Change cairo_surface_create_similar to accept a new type of + cairo_content_t rather than cairo_format_t: + + typedef enum _cairo_content { + CAIRO_CONTENT_COLOR = 0x1000, + CAIRO_CONTENT_ALPHA = 0x2000, + CAIRO_CONTENT_COLOR_ALPHA = 0x3000 + } cairo_content_t; + +* Add new CAIRO_FORMAT_VALID and CAIRO_CONTENT_VALID macros. + +* Remove unused status value: + + CAIRO_STATUS_NO_TARGET_SURFACE + +* Add new status values: + + CAIRO_STATUS_INVALID_STATUS + +* Require libpixman >= 0.1.5 (for necessary bug fixes) + +Bug fixes +--------- +* Fix cairo_surface_write_to_png for RGB24 images. + +* Fix broken metrics and rendering for bitmap fonts. Add mostly + useless bitmap glyph transformation. + +* Fix glyph caches to not eject entries that might be immediately + needed, (fixing intermittent crashes when rendering text). + +* Fix all memory leaks found by running "make check-valgrind". + +ATSUI backend changes +--------------------- +* Allow building against < 10.3 SDK. + +* Prevent crash on empty strings. + +Glitz backend changes +--------------------- +* Require glitz >= 0.4.4. + +* Use frame buffer objects instead of pbuffers for accelerated + offscreen drawing. + +* Minor improvement to gradient pattern creation. + +PostScript backend fixes +------------------------ +* Rewrite of the PS backend to generate more interesting output that + the old big-image implementation. + +Win32 backend fixes +------------------- +* Implement glyph path support. + +* Fix swap of blue and green values in the fill_rectangles path. + +Xlib backend fixes +------------------ +* Add optimization to use XCopyArea rather than XRenderComposite when + transforming only with an integer translation, and using SOURCE + operator or OVER with a source pattern without alpha. + +Snapshot 0.5.1 (2005-06-20 Carl Worth ) +========================================================== +API changes +----------- +* Removed cairo_status_string(cairo_t*) and add + cairo_status_to_string(cairo_status_t) in its place. Code using + cairo_status_string can be ported forward as follows: + + cairo_status (cr); + -> + cairo_status_to_string (cairo_status (cr)); + +* Removed the BAD_NESTING restriction which means that two different + cairo_t objects can now interleave drawing to the same + cairo_surface_t without causing an error. + +* The following functions which previously had a return type of + cairo_status_t now have a return type of void: + + cairo_pattern_add_color_stop_rgba + cairo_pattern_set_matrix + cairo_pattern_get_matrix + cairo_pattern_set_extend + cairo_pattern_set_filter + + See discussion of cairo_pattern_status below for more details. + +API additions +------------- +* Improved error handling: + + cairo_status_t + cairo_pattern_status (cairo_pattern_t *pattern); + + This snapshot expands the status-based error handling scheme from + cairo_t to cairo_path_t and cairo_pattern_t. It also expands the + scheme so that object-creating functions, (cairo_create, + cairo_pattern_create_*, cairo_copy_path_*), are now guaranteed to + not return NULL. Instead, in the case of out-of-memory these + functions will return a static object with + status==CAIRO_STATUS_NO_MEMORY. The status can be checked with the + functions cairo_status and cairo_pattern_status, or by direct + inspection of the new status field in cairo_path_t. + + Please note that some objects, including cairo_surface_t and all of + the font-related objects have not been converted to this + error-handling scheme. + +* In addition to the above changes, a new private function has been added: + + _cairo_error + + This function can be used to set a breakpoint in a debugger to make + it easier to find programming error in cairo-using code. (Currently, + _cairo_error is called when any error is detected within a cairo_t + context, but is not called for non-cairo_t errors such as for + cairo_path_t and cairo_pattern_t). + +* Fixed cairo_path_data_t so that its enum is visible to C++ code, (as + cairo_path_data_type_t). + +Performance improvements +------------------------ +* Made a minor performance improvement for clipping, (restrict clip + surface to the new intersected bounds). + +* Optimize rendering of a solid source pattern with a pixel-aligned + rectangular path to use backend clipping rather than rasterization + and backend compositing. + +* Optimize cairo_paint_with_alpha to defer to cairo_paint when alpha + is 1.0. + +Bug fixes +--------- +* Fixed memory leak in cairo_copy_path. + +* A build fix for non-srcdir builds. + +PDF backend fixes +----------------- +* New support for path-based clipping. + +* Fix for text rotated to angles other than multiples of π/2. + +Win32 backend fixes +------------------- +* Fix for text extents. + +Xlib backend +------------ +* Implemented a complex workaround for X server bug[*] related to + Render-based compositing with untransformed, repeating source + pictures. The workaround uses core Xlib when possible for + performance, (ie. with CAIRO_OPERATOR_SOURCE or CAIRO_OPERATOR_OVER + with an opaque source surface), and falls back to the pixman + image-based compositing otherwise. + + [*] https://bugs.freedesktop.org/show_bug.cgi?id=3566 + +* Various bug fixes, particularly in the fallback paths. + +Snapshot 0.5.0 (2005-05-17 Carl Worth ) +========================================================== +This is a pretty big, and fairly significant snapshot. It represents +between 2 and 3 months of solid work from a lot of people on improving +the API as much as possible. I'd like to express my appreciation and +congratulations to everyone who has worked on the big API Shakeup, +(whether in email battles over names, or fixing my silly bugs). + +This snapshot will require some effort on the part of users, since +there are a _lot_ of API changes (ie. no cairo program ever written is +safe --- they're all broken now in at least one way). But, in spite of +that, we do encourage everyone to move their code to this snapshot as +soon as possible. And we're doing everything we can think of to make +the transition as smooth as possible. + +The idea behind 0.5 is that we've tried to make every good API change +we could want now, and get them all done with. That is, between now +and the 1.0 release of cairo, we expect very few new API changes, +(though some will certainly sneak in). We will have some significant +additions, but the pain of moving code from cairo 0.4 to cairo 0.5 +should be a one time experience, and things should be much smoother as +we continue to move toward cairo 1.0. + +And with so many changes coming out for the first time in this 0.5 +release, we really do need a lot of people trying this out to make +sure the ideas are solid before we freeze the API in preparation for +the 1.0 release. + +OK, enough introduction. Here is a (not-quite-complete) description of +the API removals, changes and additions in this snapshot, (compared to +0.4.0) + +API removals +============ +The following public functions have been removed: + +- cairo_set_target_* + + This is a big change. See the description of cairo_create in + the API changes section for how to deal with this. + +- cairo_set_alpha + + Alpha blending hasn't gone away; there's just a much more + unified rendering model now. Almost all uses of + cairo_set_alpha will be trivially replaced with + cairo_set_source_rgba and a few others will be replaced just + as easily with cairo_paint_with_alpha. + +- cairo_show_surface + + Another useful function that we realized was muddling up the + rendering model. The replacement is quite easy: + cairo_set_source_surface and cairo_paint. + +- cairo_matrix_create +- cairo_matrix_destroy +- cairo_matrix_copy +- cairo_matrix_get_affine + + These functions supported an opaque cairo_matrix_t. We now + have an exposed cairo_matrix_t structure, so these can be + dropped. + +- cairo_surface_set_repeat +- cairo_surface_set_matrix +- cairo_surface_set_filter + + These properties don't belong on surfaces. If you were using + them, you'll just want to instead use + cairo_pattern_create_for_surface and then set these properties + on the pattern. + +- cairo_copy + + This was a confusing function and hopefully nobody will miss + it. But if you really don't find cairo_save/restore adequate, + let us know and we have another idea for a potential + replacement. + +And while we're on the subject of removals, we carefully tightened up +the cairo header files so they no longer gratuitously include header +files that are not strictly necessary, (stdio.h, stdint.h, pixman.h, +Xrender.h, etc. and their dependencies). This may lead to some +surprising errors, so keep your eyes open for that. + +API changes +=========== +Here are some of the API changes that have occurred: + +~ cairo_create(void) -> cairo_create(cairo_surface_t *) + + This is the big change that breaks every program. The ability + to re-target a cairo_t was not particularly useful, but it did + introduce a lot of muddy semantic questions. To eliminate + that, cairo_create now requires its target surface to be + passed in at creation time. This isn't too hard to cope with + as the typical first operation after cairo_create was often + cairo_set_target_foo. So the order of those two swap and the + application instead has cairo_foo_surface_create, then + cairo_create. + +~ cairo_current_* -> cairo_get_* + + We had a strange mixture of cairo_get and cairo_current + functions. They've all been standardized on cairo_get, (though + note one is cairo_get_current_point). + +~ CAIRO_OPERATOR_SRC -> CAIRO_OPERATOR_SOURCE +~ CAIRO_OPERATOR_OVER_REVERSE -> CAIRO_OPERATOR_DEST_OVER + + Many of the cairo_operator_t symbolic values were renamed to + reduce the amount of abbreviation. The confusing "OP_REVERSE" + naming was also changed to use "DEST_OP" instead which is + easier to read and has wider acceptance in other + libraries/languages. + +~ cairo_set_pattern -> cairo_set_source +~ cairo_set_rgb_color -> cairo_set_source_rgb + + All of the various functions that changed the source + color/pattern were unified to use cairo_set_source names to + make the relation more clear. + +~ cairo_transform_point -> cairo_user_to_device +~ cairo_transform_distance -> cairo_user_to_device_distance +~ cairo_inverse_transform_point -> cairo_device_to_user +~ cairo_inverse_transform_distance -> cairo_device_to_user_distance + + These names just seemed a lot more clear. + +~ cairo_init_clip -> cairo_reset_clip +~ cairo_concat_matrix -> cairo_transform + + More abbreviation elimination + +~ cairo_current_path -> cairo_copy_path +~ cairo_current_path_flat -> cairo_copy_path_flat + + The former mechanism for examining the current path was a + function that required 3 or 4 callbacks. This was more + complexity than warranted in most situations. The new + cairo_copy_path function copies the current path into an + exposed data structure, and the documentation provides a + convenient idiom for navigating the path data. + +API additions +------------- ++ cairo_paint + + A generalized version of the painting operators cairo_stroke + and cairo_fill. The cairo_paint call applies the source paint + everywhere within the current clip region. Very useful for + clearing a surface to a solid color, or painting an image, + (see cairo_set_source_surface). + ++ cairo_paint_with_alpha + + Like cairo_paint but applying some alpha to the source, + (making the source paint translucent, eg. to blend an image on + top of another). + ++ cairo_mask + + A more generalized version of cairo_paint_with_alpha which + allows a pattern to specify the amount of translucence at each + point rather than using a constant value everywhere. + ++ cairo_mask_surface + + A convenience function on cairo_mask for when the mask pattern + is already contained within a surface. + ++ cairo_surface_set_user_data ++ cairo_surface_get_user_data ++ cairo_font_face_set_user_data ++ cairo_font_face_get_user_data + + Associate arbitrary data with a surface or font face for later + retrieval. Get notified when a surface or font face object is + destroyed. + ++ cairo_surface_finish + + Allows the user to instruct cairo to finish all of its + operations for a given surface. This provides a safe point for + doing things such as flushing and closing files that the + surface may have had open for writing. + ++ cairo_fill_preserve ++ cairo_stroke_preserve ++ cairo_clip_preserve + + One interesting change in cairo is that the path is no longer + part of the graphics state managed by + cairo_save/restore. This allows functions to construct paths + without interfering with the graphics state. But it prevents + the traditional idiom for fill-and-stroke: + + cairo_save; cairo_fill; cairo_restore; cairo_stroke + + Instead we know have alternate versions cairo cairo_fill, + cairo_stroke, and cairo_clip that preserve the current path + rather than consuming it. So the idiom now becomes simply: + + cairo_fill_preserve; cairo_stroke + ++ cairo_surface_write_to_png ++ cairo_surface_write_to_png_stream + + In place of a single PNG backend, now a surface created + through any backend (except PDF currently) can be written out + to a PNG image. + ++ cairo_image_surface_create_from_png ++ cairo_image_surface_create_from_png_stream + + And its just as easy to load a PNG image into a surface as well. + ++ cairo_append_path + + With the new, exposed path data structure, it's now possible + to append bulk path data to the current path, (rather than + issuing a long sequence of cairo_move_to/line_to/curve_to + function calls). + +Xlib and XCB backends +--------------------- + +Any cairo_format_t and Colormap arguments have been dropped from +cairo_xlib_surface_create. There are also two new +cairo_xlib|xcb_surface_create functions: + + cairo_xlib|xcb_surface_create_for_bitmap + (Particular for creating A1 surfaces) + cairo_xlib|xcb_surface_create_with_xrender_format + (For any other surface types, not described by a Visual*) + +All of these surface create functions now accept width and height. In +addition, there are new cairo_xlib|xcb_surface_set_size functions +which must be called each time a window that is underlying a surface +changes size. + +Print backends (PS and PDF) +--------------------------- +The old FILE* based interfaces have been eliminated. In their place we +have two different functions. One accepts a simple const char +*filename. The other is a more general function which accepts a +callback write function and a void* closure. This should allow the +flexibility needed to hook up with various stream object in many +languages. + +In addition, when specifying the surface size during construction, the +units are now device-space units (ie. points) rather than inches. This +provides consistency with all the other surface types and also makes +it much easier to reason about the size of the surface when drawing to +it with the default identity matrix. + +Finally, the DPI parameters, which are only needed to control the +quality of fallbacks, have been made optional. Nothing is required +during surface_create (300 DPI is assumed) and +cairo_ps|pdf_surface_set_dpi can be used to set alternate values if +needed. + +Font system +----------- +Owen very graciously listened to feedback after the big font rework he +had done for 0.4, and came up with way to improve it even more. In 0.4 +there was a cairo_font_t that was always pre-scaled. Now, there is an +unscaled cairo_font_face_t which is easier to construct, (eg. no +scaling matrix required) and work with, (it can be scaled and +transformed after being set on the graphics state). And the font size +manipulation functions are much easier. You can set an explicit size +and read/modify/write the font matrix with: + + cairo_set_font_size + cairo_get_font_matrix + cairo_set_font_matrix + +(Previously you could only multiply in a scale factor or a matrix.) A +pleasant side effect is that we can (and do) now have a default font +size that is reasonable, as opposed to the old default height of one +device-space unit which was useless until scaled. + +Of course, the old pre-scaled font had allowed some performance +benefits when getting many metrics for a font. Those benefits are +still made available through the new cairo_scaled_font_t. And a +cairo_font_face_t can be "promoted" to a cairo_scaled_font_t by +suppling a font_matrix and the desired CTM. + +Quartz backend +-------------- +Tim Rowley put in the work to bring the Quartz backend back after it +had been disabled in the 0.4.0 snapshot. He was not able to bring back +the function that allows one to create a cairo_font_t from an ATSUI +style: + + cairo_font_t * + cairo_atsui_font_create (ATSUStyle style); + +because he didn't have a test case for it. If you care about this +function, please provide a fairly minimal test and we'll try to bring +it back in an upcoming snapshot. + +Snapshot 0.4.0 (2005-03-08 Carl Worth ) +========================================================== +New documentation +----------------- +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: + + https://cairographics.org/manual/ + +New backend: win32 +------------------ +This is the first snapshot to include a functional win32 backend, +(thanks to Owen Taylor). The interface is as follows: + + #include + + void + cairo_set_target_win32 (cairo_t *cr, + HDC hdc); + + cairo_surface_t * + cairo_win32_surface_create (HDC hdc); + + cairo_font_t * + cairo_win32_font_create_for_logfontw (LOGFONTW *logfont, + cairo_matrix_t *scale); + + cairo_status_t + cairo_win32_font_select_font (cairo_font_t *font, + HDC hdc); + + void + cairo_win32_font_done_font (cairo_font_t *font); + + double + cairo_win32_font_get_scale_factor (cairo_font_t *font); + +And see also the documentation at: + +https://cairographics.org/manual/cairo-Microsoft-Windows-Backend.html + +Disabled backend: quartz +------------------------ +Unfortunately, the quartz backend code is currently out of date with +respect to some recent backend interface changes. So, the quartz +backend is disabled in this snapshot. + +If the quartz backend is brought up-to-date before the next snapshot, +we would be glad to make a 0.4.1 snapshot that re-enables it, (we do +not expect many more big backend interface changes). + +API Changes +----------- +The font system has been revamped, (as Owen Taylor's work with +integrating pango and cairo gave us the first serious usage of the +non-toy font API). + +One fundamental, user-visible change is that the cairo_font_t object +now represents a font that is scaled to a particular device +resolution. Further changes are described below. + + cairo.h + ------- + Removed cairo_font_set_transform and cairo_font_current_transform. + + Added cairo_font_extents and cairo_font_glyph_extents. See + documentation for details: + + 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: + + https://cairographics.org/manual/cairo-FreeType-Fonts.html + +Performance +----------- +Make the fast-path clipping (pixel-aligned rectangles) faster. + +Add optimization for applying a constant alpha to a pattern. + +Optimize gradients that are horizontal or vertical in device space. + +Xlib: When RENDER is not available, use image surfaces for +intermediate surfaces rather than xlib surfaces. + +Backend-specific changes +------------------------ + Glitz + ----- + Major update to glitz backend. The output quality should now be just + as good as the image and xlib backends. + + Track changes to glitz 0.4.0. + + PDF + --- + Various improvements to produce more conformant output. + +Internals +--------- +David Reveman contributed a large re-work of the cairo_pattern_t +implementation, providing cleaner code and more optimization +opportunities. + + Backend interface changes + ------------------------- + Rework backend interface to accept patterns, not surfaces for source + and mask. + + Remove set_matrix, set_filter, and set_repeat functions. + + More sophisticated backend interface for image fallbacks, + ({acquire,release}_{source,dest}_image() and clone_similar). + +Bug fixes +--------- +Only install header files for backends that have been compiled. + +Fixed some rounding errors leading to incorrectly placed glyphs. + +Many other minor fixes. + +Snapshot 0.3.0 (2005-01-21 Carl Worth ) +========================================================== +Major API changes +----------------- +1) The public header files will no longer be directly installed into + the system include directory. They will now be installed in a + subdirectory named "cairo", (eg. in /usr/include/cairo rather than + in /usr/include). + + As always, the easiest way for applications to discover the + location of the header file is to let pkg-config generate the + necessary -I CFLAGS and -L/-l LDFLAGS. For example: + + 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. + +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-quartz.h + cairo-xcb.h + cairo-xlib.h + + Applications will need to be modified to explicitly include the new + header files where appropriate. + +3) There are two new graphics backends in this snapshot, a PDF + backend, and a Quartz backend. There is also one new font backend, + ATSUI. + +PDF backend +----------- +Kristian Høgsberg has contributed a new backend to allow cairo-based +applications to generate PDF output. The interface for creating a PDF +surface is similar to that of the PS backend, as can be seen in +cairo-pdf.h: + + void + cairo_set_target_pdf (cairo_t *cr, + FILE *file, + double width_inches, + double height_inches, + double x_pixels_per_inch, + double y_pixels_per_inch); + + cairo_surface_t * + cairo_pdf_surface_create (FILE *file, + double width_inches, + double height_inches, + double x_pixels_per_inch, + double y_pixels_per_inch); + +Once a PDF surface has been created, applications can draw to it as +any other cairo surface. + +This code is still a bit rough around the edges, and does not yet +support clipping, surface patterns, or transparent gradients. Text +only works with TrueType fonts at this point and only black text is +supported. Also, the size of the generated PDF files is currently +quite big. + +Kristian is still actively developing this backend, so watch this +space for future progress. + +Quartz backend +-------------- +Calum Robinson has contributed a new backend to allow cairo +applications to target native Mac OS X windows through the Quartz +API. Geoff Norton integrated this backend into the current +configure-based build system, while Calum also provided Xcode build +support in the separate "macosx" module available in CVS. + +The new interface, available in cairo-quartz.h, is as follows: + + void + cairo_set_target_quartz_context (cairo_t *cr, + CGContextRef context, + int width, + int height); + + cairo_surface_t * + cairo_quartz_surface_create (CGContextRef context, + int width, + int height); + +There is an example program available in CVS in cairo-demo/quartz. It +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: + + https://cairographics.org/~cworth/images/fdclock-quartz.png + +ATSUI font backend +------------------ +This new font backend complements the Quartz backend by allowing +applications to use native font selection on Mac OS X. The interface +is a single new function: + + cairo_font_t * + cairo_atsui_font_create (ATSUStyle style); + +Minor API changes +----------------- +Prototype for non-existent function "cairo_ft_font_destroy" removed. + +Now depends on libpixman 0.1.2 or newer, (0.1.3 is being released +concurrently and has some useful performance improvements). + +Default paint color is now opaque black, (was opaque white). Default +background color is transparent (as before). + +Renamed "struct cairo" to "struct _cairo" to free up the word "cairo" +from the C++ identifier name space. + +Functions returning multiple return values through provided pointers, +(cairo_matrix_get_affine, cairo_current_point, and +cairo_current_color_rgb), will now accept NULL for values the user +wants to ignore. + +CAIRO_HAS_FREETYPE_FONT has now been renamed to CAIRO_HAS_FT_FONT. + +Performance improvements +------------------------ +Alexander Larsson provided some fantastic performance improvements +yielding a 10000% performance improvement in his application, (when +also including his performance work in libpixman-0.1.3). These include + + * Fixed handling of cache misses. + + * Creating intermediate clip surfaces at the minimal size required. + + * Eliminating roundtrips when creating intermediate Xlib surfaces. + +Implementation +-------------- +Major re-work of font metrics system by Keith Packard. Font metrics +should now be much more reliable. + +Glitz backend +------------- +Updated for glitz-0.3.0. +Bug fixes in reference counting. + +Test suite +---------- +New tests for cache crashing, rotating text, improper filling of +complex polygons, and leaky rasterization. + +Bug fixes +--------- +Fixed assertion failure when selecting the same font multiple times in +sequence. + +Fixed reference counting so cache_destroy functions work. + +Remove unintended copyright statement from files generated with +PostScript backend. + +Fixed to eliminate new warnings from gcc 3.4 and gcc 4. + +Snapshot 0.2.0 (2004-10-27 Carl Worth ) +=========================================================== +New license: LGPL/MPL +--------------------- +The most significant news with this release is that the license of +cairo has changed. It is now dual-licensed under the LGPL and the +MPL. For details see the COPYING file as well as COPYING-LGPL-2.1 and +COPYING-MPL-1.1. + +I express my thanks to everyone involved in the license change process +for their patience and support! + +New font and glyph internals +---------------------------- +Graydon Hoare has put a tremendous amount of work into new internals +for handling fonts and glyphs, including caches where appropriate. +This work has no impact on the user-level API, but should result in +great performance improvements for applications using text. + +New test suite +-------------- +This snapshot of cairo includes a (small) test suite in +cairo/test. The tests can be run with "make check". The test suite was +designed to make it very easy to add new tests, and we hope to see +many contributions here. As you find bugs, please try adding a minimal +test case to the suite, and submit it with the bug report to the +cairo@cairographics.org mailing list. This will make it much easier +for us to track progress in fixing bugs. + +New name for glitz backend +-------------------------- +The gl backend has now been renamed to the glitz backend. This means +that the following names have changed: + + CAIRO_HAS_GL_SURFACE -> CAIRO_HAS_GLITZ_SURFACE + cairo_set_target_gl -> cairo_set_target_glitz + cairo_gl_surface_create -> cairo_glitz_surface_create + +This change obviously breaks backwards compatibility for applications +using the old gl backend. + +Up-to-date with latest glitz snapshots +-------------------------------------- +This snapshot of cairo is now up to date with the latest glitz +snapshot, (currently 0.2.3). We know that the latest cairo and glitz +snapshots have been incompatible for a very long time. We've finally +fixed that now and we're determined to not let that happen again. + +Revert some tessellation regression bugs +---------------------------------------- +People that have been seeing some tessellation bugs, (eg. leaked +fills), in the CVS version of cairo may have better luck with this +release. A change since the last snapshot was identified to trigger +some of these bugs and was reverted before making the snapshot. The +behavior should be the same as the previous (0.1.23) snapshot. + +Miscellaneous changes +--------------------- +Changed CAIRO_FILTER_DEFAULT to CAIRO_FILTER_BEST to make gradients +easier. + +Track XCB API change regarding iterators. + +Various bug fixes +----------------- +Fix calculation of required number of vertices for pen. + +Fix to avoid zero-dimensioned pixmaps. + +Fix broken sort of pen vertices. + +Fix bug when cairo_show_text called with a NULL string. + +Fix clipping bugs. + +Fix bug in computing image length with XCB. + +Fix infinite loop bug in cairo_arc. + +Fix memory management interactions with libpixman. + +Snapshot 0.1.23 (2004-05-11 Carl Worth ) +======================================================== +Fixes for gcc 3.4 +----------------- +Fix prototype mismatches so that cairo can be built by gcc 3.4. + +Updates to track glitz +---------------------- +Various fixes to support the latest glitz snapshot (0.1.2). + +Gradient updates +---------------- +Radial gradients now support both inner and outer circles. +Transformed linear gradients are now properly handled. +Fixes for extend type reflect. + +Glitz updates +------------- +Converted shading routines to use fixed point values and introduced a +shading operator structure for more efficient shading calculations. +Support compositing with mask surface when mask is solid or +multi-texturing is available. + +PNG backend cleanups +-------------------- +Fix output to properly compensate for pre-multiplied alpha format in cairo. +Add support for A8 and A1 image formats. + +Bug fixes +--------- +Avoid crash or infinite loop on null strings and degeneratively short +splines. + +New? bugs in cairo_clip +----------------------- +There are some fairly serious bugs in cairo_clip. It is sometimes +causing an incorrect result. And even when it does work, it is +sometimes so slow as to be unusable. Some of these bugs may not be +new, (indeed cairo_clip has only ever had a braindead-slow +implementation), but I think they're worth mentioning here. + +Snapshot 0.1.22 (2004-04-16 Carl Worth ) +======================================================== +Cairo was updated to track the changes in libpixman, and now depends +on libpixman version 0.1.1. + +Snapshot 0.1.21 (2004-04-09 David Reveman ) +============================================================= +New OpenGL backend +------------------ +The OpenGL backend provides hardware accelerated output for +X11 and OS X. The significant new functions are: + + cairo_set_target_gl + cairo_gl_surface_create + +Automatic detection of available backends +----------------------------------------- +The configure script now automatically detect what backends are +available, (use ./configure --disable-`backend' to prevent +compilation of specific backends). + +Snapshot 0.1.20 (2004-04-06 Carl Worth ) +======================================================== +New pattern API +--------------- +David Reveman has contributed a new pattern API which enable linear +and radial gradient patterns in addition to the original surface-based +patterns. The significant new top-level functions are: + + cairo_pattern_create_linear + cairo_pattern_create_radial + cairo_pattern_create_for_surface + cairo_pattern_add_color_stop + cairo_set_pattern + +Any code using the old cairo_set_pattern, (which accepted a +cairo_surface_t rather than a cairo_pattern_t), will need to be +updated. + +Update to XCB backend +--------------------- +The XCB backend is now enabled by default, (use ./configure +--disable-xcb to turn it off). + +Faster clipping +--------------- +Graydon Hoare has added optimizations that make cairo_clip much faster +when the path is a pixel-aligned, rectangular region. + +Bug fixes. + +Snapshot 0.1.19 (2004-02-24 Carl Worth ) +======================================================== +New PNG backend +--------------- +Olivier Andrieu contributed a new PNG backend. It builds on the +existing image backend to make it easy to render "directly" to a +.png file. The user never needs to deal with the actual image +buffer. The significant new functions are: + + cairo_set_target_png + cairo_png_surface_create + +The PNG backend is not enabled by default so that by default there is +not a new dependency on libpng. Use ./configure --enable-png to enable +this backend. + +Snapshot 0.1.18 (2004-02-17 Carl Worth ) +======================================================== +Path query functionality +------------------------ +It's now possible to query the current path. The two new functions +are: + + cairo_current_path + cairo_current_path_flat + +Each function accepts a number of callback functions that will be +called for each element in the path (move_to, line_to, curve_to, +close_path). The cairo_current_path_flat function does not accept a +curve_to callback. Instead, all curved portions of the path will be +converted to line segments, (within the current tolerance value). This +can be handy for doing things like text-on-path without having to +manually interpolate Bézier splines. + +New XCB backend +--------------- +Jamey Sharp has contributed a second X backend that uses the new, lean +XCB library rather than Xlib. It cannot currently be compiled at the +same time as the Xlib backend. See ./configure --enable-xcb. + +Build fixes for cygwin. + +Bug fixes. + +Snapshot 0.1.17 (2003-12-16 Carl Worth ) +======================================================== + +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 + +The text/glyph_extents functions can be used to determine the bounding +box (and advance) for text as if drawn by show_text/glyphs. + +The text/glyph_path objects functions place text shapes on the current +path, where they can be subsequently manipulated. For example, +following these functions with cairo_stroke allows outline text to be +drawn. Calling cairo_clip allows clipping to a text-shaped region. + +Combined dependencies +--------------------- +The cairo core now depends only on the libpixman library. This single +library replaces the three previous libraries libic, libpixregion, and +slim. Thanks to Dave Beckett for all of +the heavy lifting with this renaming effort. + +Conditional compilation of backends +----------------------------------- +Cairo now allows optional backends to be disabled at compile time. The +following options may now be passed to the configure script: + + --disable-xlib + --disable-ps + +Note that the first option is a change from the old --without-x option +which will no longer have any effect. + +OS X supported - several byte-order issues resolved +--------------------------------------------------- +Cairo has now been successfully compiled under OS X. Testing revealed +that there were some byte-order problems in the PostScript backend and +the PNG generation in the demos. These have now been resolved. + +2003-10 +======= +Graydon Hoare implemented the first real text +support using Freetype/fontconfig, (previous versions of cairo used +Xft and could only draw text when using an X backend). + +2003-09 +======= +Graydon Hoare added the first real support for +running cairo with a non-render-aware X server. + +Jamey Sharp virtualized the backend font and +surface interfaces in September, 2003. + +2003-06 +======= +Xr is renamed cairo to avoid confusion since it no longer had a strict +dependence on X. + +2003-05 +======= +A new image surface backend is added to Xr. Keith Packard + wrote the image compositing code in libic that is +used for the image_surface backend. This code was originally written +as the software fallback for the render extension within the X +server. + +2002-06 +======= +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 distro's URL lcd bool tarball diff --git a/gfx/cairo/cairo/README b/gfx/cairo/cairo/README new file mode 100644 index 0000000000..0bcf140121 --- /dev/null +++ b/gfx/cairo/cairo/README @@ -0,0 +1,198 @@ +Cairo - Multi-platform 2D graphics library +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 (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 +(for example, through the X Render Extension). + +The cairo API provides operations similar to the drawing operators of +PostScript and PDF. Operations in cairo include stroking and filling +cubic Bézier splines, transforming and compositing translucent images, +and antialiased text rendering. All drawing operations can be +transformed by any affine transformation (scale, rotation, shear, +etc.). + +Cairo has been designed to let you draw anything you want in a modern +2D graphical user interface. At the same time, the cairo API has been +designed to be as fun and easy to learn as possible. If you're not +having fun while programming with cairo, then we have failed +somewhere---let us know and we'll try to fix it next time around. + +Cairo is free software and 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. + +Where to get more information about cairo +========================================= +The primary source of information about cairo is: + + https://cairographics.org/ + +The latest versions of cairo can always be found at: + + https://cairographics.org/download + +Documentation on using cairo and frequently-asked questions: + + https://cairographics.org/documentation + https://cairographics.org/FAQ + +Mailing lists for contacting cairo users and developers: + + https://cairographics.org/lists + +Roadmap and unscheduled things to do, (please feel free to help out): + + https://cairographics.org/roadmap + https://cairographics.org/todo + +Dependencies +============ +The set of libraries needed to compile cairo depends on which backends +are enabled when cairo is configured. So look at the list below to +determine which dependencies are needed for the backends of interest. + +For the surface backends, we have both "supported" and "experimental" +backends. Further, the supported backends can be divided into the +"standard" backends which can be easily built on any platform, and 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 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 build-dep cairo + + Fedora (and similar): + + yum install libpng-devel zlib-devel libXrender-devel fontconfig-devel + +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.30.0 https://cairographics.org/releases + + png support (can be left out if desired, but many + ----------- applications expect it to be present) + libpng http://www.libpng.org/pub/png/libpng.html + + pdf backend + ----------- + zlib http://www.gzip.org/zlib + + postscript backend + ------------------ + zlib http://www.gzip.org/zlib + + svg backend + ----------- + [none] + +Supported, "platform" surface backends +----------------------------------- + xlib backend + ------------ + X11 https://freedesktop.org/Software/xlibs + + xlib-xrender backend + -------------------- + Xrender >= 0.6 https://freedesktop.org/Software/xlibs + + quartz backend + -------------- + 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 + --------------------- + freetype >= 2.1.9 http://freetype.org + fontconfig http://fontconfig.org + + quartz-font backend + ------------------- + MacOS X >= 10.4 with Xcode >= 2.4 + + win32 font backend + ------------------ + Microsoft Windows 2000 or newer[*]. + + [*] The Win32 backend should work on Windows 2000 and newer + (excluding Windows Me.) Most testing has been done on + Windows XP. While some portions of the code have been + adapted to work on older versions of Windows, considerable + work still needs to be done to get cairo running in those + environments. + + Cairo can be compiled on Windows with either the gcc + toolchain (see http://www.mingw.org) or with Microsoft + Visual C++. If the gcc toolchain is used, the standard + build instructions using configure apply, (see INSTALL). + If Visual C++ is desired, GNU make is required and + Makefile.win32 can be used via 'make -f Makefile.win32'. + The compiler, include paths, and library paths must be set + up correctly in the environment. + + MSVC versions earlier than 7.1 are known to miscompile + parts of cairo and pixman, and so should be avoided. MSVC + 7.1 or later, including the free Microsoft Visual Studio + Express editions, produce correct code. + +Experimental surface backends +----------------------------- + beos backend + ------------ + No dependencies in itself other than an installed BeOS system, but cairo + requires a font backend. See the freetype dependency list. + + os2 backend + ----------- + Cairo should run on any recent version of OS/2 or eComStation, but it + requires a font backend. See the freetype dependency list. Ready to use + 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 +Keith Packard . Many thanks are due to Lyle Ramshaw +without whose patient help our ignorance would be much more apparent. + +Since the original development, many more people have contributed to +cairo. See the AUTHORS files for as complete a list as we've been able +to compile so far. diff --git a/gfx/cairo/cairo/README.win32 b/gfx/cairo/cairo/README.win32 new file mode 100644 index 0000000000..ff962b72a6 --- /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/moz.yaml b/gfx/cairo/cairo/moz.yaml new file mode 100644 index 0000000000..693000cdcd --- /dev/null +++ b/gfx/cairo/cairo/moz.yaml @@ -0,0 +1,16 @@ +schema: 1 + +bugzilla: + product: "Core" + component: "Audio/Video: Playback" + +origin: + name: "cairo" + description: "2D graphics library" + + url: "https://www.cairographics.org/" + license: "MPL-1.1" + license-file: "COPYING-MPL-1.1" + + # git://anongit.freedesktop.org/git/cairo + release: "12d521df8acc483b2daa844d4f05dc2fe2765ba6 (2010-01-21)" diff --git a/gfx/cairo/cairo/src/Makefile.am.analysis b/gfx/cairo/cairo/src/Makefile.am.analysis new file mode 100644 index 0000000000..fab4cf7a51 --- /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 0000000000..47c95db1f9 --- /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 0000000000..23ba1861d4 --- /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 0000000000..ff28491201 --- /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 0000000000..0a1ce35274 --- /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 0000000000..9afefe2b79 --- /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 0000000000..377d6dd12a --- /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 0000000000..03e4455ea5 --- /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 new file mode 100644 index 0000000000..1e054c209a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h @@ -0,0 +1,74 @@ +/* + * Copyright © 2005 Keith Packard + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Keith Packard + */ + +#ifndef CAIRO_ANALYSIS_SURFACE_H +#define CAIRO_ANALYSIS_SURFACE_H + +#include "cairoint.h" + +cairo_private cairo_surface_t * +_cairo_analysis_surface_create (cairo_surface_t *target); + +cairo_private void +_cairo_analysis_surface_set_ctm (cairo_surface_t *surface, + const cairo_matrix_t *ctm); + +cairo_private void +_cairo_analysis_surface_get_ctm (cairo_surface_t *surface, + cairo_matrix_t *ctm); + +cairo_private cairo_region_t * +_cairo_analysis_surface_get_supported (cairo_surface_t *surface); + +cairo_private cairo_region_t * +_cairo_analysis_surface_get_unsupported (cairo_surface_t *surface); + +cairo_private cairo_bool_t +_cairo_analysis_surface_has_supported (cairo_surface_t *surface); + +cairo_private cairo_bool_t +_cairo_analysis_surface_has_unsupported (cairo_surface_t *surface); + +cairo_private void +_cairo_analysis_surface_get_bounding_box (cairo_surface_t *surface, + cairo_box_t *bbox); + +cairo_private cairo_int_status_t +_cairo_analysis_surface_merge_status (cairo_int_status_t status_a, + cairo_int_status_t status_b); + +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 new file mode 100644 index 0000000000..a118e338c4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-analysis-surface.c @@ -0,0 +1,1034 @@ +/* + * Copyright © 2006 Keith Packard + * Copyright © 2007 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 Keith Packard + * + * Contributor(s): + * Keith Packard + * Adrian Johnson + */ + +#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-inline.h" +#include "cairo-surface-snapshot-inline.h" +#include "cairo-surface-subsurface-inline.h" +#include "cairo-region-private.h" + +typedef struct { + cairo_surface_t base; + + cairo_surface_t *target; + + cairo_bool_t first_op; + cairo_bool_t has_supported; + cairo_bool_t has_unsupported; + + cairo_region_t supported_region; + cairo_region_t fallback_region; + cairo_box_t page_bbox; + + cairo_bool_t has_ctm; + cairo_matrix_t ctm; + +} cairo_analysis_surface_t; + +cairo_int_status_t +_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_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 || + status_b == CAIRO_INT_STATUS_UNSUPPORTED) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (status_a == CAIRO_INT_STATUS_IMAGE_FALLBACK || + status_b == CAIRO_INT_STATUS_IMAGE_FALLBACK) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + + if (status_a == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN || + status_b == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + + if (status_a == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || + status_b == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + + /* at this point we have checked all the valid internal codes, so... */ + 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 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) +{ + 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, target->content, target->is_vector); + + proxy->target = target; + _cairo_surface_attach_snapshot (source, &proxy->base, NULL); + + return &proxy->base; +} + +static void +detach_proxy (cairo_surface_t *proxy) +{ + cairo_surface_finish (proxy); + cairo_surface_destroy (proxy); +} + +static cairo_int_status_t +_add_operation (cairo_analysis_surface_t *surface, + cairo_rectangle_int_t *rect, + cairo_int_status_t backend_status) +{ + cairo_int_status_t status; + cairo_box_t bbox; + + if (rect->width == 0 || rect->height == 0) { + /* 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_INT_STATUS_SUCCESS || + backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || + backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO) + { + return CAIRO_INT_STATUS_SUCCESS; + } + else + { + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + } + } + + _cairo_box_from_rectangle (&bbox, rect); + + if (surface->has_ctm) { + int tx, ty; + + 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); + + if (bbox.p1.x == bbox.p2.x || bbox.p1.y == bbox.p2.y) { + /* 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_INT_STATUS_SUCCESS || + backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY || + backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO) + { + return CAIRO_INT_STATUS_SUCCESS; + } + else + { + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + } + } + + _cairo_box_round_to_rectangle (&bbox, rect); + } + } + + if (surface->first_op) { + surface->first_op = FALSE; + surface->page_bbox = bbox; + } 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 + * the fallback image will be painted on top. + */ + if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + + if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) { + /* A status of CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY indicates + * that the backend only supports this operation if the + * transparency removed. If the extents of this operation does + * not intersect any other native operation, the operation is + * natively supported and the backend will blend the + * transparency into the white background. + */ + if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT) + backend_status = CAIRO_INT_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. + */ + surface->has_supported = TRUE; + return cairo_region_union_rectangle (&surface->supported_region, rect); + } + + /* Add the operation to the unsupported region. This region will + * be painted as an image after all native operations have been + * emitted. + */ + surface->has_unsupported = TRUE; + status = cairo_region_union_rectangle (&surface->fallback_region, rect); + + /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate + * unsupported operations to the recording surface as using + * CAIRO_INT_STATUS_UNSUPPORTED would cause cairo-surface to + * invoke the cairo-surface-fallback path then return + * 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) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + _cairo_region_fini (&surface->supported_region); + _cairo_region_fini (&surface->fallback_region); + + cairo_surface_destroy (surface->target); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_analysis_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_analysis_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, rectangle); +} + +static void +_rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip) +{ + if (clip != NULL) + _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, + const cairo_clip_t *clip, + cairo_rectangle_int_t *extents) +{ + cairo_bool_t is_empty; + + is_empty = _cairo_surface_get_extents (&surface->base, extents); + + if (_cairo_operator_bounded_by_source (op)) { + cairo_rectangle_int_t source_extents; + + _cairo_pattern_get_extents (source, &source_extents, surface->target->is_vector); + _cairo_rectangle_intersect (extents, &source_extents); + } + + _rectangle_intersect_clip (extents, clip); +} + +static cairo_int_status_t +_cairo_analysis_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t backend_status; + cairo_rectangle_int_t extents; + + if (surface->target->backend->paint == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->paint (surface->target, + op, source, clip); + 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_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); +} + +static cairo_int_status_t +_cairo_analysis_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_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t backend_status; + cairo_rectangle_int_t extents; + + if (surface->target->backend->mask == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->mask (surface->target, + op, source, mask, clip); + 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) { + 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, &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_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, &rec_extents); + if (_cairo_int_status_is_error (backend_mask_status)) + return backend_mask_status; + + _cairo_rectangle_intersect (&extents, &rec_extents); + } + } + + backend_status = + _cairo_analysis_surface_merge_status (backend_source_status, + backend_mask_status); + } + + if (_cairo_operator_bounded_by_mask (op)) { + cairo_rectangle_int_t 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, + 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_int_status_t backend_status; + cairo_rectangle_int_t extents; + + if (surface->target->backend->stroke == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->stroke (surface->target, op, + source, path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + 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_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; + + status = _cairo_path_fixed_stroke_extents (path, style, + ctm, ctm_inverse, + tolerance, + &mask_extents); + if (unlikely (status)) + return status; + + _cairo_rectangle_intersect (&extents, &mask_extents); + } + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_int_status_t +_cairo_analysis_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_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t backend_status; + cairo_rectangle_int_t extents; + + if (surface->target->backend->fill == NULL) { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + backend_status = + surface->target->backend->fill (surface->target, op, + source, path, fill_rule, + tolerance, antialias, + clip); + 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_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_path_fixed_fill_extents (path, fill_rule, tolerance, + &mask_extents); + + _cairo_rectangle_intersect (&extents, &mask_extents); + } + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_int_status_t +_cairo_analysis_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_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t status, backend_status; + cairo_rectangle_int_t extents, glyph_extents; + + /* Adapted from _cairo_surface_show_glyphs */ + if (surface->target->backend->show_glyphs != NULL) { + backend_status = + surface->target->backend->show_glyphs (surface->target, op, + source, + glyphs, num_glyphs, + scaled_font, + clip); + if (_cairo_int_status_is_error (backend_status)) + return backend_status; + } + else if (surface->target->backend->show_text_glyphs != NULL) + { + backend_status = + surface->target->backend->show_text_glyphs (surface->target, op, + source, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + scaled_font, + clip); + if (_cairo_int_status_is_error (backend_status)) + return backend_status; + } + else + { + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + } + + _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, + glyphs, + num_glyphs, + &glyph_extents, + NULL); + if (unlikely (status)) + return status; + + _cairo_rectangle_intersect (&extents, &glyph_extents); + } + + return _add_operation (surface, &extents, backend_status); +} + +static cairo_bool_t +_cairo_analysis_surface_has_show_text_glyphs (void *abstract_surface) +{ + cairo_analysis_surface_t *surface = abstract_surface; + + return cairo_surface_has_show_text_glyphs (surface->target); +} + +static cairo_int_status_t +_cairo_analysis_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_analysis_surface_t *surface = abstract_surface; + cairo_int_status_t status, backend_status; + cairo_rectangle_int_t extents, glyph_extents; + + /* Adapted from _cairo_surface_show_glyphs */ + backend_status = CAIRO_INT_STATUS_UNSUPPORTED; + if (surface->target->backend->show_text_glyphs != NULL) { + backend_status = + surface->target->backend->show_text_glyphs (surface->target, op, + source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); + if (_cairo_int_status_is_error (backend_status)) + return backend_status; + } + if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED && + surface->target->backend->show_glyphs != NULL) + { + backend_status = + surface->target->backend->show_glyphs (surface->target, op, + source, + glyphs, num_glyphs, + scaled_font, + clip); + 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_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, + glyphs, + num_glyphs, + &glyph_extents, + NULL); + if (unlikely (status)) + return status; + + _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) +{ + 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); + 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, + + _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, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_analysis_surface_get_extents, + NULL, /* get_font_options */ + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _cairo_analysis_surface_paint, + _cairo_analysis_surface_mask, + _cairo_analysis_surface_stroke, + _cairo_analysis_surface_fill, + NULL, /* fill_stroke */ + _cairo_analysis_surface_show_glyphs, + _cairo_analysis_surface_has_show_text_glyphs, + _cairo_analysis_surface_show_text_glyphs, + NULL, /* get_supported_mime_types */ + _cairo_analysis_surface_tag +}; + +cairo_surface_t * +_cairo_analysis_surface_create (cairo_surface_t *target) +{ + cairo_analysis_surface_t *surface; + cairo_status_t status; + + status = target->status; + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface = _cairo_malloc (sizeof (cairo_analysis_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + /* I believe the content type here is truly arbitrary. I'm quite + * sure nothing will ever use this value. */ + _cairo_surface_init (&surface->base, + &cairo_analysis_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA, + target->is_vector); + + cairo_matrix_init_identity (&surface->ctm); + surface->has_ctm = FALSE; + + surface->target = cairo_surface_reference (target); + surface->first_op = TRUE; + surface->has_supported = FALSE; + surface->has_unsupported = FALSE; + + _cairo_region_init (&surface->supported_region); + _cairo_region_init (&surface->fallback_region); + + surface->page_bbox.p1.x = 0; + surface->page_bbox.p1.y = 0; + surface->page_bbox.p2.x = 0; + surface->page_bbox.p2.y = 0; + + return &surface->base; +} + +void +_cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface, + const cairo_matrix_t *ctm) +{ + cairo_analysis_surface_t *surface; + + if (abstract_surface->status) + return; + + surface = (cairo_analysis_surface_t *) abstract_surface; + + surface->ctm = *ctm; + surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm); +} + +void +_cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface, + cairo_matrix_t *ctm) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + *ctm = surface->ctm; +} + + +cairo_region_t * +_cairo_analysis_surface_get_supported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return &surface->supported_region; +} + +cairo_region_t * +_cairo_analysis_surface_get_unsupported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return &surface->fallback_region; +} + +cairo_bool_t +_cairo_analysis_surface_has_supported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return surface->has_supported; +} + +cairo_bool_t +_cairo_analysis_surface_has_unsupported (cairo_surface_t *abstract_surface) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + return surface->has_unsupported; +} + +void +_cairo_analysis_surface_get_bounding_box (cairo_surface_t *abstract_surface, + cairo_box_t *bbox) +{ + cairo_analysis_surface_t *surface = (cairo_analysis_surface_t *) abstract_surface; + + *bbox = surface->page_bbox; +} + +/* null surface type: a surface that does nothing (has no side effects, yay!) */ + +static cairo_int_status_t +_paint_return_success (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + return CAIRO_INT_STATUS_SUCCESS; +} + +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; +} + +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; +} + +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; +} + +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, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image*/ + + NULL, /* source */ + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + NULL, /* get_extents */ + NULL, /* get_font_options */ + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _paint_return_success, /* paint */ + _mask_return_success, /* mask */ + _stroke_return_success, /* stroke */ + _fill_return_success, /* fill */ + NULL, /* fill_stroke */ + _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_surface_t *surface; + + surface = _cairo_malloc (sizeof (cairo_surface_t)); + if (unlikely (surface == NULL)) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (surface, + &cairo_null_surface_backend, + NULL, /* device */ + 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 new file mode 100644 index 0000000000..a22c01ac90 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-arc-private.h @@ -0,0 +1,61 @@ +/* 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_ARC_PRIVATE_H +#define CAIRO_ARC_PRIVATE_H + +#include "cairoint.h" + +CAIRO_BEGIN_DECLS + +cairo_private void +_cairo_arc_path (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2); + +cairo_private void +_cairo_arc_path_negative (cairo_t *cr, + double xc, + double yc, + double radius, + 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 new file mode 100644 index 0000000000..390397bae1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-arc.c @@ -0,0 +1,312 @@ +/* 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 + */ + +#include "cairoint.h" + +#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 + + A simpler error function to work with is: + + e = x**2 + y**2 - 1 + + From "Good approximation of circles by curvature-continuous Bezier + curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric + Design 8 (1990) 22-41, we learn: + + abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) + + and + abs (error) =~ 1/2 * e + + Of course, this error value applies only for the particular spline + approximation that is used in _cairo_gstate_arc_segment. +*/ +static double +_arc_error_normalized (double angle) +{ + return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2); +} + +static double +_arc_max_angle_for_tolerance_normalized (double tolerance) +{ + double angle, error; + int i; + + /* Use table lookup to reduce search time in most cases. */ + struct { + double angle; + double error; + } table[] = { + { M_PI / 1.0, 0.0185185185185185036127 }, + { M_PI / 2.0, 0.000272567143730179811158 }, + { M_PI / 3.0, 2.38647043651461047433e-05 }, + { M_PI / 4.0, 4.2455377443222443279e-06 }, + { M_PI / 5.0, 1.11281001494389081528e-06 }, + { M_PI / 6.0, 3.72662000942734705475e-07 }, + { M_PI / 7.0, 1.47783685574284411325e-07 }, + { M_PI / 8.0, 6.63240432022601149057e-08 }, + { M_PI / 9.0, 3.2715520137536980553e-08 }, + { M_PI / 10.0, 1.73863223499021216974e-08 }, + { M_PI / 11.0, 9.81410988043554039085e-09 }, + }; + int table_size = ARRAY_LENGTH (table); + + for (i = 0; i < table_size; i++) + if (table[i].error < tolerance) + return table[i].angle; + + ++i; + do { + angle = M_PI / i++; + error = _arc_error_normalized (angle); + } while (error > tolerance); + + return angle; +} + +static int +_arc_segments_needed (double angle, + double radius, + cairo_matrix_t *ctm, + double tolerance) +{ + double major_axis, max_angle; + + /* the error is amplified by at most the length of the + * major axis of the circle; see cairo-pen.c for a more detailed analysis + * of this. */ + major_axis = _cairo_matrix_transformed_circle_major_axis (ctm, radius); + max_angle = _arc_max_angle_for_tolerance_normalized (tolerance / major_axis); + + return ceil (fabs (angle) / max_angle); +} + +/* We want to draw a single spline approximating a circular arc radius + R from angle A to angle B. Since we want a symmetric spline that + matches the endpoints of the arc in position and slope, we know + that the spline control points must be: + + (R * cos(A), R * sin(A)) + (R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) + (R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) + (R * cos(B), R * sin(B)) + + for some value of h. + + "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 * 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 + error expression is quite simple, (see the comment for + _arc_error_normalized). +*/ +static void +_cairo_arc_segment (cairo_t *cr, + double xc, + double yc, + double radius, + double angle_A, + double angle_B) +{ + double r_sin_A, r_cos_A; + double r_sin_B, r_cos_B; + double h; + + r_sin_A = radius * sin (angle_A); + r_cos_A = radius * cos (angle_A); + r_sin_B = radius * sin (angle_B); + r_cos_B = radius * cos (angle_B); + + h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); + + cairo_curve_to (cr, + xc + r_cos_A - h * r_sin_A, + yc + r_sin_A + h * r_cos_A, + xc + r_cos_B + h * r_sin_B, + yc + r_sin_B - h * r_cos_B, + xc + r_cos_B, + yc + r_sin_B); +} + +static void +_cairo_arc_in_direction (cairo_t *cr, + double xc, + double yc, + double radius, + double angle_min, + double angle_max, + cairo_direction_t dir) +{ + if (cairo_status (cr)) + return; + + 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) { + double angle_mid = angle_min + (angle_max - angle_min) / 2.0; + if (dir == CAIRO_DIRECTION_FORWARD) { + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_min, angle_mid, + dir); + + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_mid, angle_max, + dir); + } else { + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_mid, angle_max, + dir); + + _cairo_arc_in_direction (cr, xc, yc, radius, + angle_min, angle_mid, + dir); + } + } else if (angle_max != angle_min) { + cairo_matrix_t ctm; + int i, segments; + double step; + + cairo_get_matrix (cr, &ctm); + segments = _arc_segments_needed (angle_max - angle_min, + radius, &ctm, + cairo_get_tolerance (cr)); + step = (angle_max - angle_min) / segments; + segments -= 1; + + if (dir == CAIRO_DIRECTION_REVERSE) { + double t; + + t = angle_min; + angle_min = angle_max; + angle_max = t; + + step = -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: + * @cr: a cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * Compute a path for the given arc and append it onto the current + * path within @cr. The arc will be accurate within the current + * tolerance and given the current transformation. + **/ +void +_cairo_arc_path (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2) +{ + _cairo_arc_in_direction (cr, xc, yc, + radius, + angle1, angle2, + CAIRO_DIRECTION_FORWARD); +} + +/** + * _cairo_arc_path_negative: + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * @ctm: the current transformation matrix + * @tolerance: the current tolerance value + * @path: the path onto which the arc will be appended + * + * Compute a path for the given arc (defined in the negative + * direction) and append it onto the current path within @cr. The arc + * will be accurate within the current tolerance and given the current + * transformation. + **/ +void +_cairo_arc_path_negative (cairo_t *cr, + double xc, + double yc, + double radius, + double angle1, + double angle2) +{ + _cairo_arc_in_direction (cr, xc, yc, + radius, + angle2, angle1, + CAIRO_DIRECTION_REVERSE); +} 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 0000000000..8e779d41aa --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-array-private.h @@ -0,0 +1,93 @@ +/* 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_private void +_cairo_array_sort (const cairo_array_t *array, int (*compar)(const void *, const void *)); + +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 new file mode 100644 index 0000000000..60f45db4e4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-array.c @@ -0,0 +1,540 @@ +/* -*- 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 + * + * 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): + * Kristian Høgsberg + * Carl Worth + */ + +#include "cairoint.h" +#include "cairo-array-private.h" +#include "cairo-error-private.h" + +/** + * _cairo_array_init: + * + * Initialize a new #cairo_array_t object to store objects each of size + * @element_size. + * + * The #cairo_array_t object provides grow-by-doubling storage. It + * never interprets the data passed to it, nor does it provide any + * sort of callback mechanism for freeing resources held onto by + * stored objects. + * + * When finished using the array, _cairo_array_fini() should be + * called to free resources allocated during use of the array. + **/ +void +_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; +} + +/** + * _cairo_array_fini: + * @array: A #cairo_array_t + * + * Free all resources associated with @array. After this call, @array + * should not be used again without a subsequent call to + * _cairo_array_init() again first. + **/ +void +_cairo_array_fini (cairo_array_t *array) +{ + free (array->elements); +} + +/** + * _cairo_array_grow_by: + * @array: a #cairo_array_t + * + * Increase the size of @array (if needed) so that there are at least + * @additional free spaces in the array. The actual size of the array + * is always increased by doubling as many times as necessary. + **/ +cairo_status_t +_cairo_array_grow_by (cairo_array_t *array, unsigned int additional) +{ + char *new_elements; + unsigned int old_size = array->size; + unsigned int required_size = array->num_elements + additional; + unsigned int new_size; + + /* check for integer overflow */ + if (required_size > INT_MAX || required_size < array->num_elements) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (required_size <= old_size) + return CAIRO_STATUS_SUCCESS; + + if (old_size == 0) + new_size = 1; + else + new_size = old_size * 2; + + while (new_size < required_size) + new_size = new_size * 2; + + array->size = new_size; + new_elements = _cairo_realloc_ab (array->elements, + array->size, array->element_size); + + if (unlikely (new_elements == NULL)) { + array->size = old_size; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + array->elements = new_elements; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_array_truncate: + * @array: a #cairo_array_t + * + * Truncate size of the array to @num_elements if less than the + * current size. No memory is actually freed. The stored objects + * beyond @num_elements are simply "forgotten". + **/ +void +_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements) +{ + if (num_elements < array->num_elements) + array->num_elements = num_elements; +} + +/** + * _cairo_array_index: + * @array: a #cairo_array_t + * Returns: A pointer to the object stored at @index. + * + * If the resulting value is assigned to a pointer to an object of the same + * element_size as initially passed to _cairo_array_init() then that + * pointer may be used for further direct indexing with []. For + * example: + * + * + * cairo_array_t array; + * double *values; + * + * _cairo_array_init (&array, sizeof(double)); + * ... calls to _cairo_array_append() here ... + * + * values = _cairo_array_index (&array, 0); + * for (i = 0; i < _cairo_array_num_elements (&array); i++) + * ... use values[i] here ... + * + **/ +void * +_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, 0); + * for (i=0; i < num_elements; i++) { + * ... use 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; +} + +/** + * _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; +} + +/** + * _cairo_array_copy_element: + * @array: a #cairo_array_t + * + * Copy a single element out of the array from index @index into the + * location pointed to by @dst. + **/ +void +_cairo_array_copy_element (const cairo_array_t *array, + unsigned int index, + void *dst) +{ + memcpy (dst, _cairo_array_index_const (array, index), array->element_size); +} + +/** + * _cairo_array_append: + * @array: a #cairo_array_t + * + * Append a single item onto the array by growing the array by at + * least one element, then copying element_size bytes from @element + * into the array. The address of the resulting object within the + * array can be determined with: + * + * _cairo_array_index (array, _cairo_array_num_elements (array) - 1); + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the + * operation. + **/ +cairo_status_t +_cairo_array_append (cairo_array_t *array, + const void *element) +{ + return _cairo_array_append_multiple (array, element, 1); +} + +/** + * _cairo_array_append_multiple: + * @array: a #cairo_array_t + * + * Append one or more items onto the array by growing the array by + * @num_elements, then copying @num_elements * element_size bytes from + * @elements into the array. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the + * operation. + **/ +cairo_status_t +_cairo_array_append_multiple (cairo_array_t *array, + const void *elements, + unsigned int num_elements) +{ + cairo_status_t status; + void *dest; + + status = _cairo_array_allocate (array, num_elements, &dest); + if (unlikely (status)) + return status; + + memcpy (dest, elements, num_elements * array->element_size); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_array_allocate: + * @array: a #cairo_array_t + * + * 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 uninitialized, but will be accounted + * for in the return value of _cairo_array_num_elements(). + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available for the + * operation. + **/ +cairo_status_t +_cairo_array_allocate (cairo_array_t *array, + unsigned int num_elements, + void **elements) +{ + cairo_status_t status; + + 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; + + array->num_elements += num_elements; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_array_num_elements: + * @array: a #cairo_array_t + * Returns: The number of elements stored in @array. + * + * This space was left intentionally blank, but gtk-doc filled it. + **/ +unsigned int +_cairo_array_num_elements (const cairo_array_t *array) +{ + return array->num_elements; +} + +/** + * _cairo_array_size: + * @array: a #cairo_array_t + * Returns: The number of elements for which there is currently space + * allocated in @array. + * + * This space was left intentionally blank, but gtk-doc filled it. + **/ +unsigned int +_cairo_array_size (const cairo_array_t *array) +{ + return array->size; +} + +/** + * _cairo_user_data_array_init: + * @array: a #cairo_user_data_array_t + * + * Initializes a #cairo_user_data_array_t structure for future + * use. After initialization, the array has no keys. Call + * _cairo_user_data_array_fini() to free any allocated memory + * when done using the array. + **/ +void +_cairo_user_data_array_init (cairo_user_data_array_t *array) +{ + _cairo_array_init (array, sizeof (cairo_user_data_slot_t)); +} + +/** + * _cairo_user_data_array_fini: + * @array: a #cairo_user_data_array_t + * + * Destroys all current keys in the user data array and deallocates + * any memory allocated for the array itself. + **/ +void +_cairo_user_data_array_fini (cairo_user_data_array_t *array) +{ + unsigned int num_slots; + + num_slots = array->num_elements; + if (num_slots) { + cairo_user_data_slot_t *slots; + + slots = _cairo_array_index (array, 0); + 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); + } + } + + _cairo_array_fini (array); +} + +/** + * _cairo_user_data_array_get_data: + * @array: a #cairo_user_data_array_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Returns user data previously attached using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + **/ +void * +_cairo_user_data_array_get_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key) +{ + int i, num_slots; + cairo_user_data_slot_t *slots; + + /* We allow this to support degenerate objects such as cairo_surface_nil. */ + if (array == NULL) + return NULL; + + num_slots = array->num_elements; + slots = _cairo_array_index (array, 0); + for (i = 0; i < num_slots; i++) { + if (slots[i].key == key) + return slots[i].user_data; + } + + return NULL; +} + +/** + * _cairo_user_data_array_set_data: + * @array: a #cairo_user_data_array_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach + * @destroy: a #cairo_destroy_func_t which will be called when the + * user data array is destroyed or when new user data is attached using the + * same key. + * + * Attaches user data to a user data array. To remove user data, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + **/ +cairo_status_t +_cairo_user_data_array_set_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + cairo_status_t status; + int i, num_slots; + cairo_user_data_slot_t *slots, *slot, new_slot; + + if (user_data) { + new_slot.key = key; + new_slot.user_data = user_data; + new_slot.destroy = destroy; + } else { + new_slot.key = NULL; + new_slot.user_data = NULL; + new_slot.destroy = NULL; + } + + slot = NULL; + num_slots = array->num_elements; + slots = _cairo_array_index (array, 0); + for (i = 0; i < num_slots; i++) { + if (slots[i].key == key) { + slot = &slots[i]; + if (slot->destroy && slot->user_data) + slot->destroy (slot->user_data); + break; + } + if (user_data && slots[i].user_data == NULL) { + slot = &slots[i]; /* Have to keep searching for an exact match */ + } + } + + 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; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_user_data_array_copy (cairo_user_data_array_t *dst, + const cairo_user_data_array_t *src) +{ + /* discard any existing user-data */ + if (dst->num_elements != 0) { + _cairo_user_data_array_fini (dst); + _cairo_user_data_array_init (dst); + } + + /* 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_array_append_multiple (dst, + _cairo_array_index_const (src, 0), + src->num_elements); +} + +void +_cairo_user_data_array_foreach (cairo_user_data_array_t *array, + void (*func) (const void *key, + void *elt, + void *closure), + void *closure) +{ + cairo_user_data_slot_t *slots; + int i, num_slots; + + num_slots = array->num_elements; + slots = _cairo_array_index (array, 0); + for (i = 0; i < num_slots; i++) { + if (slots[i].user_data != NULL) + func (slots[i].key, slots[i].user_data, closure); + } +} + +void +_cairo_array_sort (const cairo_array_t *array, int (*compar)(const void *, const void *)) +{ + qsort (array->elements, array->num_elements, array->element_size, compar); +} diff --git a/gfx/cairo/cairo/src/cairo-atomic-private.h b/gfx/cairo/cairo/src/cairo-atomic-private.h new file mode 100644 index 0000000000..46761856fa --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-atomic-private.h @@ -0,0 +1,424 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Chris Wilson + * 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. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + * Andrea Canciani + */ + +#ifndef CAIRO_ATOMIC_PRIVATE_H +#define CAIRO_ATOMIC_PRIVATE_H + +# include "cairo-compiler-private.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +/* The autoconf on OpenBSD 4.5 produces the malformed constant name + * SIZEOF_VOID__ rather than SIZEOF_VOID_P. Work around that here. */ +#if !defined(SIZEOF_VOID_P) && defined(SIZEOF_VOID__) +# define SIZEOF_VOID_P SIZEOF_VOID__ +#endif + +CAIRO_BEGIN_DECLS + +/* C++11 atomic primitives were designed to be more flexible than the + * __sync_* family of primitives. Despite the name, they are available + * in C as well as C++. The motivating reason for using them is that + * for _cairo_atomic_{int,ptr}_get, the compiler is able to see that + * the load is intended to be atomic, as opposed to the __sync_* + * version, below, where the load looks like a plain load. Having + * the load appear atomic to the compiler is particular important for + * tools like ThreadSanitizer so they don't report false positives on + * memory operations that we intend to be atomic. + */ +#if HAVE_CXX11_ATOMIC_PRIMITIVES + +#define HAS_ATOMIC_OPS 1 + +typedef int cairo_atomic_int_t; + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get (cairo_atomic_int_t *x) +{ + return __atomic_load_n(x, __ATOMIC_SEQ_CST); +} + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) +{ + return __atomic_load_n(x, __ATOMIC_RELAXED); +} + +static cairo_always_inline void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) +{ + __atomic_store_n(x, val, __ATOMIC_RELAXED); +} + +static cairo_always_inline void * +_cairo_atomic_ptr_get (void **x) +{ + return __atomic_load_n(x, __ATOMIC_SEQ_CST); +} + +# define _cairo_atomic_int_inc(x) ((void) __atomic_fetch_add(x, 1, __ATOMIC_SEQ_CST)) +# define _cairo_atomic_int_dec(x) ((void) __atomic_fetch_sub(x, 1, __ATOMIC_SEQ_CST)) +# define _cairo_atomic_int_dec_and_test(x) (__atomic_fetch_sub(x, 1, __ATOMIC_SEQ_CST) == 1) + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +static cairo_always_inline cairo_bool_t +_cairo_atomic_int_cmpxchg_impl(cairo_atomic_int_t *x, + cairo_atomic_int_t oldv, + cairo_atomic_int_t newv) +{ + cairo_atomic_int_t expected = oldv; + return __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +#define _cairo_atomic_int_cmpxchg(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg_impl(x, oldv, newv) + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_cmpxchg_return_old_impl(cairo_atomic_int_t *x, + cairo_atomic_int_t oldv, + cairo_atomic_int_t newv) +{ + cairo_atomic_int_t expected = oldv; + (void) __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; +} + +#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg_return_old_impl(x, oldv, newv) + +static cairo_always_inline cairo_bool_t +_cairo_atomic_ptr_cmpxchg_impl(void **x, void *oldv, void *newv) +{ + void *expected = oldv; + return __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + _cairo_atomic_ptr_cmpxchg_impl(x, oldv, newv) + +static cairo_always_inline void * +_cairo_atomic_ptr_cmpxchg_return_old_impl(void **x, void *oldv, void *newv) +{ + void *expected = oldv; + (void) __atomic_compare_exchange_n(x, &expected, newv, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; +} + +#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \ + _cairo_atomic_ptr_cmpxchg_return_old_impl(x, oldv, newv) + +#endif + +#if HAVE_GCC_LEGACY_ATOMICS + +#define HAS_ATOMIC_OPS 1 + +typedef int cairo_atomic_int_t; + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get (cairo_atomic_int_t *x) +{ + __sync_synchronize (); + return *x; +} + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x) +{ + return *x; +} + +static cairo_always_inline void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val) +{ + *x = val; +} + +static cairo_always_inline void * +_cairo_atomic_ptr_get (void **x) +{ + __sync_synchronize (); + return *x; +} + +# 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) + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + __sync_bool_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) + +# define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \ + _cairo_atomic_intptr_to_voidptr (__sync_val_compare_and_swap ((cairo_atomic_intptr_t*)x, (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv)) + +#endif + +#if HAVE_LIB_ATOMIC_OPS +#include + +#define HAS_ATOMIC_OPS 1 + +typedef AO_t cairo_atomic_int_t; + +# define _cairo_atomic_int_get(x) (AO_load_full (x)) +# define _cairo_atomic_int_get_relaxed(x) (AO_load_full (x)) +# 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) + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef unsigned int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef unsigned long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef unsigned long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +# define _cairo_atomic_ptr_get(x) _cairo_atomic_intptr_to_voidptr (AO_load_full (x)) +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg ((cairo_atomic_intptr_t*)(x), (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) + +#endif + +#if HAVE_OS_ATOMIC_OPS +#include + +#define HAS_ATOMIC_OPS 1 + +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_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) + +#if SIZEOF_VOID_P==4 +typedef int32_t cairo_atomic_intptr_t; +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + OSAtomicCompareAndSwap32Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x) + +#elif SIZEOF_VOID_P==8 +typedef int64_t cairo_atomic_intptr_t; +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + OSAtomicCompareAndSwap64Barrier((cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv, (cairo_atomic_intptr_t *)x) + +#else +#error No matching integer pointer type +#endif + +# define _cairo_atomic_ptr_get(x) (OSMemoryBarrier(), *(x)) + +#endif + +#ifndef HAS_ATOMIC_OPS + +#if SIZEOF_VOID_P==SIZEOF_INT +typedef unsigned int cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG +typedef unsigned long cairo_atomic_intptr_t; +#elif SIZEOF_VOID_P==SIZEOF_LONG_LONG +typedef unsigned long long cairo_atomic_intptr_t; +#else +#error No matching integer pointer type +#endif + +typedef cairo_atomic_intptr_t cairo_atomic_int_t; + +cairo_private void +_cairo_atomic_int_inc (cairo_atomic_int_t *x); + +#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); + +cairo_private cairo_atomic_int_t +_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv); + +cairo_private void * +_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv); + +#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_impl (x, oldv, newv) +#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_impl (x, oldv, newv) + +#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER +cairo_private cairo_atomic_int_t +_cairo_atomic_int_get (cairo_atomic_int_t *x); +cairo_private cairo_atomic_int_t +_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x); +void +_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val); +# define _cairo_atomic_ptr_get(x) (void *) _cairo_atomic_int_get((cairo_atomic_int_t *) x) +#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 + +#else + +/* Workaround GCC complaining about casts */ +static cairo_always_inline void * +_cairo_atomic_intptr_to_voidptr (cairo_atomic_intptr_t x) +{ + return (void *) x; +} + +static cairo_always_inline cairo_atomic_int_t +_cairo_atomic_int_cmpxchg_return_old_fallback(cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv) +{ + cairo_atomic_int_t curr; + + do { + curr = _cairo_atomic_int_get (x); + } while (curr == oldv && !_cairo_atomic_int_cmpxchg (x, oldv, newv)); + + return curr; +} + +static cairo_always_inline void * +_cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv) +{ + void *curr; + + do { + curr = _cairo_atomic_ptr_get (x); + } while (curr == oldv && !_cairo_atomic_ptr_cmpxchg (x, oldv, newv)); + + return curr; +} +#endif + +#ifndef _cairo_atomic_int_cmpxchg_return_old +#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_fallback (x, oldv, newv) +#endif + +#ifndef _cairo_atomic_ptr_cmpxchg_return_old +#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_fallback (x, oldv, newv) +#endif + +#ifndef _cairo_atomic_int_cmpxchg +#define _cairo_atomic_int_cmpxchg(x, oldv, newv) (_cairo_atomic_int_cmpxchg_return_old (x, oldv, newv) == oldv) +#endif + +#ifndef _cairo_atomic_ptr_cmpxchg +#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (_cairo_atomic_ptr_cmpxchg_return_old (x, oldv, newv) == oldv) +#endif + +#define _cairo_atomic_uint_get(x) _cairo_atomic_int_get(x) +#define _cairo_atomic_uint_cmpxchg(x, oldv, 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. */ \ + 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-atomic.c b/gfx/cairo/cairo/src/cairo-atomic.c new file mode 100644 index 0000000000..2af50cd387 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-atomic.c @@ -0,0 +1,120 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 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. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-atomic-private.h" +#include "cairo-mutex-private.h" + +#ifdef HAS_ATOMIC_OPS +COMPILE_TIME_ASSERT(sizeof(void*) == sizeof(int) || + sizeof(void*) == sizeof(long) || + sizeof(void*) == sizeof(long long)); +#else +void +_cairo_atomic_int_inc (cairo_atomic_intptr_t *x) +{ + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + *x += 1; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); +} + +cairo_bool_t +_cairo_atomic_int_dec_and_test (cairo_atomic_intptr_t *x) +{ + cairo_bool_t ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = --*x == 0; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} + +cairo_atomic_intptr_t +_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t oldv, cairo_atomic_intptr_t newv) +{ + cairo_atomic_intptr_t ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = *x; + if (ret == oldv) + *x = newv; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} + +void * +_cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv) +{ + void *ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = *x; + if (ret == oldv) + *x = newv; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} + +#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER +cairo_atomic_intptr_t +_cairo_atomic_int_get (cairo_atomic_intptr_t *x) +{ + cairo_atomic_intptr_t ret; + + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + ret = *x; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); + + return ret; +} + +cairo_atomic_intptr_t +_cairo_atomic_int_get_relaxed (cairo_atomic_intptr_t *x) +{ + return _cairo_atomic_int_get (x); +} + +void +_cairo_atomic_int_set_relaxed (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t val) +{ + CAIRO_MUTEX_LOCK (_cairo_atomic_mutex); + *x = val; + CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex); +} +#endif + +#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 0000000000..67607c12f3 --- /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 new file mode 100644 index 0000000000..7f331e539c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-base64-stream.c @@ -0,0 +1,145 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005-2007 Emmanuel Pacaud + * 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 Red Hat, Inc. + * + * Author(s): + * Kristian Høgsberg + * Chris Wilson + */ + +#include "cairoint.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" + +typedef struct _cairo_base64_stream { + cairo_output_stream_t base; + cairo_output_stream_t *output; + unsigned int in_mem; + unsigned int trailing; + unsigned char src[3]; +} cairo_base64_stream_t; + +static char const base64_table[64] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static cairo_status_t +_cairo_base64_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) +{ + cairo_base64_stream_t * stream = (cairo_base64_stream_t *) base; + unsigned char *src = stream->src; + unsigned int i; + + if (stream->in_mem + length < 3) { + for (i = 0; i < length; i++) { + src[i + stream->in_mem] = *data++; + } + stream->in_mem += length; + return CAIRO_STATUS_SUCCESS; + } + + do { + unsigned char dst[4]; + + for (i = stream->in_mem; i < 3; i++) { + src[i] = *data++; + length--; + } + stream->in_mem = 0; + + dst[0] = base64_table[src[0] >> 2]; + dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; + dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; + dst[3] = base64_table[src[2] & 0xfc >> 2]; + /* Special case for the last missing bits */ + switch (stream->trailing) { + case 2: + dst[2] = '='; + /* fall through */ + case 1: + dst[3] = '='; + default: + break; + } + _cairo_output_stream_write (stream->output, dst, 4); + } while (length >= 3); + + for (i = 0; i < length; i++) { + src[i] = *data++; + } + stream->in_mem = length; + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_cairo_base64_stream_close (cairo_output_stream_t *base) +{ + cairo_base64_stream_t *stream = (cairo_base64_stream_t *) base; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (stream->in_mem > 0) { + memset (stream->src + stream->in_mem, 0, 3 - stream->in_mem); + stream->trailing = 3 - stream->in_mem; + stream->in_mem = 3; + status = _cairo_base64_stream_write (base, NULL, 0); + } + + return status; +} + +cairo_output_stream_t * +_cairo_base64_stream_create (cairo_output_stream_t *output) +{ + cairo_base64_stream_t *stream; + + if (output->status) + return _cairo_output_stream_create_in_error (output->status); + + 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; + } + + _cairo_output_stream_init (&stream->base, + _cairo_base64_stream_write, + NULL, + _cairo_base64_stream_close); + + stream->output = output; + stream->in_mem = 0; + stream->trailing = 0; + + return &stream->base; +} diff --git a/gfx/cairo/cairo/src/cairo-base85-stream.c b/gfx/cairo/cairo/src/cairo-base85-stream.c new file mode 100644 index 0000000000..c7f02ca506 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-base85-stream.c @@ -0,0 +1,131 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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. + * + * Author(s): + * Kristian Høgsberg + */ + +#include "cairoint.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" + +typedef struct _cairo_base85_stream { + cairo_output_stream_t base; + cairo_output_stream_t *output; + unsigned char four_tuple[4]; + int pending; +} cairo_base85_stream_t; + +static void +_expand_four_tuple_to_five (unsigned char four_tuple[4], + unsigned char five_tuple[5], + cairo_bool_t *all_zero) +{ + uint32_t value; + int digit, i; + + value = (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++) { + digit = value % 85; + if (digit != 0 && all_zero) + *all_zero = FALSE; + five_tuple[4-i] = digit + 33; + value = value / 85; + } +} + +static cairo_status_t +_cairo_base85_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) +{ + cairo_base85_stream_t *stream = (cairo_base85_stream_t *) base; + const unsigned char *ptr = data; + unsigned char five_tuple[5]; + cairo_bool_t is_zero; + + while (length) { + stream->four_tuple[stream->pending++] = *ptr++; + length--; + if (stream->pending == 4) { + _expand_four_tuple_to_five (stream->four_tuple, five_tuple, &is_zero); + if (is_zero) + _cairo_output_stream_write (stream->output, "z", 1); + else + _cairo_output_stream_write (stream->output, five_tuple, 5); + stream->pending = 0; + } + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_cairo_base85_stream_close (cairo_output_stream_t *base) +{ + cairo_base85_stream_t *stream = (cairo_base85_stream_t *) base; + unsigned char five_tuple[5]; + + if (stream->pending) { + memset (stream->four_tuple + stream->pending, 0, 4 - stream->pending); + _expand_four_tuple_to_five (stream->four_tuple, five_tuple, NULL); + _cairo_output_stream_write (stream->output, five_tuple, stream->pending + 1); + } + + return _cairo_output_stream_get_status (stream->output); +} + +cairo_output_stream_t * +_cairo_base85_stream_create (cairo_output_stream_t *output) +{ + cairo_base85_stream_t *stream; + + if (output->status) + return _cairo_output_stream_create_in_error (output->status); + + 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; + } + + _cairo_output_stream_init (&stream->base, + _cairo_base85_stream_write, + NULL, + _cairo_base85_stream_close); + stream->output = output; + stream->pending = 0; + + return &stream->base; +} diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c new file mode 100644 index 0000000000..65f95d7970 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c @@ -0,0 +1,895 @@ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 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 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 "cairo-traps-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 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 _sweep_line { + rectangle_t **rectangles; + 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; + +#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_push (sweep_line_t *sweep, rectangle_t *rectangle) +{ + rectangle_t **elements; + int i, parent; + + elements = sweep->stop; + for (i = ++sweep->stop_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 +rectangle_pop_stop (sweep_line_t *sweep) +{ + rectangle_t **elements = sweep->stop; + rectangle_t *tail; + int child, i; + + 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)) <= sweep->stop_size; + i = child) + { + if (child != sweep->stop_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->stop[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, + cairo_fill_rule_t fill_rule, + cairo_bool_t do_traps, + void *container) +{ + 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.prev = NULL; + sweep_line->head.next = &sweep_line->tail; + sweep_line->tail.prev = &sweep_line->head; + sweep_line->tail.next = NULL; + 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; + sweep_line->container = container; + sweep_line->do_traps = do_traps; +} + +static void +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 (sweep_line->do_traps) { + cairo_line_t _left = { + { left->x, left->top }, + { left->x, bot }, + }, _right = { + { left->right->x, left->top }, + { left->right->x, bot }, + }; + _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; + + box.p1.x = left->x; + box.p1.y = left->top; + box.p2.x = left->right->x; + box.p2.y = bot; + + status = _cairo_boxes_add (sweep_line->container, + 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 +edge_start_or_continue_box (sweep_line_t *sweep_line, + edge_t *left, + edge_t *right, + int top) +{ + if (left->right == right) + return; + + if (left->right != NULL) { + if (left->right->x == right->x) { + /* continuation on right, so just swap edges */ + left->right = right; + return; + } + + edge_end_box (sweep_line, left, top); + } + + 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) +{ + int top = sweep->current_y; + edge_t *pos; + + if (sweep->last_y == sweep->current_y) + return; + + if (sweep->insert) + active_edges_insert (sweep); + + pos = sweep->head.next; + if (pos == &sweep->tail) + return; + + if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING) { + do { + edge_t *left, *right; + int winding; + + left = pos; + winding = left->dir; + + right = left->next; + + /* Check if there is a co-linear edge with an existing trap */ + 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; + } + + 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); + + /* 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 && right->x != right->next->x) + break; + + right = right->next; + } while (TRUE); + + edge_start_or_continue_box (sweep, left, right, top); + + pos = right->next; + } while (pos != &sweep->tail); + } else { + do { + edge_t *right = pos->next; + int count = 0; + + do { + /* End all subsumed traps */ + if (unlikely (right->right != NULL)) + edge_end_box (sweep, right, top); + + /* skip co-linear edges */ + if (++count & 1 && right->x != right->next->x) + break; + + right = right->next; + } while (TRUE); + + edge_start_or_continue_box (sweep, pos, right, top); + + pos = right->next; + } while (pos != &sweep->tail); + } + + sweep->last_y = sweep->current_y; +} + +static inline void +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, edge, sweep->current_y); + } + + 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 update; + + update = TRUE; + if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING && + rectangle->left.prev->dir == rectangle->left.dir) + { + update = rectangle->left.next != &rectangle->right; + } + + sweep_line_delete_edge (sweep, &rectangle->left); + sweep_line_delete_edge (sweep, &rectangle->right); + + rectangle_pop_stop (sweep); + return update; +} + +static inline void +sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle) +{ + 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); +} + +static cairo_status_t +_cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles, + int num_rectangles, + cairo_fill_rule_t fill_rule, + cairo_bool_t do_traps, + void *container) +{ + sweep_line_t sweep_line; + rectangle_t *rectangle; + cairo_status_t status; + cairo_bool_t update; + + sweep_line_init (&sweep_line, + rectangles, num_rectangles, + fill_rule, + do_traps, container); + if ((status = setjmp (sweep_line.unwind))) + return status; + + update = FALSE; + + 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) { + if (update) { + active_edges_to_traps (&sweep_line); + update = FALSE; + } + + sweep_line.current_y = stop->bottom; + } + + update |= sweep_line_delete (&sweep_line, stop); + stop = rectangle_peek_stop (&sweep_line); + } + + if (update) { + active_edges_to_traps (&sweep_line); + update = FALSE; + } + + sweep_line.current_y = rectangle->top; + } + + 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); + update = FALSE; + } + sweep_line.current_y = rectangle->bottom; + } + + update |= sweep_line_delete (&sweep_line, rectangle); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_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 *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; + rectangle_t *rectangles, **rectangles_ptrs; + cairo_status_t status; + int i; + + assert (traps->is_rectangular); + + if (unlikely (traps->num_traps <= 1)) { + if (traps->num_traps == 1) { + cairo_trapezoid_t *trap = traps->traps; + if (trap->left.p1.x > trap->right.p1.x) { + cairo_line_t tmp = trap->left; + trap->left = trap->right; + trap->right = tmp; + } + } + return CAIRO_STATUS_SUCCESS; + } + + dump_traps (traps, "bo-rects-traps-in.txt"); + + rectangles = stack_rectangles; + 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 *), + 3*sizeof (rectangle_t *)); + if (unlikely (rectangles == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps); + } + + for (i = 0; i < traps->num_traps; i++) { + if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) { + rectangles[i].left.x = traps->traps[i].left.p1.x; + rectangles[i].left.dir = 1; + + rectangles[i].right.x = traps->traps[i].right.p1.x; + rectangles[i].right.dir = -1; + } else { + rectangles[i].right.x = traps->traps[i].left.p1.x; + rectangles[i].right.dir = 1; + + rectangles[i].left.x = traps->traps[i].right.p1.x; + rectangles[i].left.dir = -1; + } + + rectangles[i].left.right = NULL; + rectangles[i].right.right = NULL; + + rectangles[i].top = traps->traps[i].top; + rectangles[i].bottom = traps->traps[i].bottom; + + 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+2, i, + fill_rule, + TRUE, traps); + traps->is_rectilinear = TRUE; + traps->is_rectangular = TRUE; + + if (rectangles != stack_rectangles) + free (rectangles); + + dump_traps (traps, "bo-rects-traps-out.txt"); + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *out) +{ + rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; + 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, y_min, y_max; + + if (unlikely (in->num_boxes == 0)) { + _cairo_boxes_clear (out); + return CAIRO_STATUS_SUCCESS; + } + + if (in->num_boxes == 1) { + if (in == out) { + cairo_box_t *box = &in->chunks.base[0]; + + if (box->p1.x > box->p2.x) { + cairo_fixed_t tmp = box->p1.x; + box->p1.x = box->p2.x; + box->p2.x = tmp; + } + } else { + cairo_box_t box = in->chunks.base[0]; + + if (box.p1.x > box.p2.x) { + cairo_fixed_t tmp = box.p1.x; + box.p1.x = box.p2.x; + box.p2.x = tmp; + } + + _cairo_boxes_clear (out); + 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 *), + 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); + } + + j = 0; + 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; + + 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.right = NULL; + rectangles[j].right.right = NULL; + + rectangles[j].top = box[i].p1.y; + rectangles[j].bottom = box[i].p2.y; + + 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+2, j, + fill_rule, + FALSE, out); + if (rectangles != stack_rectangles) + free (rectangles); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c new file mode 100644 index 0000000000..7c0be69b71 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c @@ -0,0 +1,600 @@ +/* + * 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-boxes-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; + +/* A deferred trapezoid of an edge */ +struct _cairo_bo_trap { + cairo_bo_edge_t *right; + int32_t top; +}; + +struct _cairo_bo_edge { + cairo_edge_t edge; + cairo_bo_edge_t *prev; + cairo_bo_edge_t *next; + cairo_bo_trap_t deferred_trap; +}; + +typedef enum { + CAIRO_BO_EVENT_TYPE_START, + CAIRO_BO_EVENT_TYPE_STOP +} cairo_bo_event_type_t; + +typedef struct _cairo_bo_event { + cairo_bo_event_type_t type; + cairo_point_t point; + cairo_bo_edge_t *edge; +} cairo_bo_event_t; + +typedef struct _cairo_bo_sweep_line { + cairo_bo_event_t **events; + cairo_bo_edge_t *head; + cairo_bo_edge_t *stopped; + int32_t current_y; + cairo_bo_edge_t *current_edge; +} cairo_bo_sweep_line_t; + +static inline int +_cairo_point_compare (const cairo_point_t *a, + const cairo_point_t *b) +{ + int cmp; + + cmp = a->y - b->y; + if (likely (cmp)) + return cmp; + + return a->x - b->x; +} + +static inline int +_cairo_bo_edge_compare (const cairo_bo_edge_t *a, + const cairo_bo_edge_t *b) +{ + int cmp; + + cmp = a->edge.line.p1.x - b->edge.line.p1.x; + if (likely (cmp)) + return cmp; + + return b->edge.bottom - a->edge.bottom; +} + +static inline int +cairo_bo_event_compare (const cairo_bo_event_t *a, + const cairo_bo_event_t *b) +{ + int cmp; + + cmp = _cairo_point_compare (&a->point, &b->point); + if (likely (cmp)) + return cmp; + + cmp = a->type - b->type; + if (cmp) + return cmp; + + return a - b; +} + +static inline cairo_bo_event_t * +_cairo_bo_event_dequeue (cairo_bo_sweep_line_t *sweep_line) +{ + return *sweep_line->events++; +} + +CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort, + cairo_bo_event_t *, + cairo_bo_event_compare) + +static void +_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line, + cairo_bo_event_t **events, + int num_events) +{ + _cairo_bo_event_queue_sort (events, num_events); + events[num_events] = NULL; + sweep_line->events = events; + + sweep_line->head = NULL; + sweep_line->current_y = INT32_MIN; + sweep_line->current_edge = NULL; +} + +static void +_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_edge_compare (sweep_line->current_edge, edge); + if (cmp < 0) { + prev = sweep_line->current_edge; + next = prev->next; + while (next != NULL && _cairo_bo_edge_compare (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_edge_compare (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; +} + +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 inline cairo_bool_t +edges_collinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) +{ + return a->edge.line.p1.x == b->edge.line.p1.x; +} + +static cairo_status_t +_cairo_bo_edge_end_trap (cairo_bo_edge_t *left, + int32_t bot, + cairo_bool_t do_traps, + void *container) +{ + cairo_bo_trap_t *trap = &left->deferred_trap; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + /* Only emit (trivial) non-degenerate trapezoids with positive height. */ + if (likely (trap->top < bot)) { + if (do_traps) { + _cairo_traps_add_trap (container, + trap->top, bot, + &left->edge.line, &trap->right->edge.line); + status = _cairo_traps_status ((cairo_traps_t *) container); + } else { + cairo_box_t box; + + box.p1.x = left->edge.line.p1.x; + box.p1.y = trap->top; + box.p2.x = trap->right->edge.line.p1.x; + box.p2.y = bot; + status = _cairo_boxes_add (container, CAIRO_ANTIALIAS_DEFAULT, &box); + } + } + + trap->right = NULL; + + return status; +} + +/* 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 cairo_status_t +_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_bool_t do_traps, + void *container) +{ + cairo_status_t status; + + if (left->deferred_trap.right == right) + return CAIRO_STATUS_SUCCESS; + + if (left->deferred_trap.right != NULL) { + if (right != NULL && edges_collinear (left->deferred_trap.right, right)) + { + /* continuation on right, so just swap edges */ + left->deferred_trap.right = right; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_bo_edge_end_trap (left, top, do_traps, container); + if (unlikely (status)) + return status; + } + + if (right != NULL && ! edges_collinear (left, right)) { + left->deferred_trap.top = top; + left->deferred_trap.right = right; + } + + 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_bool_t do_traps, + void *container) +{ + cairo_bo_edge_t *right; + cairo_status_t status; + + 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 = 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_collinear (left, right)) { + /* continuation on left */ + left->deferred_trap = right->deferred_trap; + right->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, do_traps, container); + if (unlikely (status)) + return status; + } + + in_out += right->edge.dir; + if (in_out == 0) { + /* skip co-linear edges */ + if (right->next == NULL || + ! edges_collinear (right, right->next)) + { + break; + } + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, top, + do_traps, container); + 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, do_traps, container); + 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_collinear (right, next); + + if (! skip) + break; + } + + right = right->next; + } + + status = _cairo_bo_edge_start_or_continue_trap (left, right, top, + do_traps, container); + if (unlikely (status)) + return status; + + left = right; + if (left != NULL) + left = left->next; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t **start_events, + int num_events, + cairo_fill_rule_t fill_rule, + cairo_bool_t do_traps, + void *container) +{ + cairo_bo_sweep_line_t sweep_line; + cairo_bo_event_t *event; + cairo_status_t status; + + _cairo_bo_sweep_line_init (&sweep_line, start_events, num_events); + + while ((event = _cairo_bo_event_dequeue (&sweep_line))) { + if (event->point.y != sweep_line.current_y) { + status = _active_edges_to_traps (sweep_line.head, + sweep_line.current_y, + fill_rule, do_traps, container); + if (unlikely (status)) + return status; + + sweep_line.current_y = event->point.y; + } + + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + _cairo_bo_sweep_line_insert (&sweep_line, event->edge); + break; + + case CAIRO_BO_EVENT_TYPE_STOP: + _cairo_bo_sweep_line_delete (&sweep_line, event->edge); + + if (event->edge->deferred_trap.right != NULL) { + status = _cairo_bo_edge_end_trap (event->edge, + sweep_line.current_y, + do_traps, container); + if (unlikely (status)) + return status; + } + + break; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes) +{ + 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, + FALSE, boxes); + if (events != stack_events) + free (events); + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule) +{ + 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; + cairo_status_t status; + int i, j, k; + + if (unlikely (traps->num_traps == 0)) + return CAIRO_STATUS_SUCCESS; + + assert (traps->is_rectilinear); + + i = 4 * traps->num_traps; + + events = stack_events; + event_ptrs = stack_event_ptrs; + edges = stack_edges; + if (i > ARRAY_LENGTH (stack_events)) { + events = _cairo_malloc_ab_plus_c (i, + 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 + i); + edges = (cairo_bo_edge_t *) (event_ptrs + i + 1); + } + + for (i = j = k = 0; i < traps->num_traps; i++) { + edges[k].edge.top = traps->traps[i].top; + edges[k].edge.bottom = traps->traps[i].bottom; + edges[k].edge.line = traps->traps[i].left; + edges[k].edge.dir = 1; + edges[k].deferred_trap.right = NULL; + edges[k].prev = NULL; + edges[k].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = traps->traps[i].top; + events[j].point.x = traps->traps[i].left.p1.x; + events[j].edge = &edges[k]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = traps->traps[i].bottom; + events[j].point.x = traps->traps[i].left.p1.x; + events[j].edge = &edges[k]; + j++; + k++; + + edges[k].edge.top = traps->traps[i].top; + edges[k].edge.bottom = traps->traps[i].bottom; + edges[k].edge.line = traps->traps[i].right; + edges[k].edge.dir = -1; + edges[k].deferred_trap.right = NULL; + edges[k].prev = NULL; + edges[k].next = NULL; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_START; + events[j].point.y = traps->traps[i].top; + events[j].point.x = traps->traps[i].right.p1.x; + events[j].edge = &edges[k]; + j++; + + event_ptrs[j] = &events[j]; + events[j].type = CAIRO_BO_EVENT_TYPE_STOP; + events[j].point.y = traps->traps[i].bottom; + events[j].point.x = traps->traps[i].right.p1.x; + events[j].edge = &edges[k]; + j++; + k++; + } + + _cairo_traps_clear (traps); + status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, + fill_rule, + TRUE, traps); + traps->is_rectilinear = TRUE; + + if (events != stack_events) + free (events); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c new file mode 100644 index 0000000000..eb4cee4306 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c @@ -0,0 +1,1915 @@ +/* + * 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-combsort-inline.h" +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-line-inline.h" +#include "cairo-traps-private.h" + +#define DEBUG_PRINT_STATE 0 +#define DEBUG_EVENTS 0 +#define DEBUG_TRAPS 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_trap cairo_bo_trap_t; + +/* A deferred trapezoid of an edge */ +struct _cairo_bo_trap { + cairo_bo_edge_t *right; + int32_t top; +}; + +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; +}; + +/* 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; + cairo_bo_edge_t *stopped; + int32_t current_y; + cairo_bo_edge_t *current_edge; +} cairo_bo_sweep_line_t; + +#if DEBUG_TRAPS +static void +dump_traps (cairo_traps_t *traps, const char *filename) +{ + FILE *file; + cairo_box_t extents; + int n; + + if (getenv ("CAIRO_DEBUG_TRAPS") == NULL) + return; + +#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); + printf ("%s: extents=(%d, %d, %d, %d)\n", + filename, + extents.p1.x, extents.p1.y, + extents.p2.x, extents.p2.y); + + 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); + } +} + +static void +dump_edges (cairo_bo_start_event_t *events, + int num_edges, + 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 < num_edges; n++) { + fprintf (file, "(%d, %d), (%d, %d) %d %d %d\n", + events[n].edge.edge.line.p1.x, + events[n].edge.edge.line.p1.y, + events[n].edge.edge.line.p2.x, + events[n].edge.edge.line.p2.y, + events[n].edge.edge.top, + events[n].edge.edge.bottom, + events[n].edge.edge.dir); + } + fprintf (file, "\n"); + fclose (file); + } +} +#endif + +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-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 inline int +_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; + + cmp = _cairo_lines_compare_at_y (&a->edge.line, + &b->edge.line, + sweep_line->current_y); + 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) +{ + 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 (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 + * 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->stopped = NULL; + sweep_line->current_y = INT32_MIN; + sweep_line->current_edge = NULL; +} + +static void +_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; + edge->next = NULL; + } + + sweep_line->current_edge = edge; +} + +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; +} + +#if DEBUG_PRINT_STATE +static void +_cairo_bo_edge_print (cairo_bo_edge_t *edge) +{ + printf ("(0x%x, 0x%x)-(0x%x, 0x%x)", + edge->edge.line.p1.x, edge->edge.line.p1.y, + edge->edge.line.p2.x, edge->edge.line.p2.y); +} + +static void +_cairo_bo_event_print (cairo_bo_event_t *event) +{ + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + printf ("Start: "); + break; + case CAIRO_BO_EVENT_TYPE_STOP: + printf ("Stop: "); + break; + case CAIRO_BO_EVENT_TYPE_INTERSECTION: + printf ("Intersection: "); + break; + } + printf ("(%d, %d)\t", event->point.x, event->point.y); + _cairo_bo_edge_print (event->e1); + if (event->type == CAIRO_BO_EVENT_TYPE_INTERSECTION) { + printf (" X "); + _cairo_bo_edge_print (event->e2); + } + printf ("\n"); +} + +static void +_cairo_bo_event_queue_print (cairo_bo_event_queue_t *event_queue) +{ + /* XXX: fixme to print the start/stop array too. */ + printf ("Event queue:\n"); +} + +static void +_cairo_bo_sweep_line_print (cairo_bo_sweep_line_t *sweep_line) +{ + cairo_bool_t first = TRUE; + cairo_bo_edge_t *edge; + + printf ("Sweep line from edge list: "); + first = TRUE; + for (edge = sweep_line->head; + edge; + edge = edge->next) + { + if (!first) + printf (", "); + _cairo_bo_edge_print (edge); + first = FALSE; + } + printf ("\n"); +} + +static void +print_state (const char *msg, + cairo_bo_event_t *event, + cairo_bo_event_queue_t *event_queue, + cairo_bo_sweep_line_t *sweep_line) +{ + printf ("%s ", msg); + _cairo_bo_event_print (event); + _cairo_bo_event_queue_print (event_queue); + _cairo_bo_sweep_line_print (sweep_line); + printf ("\n"); +} +#endif + +#if DEBUG_EVENTS +static void CAIRO_PRINTF_FORMAT (1, 2) +event_log (const char *fmt, ...) +{ + FILE *file; + + if (getenv ("CAIRO_DEBUG_EVENTS") == NULL) + return; + + file = fopen ("bo-events.txt", "a"); + if (file != NULL) { + va_list ap; + + va_start (ap, fmt); + vfprintf (file, fmt, ap); + va_end (ap); + + fclose (file); + } +} +#endif + +#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))) + +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 (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) { + p = edge_compare_for_y_against_x (b, + a->edge.line.p1.y, + a->edge.line.p1.x) == 0; + } else { + 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 void +_cairo_bo_edge_end_trap (cairo_bo_edge_t *left, + int32_t bot, + cairo_traps_t *traps) +{ + cairo_bo_trap_t *trap = &left->deferred_trap; + + /* Only emit (trivial) non-degenerate trapezoids with positive height. */ + if (likely (trap->top < bot)) { + _cairo_traps_add_trap (traps, + trap->top, bot, + &left->edge.line, &trap->right->edge.line); + +#if DEBUG_PRINT_STATE + printf ("Deferred trap: left=(%x, %x)-(%x,%x) " + "right=(%x,%x)-(%x,%x) top=%x, bot=%x\n", + left->edge.line.p1.x, left->edge.line.p1.y, + left->edge.line.p2.x, left->edge.line.p2.y, + trap->right->edge.line.p1.x, trap->right->edge.line.p1.y, + trap->right->edge.line.p2.x, trap->right->edge.line.p2.y, + trap->top, bot); +#endif +#if DEBUG_EVENTS + event_log ("end trap: %lu %lu %d %d\n", + (long) left, + (long) trap->right, + trap->top, + bot); +#endif + } + + trap->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 +_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, + cairo_bo_edge_t *right, + int top, + cairo_traps_t *traps) +{ + if (left->deferred_trap.right == right) + return; + + assert (right); + if (left->deferred_trap.right != NULL) { + if (edges_colinear (left->deferred_trap.right, right)) + { + /* continuation on right, so just swap edges */ + left->deferred_trap.right = right; + return; + } + + _cairo_bo_edge_end_trap (left, top, traps); + } + + if (! edges_colinear (left, right)) { + left->deferred_trap.top = top; + left->deferred_trap.right = right; + +#if DEBUG_EVENTS + event_log ("begin trap: %lu %lu %d\n", + (long) left, + (long) right, + top); +#endif + } +} + +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 *left; + int in_out; + + +#if DEBUG_PRINT_STATE + printf ("Processing active edges for %x\n", top); +#endif + + 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. + */ + if (left->deferred_trap.right == NULL && + edges_colinear (left, pos)) + { + /* continuation on left */ + left->deferred_trap = pos->deferred_trap; + pos->deferred_trap.right = NULL; + } + else + { + _cairo_bo_edge_end_trap (pos, top, traps); + } + } + + 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; + } + } + + pos = pos->next; + } +} + +/* 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, + unsigned fill_rule, + cairo_traps_t *traps, + int *num_intersections) +{ + cairo_status_t status; + int intersection_count = 0; + 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; + + /* 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; + + for (i = 0; i < num_events; i++) { + cairo_bo_start_event_t *event = + ((cairo_bo_start_event_t **) start_events)[i]; + event_log ("edge: %lu (%d, %d) (%d, %d) (%d, %d) %d\n", + (long) &events[i].edge, + event->edge.edge.line.p1.x, + event->edge.edge.line.p1.y, + event->edge.edge.line.p2.x, + event->edge.edge.line.p2.y, + event->edge.top, + event->edge.bottom, + event->edge.edge.dir); + } + } +#endif + + _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) { + for (e1 = sweep_line.stopped; e1; e1 = e1->next) { + if (e1->deferred_trap.right != NULL) { + _cairo_bo_edge_end_trap (e1, + e1->edge.bottom, + traps); + } + } + sweep_line.stopped = NULL; + + _active_edges_to_traps (sweep_line.head, + sweep_line.current_y, + fill_rule, traps); + + sweep_line.current_y = event->point.y; + } + +#if DEBUG_EVENTS + event_log ("event: %d (%ld, %ld) %lu, %lu\n", + event->type, + (long) event->point.x, + (long) event->point.y, + (long) event->e1, + (long) event->e2); +#endif + + switch (event->type) { + case CAIRO_BO_EVENT_TYPE_START: + e1 = &((cairo_bo_start_event_t *) event)->edge; + + _cairo_bo_sweep_line_insert (&sweep_line, e1); + + status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); + if (unlikely (status)) + goto unwind; + + /* check to see if this is a continuation of a stopped edge */ + /* XXX change to an infinitesimal lengthening rule */ + for (left = sweep_line.stopped; left; left = left->next) { + if (e1->edge.top <= left->edge.bottom && + edges_colinear (e1, left)) + { + e1->deferred_trap = left->deferred_trap; + if (left->prev != NULL) + left->prev = left->next; + else + sweep_line.stopped = left->next; + if (left->next != NULL) + left->next->prev = left->prev; + break; + } + } + + 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); + + /* first, check to see if we have a continuation via a fresh edge */ + if (e1->deferred_trap.right != NULL) { + e1->next = sweep_line.stopped; + if (sweep_line.stopped != NULL) + sweep_line.stopped->prev = e1; + sweep_line.stopped = e1; + e1->prev = NULL; + } + + 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; + + intersection_count++; + + 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; + } + } + + *num_intersections = intersection_count; + for (e1 = sweep_line.stopped; e1; e1 = e1->next) { + if (e1->deferred_trap.right != NULL) { + _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps); + } + } + status = traps->status; + unwind: + _cairo_bo_event_queue_fini (&event_queue); + +#if DEBUG_EVENTS + event_log ("\n"); +#endif + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, + const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule) +{ + int intersections; + 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; + 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)) { + 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)) { + 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++) { + 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_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 + + /* XXX: This would be the convenient place to throw in multiple + * 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, + fill_rule, traps, + &intersections); +#if DEBUG_TRAPS + dump_traps (traps, "bo-polygon-out.txt"); +#endif + + if (events != stack_events) + free (events); + + return status; +} + +cairo_status_t +_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule) +{ + cairo_status_t status; + cairo_polygon_t polygon; + int i; + + if (unlikely (0 == traps->num_traps)) + return CAIRO_STATUS_SUCCESS; + +#if DEBUG_TRAPS + dump_traps (traps, "bo-traps-in.txt"); +#endif + + _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); + + for (i = 0; i < traps->num_traps; i++) { + status = _cairo_polygon_add_line (&polygon, + &traps->traps[i].left, + traps->traps[i].top, + traps->traps[i].bottom, + 1); + if (unlikely (status)) + goto CLEANUP; + + status = _cairo_polygon_add_line (&polygon, + &traps->traps[i].right, + traps->traps[i].top, + traps->traps[i].bottom, + -1); + if (unlikely (status)) + goto CLEANUP; + } + + _cairo_traps_clear (traps); + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &polygon, + fill_rule); + +#if DEBUG_TRAPS + dump_traps (traps, "bo-traps-out.txt"); +#endif + + CLEANUP: + _cairo_polygon_fini (&polygon); + + return status; +} + +#if 0 +static cairo_bool_t +edges_have_an_intersection_quadratic (cairo_bo_edge_t *edges, + int num_edges) + +{ + int i, j; + cairo_bo_edge_t *a, *b; + cairo_bo_point32_t intersection; + + /* We must not be given any upside-down edges. */ + for (i = 0; i < num_edges; i++) { + assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0); + edges[i].line.p1.x <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p1.y <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p2.x <<= CAIRO_BO_GUARD_BITS; + edges[i].line.p2.y <<= CAIRO_BO_GUARD_BITS; + } + + for (i = 0; i < num_edges; i++) { + for (j = 0; j < num_edges; j++) { + if (i == j) + continue; + + a = &edges[i]; + b = &edges[j]; + + if (! _cairo_bo_edge_intersect (a, b, &intersection)) + continue; + + printf ("Found intersection (%d,%d) between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)\n", + intersection.x, + intersection.y, + a->line.p1.x, a->line.p1.y, + a->line.p2.x, a->line.p2.y, + b->line.p1.x, b->line.p1.y, + b->line.p2.x, b->line.p2.y); + + return TRUE; + } + } + return FALSE; +} + +#define TEST_MAX_EDGES 10 + +typedef struct test { + const char *name; + const char *description; + int num_edges; + cairo_bo_edge_t edges[TEST_MAX_EDGES]; +} test_t; + +static test_t +tests[] = { + { + "3 near misses", + "3 edges all intersecting very close to each other", + 3, + { + { { 4, 2}, {0, 0}, { 9, 9}, NULL, NULL }, + { { 7, 2}, {0, 0}, { 2, 3}, NULL, NULL }, + { { 5, 2}, {0, 0}, { 1, 7}, NULL, NULL } + } + }, + { + "inconsistent data", + "Derived from random testing---was leading to skip list and edge list disagreeing.", + 2, + { + { { 2, 3}, {0, 0}, { 8, 9}, NULL, NULL }, + { { 2, 3}, {0, 0}, { 6, 7}, NULL, NULL } + } + }, + { + "failed sort", + "A test derived from random testing that leads to an inconsistent sort --- looks like we just can't attempt to validate the sweep line with edge_compare?", + 3, + { + { { 6, 2}, {0, 0}, { 6, 5}, NULL, NULL }, + { { 3, 5}, {0, 0}, { 5, 6}, NULL, NULL }, + { { 9, 2}, {0, 0}, { 5, 6}, NULL, NULL }, + } + }, + { + "minimal-intersection", + "Intersection of a two from among the smallest possible edges.", + 2, + { + { { 0, 0}, {0, 0}, { 1, 1}, NULL, NULL }, + { { 1, 0}, {0, 0}, { 0, 1}, NULL, NULL } + } + }, + { + "simple", + "A simple intersection of two edges at an integer (2,2).", + 2, + { + { { 1, 1}, {0, 0}, { 3, 3}, NULL, NULL }, + { { 2, 1}, {0, 0}, { 2, 3}, NULL, NULL } + } + }, + { + "bend-to-horizontal", + "With intersection truncation one edge bends to horizontal", + 2, + { + { { 9, 1}, {0, 0}, {3, 7}, NULL, NULL }, + { { 3, 5}, {0, 0}, {9, 9}, NULL, NULL } + } + } +}; + +/* + { + "endpoint", + "An intersection that occurs at the endpoint of a segment.", + { + { { 4, 6}, { 5, 6}, NULL, { { NULL }} }, + { { 4, 5}, { 5, 7}, NULL, { { NULL }} }, + { { 0, 0}, { 0, 0}, NULL, { { NULL }} }, + } + } + { + name = "overlapping", + desc = "Parallel segments that share an endpoint, with different slopes.", + edges = { + { top = { x = 2, y = 0}, bottom = { x = 1, y = 1}}, + { top = { x = 2, y = 0}, bottom = { x = 0, y = 2}}, + { top = { x = 0, y = 3}, bottom = { x = 1, y = 3}}, + { top = { x = 0, y = 3}, bottom = { x = 2, y = 3}}, + { top = { x = 0, y = 4}, bottom = { x = 0, y = 6}}, + { top = { x = 0, y = 5}, bottom = { x = 0, y = 6}} + } + }, + { + name = "hobby_stage_3", + desc = "A particularly tricky part of the 3rd stage of the 'hobby' test below.", + edges = { + { top = { x = -1, y = -2}, bottom = { x = 4, y = 2}}, + { top = { x = 5, y = 3}, bottom = { x = 9, y = 5}}, + { top = { x = 5, y = 3}, bottom = { x = 6, y = 3}}, + } + }, + { + name = "hobby", + desc = "Example from John Hobby's paper. Requires 3 passes of the iterative algorithm.", + edges = { + { top = { x = 0, y = 0}, bottom = { x = 9, y = 5}}, + { top = { x = 0, y = 0}, bottom = { x = 13, y = 6}}, + { top = { x = -1, y = -2}, bottom = { x = 9, y = 5}} + } + }, + { + name = "slope", + desc = "Edges with same start/stop points but different slopes", + edges = { + { top = { x = 4, y = 1}, bottom = { x = 6, y = 3}}, + { top = { x = 4, y = 1}, bottom = { x = 2, y = 3}}, + { top = { x = 2, y = 4}, bottom = { x = 4, y = 6}}, + { top = { x = 6, y = 4}, bottom = { x = 4, y = 6}} + } + }, + { + name = "horizontal", + desc = "Test of a horizontal edge", + edges = { + { top = { x = 1, y = 1}, bottom = { x = 6, y = 6}}, + { top = { x = 2, y = 3}, bottom = { x = 5, y = 3}} + } + }, + { + name = "vertical", + desc = "Test of a vertical edge", + edges = { + { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}}, + { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}} + } + }, + { + name = "congruent", + desc = "Two overlapping edges with the same slope", + edges = { + { top = { x = 5, y = 1}, bottom = { x = 5, y = 7}}, + { top = { x = 5, y = 2}, bottom = { x = 5, y = 6}}, + { top = { x = 2, y = 4}, bottom = { x = 8, y = 5}} + } + }, + { + name = "multi", + desc = "Several segments with a common intersection point", + edges = { + { top = { x = 1, y = 2}, bottom = { x = 5, y = 4} }, + { top = { x = 1, y = 1}, bottom = { x = 5, y = 5} }, + { top = { x = 2, y = 1}, bottom = { x = 4, y = 5} }, + { top = { x = 4, y = 1}, bottom = { x = 2, y = 5} }, + { top = { x = 5, y = 1}, bottom = { x = 1, y = 5} }, + { top = { x = 5, y = 2}, bottom = { x = 1, y = 4} } + } + } +}; +*/ + +static int +run_test (const char *test_name, + cairo_bo_edge_t *test_edges, + int num_edges) +{ + int i, intersections, passes; + cairo_bo_edge_t *edges; + cairo_array_t intersected_edges; + + printf ("Testing: %s\n", test_name); + + _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t)); + + intersections = _cairo_bentley_ottmann_intersect_edges (test_edges, num_edges, &intersected_edges); + if (intersections) + printf ("Pass 1 found %d intersections:\n", intersections); + + + /* XXX: Multi-pass Bentley-Ottmmann. Preferable would be to add a + * pass of Hobby's tolerance-square algorithm instead. */ + passes = 1; + while (intersections) { + int num_edges = _cairo_array_num_elements (&intersected_edges); + passes++; + edges = _cairo_malloc_ab (num_edges, sizeof (cairo_bo_edge_t)); + assert (edges != NULL); + memcpy (edges, _cairo_array_index (&intersected_edges, 0), num_edges * sizeof (cairo_bo_edge_t)); + _cairo_array_fini (&intersected_edges); + _cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t)); + intersections = _cairo_bentley_ottmann_intersect_edges (edges, num_edges, &intersected_edges); + free (edges); + + if (intersections){ + printf ("Pass %d found %d remaining intersections:\n", passes, intersections); + } else { + if (passes > 3) + for (i = 0; i < passes; i++) + printf ("*"); + printf ("No remainining intersections found after pass %d\n", passes); + } + } + + if (edges_have_an_intersection_quadratic (_cairo_array_index (&intersected_edges, 0), + _cairo_array_num_elements (&intersected_edges))) + printf ("*** FAIL ***\n"); + else + printf ("PASS\n"); + + _cairo_array_fini (&intersected_edges); + + return 0; +} + +#define MAX_RANDOM 300 + +int +main (void) +{ + char random_name[] = "random-XX"; + cairo_bo_edge_t random_edges[MAX_RANDOM], *edge; + unsigned int i, num_random; + test_t *test; + + for (i = 0; i < ARRAY_LENGTH (tests); i++) { + test = &tests[i]; + run_test (test->name, test->edges, test->num_edges); + } + + for (num_random = 0; num_random < MAX_RANDOM; num_random++) { + srand (0); + for (i = 0; i < num_random; i++) { + do { + edge = &random_edges[i]; + edge->line.p1.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p1.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p2.x = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + edge->line.p2.y = (int32_t) (10.0 * (rand() / (RAND_MAX + 1.0))); + if (edge->line.p1.y > edge->line.p2.y) { + int32_t tmp = edge->line.p1.y; + edge->line.p1.y = edge->line.p2.y; + edge->line.p2.y = tmp; + } + } while (edge->line.p1.y == edge->line.p2.y); + } + + sprintf (random_name, "random-%02d", num_random); + + run_test (random_name, random_edges, num_random); + } + + return 0; +} +#endif diff --git a/gfx/cairo/cairo/src/cairo-beos-surface.cpp b/gfx/cairo/cairo/src/cairo-beos-surface.cpp new file mode 100644 index 0000000000..65db0b97ac --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-beos-surface.cpp @@ -0,0 +1,984 @@ +/* vim:set ts=8 sw=4 noet cin: */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Christian Biesinger + * + * 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 Christian Biesinger + * + * + * Contributor(s): + */ + +// This is a C++ file in order to use the C++ BeOS API + +#include "cairoint.h" + +#include "cairo-beos.h" + +#include "cairo-error-private.h" +#include "cairo-image-surface-inline.h" + +#include + +#include +#include +#if 0 +#include +#endif +#include +#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; + + /* + * A view is either attached to a bitmap, a window, or unattached. + * If it is attached to a window, we can copy data out of it using BScreen. + * If it is attached to a bitmap, we can read the bitmap data. + * If it is not attached, it doesn't draw anything, we need not bother. + * + * Since there doesn't seem to be a way to get the bitmap from a view if it + * is attached to one, we have to use a special surface creation function. + */ + + BBitmap* bitmap; + + // If true, surface and view should be deleted when this surface is + // destroyed + bool owns_bitmap_view; +}; + +class AutoLockView { + public: + AutoLockView(BView* view) : mView(view) { + mOK = mView->LockLooper(); + } + + ~AutoLockView() { + if (mOK) + mView->UnlockLooper(); + } + + operator bool() { + return mOK; + } + + private: + BView* mView; + bool mOK; +}; + +static cairo_surface_t * +_cairo_beos_surface_create_internal (BView* view, + BBitmap* bmp, + bool owns_bitmap_view = false); + +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); +} + +static inline cairo_rectangle_int_t +_brect_to_cairo_rectangle (const BRect &rect) +{ + 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 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; + // Using doubles to have non-premultiplied colors + rgb_color be_color = { uint8(color->red * factor), + uint8(color->green * factor), + uint8(color->blue * factor), + uint8(color->alpha * factor) }; + + return be_color; +} + +enum ViewCopyStatus { + OK, + NOT_VISIBLE, // The view or the interest rect is not visible on screen + ERROR // The view was visible, but the rect could not be copied. Probably OOM +}; + +/** + * _cairo_beos_view_to_bitmap: + * @bitmap: [out] The resulting bitmap. + * @rect: [out] The rectangle that was copied, in the view's coordinate system + * @interestRect: If non-null, only this part of the view will be copied (view's coord system). + * + * Gets the contents of the view as a BBitmap*. Caller must delete the bitmap. + **/ +static ViewCopyStatus +_cairo_beos_view_to_bitmap (BView* view, + BBitmap** bitmap, + BRect* rect = NULL, + const BRect* interestRect = NULL) +{ + *bitmap = NULL; + + BWindow* wnd = view->Window(); + // If we have no window, can't do anything + if (!wnd) + return NOT_VISIBLE; + + view->Sync(); + wnd->Sync(); + +#if 0 + // Is it a direct window? + BDirectWindow* directWnd = dynamic_cast(wnd); + if (directWnd) { + // WRITEME + } +#endif + + // Is it visible? If so, we can copy the content off the screen + if (wnd->IsHidden()) + return NOT_VISIBLE; + + BRect rectToCopy(view->Bounds()); + if (interestRect) + rectToCopy = rectToCopy & *interestRect; + + if (!rectToCopy.IsValid()) + return NOT_VISIBLE; + + BScreen screen(wnd); + BRect screenRect(view->ConvertToScreen(rectToCopy)); + screenRect = screenRect & screen.Frame(); + + if (!screen.IsValid()) + return NOT_VISIBLE; + + if (rect) + *rect = view->ConvertFromScreen(screenRect); + + if (screen.GetBitmap(bitmap, false, &screenRect) == B_OK) + return OK; + + return ERROR; +} + +static void +unpremultiply_bgra (unsigned char* data, + int width, + int height, + int stride, + unsigned char* retdata) +{ + unsigned char* end = data + stride * height; + for (unsigned char* in = data, *out = retdata; + in < end; + in += stride, out += stride) + { + 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; + } + } + } +} + +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = (alpha * color) + 0x80; + return ((temp + (temp >> 8)) >> 8); +} + +static unsigned char* +premultiply_bgra (unsigned char* data, + int width, + int height, + int stride) +{ + uint8_t * retdata = reinterpret_cast(_cairo_malloc_ab(height, stride)); + if (!retdata) + return NULL; + + 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 ++) { + 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: + * + * Returns an addrefed image surface for a BBitmap. The bitmap need not outlive + * the surface. + **/ +static cairo_image_surface_t* +_cairo_beos_bitmap_to_surface (BBitmap* bitmap) +{ + color_space format = bitmap->ColorSpace(); + if (format != B_RGB32 && format != B_RGBA32) { + BBitmap bmp(bitmap->Bounds(), B_RGB32, true); + BView view(bitmap->Bounds(), "Cairo bitmap drawing view", + B_FOLLOW_ALL_SIDES, 0); + bmp.AddChild(&view); + + view.LockLooper(); + + view.DrawBitmap(bitmap, BPoint(0.0, 0.0)); + view.Sync(); + + cairo_image_surface_t* imgsurf = _cairo_beos_bitmap_to_surface(&bmp); + + view.UnlockLooper(); + bmp.RemoveChild(&view); + return imgsurf; + } + + cairo_format_t cformat = format == B_RGB32 ? + CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32; + + BRect bounds(bitmap->Bounds()); + unsigned char* bits = reinterpret_cast(bitmap->Bits()); + int width = bounds.IntegerWidth() + 1; + int height = bounds.IntegerHeight() + 1; + unsigned char* premultiplied; + if (cformat == CAIRO_FORMAT_ARGB32) { + premultiplied = premultiply_bgra (bits, width, height, + bitmap->BytesPerRow()); + } else { + premultiplied = reinterpret_cast( + _cairo_malloc_ab(bitmap->BytesPerRow(), height)); + if (premultiplied) + memcpy(premultiplied, bits, bitmap->BytesPerRow() * height); + } + if (!premultiplied) + 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, + cformat, + width, + height, + bitmap->BytesPerRow())); + if (surf->base.status) + free(premultiplied); + else + _cairo_image_surface_assume_ownership_of_data(surf); + return surf; +} + +/** + * _cairo_image_surface_to_bitmap: + * + * Converts an image surface to a BBitmap. The return value must be freed with + * delete. + **/ +static BBitmap* +_cairo_image_surface_to_bitmap (cairo_image_surface_t* surface) +{ + BRect size(0.0, 0.0, surface->width - 1, surface->height - 1); + switch (surface->format) { + case CAIRO_FORMAT_ARGB32: { + BBitmap* data = new BBitmap(size, B_RGBA32); + unpremultiply_bgra (surface->data, + surface->width, + surface->height, + surface->stride, + reinterpret_cast(data->Bits())); + return data; + } + case CAIRO_FORMAT_RGB24: { + BBitmap* data = new BBitmap(size, B_RGB32); + memcpy(data->Bits(), surface->data, surface->height * surface->stride); + return data; + } + default: + assert(0); + return NULL; + } +} + +/** + * _cairo_op_to_be_op: + * + * Converts a cairo drawing operator to a beos drawing_mode. Returns true if + * the operator could be converted, false otherwise. + **/ +static bool +_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_ADD: + // Does not actually work + // XXX This is a fundamental compositing operator, it has to work! +#if 1 + return false; +#else + *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_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_SATURATE: + + default: + return false; + } +} + +static cairo_surface_t * +_cairo_beos_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + + if (width <= 0) + width = 1; + if (height <= 0) + height = 1; + + BRect rect(0.0, 0.0, width - 1, height - 1); + BBitmap* bmp; + switch (content) { + case CAIRO_CONTENT_ALPHA: + return NULL; + case CAIRO_CONTENT_COLOR_ALPHA: + bmp = new BBitmap(rect, B_RGBA32, true); + break; + case CAIRO_CONTENT_COLOR: + // Match the color depth + if (surface->bitmap) { + color_space space = surface->bitmap->ColorSpace(); + // No alpha was requested -> make sure not to return + // a surface with alpha + if (space == B_RGBA32) + space = B_RGB32; + if (space == B_RGBA15) + space = B_RGB15; + bmp = new BBitmap(rect, space, true); + } else { + BScreen scr(surface->view->Window()); + color_space space = B_RGB32; + if (scr.IsValid()) + space = scr.ColorSpace(); + bmp = new BBitmap(rect, space, true); + } + break; + default: + ASSERT_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); +} + +static cairo_status_t +_cairo_beos_surface_finish (void *abstract_surface) +{ + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + if (surface->owns_bitmap_view) { + if (surface->bitmap) + surface->bitmap->RemoveChild(surface->view); + + delete surface->view; + delete surface->bitmap; + + surface->view = NULL; + surface->bitmap = NULL; + } + + cairo_region_destroy (surface->clip_region); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_beos_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + AutoLockView locker(surface->view); + if (!locker) + return CAIRO_STATUS_NO_MEMORY; /// XXX not exactly right, but what can we do? + + + surface->view->Sync(); + + if (surface->bitmap) { + *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; + } + + BBitmap* bmp; + 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 (unlikely ((*image_out)->base.status)) { + delete bmp; + return (*image_out)->base.status; + } + *image_extra = bmp; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_beos_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); + + 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_int_t *interest_rect, + cairo_image_surface_t **image_out, + cairo_rectangle_int_t *image_rect, + void **image_extra) +{ + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + + AutoLockView locker(surface->view); + if (!locker) { + *image_out = NULL; + *image_extra = NULL; + 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 (unlikely ((*image_out)->base.status)) + return (*image_out)->base.status; + + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = (*image_out)->width; + image_rect->height = (*image_out)->height; + + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + BRect b_interest_rect (_cairo_rectangle_to_brect (interest_rect)); + + BRect rect; + BBitmap* bitmap; + ViewCopyStatus status = _cairo_beos_view_to_bitmap(surface->view, &bitmap, + &rect, &b_interest_rect); + if (status == NOT_VISIBLE) { + *image_out = NULL; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + if (status == ERROR) + return CAIRO_STATUS_NO_MEMORY; + + *image_rect = _brect_to_cairo_rectangle(rect); + *image_out = _cairo_beos_bitmap_to_surface(bitmap); + delete bitmap; + if (unlikely ((*image_out)->base.status)) + return (*image_out)->base.status; + + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + + +static void +_cairo_beos_surface_release_dest_image (void *abstract_surface, + cairo_rectangle_int_t *intersect_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + + AutoLockView locker(surface->view); + if (!locker) + return; + + BBitmap* bitmap_to_draw = _cairo_image_surface_to_bitmap(image); + surface->view->PushState(); + + surface->view->SetDrawingMode(B_OP_COPY); + + surface->view->DrawBitmap (bitmap_to_draw, + _cairo_rectangle_to_brect (image_rect)); + + surface->view->PopState(); + + delete bitmap_to_draw; + cairo_surface_destroy(&image->base); +} + +static cairo_int_status_t +_cairo_beos_surface_composite (cairo_operator_t op, + cairo_pattern_t *src, + cairo_pattern_t *mask, + void *dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_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; + + drawing_mode mode; + if (!_cairo_op_to_be_op(op, &mode)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + // XXX Masks are not yet supported + if (mask) + return CAIRO_INT_STATUS_UNSUPPORTED; + + // XXX should eventually support the others + if (src->type != CAIRO_PATTERN_TYPE_SURFACE || + src->extend != CAIRO_EXTEND_NONE) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + // Can we maybe support other matrices as well? (scale? if the filter is right) + int itx, ity; + 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, + src_y + ity + height - 1); + BRect dstRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1); + + cairo_surface_t* src_surface = reinterpret_cast(src)-> + surface; + + // Get a bitmap + BBitmap* bmp = NULL; + bool free_bmp = false; + if (_cairo_surface_is_image(src_surface)) { + cairo_image_surface_t* img_surface = + reinterpret_cast(src_surface); + + bmp = _cairo_image_surface_to_bitmap(img_surface); + free_bmp = true; + } else if (src_surface->backend == surface->base.backend) { + cairo_beos_surface_t *beos_surface = + reinterpret_cast(src_surface); + if (beos_surface->bitmap) { + AutoLockView locker(beos_surface->view); + if (locker) + beos_surface->view->Sync(); + bmp = beos_surface->bitmap; + } else { + _cairo_beos_view_to_bitmap(surface->view, &bmp); + free_bmp = true; + } + } + + if (!bmp) + return CAIRO_INT_STATUS_UNSUPPORTED; + + // So, BeOS seems to screw up painting an opaque bitmap onto a + // translucent one (it makes them partly transparent). Just return + // unsupported. + if (bmp->ColorSpace() == B_RGB32 && surface->bitmap && + surface->bitmap->ColorSpace() == B_RGBA32 && + (mode == B_OP_COPY || mode == B_OP_ALPHA)) + { + if (free_bmp) + delete bmp; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + // Draw it on screen. + surface->view->PushState(); + + // If our image rect is only a subrect of the desired size, and we + // aren't using B_OP_ALPHA, then we need to fill the rect first. + if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) { + rgb_color black = { 0, 0, 0, 0 }; + + surface->view->SetDrawingMode(mode); + surface->view->SetHighColor(black); + surface->view->FillRect(dstRect); + } + + if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) { + mode = B_OP_COPY; + } + surface->view->SetDrawingMode(mode); + + if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32) + surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); + else + surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); + + surface->view->DrawBitmap(bmp, srcRect, dstRect); + + surface->view->PopState(); + + if (free_bmp) + delete bmp; + + return CAIRO_INT_STATUS_SUCCESS; +} + + +static cairo_int_status_t +_cairo_beos_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects, + cairo_region_t *clip_region) +{ + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + cairo_int_status_t status; + + if (num_rects <= 0) + return CAIRO_INT_STATUS_SUCCESS; + + AutoLockView locker(surface->view); + if (!locker) + return CAIRO_INT_STATUS_SUCCESS; + + drawing_mode mode; + 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) + mode = B_OP_COPY; + + // For CAIRO_OPERATOR_SOURCE, cairo expects us to use the premultiplied + // color info. This is only relevant when drawing into an rgb24 buffer + // (as for others, we can convert when asked for the image) + if (mode == B_OP_COPY && be_color.alpha != 0xFF && + (!surface->bitmap || surface->bitmap->ColorSpace() != B_RGBA32)) + { + be_color.red = color->red_short >> 8; + be_color.green = color->green_short >> 8; + be_color.blue = color->blue_short >> 8; + } + + surface->view->PushState(); + + surface->view->SetDrawingMode(mode); + surface->view->SetHighColor(be_color); + if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32) + surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); + else + surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); + + 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_bool_t +_cairo_beos_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_beos_surface_t *surface = reinterpret_cast( + abstract_surface); + AutoLockView locker(surface->view); + if (!locker) + return FALSE; + + *rectangle = _brect_to_cairo_rectangle (surface->view->Bounds()); + return TRUE; +} + +static const struct _cairo_surface_backend cairo_beos_surface_backend = { + CAIRO_SURFACE_TYPE_BEOS, + _cairo_beos_surface_create_similar, + _cairo_beos_surface_finish, + _cairo_beos_surface_acquire_source_image, + _cairo_beos_surface_release_source_image, + _cairo_beos_surface_acquire_dest_image, + _cairo_beos_surface_release_dest_image, + NULL, /* clone_similar */ + _cairo_beos_surface_composite, /* composite */ + _cairo_beos_surface_fill_rectangles, + NULL, /* composite_trapezoids */ + NULL, /* create_span_renderer */ + NULL, /* check_span_renderer */ + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_beos_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 */ +}; + +static cairo_surface_t * +_cairo_beos_surface_create_internal (BView* view, + BBitmap* bmp, + bool owns_bitmap_view) +{ + // Must use malloc, because cairo code will use free() on the surface + cairo_beos_surface_t *surface = static_cast( + malloc(sizeof(cairo_beos_surface_t))); + if (surface == NULL) { + _cairo_error (CAIRO_STATUS_NO_MEMORY); + return const_cast(&_cairo_surface_nil); + } + + cairo_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, + NULL, /* device */ + content); + + surface->view = view; + surface->bitmap = bmp; + surface->owns_bitmap_view = owns_bitmap_view; + + surface->clip_region = NULL; + + return &surface->base; +} + +/** + * cairo_beos_surface_create: + * @view: The view to draw on + * + * Creates a Cairo surface that draws onto a BeOS BView. + * 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) +{ + return cairo_beos_surface_create_for_bitmap(view, NULL); +} + +/** + * cairo_beos_surface_create_for_bitmap: + * @view: The view to draw on + * @bmp: The bitmap to which the view is attached + * + * Creates a Cairo surface that draws onto a BeOS BView which is attached to a + * BBitmap. + * The caller must ensure that the view and the bitmap do not get deleted + * before the surface. + * + * For views that draw to a bitmap (as opposed to a screen), use this function + * rather than cairo_beos_surface_create(). Not using this function WILL lead to + * incorrect behaviour. + * + * 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, + BBitmap* bmp) +{ + return _cairo_beos_surface_create_internal(view, bmp); +} diff --git a/gfx/cairo/cairo/src/cairo-beos.h b/gfx/cairo/cairo/src/cairo-beos.h new file mode 100644 index 0000000000..fdb89a6c4d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-beos.h @@ -0,0 +1,60 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Christian Biesinger + * + * 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 Christian Biesinger + * + * + * Contributor(s): + */ + +#ifndef CAIRO_BEOS_H +#define CAIRO_BEOS_H + +#include "cairo.h" + +#if CAIRO_HAS_BEOS_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_beos_surface_create (BView* view); + +cairo_public cairo_surface_t * +cairo_beos_surface_create_for_bitmap (BView* view, + BBitmap* bmp); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_BEOS_SURFACE */ +# error Cairo was not compiled with support for the beos backend +#endif /* CAIRO_HAS_BEOS_SURFACE */ + +#endif /* CAIRO_BEOS_H */ diff --git a/gfx/cairo/cairo/src/cairo-botor-scan-converter.c b/gfx/cairo/cairo/src/cairo-botor-scan-converter.c new file mode 100644 index 0000000000..438f96bb86 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-botor-scan-converter.c @@ -0,0 +1,2202 @@ +/* + * Copyright © 2004 Carl Worth + * Copyright © 2006 Red Hat, Inc. + * Copyright © 2007 David Turner + * Copyright © 2008 M Joonas Pihlaja + * 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. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Carl Worth + * + * Contributor(s): + * Carl D. Worth + * M Joonas Pihlaja + * Chris Wilson + */ + +/* Provide definitions for standalone compilation */ +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-list-inline.h" +#include "cairo-freelist-private.h" +#include "cairo-combsort-inline.h" + +#include + +#define STEP_X CAIRO_FIXED_ONE +#define STEP_Y CAIRO_FIXED_ONE +#define UNROLL3(x) x x x + +#define STEP_XY (2*STEP_X*STEP_Y) /* Unit area in the step. */ +#define AREA_TO_ALPHA(c) (((c)*255 + STEP_XY/2) / STEP_XY) + +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; + +struct quorem { + cairo_fixed_t quo; + cairo_fixed_t rem; +}; + +struct run { + struct run *next; + int sign; + cairo_fixed_t y; +}; + +typedef struct edge { + cairo_list_t link; + + cairo_edge_t edge; + + /* Current x coordinate and advancement. + * Initialised to the x coordinate of the top of the + * edge. The quotient is in cairo_fixed_t units and the + * remainder is mod dy in cairo_fixed_t units. + */ + cairo_fixed_t dy; + struct quorem x; + struct quorem dxdy; + struct quorem dxdy_full; + + cairo_bool_t vertical; + unsigned int flags; + + int current_sign; + struct run *runs; +} edge_t; + +enum { + START = 0x1, + STOP = 0x2, +}; + +/* 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 { + EVENT_TYPE_STOP, + EVENT_TYPE_INTERSECTION, + EVENT_TYPE_START +} event_type_t; + +typedef struct _event { + cairo_fixed_t y; + event_type_t type; +} event_t; + +typedef struct _start_event { + cairo_fixed_t y; + event_type_t type; + edge_t *edge; +} start_event_t; + +typedef struct _queue_event { + cairo_fixed_t y; + event_type_t type; + edge_t *e1; + edge_t *e2; +} queue_event_t; + +typedef struct _pqueue { + int size, max_size; + + event_t **elements; + event_t *elements_embedded[1024]; +} pqueue_t; + +struct cell { + struct cell *prev; + struct cell *next; + int x; + int uncovered_area; + int covered_height; +}; + +typedef struct _sweep_line { + cairo_list_t active; + cairo_list_t stopped; + cairo_list_t *insert_cursor; + cairo_bool_t is_vertical; + + cairo_fixed_t current_row; + cairo_fixed_t current_subrow; + + struct coverage { + struct cell head; + struct cell tail; + + struct cell *cursor; + int count; + + cairo_freepool_t pool; + } coverage; + + struct event_queue { + pqueue_t pq; + event_t **start_events; + + cairo_freepool_t pool; + } queue; + + cairo_freepool_t runs; + + jmp_buf unwind; +} sweep_line_t; + +cairo_always_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--; + qr.rem += b; + } + return qr; +} + +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--; + qr.rem += b; + } + return qr; +} + +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; +} + +/* + * 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_edge_t *a, + const cairo_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->line.p1.x < a->line.p2.x) { + amin = a->line.p1.x; + amax = a->line.p2.x; + } else { + amin = a->line.p2.x; + amax = a->line.p1.x; + } + if (b->line.p1.x < b->line.p2.x) { + bmin = b->line.p1.x; + bmax = b->line.p2.x; + } else { + bmin = b->line.p2.x; + bmax = b->line.p1.x; + } + if (amax < bmin) return -1; + if (amin > bmax) return +1; + } + + ady = a->line.p2.y - a->line.p1.y; + adx = a->line.p2.x - a->line.p1.x; + if (adx == 0) + have_dx_adx_bdx &= ~HAVE_ADX; + + bdy = b->line.p2.y - b->line.p1.y; + bdx = b->line.p2.x - b->line.p1.x; + if (bdx == 0) + have_dx_adx_bdx &= ~HAVE_BDX; + + dx = a->line.p1.x - b->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->line.p1.y) +#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->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->line.p1.y == b->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->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->line.p1.y, bdx); + + return _cairo_int64_cmp (bdy_dx, dy_bdx); + } + case HAVE_ALL: + /* XXX try comparing (a->line.p2.x - b->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_edge_t *a, + int32_t y, + int32_t x) +{ + int32_t adx, ady; + int32_t dx, dy; + cairo_int64_t L, R; + + if (a->line.p1.x <= a->line.p2.x) { + if (x < a->line.p1.x) + return 1; + if (x > a->line.p2.x) + return -1; + } else { + if (x < a->line.p2.x) + return 1; + if (x > a->line.p1.x) + return -1; + } + + adx = a->line.p2.x - a->line.p1.x; + dx = x - a->line.p1.x; + + if (adx == 0) + return -dx; + if (dx == 0 || (adx ^ dx) < 0) + return adx; + + dy = y - a->line.p1.y; + ady = a->line.p2.y - a->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_edge_t *a, + const cairo_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; + + /* XXX given we have x and dx? */ + + if (y == a->line.p1.y) + ax = a->line.p1.x; + else if (y == a->line.p2.y) + ax = a->line.p2.x; + else + have_ax_bx &= ~HAVE_AX; + + if (y == b->line.p1.y) + bx = b->line.p1.x; + else if (y == b->line.p2.y) + bx = b->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 +slope_compare (const edge_t *a, + const edge_t *b) +{ + cairo_int64_t L, R; + int cmp; + + cmp = a->dxdy.quo - b->dxdy.quo; + if (cmp) + return cmp; + + if (a->dxdy.rem == 0) + return -b->dxdy.rem; + if (b->dxdy.rem == 0) + return a->dxdy.rem; + + L = _cairo_int32x32_64_mul (b->dy, a->dxdy.rem); + R = _cairo_int32x32_64_mul (a->dy, b->dxdy.rem); + return _cairo_int64_cmp (L, R); +} + +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 inline int +sweep_line_compare_edges (const edge_t *a, + const edge_t *b, + cairo_fixed_t y) +{ + int cmp; + + if (line_equal (&a->edge.line, &b->edge.line)) + return 0; + + cmp = edges_compare_x_for_y (&a->edge, &b->edge, y); + if (cmp) + return cmp; + + return slope_compare (a, b); +} + +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 (const edge_t *a, const 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)) { + /* compute ceiling away from zero */ + qr.quo = _cairo_int64_add (qr.quo, + _cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1)); + intersection->y.exactness = INEXACT; + } +#endif + intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo); + + return TRUE; +} + +static int +bo_intersect_ordinate_32_compare (int32_t a, int32_t b, int exactness) +{ + int cmp; + + /* First compare the quotient */ + cmp = a - b; + if (cmp) + return cmp; + + /* With quotient identical, if remainder is 0 then compare equal */ + /* Otherwise, the non-zero remainder makes a > b */ + return -(INEXACT == 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 +bo_edge_contains_intersect_point (const 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 = bo_intersect_ordinate_32_compare (point->y.ordinate, + edge->edge.top, + point->y.exactness); + if (cmp_top < 0) + return FALSE; + + cmp_bottom = bo_intersect_ordinate_32_compare (point->y.ordinate, + edge->edge.bottom, + point->y.exactness); + if (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 bo_intersect_ordinate_32_compare (top_x, point->x.ordinate, point->x.exactness) < 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 bo_intersect_ordinate_32_compare (point->x.ordinate, bot_x, point->x.exactness) < 0; + } +} + +static cairo_bool_t +edge_intersect (const edge_t *a, + const edge_t *b, + cairo_point_t *intersection) +{ + cairo_bo_intersect_point_t quorem; + + if (! intersect_lines (a, b, &quorem)) + return FALSE; + + if (a->edge.top != a->edge.line.p1.y || a->edge.bottom != a->edge.line.p2.y) { + if (! bo_edge_contains_intersect_point (a, &quorem)) + return FALSE; + } + + if (b->edge.top != b->edge.line.p1.y || b->edge.bottom != b->edge.line.p2.y) { + if (! 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 +event_compare (const event_t *a, const event_t *b) +{ + return a->y - b->y; +} + +static void +pqueue_init (pqueue_t *pq) +{ + pq->max_size = ARRAY_LENGTH (pq->elements_embedded); + pq->size = 0; + + pq->elements = pq->elements_embedded; +} + +static void +pqueue_fini (pqueue_t *pq) +{ + if (pq->elements != pq->elements_embedded) + free (pq->elements); +} + +static cairo_bool_t +pqueue_grow (pqueue_t *pq) +{ + event_t **new_elements; + pq->max_size *= 2; + + if (pq->elements == pq->elements_embedded) { + new_elements = _cairo_malloc_ab (pq->max_size, + sizeof (event_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 (event_t *)); + if (unlikely (new_elements == NULL)) + return FALSE; + } + + pq->elements = new_elements; + return TRUE; +} + +static inline void +pqueue_push (sweep_line_t *sweep_line, event_t *event) +{ + event_t **elements; + int i, parent; + + if (unlikely (sweep_line->queue.pq.size + 1 == sweep_line->queue.pq.max_size)) { + if (unlikely (! pqueue_grow (&sweep_line->queue.pq))) { + longjmp (sweep_line->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + elements = sweep_line->queue.pq.elements; + for (i = ++sweep_line->queue.pq.size; + i != PQ_FIRST_ENTRY && + event_compare (event, + elements[parent = PQ_PARENT_INDEX (i)]) < 0; + i = parent) + { + elements[i] = elements[parent]; + } + + elements[i] = event; +} + +static inline void +pqueue_pop (pqueue_t *pq) +{ + event_t **elements = pq->elements; + 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 && + event_compare (elements[child+1], + elements[child]) < 0) + { + child++; + } + + if (event_compare (elements[child], tail) >= 0) + break; + + elements[i] = elements[child]; + } + elements[i] = tail; +} + +static inline void +event_insert (sweep_line_t *sweep_line, + event_type_t type, + edge_t *e1, + edge_t *e2, + cairo_fixed_t y) +{ + queue_event_t *event; + + event = _cairo_freepool_alloc (&sweep_line->queue.pool); + if (unlikely (event == NULL)) { + longjmp (sweep_line->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + event->y = y; + event->type = type; + event->e1 = e1; + event->e2 = e2; + + pqueue_push (sweep_line, (event_t *) event); +} + +static void +event_delete (sweep_line_t *sweep_line, + event_t *event) +{ + _cairo_freepool_free (&sweep_line->queue.pool, event); +} + +static inline event_t * +event_next (sweep_line_t *sweep_line) +{ + event_t *event, *cmp; + + event = sweep_line->queue.pq.elements[PQ_FIRST_ENTRY]; + cmp = *sweep_line->queue.start_events; + if (event == NULL || + (cmp != NULL && event_compare (cmp, event) < 0)) + { + event = cmp; + sweep_line->queue.start_events++; + } + else + { + pqueue_pop (&sweep_line->queue.pq); + } + + return event; +} + +CAIRO_COMBSORT_DECLARE (start_event_sort, event_t *, event_compare) + +static inline void +event_insert_stop (sweep_line_t *sweep_line, + edge_t *edge) +{ + event_insert (sweep_line, + EVENT_TYPE_STOP, + edge, NULL, + edge->edge.bottom); +} + +static inline void +event_insert_if_intersect_below_current_y (sweep_line_t *sweep_line, + edge_t *left, + edge_t *right) +{ + cairo_point_t intersection; + + /* start points intersect */ + if (left->edge.line.p1.x == right->edge.line.p1.x && + left->edge.line.p1.y == right->edge.line.p1.y) + { + return; + } + + /* end points intersect, process DELETE events first */ + if (left->edge.line.p2.x == right->edge.line.p2.x && + left->edge.line.p2.y == right->edge.line.p2.y) + { + return; + } + + if (slope_compare (left, right) <= 0) + return; + + if (! edge_intersect (left, right, &intersection)) + return; + + event_insert (sweep_line, + EVENT_TYPE_INTERSECTION, + left, right, + intersection.y); +} + +static inline edge_t * +link_to_edge (cairo_list_t *link) +{ + return (edge_t *) link; +} + +static void +sweep_line_insert (sweep_line_t *sweep_line, + edge_t *edge) +{ + cairo_list_t *pos; + cairo_fixed_t y = sweep_line->current_subrow; + + pos = sweep_line->insert_cursor; + if (pos == &sweep_line->active) + pos = sweep_line->active.next; + if (pos != &sweep_line->active) { + int cmp; + + cmp = sweep_line_compare_edges (link_to_edge (pos), + edge, + y); + if (cmp < 0) { + while (pos->next != &sweep_line->active && + sweep_line_compare_edges (link_to_edge (pos->next), + edge, + y) < 0) + { + pos = pos->next; + } + } else if (cmp > 0) { + do { + pos = pos->prev; + } while (pos != &sweep_line->active && + sweep_line_compare_edges (link_to_edge (pos), + edge, + y) > 0); + } + } + cairo_list_add (&edge->link, pos); + sweep_line->insert_cursor = &edge->link; +} + +inline static void +coverage_rewind (struct coverage *cells) +{ + cells->cursor = &cells->head; +} + +static void +coverage_init (struct coverage *cells) +{ + _cairo_freepool_init (&cells->pool, + sizeof (struct cell)); + cells->head.prev = NULL; + cells->head.next = &cells->tail; + cells->head.x = INT_MIN; + cells->tail.prev = &cells->head; + cells->tail.next = NULL; + cells->tail.x = INT_MAX; + cells->count = 0; + coverage_rewind (cells); +} + +static void +coverage_fini (struct coverage *cells) +{ + _cairo_freepool_fini (&cells->pool); +} + +inline static void +coverage_reset (struct coverage *cells) +{ + cells->head.next = &cells->tail; + cells->tail.prev = &cells->head; + cells->count = 0; + _cairo_freepool_reset (&cells->pool); + coverage_rewind (cells); +} + +static struct cell * +coverage_alloc (sweep_line_t *sweep_line, + struct cell *tail, + int x) +{ + struct cell *cell; + + cell = _cairo_freepool_alloc (&sweep_line->coverage.pool); + if (unlikely (NULL == cell)) { + longjmp (sweep_line->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + tail->prev->next = cell; + cell->prev = tail->prev; + cell->next = tail; + tail->prev = cell; + cell->x = x; + cell->uncovered_area = 0; + cell->covered_height = 0; + sweep_line->coverage.count++; + return cell; +} + +inline static struct cell * +coverage_find (sweep_line_t *sweep_line, int x) +{ + struct cell *cell; + + cell = sweep_line->coverage.cursor; + if (unlikely (cell->x > x)) { + do { + if (cell->prev->x < x) + break; + cell = cell->prev; + } while (TRUE); + } else { + if (cell->x == x) + return cell; + + do { + UNROLL3({ + cell = cell->next; + if (cell->x >= x) + break; + }); + } while (TRUE); + } + + if (cell->x != x) + cell = coverage_alloc (sweep_line, cell, x); + + return sweep_line->coverage.cursor = cell; +} + +static void +coverage_render_cells (sweep_line_t *sweep_line, + cairo_fixed_t left, cairo_fixed_t right, + cairo_fixed_t y1, cairo_fixed_t y2, + int sign) +{ + int fx1, fx2; + int ix1, ix2; + int dx, dy; + + /* Orient the edge left-to-right. */ + dx = right - left; + if (dx >= 0) { + ix1 = _cairo_fixed_integer_part (left); + fx1 = _cairo_fixed_fractional_part (left); + + ix2 = _cairo_fixed_integer_part (right); + fx2 = _cairo_fixed_fractional_part (right); + + dy = y2 - y1; + } else { + ix1 = _cairo_fixed_integer_part (right); + fx1 = _cairo_fixed_fractional_part (right); + + ix2 = _cairo_fixed_integer_part (left); + fx2 = _cairo_fixed_fractional_part (left); + + dx = -dx; + sign = -sign; + dy = y1 - y2; + y1 = y2 - dy; + y2 = y1 + dy; + } + + /* Add coverage for all pixels [ix1,ix2] on this row crossed + * by the edge. */ + { + struct quorem y = floored_divrem ((STEP_X - fx1)*dy, dx); + struct cell *cell; + + cell = sweep_line->coverage.cursor; + if (cell->x != ix1) { + if (unlikely (cell->x > ix1)) { + do { + if (cell->prev->x < ix1) + break; + cell = cell->prev; + } while (TRUE); + } else do { + UNROLL3({ + if (cell->x >= ix1) + break; + cell = cell->next; + }); + } while (TRUE); + + if (cell->x != ix1) + cell = coverage_alloc (sweep_line, cell, ix1); + } + + cell->uncovered_area += sign * y.quo * (STEP_X + fx1); + cell->covered_height += sign * y.quo; + y.quo += y1; + + cell = cell->next; + if (cell->x != ++ix1) + cell = coverage_alloc (sweep_line, cell, ix1); + if (ix1 < ix2) { + struct quorem dydx_full = floored_divrem (STEP_X*dy, dx); + + do { + cairo_fixed_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->covered_height += y_skip; + cell->uncovered_area += y_skip*STEP_X; + + cell = cell->next; + if (cell->x != ++ix1) + cell = coverage_alloc (sweep_line, cell, ix1); + } while (ix1 != ix2); + } + cell->uncovered_area += sign*(y2 - y.quo)*fx2; + cell->covered_height += sign*(y2 - y.quo); + sweep_line->coverage.cursor = cell; + } +} + +inline static void +full_inc_edge (edge_t *edge) +{ + edge->x.quo += edge->dxdy_full.quo; + edge->x.rem += edge->dxdy_full.rem; + if (edge->x.rem >= 0) { + ++edge->x.quo; + edge->x.rem -= edge->dy; + } +} + +static void +full_add_edge (sweep_line_t *sweep_line, edge_t *edge, int sign) +{ + struct cell *cell; + cairo_fixed_t x1, x2; + int ix1, ix2; + int frac; + + edge->current_sign = sign; + + ix1 = _cairo_fixed_integer_part (edge->x.quo); + + if (edge->vertical) { + frac = _cairo_fixed_fractional_part (edge->x.quo); + cell = coverage_find (sweep_line, ix1); + cell->covered_height += sign * STEP_Y; + cell->uncovered_area += sign * 2 * frac * STEP_Y; + return; + } + + x1 = edge->x.quo; + full_inc_edge (edge); + x2 = edge->x.quo; + + ix2 = _cairo_fixed_integer_part (edge->x.quo); + + /* Edge is entirely within a column? */ + if (likely (ix1 == ix2)) { + frac = _cairo_fixed_fractional_part (x1) + + _cairo_fixed_fractional_part (x2); + cell = coverage_find (sweep_line, ix1); + cell->covered_height += sign * STEP_Y; + cell->uncovered_area += sign * frac * STEP_Y; + return; + } + + coverage_render_cells (sweep_line, x1, x2, 0, STEP_Y, sign); +} + +static void +full_nonzero (sweep_line_t *sweep_line) +{ + cairo_list_t *pos; + + sweep_line->is_vertical = TRUE; + pos = sweep_line->active.next; + do { + edge_t *left = link_to_edge (pos), *right; + int winding = left->edge.dir; + + sweep_line->is_vertical &= left->vertical; + + pos = left->link.next; + do { + if (unlikely (pos == &sweep_line->active)) { + full_add_edge (sweep_line, left, +1); + return; + } + + right = link_to_edge (pos); + pos = pos->next; + sweep_line->is_vertical &= right->vertical; + + winding += right->edge.dir; + if (0 == winding) { + if (pos == &sweep_line->active || + link_to_edge (pos)->x.quo != right->x.quo) + { + break; + } + } + + if (! right->vertical) + full_inc_edge (right); + } while (TRUE); + + full_add_edge (sweep_line, left, +1); + full_add_edge (sweep_line, right, -1); + } while (pos != &sweep_line->active); +} + +static void +full_evenodd (sweep_line_t *sweep_line) +{ + cairo_list_t *pos; + + sweep_line->is_vertical = TRUE; + pos = sweep_line->active.next; + do { + edge_t *left = link_to_edge (pos), *right; + int winding = 0; + + sweep_line->is_vertical &= left->vertical; + + pos = left->link.next; + do { + if (pos == &sweep_line->active) { + full_add_edge (sweep_line, left, +1); + return; + } + + right = link_to_edge (pos); + pos = pos->next; + sweep_line->is_vertical &= right->vertical; + + if (++winding & 1) { + if (pos == &sweep_line->active || + link_to_edge (pos)->x.quo != right->x.quo) + { + break; + } + } + + if (! right->vertical) + full_inc_edge (right); + } while (TRUE); + + full_add_edge (sweep_line, left, +1); + full_add_edge (sweep_line, right, -1); + } while (pos != &sweep_line->active); +} + +static void +render_rows (cairo_botor_scan_converter_t *self, + sweep_line_t *sweep_line, + int y, int height, + cairo_span_renderer_t *renderer) +{ + cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; + cairo_half_open_span_t *spans = spans_stack; + struct cell *cell; + int prev_x, cover; + int num_spans; + cairo_status_t status; + + if (unlikely (sweep_line->coverage.count == 0)) { + status = renderer->render_rows (renderer, y, height, NULL, 0); + if (unlikely (status)) + longjmp (sweep_line->unwind, status); + return; + } + + /* Allocate enough spans for the row. */ + + num_spans = 2*sweep_line->coverage.count+2; + if (unlikely (num_spans > ARRAY_LENGTH (spans_stack))) { + spans = _cairo_malloc_ab (num_spans, sizeof (cairo_half_open_span_t)); + if (unlikely (spans == NULL)) { + longjmp (sweep_line->unwind, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + /* Form the spans from the coverage and areas. */ + num_spans = 0; + prev_x = self->xmin; + cover = 0; + cell = sweep_line->coverage.head.next; + do { + int x = cell->x; + int area; + + 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; + } + + cover += cell->covered_height*STEP_X*2; + area = cover - cell->uncovered_area; + + spans[num_spans].x = x; + spans[num_spans].coverage = AREA_TO_ALPHA (area); + ++num_spans; + + prev_x = x + 1; + } while ((cell = cell->next) != &sweep_line->coverage.tail); + + 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; + } + + status = renderer->render_rows (renderer, y, height, spans, num_spans); + + if (unlikely (spans != spans_stack)) + free (spans); + + coverage_reset (&sweep_line->coverage); + + if (unlikely (status)) + longjmp (sweep_line->unwind, status); +} + +static void +full_repeat (sweep_line_t *sweep) +{ + edge_t *edge; + + cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) { + if (edge->current_sign) + full_add_edge (sweep, edge, edge->current_sign); + else if (! edge->vertical) + full_inc_edge (edge); + } +} + +static void +full_reset (sweep_line_t *sweep) +{ + edge_t *edge; + + cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) + edge->current_sign = 0; +} + +static void +full_step (cairo_botor_scan_converter_t *self, + sweep_line_t *sweep_line, + cairo_fixed_t row, + cairo_span_renderer_t *renderer) +{ + int top, bottom; + + top = _cairo_fixed_integer_part (sweep_line->current_row); + bottom = _cairo_fixed_integer_part (row); + if (cairo_list_is_empty (&sweep_line->active)) { + cairo_status_t status; + + status = renderer->render_rows (renderer, top, bottom - top, NULL, 0); + if (unlikely (status)) + longjmp (sweep_line->unwind, status); + + return; + } + + if (self->fill_rule == CAIRO_FILL_RULE_WINDING) + full_nonzero (sweep_line); + else + full_evenodd (sweep_line); + + if (sweep_line->is_vertical || bottom == top + 1) { + render_rows (self, sweep_line, top, bottom - top, renderer); + full_reset (sweep_line); + return; + } + + render_rows (self, sweep_line, top++, 1, renderer); + do { + full_repeat (sweep_line); + render_rows (self, sweep_line, top, 1, renderer); + } while (++top != bottom); + + full_reset (sweep_line); +} + +cairo_always_inline static void +sub_inc_edge (edge_t *edge, + cairo_fixed_t height) +{ + if (height == 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 { + edge->x.quo += height * edge->dxdy.quo; + edge->x.rem += height * edge->dxdy.rem; + if (edge->x.rem >= 0) { + int carry = edge->x.rem / edge->dy + 1; + edge->x.quo += carry; + edge->x.rem -= carry * edge->dy; + } + } +} + +static void +sub_add_run (sweep_line_t *sweep_line, edge_t *edge, int y, int sign) +{ + struct run *run; + + run = _cairo_freepool_alloc (&sweep_line->runs); + if (unlikely (run == NULL)) + longjmp (sweep_line->unwind, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + run->y = y; + run->sign = sign; + run->next = edge->runs; + edge->runs = run; + + edge->current_sign = sign; +} + +inline static cairo_bool_t +edges_coincident (edge_t *left, edge_t *right, cairo_fixed_t y) +{ + /* XXX is compare_x_for_y() worth executing during sub steps? */ + return line_equal (&left->edge.line, &right->edge.line); + //edges_compare_x_for_y (&left->edge, &right->edge, y) >= 0; +} + +static void +sub_nonzero (sweep_line_t *sweep_line) +{ + cairo_fixed_t y = sweep_line->current_subrow; + cairo_fixed_t fy = _cairo_fixed_fractional_part (y); + cairo_list_t *pos; + + pos = sweep_line->active.next; + do { + edge_t *left = link_to_edge (pos), *right; + int winding = left->edge.dir; + + pos = left->link.next; + do { + if (unlikely (pos == &sweep_line->active)) { + if (left->current_sign != +1) + sub_add_run (sweep_line, left, fy, +1); + return; + } + + right = link_to_edge (pos); + pos = pos->next; + + winding += right->edge.dir; + if (0 == winding) { + if (pos == &sweep_line->active || + ! edges_coincident (right, link_to_edge (pos), y)) + { + break; + } + } + + if (right->current_sign) + sub_add_run (sweep_line, right, fy, 0); + } while (TRUE); + + if (left->current_sign != +1) + sub_add_run (sweep_line, left, fy, +1); + if (right->current_sign != -1) + sub_add_run (sweep_line, right, fy, -1); + } while (pos != &sweep_line->active); +} + +static void +sub_evenodd (sweep_line_t *sweep_line) +{ + cairo_fixed_t y = sweep_line->current_subrow; + cairo_fixed_t fy = _cairo_fixed_fractional_part (y); + cairo_list_t *pos; + + pos = sweep_line->active.next; + do { + edge_t *left = link_to_edge (pos), *right; + int winding = 0; + + pos = left->link.next; + do { + if (unlikely (pos == &sweep_line->active)) { + if (left->current_sign != +1) + sub_add_run (sweep_line, left, fy, +1); + return; + } + + right = link_to_edge (pos); + pos = pos->next; + + if (++winding & 1) { + if (pos == &sweep_line->active || + ! edges_coincident (right, link_to_edge (pos), y)) + { + break; + } + } + + if (right->current_sign) + sub_add_run (sweep_line, right, fy, 0); + } while (TRUE); + + if (left->current_sign != +1) + sub_add_run (sweep_line, left, fy, +1); + if (right->current_sign != -1) + sub_add_run (sweep_line, right, fy, -1); + } while (pos != &sweep_line->active); +} + +cairo_always_inline static void +sub_step (cairo_botor_scan_converter_t *self, + sweep_line_t *sweep_line) +{ + if (cairo_list_is_empty (&sweep_line->active)) + return; + + if (self->fill_rule == CAIRO_FILL_RULE_WINDING) + sub_nonzero (sweep_line); + else + sub_evenodd (sweep_line); +} + +static void +coverage_render_runs (sweep_line_t *sweep, edge_t *edge, + cairo_fixed_t y1, cairo_fixed_t y2) +{ + struct run tail; + struct run *run = &tail; + + tail.next = NULL; + tail.y = y2; + + /* Order the runs top->bottom */ + while (edge->runs) { + struct run *r; + + r = edge->runs; + edge->runs = r->next; + r->next = run; + run = r; + } + + if (run->y > y1) + sub_inc_edge (edge, run->y - y1); + + do { + cairo_fixed_t x1, x2; + + y1 = run->y; + y2 = run->next->y; + + x1 = edge->x.quo; + if (y2 - y1 == STEP_Y) + full_inc_edge (edge); + else + sub_inc_edge (edge, y2 - y1); + x2 = edge->x.quo; + + if (run->sign) { + int ix1, ix2; + + ix1 = _cairo_fixed_integer_part (x1); + ix2 = _cairo_fixed_integer_part (x2); + + /* Edge is entirely within a column? */ + if (likely (ix1 == ix2)) { + struct cell *cell; + int frac; + + frac = _cairo_fixed_fractional_part (x1) + + _cairo_fixed_fractional_part (x2); + cell = coverage_find (sweep, ix1); + cell->covered_height += run->sign * (y2 - y1); + cell->uncovered_area += run->sign * (y2 - y1) * frac; + } else { + coverage_render_cells (sweep, x1, x2, y1, y2, run->sign); + } + } + + run = run->next; + } while (run->next != NULL); +} + +static void +coverage_render_vertical_runs (sweep_line_t *sweep, edge_t *edge, cairo_fixed_t y2) +{ + struct cell *cell; + struct run *run; + int height = 0; + + for (run = edge->runs; run != NULL; run = run->next) { + if (run->sign) + height += run->sign * (y2 - run->y); + y2 = run->y; + } + + cell = coverage_find (sweep, _cairo_fixed_integer_part (edge->x.quo)); + cell->covered_height += height; + cell->uncovered_area += 2 * _cairo_fixed_fractional_part (edge->x.quo) * height; +} + +cairo_always_inline static void +sub_emit (cairo_botor_scan_converter_t *self, + sweep_line_t *sweep, + cairo_span_renderer_t *renderer) +{ + edge_t *edge; + + sub_step (self, sweep); + + /* convert the runs into coverages */ + + cairo_list_foreach_entry (edge, edge_t, &sweep->active, link) { + if (edge->runs == NULL) { + if (! edge->vertical) { + if (edge->flags & START) { + sub_inc_edge (edge, + STEP_Y - _cairo_fixed_fractional_part (edge->edge.top)); + edge->flags &= ~START; + } else + full_inc_edge (edge); + } + } else { + if (edge->vertical) { + coverage_render_vertical_runs (sweep, edge, STEP_Y); + } else { + int y1 = 0; + if (edge->flags & START) { + y1 = _cairo_fixed_fractional_part (edge->edge.top); + edge->flags &= ~START; + } + coverage_render_runs (sweep, edge, y1, STEP_Y); + } + } + edge->current_sign = 0; + edge->runs = NULL; + } + + cairo_list_foreach_entry (edge, edge_t, &sweep->stopped, link) { + int y2 = _cairo_fixed_fractional_part (edge->edge.bottom); + if (edge->vertical) { + coverage_render_vertical_runs (sweep, edge, y2); + } else { + int y1 = 0; + if (edge->flags & START) + y1 = _cairo_fixed_fractional_part (edge->edge.top); + coverage_render_runs (sweep, edge, y1, y2); + } + } + cairo_list_init (&sweep->stopped); + + _cairo_freepool_reset (&sweep->runs); + + render_rows (self, sweep, + _cairo_fixed_integer_part (sweep->current_row), 1, + renderer); +} + +static void +sweep_line_init (sweep_line_t *sweep_line, + event_t **start_events, + int num_events) +{ + cairo_list_init (&sweep_line->active); + cairo_list_init (&sweep_line->stopped); + sweep_line->insert_cursor = &sweep_line->active; + + sweep_line->current_row = INT32_MIN; + sweep_line->current_subrow = INT32_MIN; + + coverage_init (&sweep_line->coverage); + _cairo_freepool_init (&sweep_line->runs, sizeof (struct run)); + + start_event_sort (start_events, num_events); + start_events[num_events] = NULL; + + sweep_line->queue.start_events = start_events; + + _cairo_freepool_init (&sweep_line->queue.pool, + sizeof (queue_event_t)); + pqueue_init (&sweep_line->queue.pq); + sweep_line->queue.pq.elements[PQ_FIRST_ENTRY] = NULL; +} + +static void +sweep_line_delete (sweep_line_t *sweep_line, + edge_t *edge) +{ + if (sweep_line->insert_cursor == &edge->link) + sweep_line->insert_cursor = edge->link.prev; + + cairo_list_del (&edge->link); + if (edge->runs) + cairo_list_add_tail (&edge->link, &sweep_line->stopped); + edge->flags |= STOP; +} + +static void +sweep_line_swap (sweep_line_t *sweep_line, + edge_t *left, + edge_t *right) +{ + right->link.prev = left->link.prev; + left->link.next = right->link.next; + right->link.next = &left->link; + left->link.prev = &right->link; + left->link.next->prev = &left->link; + right->link.prev->next = &right->link; +} + +static void +sweep_line_fini (sweep_line_t *sweep_line) +{ + pqueue_fini (&sweep_line->queue.pq); + _cairo_freepool_fini (&sweep_line->queue.pool); + coverage_fini (&sweep_line->coverage); + _cairo_freepool_fini (&sweep_line->runs); +} + +static cairo_status_t +botor_generate (cairo_botor_scan_converter_t *self, + event_t **start_events, + cairo_span_renderer_t *renderer) +{ + cairo_status_t status; + sweep_line_t sweep_line; + cairo_fixed_t ybot; + event_t *event; + cairo_list_t *left, *right; + edge_t *e1, *e2; + int bottom; + + sweep_line_init (&sweep_line, start_events, self->num_edges); + if ((status = setjmp (sweep_line.unwind))) + goto unwind; + + ybot = self->extents.p2.y; + sweep_line.current_subrow = self->extents.p1.y; + sweep_line.current_row = _cairo_fixed_floor (self->extents.p1.y); + event = *sweep_line.queue.start_events++; + do { + /* Can we process a full step in one go? */ + if (event->y >= sweep_line.current_row + STEP_Y) { + bottom = _cairo_fixed_floor (event->y); + full_step (self, &sweep_line, bottom, renderer); + sweep_line.current_row = bottom; + sweep_line.current_subrow = bottom; + } + + do { + if (event->y > sweep_line.current_subrow) { + sub_step (self, &sweep_line); + sweep_line.current_subrow = event->y; + } + + do { + /* Update the active list using Bentley-Ottmann */ + switch (event->type) { + case EVENT_TYPE_START: + e1 = ((start_event_t *) event)->edge; + + sweep_line_insert (&sweep_line, e1); + event_insert_stop (&sweep_line, e1); + + left = e1->link.prev; + right = e1->link.next; + + if (left != &sweep_line.active) { + event_insert_if_intersect_below_current_y (&sweep_line, + link_to_edge (left), e1); + } + + if (right != &sweep_line.active) { + event_insert_if_intersect_below_current_y (&sweep_line, + e1, link_to_edge (right)); + } + + break; + + case EVENT_TYPE_STOP: + e1 = ((queue_event_t *) event)->e1; + event_delete (&sweep_line, event); + + left = e1->link.prev; + right = e1->link.next; + + sweep_line_delete (&sweep_line, e1); + + if (left != &sweep_line.active && + right != &sweep_line.active) + { + event_insert_if_intersect_below_current_y (&sweep_line, + link_to_edge (left), + link_to_edge (right)); + } + + break; + + case EVENT_TYPE_INTERSECTION: + e1 = ((queue_event_t *) event)->e1; + e2 = ((queue_event_t *) event)->e2; + + event_delete (&sweep_line, event); + if (e1->flags & STOP) + break; + if (e2->flags & STOP) + break; + + /* skip this intersection if its edges are not adjacent */ + if (&e2->link != e1->link.next) + break; + + left = e1->link.prev; + right = e2->link.next; + + sweep_line_swap (&sweep_line, e1, e2); + + /* after the swap e2 is left of e1 */ + if (left != &sweep_line.active) { + event_insert_if_intersect_below_current_y (&sweep_line, + link_to_edge (left), e2); + } + + if (right != &sweep_line.active) { + event_insert_if_intersect_below_current_y (&sweep_line, + e1, link_to_edge (right)); + } + + break; + } + + event = event_next (&sweep_line); + if (event == NULL) + goto end; + } while (event->y == sweep_line.current_subrow); + } while (event->y < sweep_line.current_row + STEP_Y); + + bottom = sweep_line.current_row + STEP_Y; + sub_emit (self, &sweep_line, renderer); + sweep_line.current_subrow = bottom; + sweep_line.current_row = sweep_line.current_subrow; + } while (TRUE); + + end: + /* flush any partial spans */ + if (sweep_line.current_subrow != sweep_line.current_row) { + sub_emit (self, &sweep_line, renderer); + sweep_line.current_row += STEP_Y; + sweep_line.current_subrow = sweep_line.current_row; + } + /* clear the rest */ + if (sweep_line.current_subrow < ybot) { + bottom = _cairo_fixed_integer_part (sweep_line.current_row); + status = renderer->render_rows (renderer, + bottom, _cairo_fixed_integer_ceil (ybot) - bottom, + NULL, 0); + } + + unwind: + sweep_line_fini (&sweep_line); + + return status; +} + +static cairo_status_t +_cairo_botor_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_botor_scan_converter_t *self = converter; + start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (start_event_t)]; + start_event_t *events; + event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; + event_t **event_ptrs; + struct _cairo_botor_scan_converter_chunk *chunk; + cairo_status_t status; + int num_events; + int i, j; + + num_events = self->num_edges; + if (unlikely (0 == num_events)) { + return renderer->render_rows (renderer, + _cairo_fixed_integer_floor (self->extents.p1.y), + _cairo_fixed_integer_ceil (self->extents.p2.y) - + _cairo_fixed_integer_floor (self->extents.p1.y), + NULL, 0); + } + + events = stack_events; + event_ptrs = stack_event_ptrs; + if (unlikely (num_events >= ARRAY_LENGTH (stack_events))) { + events = _cairo_malloc_ab_plus_c (num_events, + sizeof (start_event_t) + sizeof (event_t *), + sizeof (event_t *)); + if (unlikely (events == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + event_ptrs = (event_t **) (events + num_events); + } + + j = 0; + for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { + edge_t *edge; + + edge = chunk->base; + for (i = 0; i < chunk->count; i++) { + event_ptrs[j] = (event_t *) &events[j]; + + events[j].y = edge->edge.top; + events[j].type = EVENT_TYPE_START; + events[j].edge = edge; + + edge++, j++; + } + } + + status = botor_generate (self, event_ptrs, renderer); + + if (events != stack_events) + free (events); + + return status; +} + +static edge_t * +botor_allocate_edge (cairo_botor_scan_converter_t *self) +{ + struct _cairo_botor_scan_converter_chunk *chunk; + + chunk = self->tail; + if (chunk->count == chunk->size) { + int size; + + size = chunk->size * 2; + chunk->next = _cairo_malloc_ab_plus_c (size, + sizeof (edge_t), + sizeof (struct _cairo_botor_scan_converter_chunk)); + if (unlikely (chunk->next == NULL)) + return NULL; + + chunk = chunk->next; + chunk->next = NULL; + chunk->count = 0; + chunk->size = size; + chunk->base = chunk + 1; + self->tail = chunk; + } + + return (edge_t *) chunk->base + chunk->count++; +} + +static cairo_status_t +botor_add_edge (cairo_botor_scan_converter_t *self, + const cairo_edge_t *edge) +{ + edge_t *e; + cairo_fixed_t dx, dy; + + e = botor_allocate_edge (self); + if (unlikely (e == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_list_init (&e->link); + e->edge = *edge; + + dx = edge->line.p2.x - edge->line.p1.x; + dy = edge->line.p2.y - edge->line.p1.y; + e->dy = dy; + + 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 (edge->top == edge->line.p1.y) { + e->x.quo = edge->line.p1.x; + e->x.rem = 0; + } else { + e->x = floored_muldivrem (edge->top - edge->line.p1.y, + dx, dy); + e->x.quo += edge->line.p1.x; + } + + if (_cairo_fixed_integer_part (edge->bottom) - _cairo_fixed_integer_part (edge->top) > 1) { + e->dxdy_full = floored_muldivrem (STEP_Y, dx, dy); + } else { + e->dxdy_full.quo = 0; + e->dxdy_full.rem = 0; + } + } + + e->x.rem = -e->dy; + e->current_sign = 0; + e->runs = NULL; + e->flags = START; + + self->num_edges++; + + return CAIRO_STATUS_SUCCESS; +} + +#if 0 +static cairo_status_t +_cairo_botor_scan_converter_add_edge (void *converter, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) +{ + cairo_botor_scan_converter_t *self = converter; + cairo_edge_t edge; + + edge.line.p1 = *p1; + edge.line.p2 = *p2; + edge.top = top; + edge.bottom = bottom; + edge.dir = dir; + + return botor_add_edge (self, &edge); +} +#endif + +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; + cairo_status_t status; + int i; + + for (i = 0; i < polygon->num_edges; i++) { + status = botor_add_edge (self, &polygon->edges[i]); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_botor_scan_converter_destroy (void *converter) +{ + cairo_botor_scan_converter_t *self = converter; + struct _cairo_botor_scan_converter_chunk *chunk, *next; + + for (chunk = self->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } +} + +void +_cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, + const cairo_box_t *extents, + cairo_fill_rule_t fill_rule) +{ + self->base.destroy = _cairo_botor_scan_converter_destroy; + self->base.generate = _cairo_botor_scan_converter_generate; + + self->extents = *extents; + self->fill_rule = fill_rule; + + self->xmin = _cairo_fixed_integer_floor (extents->p1.x); + self->xmax = _cairo_fixed_integer_ceil (extents->p2.x); + + self->chunks.base = self->buf; + self->chunks.next = NULL; + self->chunks.count = 0; + self->chunks.size = sizeof (self->buf) / sizeof (edge_t); + self->tail = &self->chunks; + + self->num_edges = 0; +} 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 0000000000..024e7f1510 --- /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 0000000000..96ae66334e --- /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 new file mode 100644 index 0000000000..fd622aec31 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-boxes-private.h @@ -0,0 +1,122 @@ +/* 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 + */ + +#ifndef CAIRO_BOXES_H +#define CAIRO_BOXES_H + +#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; + + struct _cairo_boxes_chunk { + struct _cairo_boxes_chunk *next; + cairo_box_t *base; + int count; + int size; + } chunks, *tail; + cairo_box_t boxes_embedded[32]; +}; + +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, + int num_limits); + +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_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 new file mode 100644 index 0000000000..3c5d2a7502 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-boxes.c @@ -0,0 +1,486 @@ +/* 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-box-inline.h" +#include "cairo-boxes-private.h" +#include "cairo-error-private.h" + +void +_cairo_boxes_init (cairo_boxes_t *boxes) +{ + boxes->status = CAIRO_STATUS_SUCCESS; + boxes->num_limits = 0; + boxes->num_boxes = 0; + + boxes->tail = &boxes->chunks; + boxes->chunks.next = NULL; + boxes->chunks.base = boxes->boxes_embedded; + boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded); + boxes->chunks.count = 0; + + 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, + int num_boxes) +{ + int n; + + boxes->status = CAIRO_STATUS_SUCCESS; + boxes->num_limits = 0; + boxes->num_boxes = num_boxes; + + boxes->tail = &boxes->chunks; + boxes->chunks.next = NULL; + boxes->chunks.base = array; + boxes->chunks.size = num_boxes; + boxes->chunks.count = num_boxes; + + for (n = 0; n < num_boxes; n++) { + if (! _cairo_fixed_is_integer (array[n].p1.x) || + ! _cairo_fixed_is_integer (array[n].p1.y) || + ! _cairo_fixed_is_integer (array[n].p2.x) || + ! _cairo_fixed_is_integer (array[n].p2.y)) + { + break; + } + } + + 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, + int num_limits) +{ + int n; + + boxes->limits = limits; + boxes->num_limits = num_limits; + + if (boxes->num_limits) { + boxes->limit = limits[0]; + for (n = 1; n < num_limits; n++) { + if (limits[n].p1.x < boxes->limit.p1.x) + boxes->limit.p1.x = limits[n].p1.x; + + if (limits[n].p1.y < boxes->limit.p1.y) + boxes->limit.p1.y = limits[n].p1.y; + + if (limits[n].p2.x > boxes->limit.p2.x) + boxes->limit.p2.x = limits[n].p2.x; + + if (limits[n].p2.y > boxes->limit.p2.y) + boxes->limit.p2.y = limits[n].p2.y; + } + } +} + +static void +_cairo_boxes_add_internal (cairo_boxes_t *boxes, + const cairo_box_t *box) +{ + struct _cairo_boxes_chunk *chunk; + + if (unlikely (boxes->status)) + return; + + chunk = boxes->tail; + if (unlikely (chunk->count == chunk->size)) { + int size; + + size = chunk->size * 2; + chunk->next = _cairo_malloc_ab_plus_c (size, + sizeof (cairo_box_t), + sizeof (struct _cairo_boxes_chunk)); + + if (unlikely (chunk->next == NULL)) { + boxes->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return; + } + + chunk = chunk->next; + boxes->tail = chunk; + + chunk->next = NULL; + chunk->count = 0; + chunk->size = size; + chunk->base = (cairo_box_t *) (chunk + 1); + } + + chunk->base[chunk->count++] = *box; + boxes->num_boxes++; + + 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; + + if (box->p1.x == box->p2.x) + return CAIRO_STATUS_SUCCESS; + + if (boxes->num_limits) { + cairo_point_t p1, p2; + cairo_bool_t reversed = FALSE; + int n; + + /* support counter-clockwise winding for rectangular tessellation */ + if (box->p1.x < box->p2.x) { + p1.x = box->p1.x; + p2.x = box->p2.x; + } else { + p2.x = box->p1.x; + p1.x = box->p2.x; + reversed = ! reversed; + } + + if (p1.x >= boxes->limit.p2.x || p2.x <= boxes->limit.p1.x) + return CAIRO_STATUS_SUCCESS; + + if (box->p1.y < box->p2.y) { + p1.y = box->p1.y; + p2.y = box->p2.y; + } else { + p2.y = box->p1.y; + p1.y = box->p2.y; + reversed = ! reversed; + } + + if (p1.y >= boxes->limit.p2.y || p2.y <= boxes->limit.p1.y) + return CAIRO_STATUS_SUCCESS; + + for (n = 0; n < boxes->num_limits; n++) { + const cairo_box_t *limits = &boxes->limits[n]; + cairo_box_t _box; + cairo_point_t _p1, _p2; + + if (p1.x >= limits->p2.x || p2.x <= limits->p1.x) + continue; + if (p1.y >= limits->p2.y || p2.y <= limits->p1.y) + continue; + + /* Otherwise, clip the box to the limits. */ + _p1 = p1; + if (_p1.x < limits->p1.x) + _p1.x = limits->p1.x; + if (_p1.y < limits->p1.y) + _p1.y = limits->p1.y; + + _p2 = p2; + if (_p2.x > limits->p2.x) + _p2.x = limits->p2.x; + if (_p2.y > limits->p2.y) + _p2.y = limits->p2.y; + + if (_p2.y <= _p1.y || _p2.x <= _p1.x) + continue; + + _box.p1.y = _p1.y; + _box.p2.y = _p2.y; + if (reversed) { + _box.p1.x = _p2.x; + _box.p2.x = _p1.x; + } else { + _box.p1.x = _p1.x; + _box.p2.x = _p2.x; + } + + _cairo_boxes_add_internal (boxes, &_box); + } + } else { + _cairo_boxes_add_internal (boxes, box); + } + + 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_box_t *box) +{ + const struct _cairo_boxes_chunk *chunk; + cairo_box_t b; + int i; + + if (boxes->num_boxes == 0) { + box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0; + return; + } + + 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 +_cairo_boxes_clear (cairo_boxes_t *boxes) +{ + struct _cairo_boxes_chunk *chunk, *next; + + for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } + + 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) +{ + struct _cairo_boxes_chunk *chunk, *next; + + for (chunk = boxes->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + 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 new file mode 100644 index 0000000000..24b6d0b209 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-cache-private.h @@ -0,0 +1,145 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc. + * 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): + * Keith Packard + * Graydon Hoare + * Carl Worth + */ + +#ifndef CAIRO_CACHE_PRIVATE_H +#define CAIRO_CACHE_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +/** + * _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 + * have a #cairo_cache_entry_t as their first field. For example: + * + * typedef _my_entry { + * cairo_cache_entry_t base; + * ... Remainder of key and value fields here .. + * } my_entry_t; + * + * which then allows a pointer to my_entry_t to be passed to any of + * the #cairo_cache_t functions as follows without requiring a cast: + * + * _cairo_cache_insert (cache, &my_entry->base, size); + * + * 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 + * code will reduce the frequency of two keys with the same code for + * which keys_equal returns %FALSE. + * + * The user must also initialize my_entry->base.size to indicate + * the size of the current entry. What units to use for size is + * entirely up to the caller, (though the same units must be used for + * the max_size parameter passed to _cairo_cache_create()). If all + * entries are close to the same size, the simplest thing to do is to + * just use units of "entries", (eg. set size==1 in all entries and + * set max_size to the number of entries which you want to be saved + * in the cache). + * + * Which parts of the entry make up the "key" and which part make up + * the value are entirely up to the caller, (as determined by the + * computation going into base.hash as well as the keys_equal + * function). A few of the #cairo_cache_t functions accept an entry which + * will be used exclusively as a "key", (indicated by a parameter name + * of key). In these cases, the value-related fields of the entry need + * not be initialized if so desired. + **/ +typedef struct _cairo_cache_entry { + unsigned long hash; + unsigned long size; +} cairo_cache_entry_t; + +typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry); + +struct _cairo_cache { + cairo_hash_table_t *hash_table; + + cairo_cache_predicate_func_t predicate; + cairo_destroy_func_t entry_destroy; + + unsigned long max_size; + unsigned long size; + + int freeze_count; +}; + +typedef cairo_bool_t +(*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b); + +typedef void +(*cairo_cache_callback_func_t) (void *entry, + void *closure); + +cairo_private cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + cairo_cache_keys_equal_func_t keys_equal, + cairo_cache_predicate_func_t predicate, + cairo_destroy_func_t entry_destroy, + unsigned long max_size); + +cairo_private void +_cairo_cache_fini (cairo_cache_t *cache); + +cairo_private void +_cairo_cache_freeze (cairo_cache_t *cache); + +cairo_private void +_cairo_cache_thaw (cairo_cache_t *cache); + +cairo_private void * +_cairo_cache_lookup (cairo_cache_t *cache, + cairo_cache_entry_t *key); + +cairo_private cairo_status_t +_cairo_cache_insert (cairo_cache_t *cache, + cairo_cache_entry_t *entry); + +cairo_private void +_cairo_cache_remove (cairo_cache_t *cache, + cairo_cache_entry_t *entry); + +cairo_private void +_cairo_cache_foreach (cairo_cache_t *cache, + cairo_cache_callback_func_t cache_callback, + void *closure); + +#endif diff --git a/gfx/cairo/cairo/src/cairo-cache.c b/gfx/cairo/cairo/src/cairo-cache.c new file mode 100644 index 0000000000..5c4e4caa3d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-cache.c @@ -0,0 +1,338 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc. + * 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): + * Keith Packard + * Graydon Hoare + * Carl Worth + */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +static void +_cairo_cache_shrink_to_accommodate (cairo_cache_t *cache, + unsigned long additional); + +static cairo_bool_t +_cairo_cache_entry_is_non_zero (const void *entry) +{ + return ((const cairo_cache_entry_t *) entry)->size; +} + + +/** + * _cairo_cache_init: + * @cache: the #cairo_cache_t to initialise + * @keys_equal: a function to return %TRUE if two keys are equal + * @entry_destroy: destroy notifier for cache entries + * @max_size: the maximum size for this cache + * Returns: the newly created #cairo_cache_t + * + * Creates a new cache using the keys_equal() function to determine + * the equality of entries. + * + * Data is provided to the cache in the form of user-derived version + * of #cairo_cache_entry_t. A cache entry must be able to hold hash + * code, a size, and the key/value pair being stored in the + * cache. Sometimes only the key will be necessary, (as in + * _cairo_cache_lookup()), and in these cases the value portion of the + * entry need not be initialized. + * + * The units for max_size can be chosen by the caller, but should be + * consistent with the units of the size field of cache entries. When + * adding an entry with _cairo_cache_insert() if the total size of + * entries in the cache would exceed max_size then entries will be + * removed at random until the new entry would fit or the cache is + * empty. Then the new entry is inserted. + * + * There are cases in which the automatic removal of entries is + * undesired. If the cache entries have reference counts, then it is a + * simple matter to use the reference counts to ensure that entries + * continue to live even after being ejected from the cache. However, + * in some cases the memory overhead of adding a reference count to + * the entry would be objectionable. In such cases, the + * _cairo_cache_freeze() and _cairo_cache_thaw() calls can be + * used to establish a window during which no automatic removal of + * entries will occur. + **/ +cairo_status_t +_cairo_cache_init (cairo_cache_t *cache, + cairo_cache_keys_equal_func_t keys_equal, + cairo_cache_predicate_func_t predicate, + cairo_destroy_func_t entry_destroy, + unsigned long max_size) +{ + cache->hash_table = _cairo_hash_table_create (keys_equal); + if (unlikely (cache->hash_table == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (predicate == NULL) + predicate = _cairo_cache_entry_is_non_zero; + cache->predicate = predicate; + cache->entry_destroy = entry_destroy; + + cache->max_size = max_size; + cache->size = 0; + + cache->freeze_count = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_cache_pluck (void *entry, void *closure) +{ + _cairo_cache_remove (closure, entry); +} + +/** + * _cairo_cache_fini: + * @cache: a cache to destroy + * + * Immediately destroys the given cache, freeing all resources + * associated with it. As part of this process, the entry_destroy() + * function, (as passed to _cairo_cache_init()), will be called for + * each entry in the cache. + **/ +void +_cairo_cache_fini (cairo_cache_t *cache) +{ + _cairo_hash_table_foreach (cache->hash_table, + _cairo_cache_pluck, + cache); + assert (cache->size == 0); + _cairo_hash_table_destroy (cache->hash_table); +} + +/** + * _cairo_cache_freeze: + * @cache: a cache with some precious entries in it (or about to be + * added) + * + * Disable the automatic ejection of entries from the cache. For as + * long as the cache is "frozen", calls to _cairo_cache_insert() will + * add new entries to the cache regardless of how large the cache + * grows. See _cairo_cache_thaw(). + * + * Note: Multiple calls to _cairo_cache_freeze() will stack, in that + * the cache will remain "frozen" until a corresponding number of + * calls are made to _cairo_cache_thaw(). + **/ +void +_cairo_cache_freeze (cairo_cache_t *cache) +{ + assert (cache->freeze_count >= 0); + + cache->freeze_count++; +} + +/** + * _cairo_cache_thaw: + * @cache: a cache, just after the entries in it have become less + * precious + * + * Cancels the effects of _cairo_cache_freeze(). + * + * When a number of calls to _cairo_cache_thaw() is made corresponding + * to the number of calls to _cairo_cache_freeze() the cache will no + * longer be "frozen". If the cache had grown larger than max_size + * while frozen, entries will immediately be ejected (by random) from + * the cache until the cache is smaller than max_size. Also, the + * automatic ejection of entries on _cairo_cache_insert() will resume. + **/ +void +_cairo_cache_thaw (cairo_cache_t *cache) +{ + assert (cache->freeze_count > 0); + + if (--cache->freeze_count == 0) + _cairo_cache_shrink_to_accommodate (cache, 0); +} + +/** + * _cairo_cache_lookup: + * @cache: a cache + * @key: the key of interest + * @entry_return: pointer for return value + * + * Performs a lookup in @cache looking for an entry which has a key + * that matches @key, (as determined by the keys_equal() function + * passed to _cairo_cache_init()). + * + * Return value: %TRUE if there is an entry in the cache that matches + * @key, (which will now be in *entry_return). %FALSE otherwise, (in + * which case *entry_return will be %NULL). + **/ +void * +_cairo_cache_lookup (cairo_cache_t *cache, + cairo_cache_entry_t *key) +{ + return _cairo_hash_table_lookup (cache->hash_table, + (cairo_hash_entry_t *) key); +} + +/** + * _cairo_cache_remove_random: + * @cache: a cache + * + * Remove a random entry from the cache. + * + * Return value: %TRUE if an entry was successfully removed. + * %FALSE if there are no entries that can be removed. + **/ +static cairo_bool_t +_cairo_cache_remove_random (cairo_cache_t *cache) +{ + cairo_cache_entry_t *entry; + + entry = _cairo_hash_table_random_entry (cache->hash_table, + cache->predicate); + if (unlikely (entry == NULL)) + return FALSE; + + _cairo_cache_remove (cache, entry); + + return TRUE; +} + +/** + * _cairo_cache_shrink_to_accommodate: + * @cache: a cache + * @additional: additional size requested in bytes + * + * If cache is not frozen, eject entries randomly until the size of + * the cache is at least @additional bytes less than + * cache->max_size. That is, make enough room to accommodate a new + * entry of size @additional. + **/ +static void +_cairo_cache_shrink_to_accommodate (cairo_cache_t *cache, + unsigned long additional) +{ + while (cache->size + additional > cache->max_size) { + if (! _cairo_cache_remove_random (cache)) + return; + } +} + +/** + * _cairo_cache_insert: + * @cache: a cache + * @entry: an entry to be inserted + * + * Insert @entry into the cache. If an entry exists in the cache with + * a matching key, then the old entry will be removed first, (and the + * entry_destroy() callback will be called on it). + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available. + **/ +cairo_status_t +_cairo_cache_insert (cairo_cache_t *cache, + cairo_cache_entry_t *entry) +{ + cairo_status_t status; + + if (entry->size && ! cache->freeze_count) + _cairo_cache_shrink_to_accommodate (cache, entry->size); + + status = _cairo_hash_table_insert (cache->hash_table, + (cairo_hash_entry_t *) entry); + if (unlikely (status)) + return status; + + cache->size += entry->size; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_cache_remove: + * @cache: a cache + * @entry: an entry that exists in the cache + * + * Remove an existing entry from the cache. + **/ +void +_cairo_cache_remove (cairo_cache_t *cache, + cairo_cache_entry_t *entry) +{ + cache->size -= entry->size; + + _cairo_hash_table_remove (cache->hash_table, + (cairo_hash_entry_t *) entry); + + if (cache->entry_destroy) + cache->entry_destroy (entry); +} + +/** + * _cairo_cache_foreach: + * @cache: a cache + * @cache_callback: function to be called for each entry + * @closure: additional argument to be passed to @cache_callback + * + * Call @cache_callback for each entry in the cache, in a + * non-specified order. + **/ +void +_cairo_cache_foreach (cairo_cache_t *cache, + cairo_cache_callback_func_t cache_callback, + void *closure) +{ + _cairo_hash_table_foreach (cache->hash_table, + cache_callback, + closure); +} + +unsigned long +_cairo_hash_string (const char *c) +{ + /* This is the djb2 hash. */ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + while (c && *c) + hash = ((hash << 5) + hash) + *c++; + return hash; +} + +unsigned long +_cairo_hash_bytes (unsigned long hash, + const void *ptr, + unsigned int length) +{ + const uint8_t *bytes = ptr; + /* This is the djb2 hash. */ + while (length--) + hash = ((hash << 5) + hash) + *bytes++; + return hash; +} diff --git a/gfx/cairo/cairo/src/cairo-cff-subset.c b/gfx/cairo/cairo/src/cairo-cff-subset.c new file mode 100644 index 0000000000..32a22ee3e0 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-cff-subset.c @@ -0,0 +1,3454 @@ +/* -*- 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 + * + * 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 + * Eugeniy Meshcheryakov + */ + +/* + * Useful links: + * 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 _DEFAULT_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" + +#include "cairo-array-private.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#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 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; + uint8_t header_size; + uint8_t offset_size; +} cff_header_t; + +typedef struct _cff_index_element { + cairo_bool_t is_copy; + unsigned char *data; + int length; +} cff_index_element_t; + +typedef struct _cff_dict_operator { + cairo_hash_entry_t base; + + unsigned short operator; + unsigned char *operand; + int operand_length; + int operand_offset; +} cff_dict_operator_t; + +typedef struct _cairo_cff_font { + + cairo_scaled_font_subset_t *scaled_font_subset; + const cairo_scaled_font_backend_t *backend; + + /* Font Data */ + unsigned char *data; + unsigned long data_length; + unsigned char *current_ptr; + unsigned char *data_end; + cff_header_t *header; + char *font_name; + char *ps_name; + cairo_hash_table_t *top_dict; + cairo_hash_table_t *private_dict; + cairo_array_t strings_index; + 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; + unsigned int num_fontdicts; + 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 */ + int *widths; + 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 + * operands that are later modified after encoding. */ +static unsigned char * +encode_integer_max (unsigned char *p, int i) +{ + *p++ = 29; + *p++ = i >> 24; + *p++ = (i >> 16) & 0xff; + *p++ = (i >> 8) & 0xff; + *p++ = i & 0xff; + return p; +} + +static unsigned char * +encode_integer (unsigned char *p, int i) +{ + if (i >= -107 && i <= 107) { + *p++ = i + 139; + } else if (i >= 108 && i <= 1131) { + i -= 108; + *p++ = (i >> 8)+ 247; + *p++ = i & 0xff; + } else if (i >= -1131 && i <= -108) { + i = -i - 108; + *p++ = (i >> 8)+ 251; + *p++ = i & 0xff; + } else if (i >= -32768 && i <= 32767) { + *p++ = 28; + *p++ = (i >> 8) & 0xff; + *p++ = i & 0xff; + } else { + p = encode_integer_max (p, i); + } + return p; +} + +static unsigned char * +decode_integer (unsigned char *p, int *integer) +{ + if (*p == 28) { + *integer = (int)(p[1]<<8 | p[2]); + p += 3; + } else if (*p == 29) { + *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; + } 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 { + *integer = 0; + p += 1; + } + 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) +{ + unsigned short op = 0; + + op = *p++; + if (op == 12) { + op <<= 8; + op |= *p++; + } + *operator = op; + return p; +} + +/* return 0 if not an operand */ +static int +operand_length (unsigned char *p) +{ + unsigned char *begin = p; + + if (*p == 28) + return 3; + + if (*p == 29) + return 5; + + if (*p >= 32 && *p <= 246) + return 1; + + if (*p >= 247 && *p <= 254) + return 2; + + if (*p == 30) { + while ((*p & 0x0f) != 0x0f) + p++; + return p - begin + 1; + } + + return 0; +} + +static unsigned char * +encode_index_offset (unsigned char *p, int offset_size, unsigned long offset) +{ + while (--offset_size >= 0) { + p[offset_size] = (unsigned char) (offset & 0xff); + offset >>= 8; + } + return p + offset_size; +} + +static unsigned long +decode_index_offset(unsigned char *p, int off_size) +{ + unsigned long offset = 0; + + while (off_size-- > 0) + offset = offset*256 + *p++; + return offset; +} + +static void +cff_index_init (cairo_array_t *index) +{ + _cairo_array_init (index, sizeof (cff_index_element_t)); +} + +static cairo_int_status_t +cff_index_read (cairo_array_t *index, unsigned char **ptr, unsigned char *end_ptr) +{ + cff_index_element_t element; + unsigned char *data, *p; + cairo_status_t status; + int offset_size, count, start, i; + int end = 0; + + p = *ptr; + if (p + 2 > end_ptr) + return CAIRO_INT_STATUS_UNSUPPORTED; + count = get_unaligned_be16 (p); + p += 2; + if (count > 0) { + offset_size = *p++; + if (p + (count + 1)*offset_size > end_ptr) + return CAIRO_INT_STATUS_UNSUPPORTED; + data = p + offset_size*(count + 1) - 1; + start = decode_index_offset (p, offset_size); + p += offset_size; + for (i = 0; i < count; i++) { + end = decode_index_offset (p, offset_size); + p += offset_size; + if (p > end_ptr) + return CAIRO_INT_STATUS_UNSUPPORTED; + element.length = end - start; + element.is_copy = FALSE; + element.data = data + start; + status = _cairo_array_append (index, &element); + if (unlikely (status)) + return status; + start = end; + } + p = data + end; + } + *ptr = p; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cff_index_write (cairo_array_t *index, cairo_array_t *output) +{ + int offset_size; + int offset; + int num_elem; + int i; + cff_index_element_t *element; + uint16_t count; + unsigned char buf[5]; + cairo_status_t status; + + num_elem = _cairo_array_num_elements (index); + count = cpu_to_be16 ((uint16_t) num_elem); + status = _cairo_array_append_multiple (output, &count, 2); + if (unlikely (status)) + return status; + + if (num_elem == 0) + return CAIRO_STATUS_SUCCESS; + + /* Find maximum offset to determine offset size */ + offset = 1; + for (i = 0; i < num_elem; i++) { + element = _cairo_array_index (index, i); + offset += element->length; + } + if (offset < 0x100) + offset_size = 1; + else if (offset < 0x10000) + offset_size = 2; + else if (offset < 0x1000000) + offset_size = 3; + else + offset_size = 4; + + buf[0] = (unsigned char) offset_size; + status = _cairo_array_append (output, buf); + if (unlikely (status)) + return status; + + offset = 1; + encode_index_offset (buf, offset_size, offset); + status = _cairo_array_append_multiple (output, buf, offset_size); + if (unlikely (status)) + return status; + + for (i = 0; i < num_elem; i++) { + element = _cairo_array_index (index, i); + offset += element->length; + encode_index_offset (buf, offset_size, offset); + status = _cairo_array_append_multiple (output, buf, offset_size); + if (unlikely (status)) + return status; + } + + for (i = 0; i < num_elem; i++) { + element = _cairo_array_index (index, i); + 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) +{ + cff_index_element_t element; + + element.length = length; + element.is_copy = FALSE; + element.data = object; + + return _cairo_array_append (index, &element); +} + +static cairo_status_t +cff_index_append_copy (cairo_array_t *index, + const unsigned char *object, + unsigned int length) +{ + cff_index_element_t element; + cairo_status_t status; + + element.length = length; + element.is_copy = TRUE; + element.data = _cairo_malloc (element.length); + if (unlikely (element.data == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (element.data, object, element.length); + + status = _cairo_array_append (index, &element); + if (unlikely (status)) { + free (element.data); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +cff_index_fini (cairo_array_t *index) +{ + cff_index_element_t *element; + unsigned int i; + + for (i = 0; i < _cairo_array_num_elements (index); i++) { + element = _cairo_array_index (index, i); + if (element->is_copy && element->data) + free (element->data); + } + _cairo_array_fini (index); +} + +static cairo_bool_t +_cairo_cff_dict_equal (const void *key_a, const void *key_b) +{ + const cff_dict_operator_t *op_a = key_a; + const cff_dict_operator_t *op_b = key_b; + + return op_a->operator == op_b->operator; +} + +static cairo_status_t +cff_dict_init (cairo_hash_table_t **dict) +{ + *dict = _cairo_hash_table_create (_cairo_cff_dict_equal); + if (unlikely (*dict == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_dict_init_key (cff_dict_operator_t *key, int operator) +{ + key->base.hash = (unsigned long) operator; + key->operator = operator; +} + +static cairo_status_t +cff_dict_create_operator (int operator, + unsigned char *operand, + int size, + cff_dict_operator_t **out) +{ + cff_dict_operator_t *op; + + 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); + 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); + } + } + + op->operand_length = size; + op->operand_offset = -1; + + *out = op; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cff_dict_read (cairo_hash_table_t *dict, unsigned char *p, int dict_size) +{ + unsigned char *end; + cairo_array_t operands; + cff_dict_operator_t *op; + unsigned short operator; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int size; + + end = p + dict_size; + _cairo_array_init (&operands, 1); + while (p < end) { + size = operand_length (p); + if (size != 0) { + status = _cairo_array_append_multiple (&operands, p, size); + if (unlikely (status)) + goto fail; + + p += size; + } else { + p = decode_operator (p, &operator); + status = cff_dict_create_operator (operator, + _cairo_array_index (&operands, 0), + _cairo_array_num_elements (&operands), + &op); + if (unlikely (status)) + goto fail; + + status = _cairo_hash_table_insert (dict, &op->base); + if (unlikely (status)) + goto fail; + + _cairo_array_truncate (&operands, 0); + } + } + +fail: + _cairo_array_fini (&operands); + + return status; +} + +static void +cff_dict_remove (cairo_hash_table_t *dict, unsigned short operator) +{ + cff_dict_operator_t key, *op; + + _cairo_dict_init_key (&key, operator); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { + free (op->operand); + _cairo_hash_table_remove (dict, (cairo_hash_entry_t *) op); + free (op); + } +} + +static unsigned char * +cff_dict_get_operands (cairo_hash_table_t *dict, + unsigned short operator, + int *size) +{ + cff_dict_operator_t key, *op; + + _cairo_dict_init_key (&key, operator); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { + *size = op->operand_length; + return op->operand; + } + + return NULL; +} + +static cairo_status_t +cff_dict_set_operands (cairo_hash_table_t *dict, + unsigned short operator, + unsigned char *operand, + int size) +{ + cff_dict_operator_t key, *op; + cairo_status_t status; + + _cairo_dict_init_key (&key, operator); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { + free (op->operand); + op->operand = _cairo_malloc (size); + if (unlikely (op->operand == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (op->operand, operand, size); + op->operand_length = size; + } + else + { + status = cff_dict_create_operator (operator, operand, size, &op); + if (unlikely (status)) + return status; + + status = _cairo_hash_table_insert (dict, &op->base); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static int +cff_dict_get_location (cairo_hash_table_t *dict, + unsigned short operator, + int *size) +{ + cff_dict_operator_t key, *op; + + _cairo_dict_init_key (&key, operator); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) { + *size = op->operand_length; + return op->operand_offset; + } + + return -1; +} + +typedef struct _dict_write_info { + cairo_array_t *output; + cairo_status_t status; +} dict_write_info_t; + +static void +cairo_dict_write_operator (cff_dict_operator_t *op, dict_write_info_t *write_info) +{ + unsigned char data; + + op->operand_offset = _cairo_array_num_elements (write_info->output); + write_info->status = _cairo_array_append_multiple (write_info->output, op->operand, op->operand_length); + if (write_info->status) + return; + + if (op->operator & 0xff00) { + data = op->operator >> 8; + write_info->status = _cairo_array_append (write_info->output, &data); + if (write_info->status) + return; + } + data = op->operator & 0xff; + write_info->status = _cairo_array_append (write_info->output, &data); +} + +static void +_cairo_dict_collect (void *entry, void *closure) +{ + dict_write_info_t *write_info = closure; + cff_dict_operator_t *op = entry; + + if (write_info->status) + return; + + /* The ROS operator is handled separately in cff_dict_write() */ + if (op->operator != ROS_OP) + cairo_dict_write_operator (op, write_info); +} + +static cairo_status_t +cff_dict_write (cairo_hash_table_t *dict, cairo_array_t *output) +{ + dict_write_info_t write_info; + cff_dict_operator_t key, *op; + + write_info.output = output; + write_info.status = CAIRO_STATUS_SUCCESS; + + /* The CFF specification requires that the Top Dict of CID fonts + * begin with the ROS operator. */ + _cairo_dict_init_key (&key, ROS_OP); + op = _cairo_hash_table_lookup (dict, &key.base); + if (op != NULL) + cairo_dict_write_operator (op, &write_info); + + _cairo_hash_table_foreach (dict, _cairo_dict_collect, &write_info); + + return write_info.status; +} + +static void +_cff_dict_entry_pluck (void *_entry, void *dict) +{ + cff_dict_operator_t *entry = _entry; + + _cairo_hash_table_remove (dict, &entry->base); + free (entry->operand); + free (entry); +} + +static void +cff_dict_fini (cairo_hash_table_t *dict) +{ + _cairo_hash_table_foreach (dict, _cff_dict_entry_pluck, dict); + _cairo_hash_table_destroy (dict); +} + +static cairo_int_status_t +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; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +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; + + 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; +} + +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) +{ + cairo_int_status_t status; + unsigned char buf[10]; + unsigned char *end_buf; + int offset; + int i; + unsigned char *operand; + unsigned char *p; + int num_subs; + + status = cff_dict_read (private_dict, ptr, size); + if (unlikely (status)) + return status; + + operand = cff_dict_get_operands (private_dict, LOCAL_SUB_OP, &i); + if (operand) { + decode_integer (operand, &offset); + p = ptr + offset; + status = cff_index_read (local_sub_index, &p, font->data_end); + if (unlikely (status)) + return status; + + /* Use maximum sized encoding to reserve space for later modification. */ + end_buf = encode_integer_max (buf, 0); + status = cff_dict_set_operands (private_dict, LOCAL_SUB_OP, buf, end_buf - buf); + if (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; +} + +static cairo_int_status_t +cairo_cff_font_read_fdselect (cairo_cff_font_t *font, unsigned char *p) +{ + int type, num_ranges, first, last, fd, i, j; + + font->fdselect = calloc (font->num_glyphs, sizeof (int)); + if (unlikely (font->fdselect == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + type = *p++; + if (type == 0) + { + for (i = 0; i < font->num_glyphs; i++) + font->fdselect[i] = *p++; + } else if (type == 3) { + num_ranges = get_unaligned_be16 (p); + p += 2; + for (i = 0; i < num_ranges; i++) + { + first = get_unaligned_be16 (p); + p += 2; + fd = *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; + } + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr) +{ + cairo_array_t index; + cff_index_element_t *element; + unsigned int i; + int size; + unsigned char *operand; + int offset; + cairo_int_status_t status; + unsigned char buf[100]; + unsigned char *end_buf; + + cff_index_init (&index); + status = cff_index_read (&index, &ptr, font->data_end); + if (unlikely (status)) + goto fail; + + font->num_fontdicts = _cairo_array_num_elements (&index); + + font->fd_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); + if (unlikely (font->fd_dict == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + font->fd_private_dict = calloc (sizeof (cairo_hash_table_t *), font->num_fontdicts); + if (unlikely (font->fd_private_dict == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail; + } + + font->fd_local_sub_index = calloc (sizeof (cairo_array_t), font->num_fontdicts); + if (unlikely (font->fd_local_sub_index == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + 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)) + goto fail; + + element = _cairo_array_index (&index, i); + status = cff_dict_read (font->fd_dict[i], element->data, element->length); + if (unlikely (status)) + goto fail; + + operand = cff_dict_get_operands (font->fd_dict[i], PRIVATE_OP, &size); + if (operand == NULL) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto fail; + } + operand = decode_integer (operand, &size); + decode_integer (operand, &offset); + status = cff_dict_init (&font->fd_private_dict[i]); + if (unlikely (status)) + goto fail; + + cff_index_init (&font->fd_local_sub_index[i]); + 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 + * space for any value later */ + end_buf = encode_integer_max (buf, 0); + end_buf = encode_integer_max (end_buf, 0); + status = cff_dict_set_operands (font->fd_dict[i], PRIVATE_OP, buf, end_buf - buf); + if (unlikely (status)) + goto fail; + } + + status = CAIRO_STATUS_SUCCESS; + +fail: + cff_index_fini (&index); + + 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) +{ + cairo_array_t index; + cff_index_element_t *element; + unsigned char buf[20]; + unsigned char *end_buf; + unsigned char *operand; + cairo_int_status_t status; + unsigned char *p; + int size; + int offset; + + cff_index_init (&index); + status = cff_index_read (&index, &font->current_ptr, font->data_end); + if (unlikely (status)) + goto fail; + + element = _cairo_array_index (&index, 0); + status = cff_dict_read (font->top_dict, element->data, element->length); + if (unlikely (status)) + goto fail; + + if (cff_dict_get_operands (font->top_dict, ROS_OP, &size) != NULL) + font->is_cid = TRUE; + else + font->is_cid = FALSE; + + operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size); + decode_integer (operand, &offset); + p = font->data + offset; + status = cff_index_read (&font->charstrings_index, &p, font->data_end); + if (unlikely (status)) + 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); + status = cairo_cff_font_read_fdselect (font, font->data + offset); + if (unlikely (status)) + goto fail; + + operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size); + decode_integer (operand, &offset); + status = cairo_cff_font_read_cid_fontdict (font, font->data + offset); + if (unlikely (status)) + goto fail; + } else { + 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, + 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)) + goto fail; + } + + /* Use maximum sized encoding to reserve space for later modification. */ + end_buf = encode_integer_max (buf, 0); + status = cff_dict_set_operands (font->top_dict, + CHARSTRINGS_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; + + 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. */ + cff_dict_remove (font->top_dict, UNIQUEID_OP); + cff_dict_remove (font->top_dict, XUID_OP); + +fail: + cff_index_fini (&index); + + return status; +} + +static cairo_int_status_t +cairo_cff_font_read_strings (cairo_cff_font_t *font) +{ + return cff_index_read (&font->strings_index, &font->current_ptr, font->data_end); +} + +static cairo_int_status_t +cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font) +{ + 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 +(*font_read_t) (cairo_cff_font_t *font); + +static const font_read_t font_read_funcs[] = { + cairo_cff_font_read_header, + cairo_cff_font_read_name, + cairo_cff_font_read_top_dict, + cairo_cff_font_read_strings, + cairo_cff_font_read_global_subroutines, +}; + +static cairo_int_status_t +cairo_cff_font_read_font (cairo_cff_font_t *font) +{ + cairo_int_status_t status; + unsigned int i; + + for (i = 0; i < ARRAY_LENGTH (font_read_funcs); i++) { + status = font_read_funcs[i] (font); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_set_ros_strings (cairo_cff_font_t *font) +{ + cairo_status_t status; + unsigned char buf[30]; + unsigned char *p; + int sid1, sid2; + const char *registry = "Adobe"; + const char *ordering = "Identity"; + + sid1 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); + status = cff_index_append_copy (&font->strings_subset_index, + (unsigned char *)registry, + strlen(registry)); + if (unlikely (status)) + return status; + + sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); + status = cff_index_append_copy (&font->strings_subset_index, + (unsigned char *)ordering, + strlen(ordering)); + if (unlikely (status)) + return status; + + p = encode_integer (buf, sid1); + p = encode_integer (p, sid2); + p = encode_integer (p, 0); + status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf); + if (unlikely (status)) + return status; + + p = encode_integer (buf, font->scaled_font_subset->num_glyphs); + status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_subset_dict_string(cairo_cff_font_t *font, + cairo_hash_table_t *dict, + int operator) +{ + int size; + unsigned char *p; + int sid; + unsigned char buf[100]; + cff_index_element_t *element; + cairo_status_t status; + + p = cff_dict_get_operands (dict, operator, &size); + if (!p) + return CAIRO_STATUS_SUCCESS; + + decode_integer (p, &sid); + if (sid < NUM_STD_STRINGS) + return CAIRO_STATUS_SUCCESS; + + element = _cairo_array_index (&font->strings_index, sid - NUM_STD_STRINGS); + sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index); + status = cff_index_append (&font->strings_subset_index, element->data, element->length); + if (unlikely (status)) + return status; + + p = encode_integer (buf, sid); + status = cff_dict_set_operands (dict, operator, buf, p - buf); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static const int dict_strings[] = { + VERSION_OP, + NOTICE_OP, + COPYRIGHT_OP, + FULLNAME_OP, + FAMILYNAME_OP, + WEIGHT_OP, + POSTSCRIPT_OP, + BASEFONTNAME_OP, + FONTNAME_OP, +}; + +static cairo_status_t +cairo_cff_font_subset_dict_strings (cairo_cff_font_t *font, + cairo_hash_table_t *dict) +{ + cairo_status_t status; + unsigned int i; + + for (i = 0; i < ARRAY_LENGTH (dict_strings); i++) { + status = cairo_cff_font_subset_dict_string (font, dict, dict_strings[i]); + if (unlikely (status)) + return status; + } + + 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_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_int_status_t status; + unsigned long glyph, cid; + + font->subset_subroutines = TRUE; + for (i = 0; i < font->scaled_font_subset->num_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; +} + +static cairo_status_t +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)); + if (unlikely (font->fdselect_subset == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int)); + if (unlikely (font->fd_subset_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int)); + if (unlikely (font->private_dict_offset == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + reverse_map = calloc (font->num_fontdicts, sizeof (int)); + if (unlikely (reverse_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + for (i = 0; i < font->num_fontdicts; i++) + reverse_map[i] = -1; + + font->num_subset_fontdicts = 0; + for (i = 0; i < font->scaled_font_subset->num_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++; + } + font->fdselect_subset[i] = reverse_map[fd]; + } + + free (reverse_map); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font) +{ + unsigned char buf[100]; + unsigned char *end_buf; + cairo_status_t status; + + font->num_fontdicts = 1; + font->fd_dict = _cairo_malloc (sizeof (cairo_hash_table_t *)); + if (unlikely (font->fd_dict == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (cff_dict_init (&font->fd_dict[0])) { + free (font->fd_dict); + font->fd_dict = NULL; + font->num_fontdicts = 0; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + 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 = _cairo_malloc (sizeof (int)); + if (unlikely (font->private_dict_offset == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->fd_subset_map[0] = 0; + font->num_subset_fontdicts = 1; + + /* 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); + status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_subset_strings (cairo_cff_font_t *font) +{ + cairo_status_t status; + unsigned int i; + + status = cairo_cff_font_subset_dict_strings (font, font->top_dict); + if (unlikely (status)) + return status; + + if (font->is_cid) { + for (i = 0; i < font->num_subset_fontdicts; i++) { + status = cairo_cff_font_subset_dict_strings (font, font->fd_dict[font->fd_subset_map[i]]); + if (unlikely (status)) + return status; + + status = cairo_cff_font_subset_dict_strings (font, font->fd_private_dict[font->fd_subset_map[i]]); + if (unlikely (status)) + return status; + } + } else { + status = cairo_cff_font_subset_dict_strings (font, font->private_dict); + } + + 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; + + 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_and_subroutines (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; +} + +/* Set the operand of the specified operator in the (already written) + * top dict to point to the current position in the output + * array. Operands updated with this function must have previously + * been encoded with the 5-byte (max) integer encoding. */ +static void +cairo_cff_font_set_topdict_operator_to_cur_pos (cairo_cff_font_t *font, + int operator) +{ + int cur_pos; + int offset; + int size; + unsigned char buf[10]; + unsigned char *buf_end; + unsigned char *op_ptr; + + cur_pos = _cairo_array_num_elements (&font->output); + buf_end = encode_integer_max (buf, cur_pos); + offset = cff_dict_get_location (font->top_dict, operator, &size); + assert (offset > 0); + op_ptr = _cairo_array_index (&font->output, offset); + memcpy (op_ptr, buf, buf_end - buf); +} + +static cairo_status_t +cairo_cff_font_write_header (cairo_cff_font_t *font) +{ + return _cairo_array_append_multiple (&font->output, + font->header, + font->header->header_size); +} + +static cairo_status_t +cairo_cff_font_write_name (cairo_cff_font_t *font) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_array_t index; + + cff_index_init (&index); + + status = cff_index_append_copy (&index, + (unsigned char *) font->ps_name, + strlen(font->ps_name)); + if (unlikely (status)) + goto FAIL; + + status = cff_index_write (&index, &font->output); + if (unlikely (status)) + goto FAIL; + +FAIL: + cff_index_fini (&index); + + return status; +} + +static cairo_status_t +cairo_cff_font_write_top_dict (cairo_cff_font_t *font) +{ + uint16_t count; + unsigned char buf[10]; + unsigned char *p; + int offset_index; + int dict_start, dict_size; + int offset_size = 4; + cairo_status_t status; + + /* Write an index containing the top dict */ + + count = cpu_to_be16 (1); + status = _cairo_array_append_multiple (&font->output, &count, 2); + if (unlikely (status)) + return status; + buf[0] = offset_size; + status = _cairo_array_append (&font->output, buf); + if (unlikely (status)) + return status; + encode_index_offset (buf, offset_size, 1); + status = _cairo_array_append_multiple (&font->output, buf, offset_size); + if (unlikely (status)) + return status; + + /* Reserve space for last element of offset array and update after + * dict is written */ + offset_index = _cairo_array_num_elements (&font->output); + status = _cairo_array_append_multiple (&font->output, buf, offset_size); + if (unlikely (status)) + return status; + + dict_start = _cairo_array_num_elements (&font->output); + status = cff_dict_write (font->top_dict, &font->output); + if (unlikely (status)) + return status; + dict_size = _cairo_array_num_elements (&font->output) - dict_start; + + encode_index_offset (buf, offset_size, dict_size + 1); + p = _cairo_array_index (&font->output, offset_index); + memcpy (p, buf, offset_size); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_write_strings (cairo_cff_font_t *font) +{ + return cff_index_write (&font->strings_subset_index, &font->output); +} + +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) +{ + unsigned char data; + unsigned int i; + cairo_int_status_t status; + + cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDSELECT_OP); + + if (font->is_cid) { + data = 0; + status = _cairo_array_append (&font->output, &data); + if (unlikely (status)) + return status; + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + data = font->fdselect_subset[i]; + status = _cairo_array_append (&font->output, &data); + if (unlikely (status)) + return status; + } + } else { + unsigned char byte; + uint16_t word; + + status = _cairo_array_grow_by (&font->output, 9); + if (unlikely (status)) + return status; + + byte = 3; + status = _cairo_array_append (&font->output, &byte); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + word = cpu_to_be16 (1); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + word = cpu_to_be16 (0); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + byte = 0; + status = _cairo_array_append (&font->output, &byte); + 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_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_cid_charset (cairo_cff_font_t *font) +{ + unsigned char byte; + uint16_t word; + cairo_status_t status; + + cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP); + status = _cairo_array_grow_by (&font->output, 5); + if (unlikely (status)) + return status; + + byte = 2; + status = _cairo_array_append (&font->output, &byte); + assert (status == CAIRO_STATUS_SUCCESS); + + word = cpu_to_be16 (1); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_STATUS_SUCCESS); + + word = cpu_to_be16 (font->scaled_font_subset->num_glyphs - 2); + status = _cairo_array_append_multiple (&font->output, &word, 2); + assert (status == CAIRO_STATUS_SUCCESS); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_cff_font_write_charstrings (cairo_cff_font_t *font) +{ + cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSTRINGS_OP); + + return cff_index_write (&font->charstrings_subset_index, &font->output); +} + +static cairo_status_t +cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font) +{ + unsigned int i; + cairo_int_status_t status; + unsigned int offset_array; + unsigned char *offset_array_ptr; + int offset_base; + uint16_t count; + uint8_t offset_size = 4; + + cairo_cff_font_set_topdict_operator_to_cur_pos (font, FDARRAY_OP); + count = cpu_to_be16 (font->num_subset_fontdicts); + status = _cairo_array_append_multiple (&font->output, &count, sizeof (uint16_t)); + if (unlikely (status)) + return status; + status = _cairo_array_append (&font->output, &offset_size); + if (unlikely (status)) + return status; + + offset_array = _cairo_array_num_elements (&font->output); + status = _cairo_array_allocate (&font->output, + (font->num_subset_fontdicts + 1)*offset_size, + (void **) &offset_array_ptr); + if (unlikely (status)) + return status; + offset_base = _cairo_array_num_elements (&font->output) - 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]], + &font->output); + if (unlikely (status)) + return status; + + 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); + } + + return CAIRO_STATUS_SUCCESS; +} + +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 offset; + int size; + unsigned char buf[10]; + unsigned char *buf_end; + unsigned char *p; + cairo_status_t status; + + /* Write private dict and update offset and size in top dict */ + font->private_dict_offset[dict_num] = _cairo_array_num_elements (&font->output); + status = cff_dict_write (private_dict, &font->output); + if (unlikely (status)) + return status; + + size = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; + /* private entry has two operands - size and offset */ + buf_end = encode_integer_max (buf, size); + buf_end = encode_integer_max (buf_end, font->private_dict_offset[dict_num]); + offset = cff_dict_get_location (parent_dict, PRIVATE_OP, &size); + assert (offset > 0); + p = _cairo_array_index (&font->output, offset); + memcpy (p, buf, buf_end - buf); + + return CAIRO_STATUS_SUCCESS; +} + +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_bool_t *local_subs_used) +{ + int offset; + int size; + unsigned char buf[10]; + 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 + * dict. Local subroutines offset is relative to start of + * private dict */ + offset = _cairo_array_num_elements (&font->output) - font->private_dict_offset[dict_num]; + buf_end = encode_integer_max (buf, offset); + offset = cff_dict_get_location (private_dict, LOCAL_SUB_OP, &size); + 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; + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font) +{ + unsigned int i; + cairo_int_status_t status; + + if (font->is_cid) { + for (i = 0; i < font->num_subset_fontdicts; i++) { + status = cairo_cff_font_write_private_dict ( + font, + i, + font->fd_dict[font->fd_subset_map[i]], + font->fd_private_dict[font->fd_subset_map[i]]); + if (unlikely (status)) + return status; + } + + for (i = 0; i < font->num_subset_fontdicts; i++) { + status = cairo_cff_font_write_local_sub ( + font, + i, + font->fd_private_dict[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; + } + } else { + status = cairo_cff_font_write_private_dict (font, + 0, + font->fd_dict[0], + 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; +} + +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_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_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; + + 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; +} + +static cairo_int_status_t +cairo_cff_font_generate (cairo_cff_font_t *font, + const char **data, + unsigned long *length) +{ + cairo_int_status_t status; + + status = cairo_cff_font_read_font (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; + + status = cairo_cff_font_write_subset (font); + if (unlikely (status)) + return status; + + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_cff_font_create_set_widths (cairo_cff_font_t *font) +{ + unsigned long size; + unsigned long long_entry_size; + unsigned long short_entry_size; + unsigned int i; + tt_hhea_t hhea; + int num_hmetrics; + uint16_t short_entry; + int glyph_index; + cairo_int_status_t status; + + size = sizeof (tt_hhea_t); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hhea, 0, + (unsigned char*) &hhea, &size); + if (unlikely (status)) + return status; + num_hmetrics = be16_to_cpu (hhea.num_hmetrics); + + 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); + if (glyph_index < num_hmetrics) { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + glyph_index * long_entry_size, + (unsigned char *) &short_entry, + &short_entry_size); + if (unlikely (status)) + return status; + } + else + { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + (num_hmetrics - 1) * long_entry_size, + (unsigned char *) &short_entry, + &short_entry_size); + if (unlikely (status)) + return status; + } + font->widths[i] = be16_to_cpu (short_entry); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +check_fontdata_is_cff (const unsigned char *data, long length) +{ + 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; + tt_head_t head; + tt_hhea_t hhea; + unsigned long size, data_length; + + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + data_length = 0; + status = backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_CFF, 0, NULL, &data_length); + if (status) + return status; + + size = sizeof (tt_head_t); + 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 (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 (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, 0, NULL, &size); + if (unlikely (status)) + return status; + + 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); + font->y_max = (int16_t) be16_to_cpu (head.y_max); + font->ascent = (int16_t) be16_to_cpu (hhea.ascender); + font->descent = (int16_t) be16_to_cpu (hhea.descender); + font->units_per_em = (int16_t) be16_to_cpu (head.units_per_em); + if (font->units_per_em == 0) + font->units_per_em = 1000; + + font->font_name = NULL; + status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font, + &font->ps_name, + &font->font_name); + if (_cairo_status_is_error (status)) + return status; + + 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); + + 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 fail3; + } + + if (font->is_opentype) { + status = cairo_cff_font_create_set_widths (font); + if (unlikely (status)) + goto fail4; + } + + status = cff_dict_init (&font->top_dict); + if (unlikely (status)) + goto fail4; + + status = cff_dict_init (&font->private_dict); + if (unlikely (status)) + goto fail5; + + cff_index_init (&font->strings_index); + cff_index_init (&font->charstrings_index); + cff_index_init (&font->global_sub_index); + 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; + +fail5: + _cairo_hash_table_destroy (font->top_dict); +fail4: + 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; +} + +static void +cairo_cff_font_destroy (cairo_cff_font_t *font) +{ + unsigned int i; + + free (font->widths); + free (font->font_name); + free (font->ps_name); + free (font->subset_font_name); + _cairo_array_fini (&font->output); + cff_dict_fini (font->top_dict); + cff_dict_fini (font->private_dict); + cff_index_fini (&font->strings_index); + cff_index_fini (&font->charstrings_index); + cff_index_fini (&font->global_sub_index); + cff_index_fini (&font->local_sub_index); + cff_index_fini (&font->charstrings_subset_index); + cff_index_fini (&font->strings_subset_index); + + /* If we bailed out early as a result of an error some of the + * following cairo_cff_font_t members may still be NULL */ + if (font->fd_dict) { + for (i = 0; i < font->num_fontdicts; i++) { + if (font->fd_dict[i]) + cff_dict_fini (font->fd_dict[i]); + } + free (font->fd_dict); + } + free (font->global_subs_used); + free (font->local_subs_used); + free (font->fd_subset_map); + free (font->private_dict_offset); + + if (font->is_cid) { + 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]) + cff_dict_fini (font->fd_private_dict[i]); + } + free (font->fd_private_dict); + } + if (font->fd_local_sub_index) { + for (i = 0; i < font->num_fontdicts; i++) + 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); + } + + free (font->data); + + free (font); +} + +cairo_status_t +_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, + const char *subset_name, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ + cairo_status_t status; + const char *data = NULL; /* squelch bogus compiler warning */ + unsigned long length = 0; /* squelch bogus compiler warning */ + unsigned int i; + + status = _cairo_cff_font_create (font_subset, &font, subset_name); + if (unlikely (status)) + return status; + + status = cairo_cff_font_generate (font, &data, &length); + if (unlikely (status)) + goto fail1; + + cff_subset->ps_name = strdup (font->ps_name); + if (unlikely (cff_subset->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + if (font->font_name) { + cff_subset->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->family_name_utf8 = NULL; + } + + cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); + if (unlikely (cff_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em; + + cff_subset->x_min = (double)font->x_min/font->units_per_em; + cff_subset->y_min = (double)font->y_min/font->units_per_em; + cff_subset->x_max = (double)font->x_max/font->units_per_em; + cff_subset->y_max = (double)font->y_max/font->units_per_em; + cff_subset->ascent = (double)font->ascent/font->units_per_em; + cff_subset->descent = (double)font->descent/font->units_per_em; + + cff_subset->data = _cairo_malloc (length); + if (unlikely (cff_subset->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail4; + } + + memcpy (cff_subset->data, data, length); + cff_subset->data_length = length; + + cairo_cff_font_destroy (font); + + return CAIRO_STATUS_SUCCESS; + + fail4: + free (cff_subset->widths); + fail3: + free (cff_subset->family_name_utf8); + fail2: + free (cff_subset->ps_name); + fail1: + cairo_cff_font_destroy (font); + + return status; +} + +void +_cairo_cff_subset_fini (cairo_cff_subset_t *subset) +{ + free (subset->ps_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, + const char *subset_name) +{ + cairo_status_t status; + cairo_cff_font_t *font; + + font = _cairo_malloc (sizeof (cairo_cff_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->backend = NULL; + 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 fail1; + + font->subset_font_name = strdup (subset_name); + if (unlikely (font->subset_font_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + font->ps_name = strdup (subset_name); + if (unlikely (font->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + font->font_name = NULL; + + font->x_min = 0; + font->y_min = 0; + font->x_max = 0; + font->y_max = 0; + font->ascent = 0; + font->descent = 0; + + font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int)); + if (unlikely (font->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + + font->data_length = 0; + font->data = NULL; + font->data_end = NULL; + + status = cff_dict_init (&font->top_dict); + if (unlikely (status)) + goto fail4; + + status = cff_dict_init (&font->private_dict); + if (unlikely (status)) + goto fail5; + + cff_index_init (&font->strings_index); + cff_index_init (&font->charstrings_index); + cff_index_init (&font->global_sub_index); + 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; + font->fd_local_sub_index = NULL; + font->fdselect_subset = NULL; + font->fd_subset_map = NULL; + font->private_dict_offset = NULL; + + *font_return = font; + + return CAIRO_STATUS_SUCCESS; + +fail5: + _cairo_hash_table_destroy (font->top_dict); +fail4: + free (font->widths); +fail3: + free (font->font_name); + free (font->ps_name); +fail2: + free (font->subset_font_name); +fail1: + _cairo_array_fini (&font->output); + free (font); + return status; +} + +static cairo_int_status_t +cairo_cff_font_fallback_generate (cairo_cff_font_t *font, + cairo_type2_charstrings_t *type2_subset, + const char **data, + unsigned long *length) +{ + cairo_int_status_t status; + cff_header_t header; + cairo_array_t *charstring; + unsigned char buf[40]; + unsigned char *end_buf, *end_buf2; + unsigned int i; + int sid; + + /* Create header */ + header.major = 1; + header.minor = 0; + header.header_size = 4; + header.offset_size = 4; + font->header = &header; + + /* 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); + end_buf = encode_integer (end_buf, type2_subset->y_max); + status = cff_dict_set_operands (font->top_dict, + FONTBBOX_OP, buf, end_buf - buf); + if (unlikely (status)) + return status; + + end_buf = encode_integer_max (buf, 0); + status = cff_dict_set_operands (font->top_dict, + CHARSTRINGS_OP, buf, end_buf - buf); + if (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; + + 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; + } 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++) { + charstring = _cairo_array_index(&type2_subset->charstrings, i); + + status = cff_index_append (&font->charstrings_subset_index, + _cairo_array_index (charstring, 0), + _cairo_array_num_elements (charstring)); + + if (unlikely (status)) + 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; + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, + const char *subset_name, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */ + cairo_status_t status; + const char *data = NULL; /* squelch bogus compiler warning */ + unsigned long length = 0; /* squelch bogus compiler warning */ + unsigned int i; + cairo_type2_charstrings_t type2_subset; + + status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name); + if (unlikely (status)) + return status; + + status = _cairo_type2_charstrings_init (&type2_subset, font_subset); + if (unlikely (status)) + goto fail1; + + status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length); + if (unlikely (status)) + goto fail2; + + 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); + goto fail2; + } + + cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); + if (unlikely (cff_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + cff_subset->widths[i] = (double)type2_subset.widths[i]/1000; + + cff_subset->x_min = (double)type2_subset.x_min/1000; + cff_subset->y_min = (double)type2_subset.y_min/1000; + cff_subset->x_max = (double)type2_subset.x_max/1000; + cff_subset->y_max = (double)type2_subset.y_max/1000; + cff_subset->ascent = (double)type2_subset.y_max/1000; + cff_subset->descent = (double)type2_subset.y_min/1000; + + cff_subset->data = _cairo_malloc (length); + if (unlikely (cff_subset->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail4; + } + + memcpy (cff_subset->data, data, length); + cff_subset->data_length = length; + + _cairo_type2_charstrings_fini (&type2_subset); + cairo_cff_font_destroy (font); + + return CAIRO_STATUS_SUCCESS; + + fail4: + free (cff_subset->widths); + fail3: + free (cff_subset->ps_name); + fail2: + _cairo_type2_charstrings_fini (&type2_subset); + fail1: + cairo_cff_font_destroy (font); + + return status; +} + +void +_cairo_cff_fallback_fini (cairo_cff_subset_t *subset) +{ + free (subset->ps_name); + free (subset->widths); + free (subset->data); +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ 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 0000000000..aaddeb7f7b --- /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 0000000000..d52afd3136 --- /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 0000000000..f40faefba8 --- /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 new file mode 100644 index 0000000000..5fc05a64e9 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-clip-private.h @@ -0,0 +1,198 @@ +/* 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_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; + +struct _cairo_clip_path { + cairo_reference_count_t ref_count; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + double tolerance; + cairo_antialias_t antialias; + cairo_clip_path_t *prev; +}; + +struct _cairo_clip { + cairo_rectangle_int_t extents; + cairo_clip_path_t *path; + + cairo_box_t *boxes; + int num_boxes; + + cairo_region_t *region; + cairo_bool_t is_region; + + cairo_box_t embedded_box; +}; + +cairo_private cairo_clip_t * +_cairo_clip_create (void); + +cairo_private cairo_clip_path_t * +_cairo_clip_path_reference (cairo_clip_path_t *clip_path); + +cairo_private void +_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); + +cairo_private cairo_clip_t * +_cairo_clip_intersect_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_clip_t * +_cairo_clip_intersect_box (cairo_clip_t *clip, + const cairo_box_t *box); + +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 (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 (const cairo_clip_t *clip, + cairo_surface_t *dst, + int dst_x, int dst_y); + +cairo_private cairo_clip_t * +_cairo_clip_from_boxes (const cairo_boxes_t *boxes); + +cairo_private cairo_region_t * +_cairo_clip_get_region (const cairo_clip_t *clip); + +cairo_private cairo_bool_t +_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_box (const cairo_clip_t *clip, + const cairo_box_t *box); + +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 0000000000..e3f4891e3f --- /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 0000000000..85feaa649e --- /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 0000000000..2ac1d32b13 --- /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 new file mode 100644 index 0000000000..d499bf0ade --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-clip.c @@ -0,0 +1,838 @@ +/* -*- 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-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 freed_pool_t clip_path_pool; +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) +{ + cairo_clip_path_t *clip_path; + + clip_path = _freed_pool_get (&clip_path_pool); + if (unlikely (clip_path == NULL)) { + 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->prev = clip->path; + clip->path = clip_path; + + return clip_path; +} + +cairo_clip_path_t * +_cairo_clip_path_reference (cairo_clip_path_t *clip_path) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); + + _cairo_reference_count_inc (&clip_path->ref_count); + + return clip_path; +} + +void +_cairo_clip_path_destroy (cairo_clip_path_t *clip_path) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count)) + return; + + _cairo_path_fixed_fini (&clip_path->path); + + if (clip_path->prev != NULL) + _cairo_clip_path_destroy (clip_path->prev); + + _freed_pool_put (&clip_path_pool, clip_path); +} + +cairo_clip_t * +_cairo_clip_create (void) +{ + 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_destroy (cairo_clip_t *clip) +{ + if (clip == NULL || _cairo_clip_is_all_clipped (clip)) + return; + + if (clip->path != NULL) + _cairo_clip_path_destroy (clip->path); + + if (clip->boxes != &clip->embedded_box) + free (clip->boxes); + cairo_region_destroy (clip->region); + + _freed_pool_put (&clip_pool, clip); +} + +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, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_clip_path_t *clip_path; + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_box_t box; + + 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) + return _cairo_clip_set_all_clipped (clip); + + 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_clip_set_all_clipped (clip); + + status = _cairo_path_fixed_init_copy (&clip_path->path, path); + if (unlikely (status)) + return _cairo_clip_set_all_clipped (clip); + + clip_path->fill_rule = fill_rule; + clip_path->tolerance = tolerance; + clip_path->antialias = antialias; + + 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 *cp_a, *cp_b; + + /* are both all-clipped or no-clip? */ + if (clip_a == clip_b) + return TRUE; + + /* 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; + + /* XXX compare reduced polygons? */ + + if (cp_a->antialias != cp_b->antialias) + return FALSE; + + if (cp_a->tolerance != cp_b->tolerance) + return FALSE; + + if (cp_a->fill_rule != cp_b->fill_rule) + return FALSE; + + if (! _cairo_path_fixed_equal (&cp_a->path, + &cp_b->path)) + return FALSE; + + cp_a = cp_a->prev; + cp_b = cp_b->prev; + } + + return cp_a == NULL && cp_b == NULL; +} + +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; + + 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_clip_set_all_clipped (clip); + + status = _cairo_path_fixed_init_copy (&clip_path->path, + &other_path->path); + if (unlikely (status)) + return _cairo_clip_set_all_clipped (clip); + + _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 clip; +} + +cairo_clip_t * +_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty) +{ + int fx, fy, i; + cairo_clip_path_t *clip_path; + + 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->extents.x += tx; + clip->extents.y += ty; + + if (clip->path == NULL) + return clip; + + clip_path = clip->path; + 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; + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +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 (clip_path->prev) + clip = _cairo_clip_intersect_clip_path_transformed (clip, + clip_path->prev, + m); + + 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); + } + + 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 (const cairo_clip_t *clip, + const cairo_composite_rectangles_t *extents) +{ + const cairo_rectangle_int_t *rect; + + rect = extents->is_bounded ? &extents->bounded : &extents->unbounded; + return _cairo_clip_contains_rectangle (clip, rect); +} + +void +_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip) +{ + int i; + + if (clip == NULL) { + fprintf (stream, "no clip\n"); + return; + } + + if (_cairo_clip_is_all_clipped (clip)) { + fprintf (stream, "clip: all-clipped\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)); + } + + 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); + } +} + +const cairo_rectangle_int_t * +_cairo_clip_get_extents (const cairo_clip_t *clip) +{ + if (clip == NULL) + return &_cairo_unbounded_rectangle; + + if (_cairo_clip_is_all_clipped (clip)) + return &_cairo_empty_rectangle; + + return &clip->extents; +} + +const cairo_rectangle_list_t _cairo_rectangles_nil = + { CAIRO_STATUS_NO_MEMORY, NULL, 0 }; +static const cairo_rectangle_list_t _cairo_rectangles_not_representable = + { CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, NULL, 0 }; + +static cairo_bool_t +_cairo_clip_int_rect_to_user (cairo_gstate_t *gstate, + cairo_rectangle_int_t *clip_rect, + cairo_rectangle_t *user_rect) +{ + cairo_bool_t is_tight; + + double x1 = clip_rect->x; + double y1 = clip_rect->y; + double x2 = clip_rect->x + (int) clip_rect->width; + double y2 = clip_rect->y + (int) clip_rect->height; + + _cairo_gstate_backend_to_user_rectangle (gstate, + &x1, &y1, &x2, &y2, + &is_tight); + + user_rect->x = x1; + user_rect->y = y1; + user_rect->width = x2 - x1; + user_rect->height = y2 - y1; + + return is_tight; +} + +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; + if (status == CAIRO_STATUS_CLIP_NOT_REPRESENTABLE) + return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; + + list = _cairo_malloc (sizeof (*list)); + 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; +} + +cairo_rectangle_list_t * +_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) +{ +#define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S)) + + cairo_rectangle_list_t *list; + cairo_rectangle_t *rectangles = NULL; + cairo_region_t *region = NULL; + int n_rects = 0; + int i; + + if (clip == NULL) + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); + + if (_cairo_clip_is_all_clipped (clip)) + goto DONE; + + if (! _cairo_clip_is_region (clip)) + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); + + 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) { + rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t)); + if (unlikely (rectangles == NULL)) { + return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < n_rects; ++i) { + cairo_rectangle_int_t clip_rect; + + cairo_region_get_rectangle (region, i, &clip_rect); + + if (! _cairo_clip_int_rect_to_user (gstate, + &clip_rect, + &rectangles[i])) + { + free (rectangles); + return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); + } + } + } + + DONE: + list = _cairo_malloc (sizeof (cairo_rectangle_list_t)); + if (unlikely (list == NULL)) { + free (rectangles); + return ERROR_LIST (CAIRO_STATUS_NO_MEMORY); + } + + list->status = CAIRO_STATUS_SUCCESS; + list->rectangles = rectangles; + list->num_rectangles = n_rects; + return list; + +#undef ERROR_LIST +} + +/** + * cairo_rectangle_list_destroy: + * @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 + * be dereferenced. + * + * Since: 1.4 + **/ +void +cairo_rectangle_list_destroy (cairo_rectangle_list_t *rectangle_list) +{ + if (rectangle_list == NULL || rectangle_list == &_cairo_rectangles_nil || + rectangle_list == &_cairo_rectangles_not_representable) + return; + + free (rectangle_list->rectangles); + free (rectangle_list); +} + +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 0000000000..9367044f3f --- /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 0000000000..cdb4cae82d --- /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 0000000000..b0be2661eb --- /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 0000000000..36e5f77d8b --- /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 0000000000..b7a5b8e5c0 --- /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 new file mode 100644 index 0000000000..c2a11a1773 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-color.c @@ -0,0 +1,193 @@ +/* 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" + +static cairo_color_t const cairo_color_white = { + 1.0, 1.0, 1.0, 1.0, + 0xffff, 0xffff, 0xffff, 0xffff +}; + +static cairo_color_t const cairo_color_black = { + 0.0, 0.0, 0.0, 1.0, + 0x0, 0x0, 0x0, 0xffff +}; + +static cairo_color_t const cairo_color_transparent = { + 0.0, 0.0, 0.0, 0.0, + 0x0, 0x0, 0x0, 0x0 +}; + +static cairo_color_t const cairo_color_magenta = { + 1.0, 0.0, 1.0, 1.0, + 0xffff, 0x0, 0xffff, 0xffff +}; + +const cairo_color_t * +_cairo_stock_color (cairo_stock_t stock) +{ + switch (stock) { + case CAIRO_STOCK_WHITE: + return &cairo_color_white; + case CAIRO_STOCK_BLACK: + return &cairo_color_black; + case CAIRO_STOCK_TRANSPARENT: + return &cairo_color_transparent; + + case CAIRO_STOCK_NUM_COLORS: + default: + ASSERT_NOT_REACHED; + /* If the user can get here somehow, give a color that indicates a + * problem. */ + return &cairo_color_magenta; + } +} + +/* Convert a double in [0.0, 1.0] to an integer in [0, 65535] + * 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) +{ + return d * 65535.0 + 0.5; +} + +static void +_cairo_color_compute_shorts (cairo_color_t *color) +{ + color->red_short = _cairo_color_double_to_short (color->red * color->alpha); + color->green_short = _cairo_color_double_to_short (color->green * color->alpha); + color->blue_short = _cairo_color_double_to_short (color->blue * color->alpha); + color->alpha_short = _cairo_color_double_to_short (color->alpha); +} + +void +_cairo_color_init_rgba (cairo_color_t *color, + double red, double green, double blue, + double alpha) +{ + color->red = red; + color->green = green; + color->blue = blue; + color->alpha = alpha; + + _cairo_color_compute_shorts (color); +} + +void +_cairo_color_multiply_alpha (cairo_color_t *color, + double alpha) +{ + color->alpha *= alpha; + + _cairo_color_compute_shorts (color); +} + +void +_cairo_color_get_rgba (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha) +{ + *red = color->red; + *green = color->green; + *blue = color->blue; + *alpha = color->alpha; +} + +void +_cairo_color_get_rgba_premultiplied (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha) +{ + *red = color->red * color->alpha; + *green = color->green * color->alpha; + *blue = color->blue * color->alpha; + *alpha = color->alpha; +} + +/* NB: This function works both for unmultiplied and premultiplied colors */ +cairo_bool_t +_cairo_color_equal (const cairo_color_t *color_a, + const cairo_color_t *color_b) +{ + if (color_a == color_b) + return TRUE; + + if (color_a->alpha_short != color_b->alpha_short) + return FALSE; + + if (color_a->alpha_short == 0) + return TRUE; + + return color_a->red_short == color_b->red_short && + color_a->green_short == color_b->green_short && + color_a->blue_short == color_b->blue_short; +} + +cairo_bool_t +_cairo_color_stop_equal (const cairo_color_stop_t *color_a, + const cairo_color_stop_t *color_b) +{ + if (color_a == color_b) + return TRUE; + + return color_a->alpha_short == color_b->alpha_short && + color_a->red_short == color_b->red_short && + color_a->green_short == color_b->green_short && + color_a->blue_short == color_b->blue_short; +} + +cairo_content_t +_cairo_color_get_content (const cairo_color_t *color) +{ + if (CAIRO_COLOR_IS_OPAQUE (color)) + return CAIRO_CONTENT_COLOR; + + if (color->red_short == 0 && + color->green_short == 0 && + color->blue_short == 0) + { + return CAIRO_CONTENT_ALPHA; + } + + return CAIRO_CONTENT_COLOR_ALPHA; +} diff --git a/gfx/cairo/cairo/src/cairo-combsort-inline.h b/gfx/cairo/cairo/src/cairo-combsort-inline.h new file mode 100644 index 0000000000..d359faeb5b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-combsort-inline.h @@ -0,0 +1,94 @@ +/* + * 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 + */ + +/* This fragment implements a comb sort (specifically combsort11) */ +#ifndef _HAVE_CAIRO_COMBSORT_NEWGAP +#define _HAVE_CAIRO_COMBSORT_NEWGAP +static inline unsigned int +_cairo_combsort_newgap (unsigned int gap) +{ + gap = 10 * gap / 13; + if (gap == 9 || gap == 10) + gap = 11; + if (gap < 1) + gap = 1; + return gap; +} +#endif + +#define CAIRO_COMBSORT_DECLARE(NAME, TYPE, CMP) \ +static void \ +NAME (TYPE *base, unsigned int nmemb) \ +{ \ + unsigned int gap = nmemb; \ + unsigned int i, j; \ + int swapped; \ + do { \ + gap = _cairo_combsort_newgap (gap); \ + swapped = gap > 1; \ + for (i = 0; i < nmemb-gap ; i++) { \ + j = i + gap; \ + if (CMP (base[i], base[j]) > 0 ) { \ + TYPE tmp; \ + tmp = base[i]; \ + base[i] = base[j]; \ + base[j] = tmp; \ + swapped = 1; \ + } \ + } \ + } while (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 new file mode 100644 index 0000000000..4562bea666 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-compiler-private.h @@ -0,0 +1,246 @@ +/* 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_COMPILER_PRIVATE_H +#define CAIRO_COMPILER_PRIVATE_H + +#include "cairo.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +/* Size in bytes of buffer to use off the stack per functions. + * Mostly used by text functions. For larger allocations, they'll + * malloc(). */ +#ifndef CAIRO_STACK_BUFFER_SIZE +#define CAIRO_STACK_BUFFER_SIZE (512 * sizeof (int)) +#endif + +#define CAIRO_STACK_ARRAY_LENGTH(T) (CAIRO_STACK_BUFFER_SIZE / sizeof(T)) + +/* + * The goal of this block is to define the following macros for + * providing faster linkage to functions in the public API for calls + * from within cairo. + * + * slim_hidden_proto(f) + * slim_hidden_proto_no_warn(f) + * + * Declares `f' as a library internal function and hides the + * function from the global symbol table. This macro must be + * expanded after `f' has been declared with a prototype but before + * any calls to the function are seen by the compiler. The no_warn + * variant inhibits warnings about the return value being unused at + * 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 procedure linkage table (PLT). + * + * slim_hidden_def(f) + * + * Exports `f' back to the global symbol table. This macro must be + * expanded right after the function definition and only for symbols + * hidden previously with slim_hidden_proto(). The macro works by + * adding a global entry to the symbol table which points at the + * internal name of `f' created by slim_hidden_proto(). + * + * Functions in the public API which aren't called by the library + * don't need to be hidden and re-exported using the slim hidden + * macros. + */ +#if __GNUC__ >= 3 && defined(__ELF__) && !defined(__sun) +# define slim_hidden_proto(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private +# define slim_hidden_proto_no_warn(name) slim_hidden_proto1(name, slim_hidden_int_name(name)) cairo_private_no_warn +# define slim_hidden_def(name) slim_hidden_def1(name, slim_hidden_int_name(name)) +# define slim_hidden_int_name(name) INT_##name +# define slim_hidden_proto1(name, internal) \ + extern __typeof (name) name \ + __asm__ (slim_hidden_asmname (internal)) +# define slim_hidden_def1(name, internal) \ + extern __typeof (name) EXT_##name __asm__(slim_hidden_asmname(name)) \ + __attribute__((__alias__(slim_hidden_asmname(internal)))) +# define slim_hidden_ulp slim_hidden_ulp1(__USER_LABEL_PREFIX__) +# define slim_hidden_ulp1(x) slim_hidden_ulp2(x) +# define slim_hidden_ulp2(x) #x +# define slim_hidden_asmname(name) slim_hidden_asmname1(name) +# define slim_hidden_asmname1(name) slim_hidden_ulp #name +#else +# define slim_hidden_proto(name) int _cairo_dummy_prototype(void) +# define slim_hidden_proto_no_warn(name) int _cairo_dummy_prototype(void) +# define slim_hidden_def(name) int _cairo_dummy_prototype(void) +#endif + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +#define CAIRO_PRINTF_FORMAT(fmt_index, va_index) \ + __attribute__((__format__(__printf__, fmt_index, va_index))) +#else +#define CAIRO_PRINTF_FORMAT(fmt_index, va_index) +#endif + +/* slim_internal.h */ +#define CAIRO_HAS_HIDDEN_SYMBOLS 1 +#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 +#else /* not gcc >= 3.3 and not Sun Studio >= 8 */ +#define cairo_private_no_warn +#undef CAIRO_HAS_HIDDEN_SYMBOLS +#endif + +#ifndef WARN_UNUSED_RESULT +#define WARN_UNUSED_RESULT +#endif +/* Add attribute(warn_unused_result) if supported */ +#define cairo_warn WARN_UNUSED_RESULT +#define cairo_private cairo_private_no_warn cairo_warn + +/* This macro allow us to deprecate a function by providing an alias + for the old function name to the new function name. With this + macro, binary compatibility is preserved. The macro only works on + some platforms --- tough. + + Meanwhile, new definitions in the public header file break the + source code so that it will no longer link against the old + symbols. Instead it will give a descriptive error message + indicating that the old function has been deprecated by the new + function. +*/ +#if __GNUC__ >= 2 && defined(__ELF__) +# define CAIRO_FUNCTION_ALIAS(old, new) \ + extern __typeof (new) old \ + __asm__ ("" #old) \ + __attribute__((__alias__("" #new))) +#else +# define CAIRO_FUNCTION_ALIAS(old, new) +#endif + +/* + * Cairo uses the following function attributes in order to improve the + * generated code (effectively by manual inter-procedural analysis). + * + * 'cairo_pure': The function is only allowed to read from its arguments + * and global memory (i.e. following a pointer argument or + * accessing a shared variable). The return value should + * only depend on its arguments, and for an identical set of + * arguments should return the same value. + * + * 'cairo_const': The function is only allowed to read from its arguments. + * It is not allowed to access global memory. The return + * value should only depend its arguments, and for an + * identical set of arguments should return the same value. + * This is currently the most strict function attribute. + * + * Both these function attributes allow gcc to perform CSE and + * constant-folding, with 'cairo_const 'also guaranteeing that pointer contents + * do not change across the function call. + */ +#if __GNUC__ >= 3 +#define cairo_pure __attribute__((pure)) +#define cairo_const __attribute__((const)) +#define cairo_always_inline inline __attribute__((always_inline)) +#else +#define cairo_pure +#define cairo_const +#define cairo_always_inline inline +#endif + +#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#define unlikely(expr) (__builtin_expect (!!(expr), 0)) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#endif + +#ifndef __GNUC__ +#undef __attribute__ +#define __attribute__(x) +#endif + +#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER) +#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 +#ifndef __cplusplus +#undef inline +#define inline __inline +#endif +#endif + +#if defined(_MSC_VER) && defined(_M_IX86) +/* When compiling with /Gy and /OPT:ICF identical functions will be folded in together. + The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and + will never be folded into another one. Something like this might eventually + be needed for GCC but it seems fine for now. */ +#define CAIRO_ENSURE_UNIQUE \ + do { \ + char file[] = __FILE__; \ + __asm { \ + __asm jmp __internal_skip_line_no \ + __asm _emit (__COUNTER__ & 0xff) \ + __asm _emit ((__COUNTER__>>8) & 0xff) \ + __asm _emit ((__COUNTER__>>16) & 0xff)\ + __asm _emit ((__COUNTER__>>24) & 0xff)\ + __asm lea eax, dword ptr file \ + __asm __internal_skip_line_no: \ + }; \ + } while (0) +#else +#define CAIRO_ENSURE_UNIQUE do { } while (0) +#endif + +#ifdef __STRICT_ANSI__ +#undef inline +#define inline __inline__ +#endif + +#endif diff --git a/gfx/cairo/cairo/src/cairo-composite-rectangles-private.h b/gfx/cairo/cairo/src/cairo-composite-rectangles-private.h new file mode 100644 index 0000000000..fd7728995d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-composite-rectangles-private.h @@ -0,0 +1,159 @@ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H +#define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-error-private.h" +#include "cairo-pattern-private.h" + +CAIRO_BEGIN_DECLS + +/* Rectangles that take part in a composite operation. + * + * The source and mask track the extents of the respective patterns in device + * space. The unbounded rectangle is essentially the clip rectangle. And the + * intersection of all is the bounded rectangle, which is the minimum extents + * the operation may require. Whether or not the operation is actually bounded + * is tracked in the is_bounded boolean. + * + */ +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 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, + 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, + 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, + 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_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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, + 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, + 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 new file mode 100644 index 0000000000..f102eddbce --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-composite-rectangles.c @@ -0,0 +1,499 @@ +/* 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-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, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + if (_cairo_clip_is_all_clipped (clip)) + return FALSE; + + extents->surface = surface; + extents->op = op; + + _cairo_surface_get_extents (surface, &extents->destination); + extents->clip = NULL; + + 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); + + 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, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + surface, op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + 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, + const cairo_clip_t *clip) +{ + 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, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + surface, op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + 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, clip); +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, + 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_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + surface, op, source, clip)) + { + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, surface->is_vector, &extents->mask); + + return _cairo_composite_rectangles_intersect (extents, clip); +} + +cairo_int_status_t +_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, + cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_clip_t *clip) +{ + if (! _cairo_composite_rectangles_init (extents, + 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, 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, + 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, + const cairo_clip_t *clip, + cairo_bool_t *overlap) +{ + cairo_status_t status; + + 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, + glyphs, num_glyphs, + &extents->mask, + overlap); + if (unlikely (status)) + return status; + + 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 0000000000..cce1c09f79 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-compositor-private.h @@ -0,0 +1,366 @@ +/* -*- 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, + cairo_bool_t permit_subpixel_antialiasing); +}; + +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 0000000000..6f35ee04f9 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-compositor.c @@ -0,0 +1,269 @@ +/* -*- 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, + surface->permit_subpixel_antialiasing); + + 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 0000000000..7972c1ac5e --- /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 0000000000..1dfc46f3ac --- /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 0000000000..9ad75bdbf8 --- /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-damage-private.h b/gfx/cairo/cairo/src/cairo-damage-private.h new file mode 100644 index 0000000000..97b177e86b --- /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 0000000000..97d9fe9095 --- /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 new file mode 100644 index 0000000000..6acdea9dde --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-debug.c @@ -0,0 +1,325 @@ +/* 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 + */ + +#include "cairoint.h" +#include "cairo-image-surface-private.h" + +/** + * cairo_debug_reset_static_data: + * + * Resets all static data within cairo to its original state, + * (ie. identical to the state at the time of program invocation). For + * example, all caches within cairo will be flushed empty. + * + * This function is intended to be useful when using memory-checking + * tools such as valgrind. When valgrind's memcheck analyzes a + * cairo-using program without a call to cairo_debug_reset_static_data(), + * it will report all data reachable via cairo's static objects as + * "still reachable". Calling cairo_debug_reset_static_data() just prior + * to program termination will make it easier to get squeaky clean + * reports from valgrind. + * + * WARNING: It is only safe to call this function when there are no + * active cairo objects remaining, (ie. the appropriate destroy + * 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) +{ + CAIRO_MUTEX_INITIALIZE (); + + _cairo_scaled_font_map_destroy (); + + _cairo_toy_font_face_reset_static_data (); + +#if CAIRO_HAS_FT_FONT + _cairo_ft_font_reset_static_data (); +#endif + +#if CAIRO_HAS_WIN32_FONT + _cairo_win32_font_reset_static_data (); +#endif + + _cairo_intern_string_reset_static_data (); + + _cairo_scaled_font_reset_static_data (); + + _cairo_pattern_reset_static_data (); + + _cairo_clip_reset_static_data (); + + _cairo_image_reset_static_data (); + + _cairo_image_compositor_reset_static_data (); + +#if CAIRO_HAS_DRM_SURFACE + _cairo_drm_device_reset_static_data (); +#endif + + _cairo_default_context_reset_static_data (); + + CAIRO_MUTEX_FINALIZE (); +} + +#if HAVE_VALGRIND +void +_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface) +{ + const cairo_image_surface_t *image = (cairo_image_surface_t *) surface; + const uint8_t *bits; + int row, width; + + if (surface == NULL) + return; + + if (! RUNNING_ON_VALGRIND) + return; + + bits = image->data; + switch (image->format) { + case CAIRO_FORMAT_A1: + width = (image->width + 7)/8; + break; + case CAIRO_FORMAT_A8: + width = image->width; + break; + case CAIRO_FORMAT_RGB16_565: + 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 */ + return; + } + + for (row = 0; row < image->height; row++) { + VALGRIND_CHECK_MEM_IS_DEFINED (bits, width); + /* and then silence any future valgrind warnings */ + VALGRIND_MAKE_MEM_DEFINED (bits, width); + bits += image->stride; + } +} +#endif + + +#if 0 +void +_cairo_image_surface_write_to_ppm (cairo_image_surface_t *isurf, const char *fn) +{ + char *fmt; + if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) + fmt = "P6"; + else if (isurf->format == CAIRO_FORMAT_A8) + fmt = "P5"; + else + return; + + FILE *fp = fopen(fn, "wb"); + if (!fp) + return; + + fprintf (fp, "%s %d %d 255\n", fmt,isurf->width, isurf->height); + for (int j = 0; j < isurf->height; j++) { + unsigned char *row = isurf->data + isurf->stride * j; + for (int i = 0; i < isurf->width; i++) { + if (isurf->format == CAIRO_FORMAT_ARGB32 || isurf->format == CAIRO_FORMAT_RGB24) { + unsigned char r = *row++; + unsigned char g = *row++; + unsigned char b = *row++; + *row++; + putc(r, fp); + putc(g, fp); + putc(b, fp); + } else { + unsigned char a = *row++; + putc(a, fp); + } + } + } + + fclose (fp); + + fprintf (stderr, "Wrote %s\n", fn); +} +#endif + +static cairo_status_t +_print_move_to (void *closure, + const cairo_point_t *point) +{ + fprintf (closure, + " %f %f m", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_line_to (void *closure, + const cairo_point_t *point) +{ + fprintf (closure, + " %f %f l", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + fprintf (closure, + " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_print_close (void *closure) +{ + fprintf (closure, " h"); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_debug_print_path (FILE *stream, const cairo_path_fixed_t *path) +{ + cairo_status_t status; + cairo_box_t box; + + 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, + _print_move_to, + _print_line_to, + _print_curve_to, + _print_close, + stream); + assert (status == CAIRO_STATUS_SUCCESS); + + 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 0000000000..fd159b4967 --- /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 0000000000..d2c9cae10d --- /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 new file mode 100644 index 0000000000..b51a6399cd --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-deflate-stream.c @@ -0,0 +1,156 @@ +/* -*- 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 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. + * + * Author(s): + * Adrian Johnson + */ + +#include "cairoint.h" + +#if CAIRO_HAS_DEFLATE_STREAM + +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" +#include + +#define BUFFER_SIZE 16384 + +typedef struct _cairo_deflate_stream { + cairo_output_stream_t base; + cairo_output_stream_t *output; + z_stream zlib_stream; + unsigned char input_buf[BUFFER_SIZE]; + unsigned char output_buf[BUFFER_SIZE]; +} cairo_deflate_stream_t; + +static void +cairo_deflate_stream_deflate (cairo_deflate_stream_t *stream, cairo_bool_t flush) +{ + int ret; + cairo_bool_t finished; + + do { + ret = deflate (&stream->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH); + if (flush || stream->zlib_stream.avail_out == 0) + { + _cairo_output_stream_write (stream->output, + stream->output_buf, + BUFFER_SIZE - stream->zlib_stream.avail_out); + stream->zlib_stream.next_out = stream->output_buf; + stream->zlib_stream.avail_out = BUFFER_SIZE; + } + + finished = TRUE; + if (stream->zlib_stream.avail_in != 0) + finished = FALSE; + if (flush && ret != Z_STREAM_END) + finished = FALSE; + + } while (!finished); + + stream->zlib_stream.next_in = stream->input_buf; +} + +static cairo_status_t +_cairo_deflate_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) +{ + cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base; + unsigned int count; + const unsigned char *p = data; + + while (length) { + count = length; + if (count > BUFFER_SIZE - stream->zlib_stream.avail_in) + count = BUFFER_SIZE - stream->zlib_stream.avail_in; + memcpy (stream->input_buf + stream->zlib_stream.avail_in, p, count); + p += count; + stream->zlib_stream.avail_in += count; + length -= count; + + if (stream->zlib_stream.avail_in == BUFFER_SIZE) + cairo_deflate_stream_deflate (stream, FALSE); + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_cairo_deflate_stream_close (cairo_output_stream_t *base) +{ + cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base; + + cairo_deflate_stream_deflate (stream, TRUE); + deflateEnd (&stream->zlib_stream); + + return _cairo_output_stream_get_status (stream->output); +} + +cairo_output_stream_t * +_cairo_deflate_stream_create (cairo_output_stream_t *output) +{ + cairo_deflate_stream_t *stream; + + if (output->status) + return _cairo_output_stream_create_in_error (output->status); + + 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; + } + + _cairo_output_stream_init (&stream->base, + _cairo_deflate_stream_write, + NULL, + _cairo_deflate_stream_close); + stream->output = output; + + stream->zlib_stream.zalloc = Z_NULL; + stream->zlib_stream.zfree = Z_NULL; + stream->zlib_stream.opaque = Z_NULL; + + if (deflateInit (&stream->zlib_stream, Z_DEFAULT_COMPRESSION) != Z_OK) { + free (stream); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + stream->zlib_stream.next_in = stream->input_buf; + stream->zlib_stream.avail_in = 0; + stream->zlib_stream.next_out = stream->output_buf; + stream->zlib_stream.avail_out = BUFFER_SIZE; + + return &stream->base; +} + +#endif /* CAIRO_HAS_DEFLATE_STREAM */ diff --git a/gfx/cairo/cairo/src/cairo-deprecated.h b/gfx/cairo/cairo/src/cairo-deprecated.h new file mode 100644 index 0000000000..7a56aadbfe --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-deprecated.h @@ -0,0 +1,123 @@ +/* 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): + * Carl D. Worth + */ + +#ifndef CAIRO_DEPRECATED_H +#define CAIRO_DEPRECATED_H + +#define CAIRO_FONT_TYPE_ATSUI CAIRO_FONT_TYPE_QUARTZ + +/* Obsolete functions. These definitions exist to coerce the compiler + * into providing a little bit of guidance with its error + * messages. The idea is to help users port their old code without + * having to dig through lots of documentation. + * + * The first set of REPLACED_BY functions is for functions whose names + * have just been changed. So fixing these up is mechanical, (and + * automated by means of the cairo/util/cairo-api-update script. + * + * The second set of DEPRECATED_BY functions is for functions where + * the replacement is used in a different way, (ie. different + * arguments, multiple functions instead of one, etc). Fixing these up + * will require a bit more work on the user's part, (and hopefully we + * can get cairo-api-update to find these and print some guiding + * information). + */ +#define cairo_current_font_extents cairo_current_font_extents_REPLACED_BY_cairo_font_extents +#define cairo_get_font_extents cairo_get_font_extents_REPLACED_BY_cairo_font_extents +#define cairo_current_operator cairo_current_operator_REPLACED_BY_cairo_get_operator +#define cairo_current_tolerance cairo_current_tolerance_REPLACED_BY_cairo_get_tolerance +#define cairo_current_point cairo_current_point_REPLACED_BY_cairo_get_current_point +#define cairo_current_fill_rule cairo_current_fill_rule_REPLACED_BY_cairo_get_fill_rule +#define cairo_current_line_width cairo_current_line_width_REPLACED_BY_cairo_get_line_width +#define cairo_current_line_cap cairo_current_line_cap_REPLACED_BY_cairo_get_line_cap +#define cairo_current_line_join cairo_current_line_join_REPLACED_BY_cairo_get_line_join +#define cairo_current_miter_limit cairo_current_miter_limit_REPLACED_BY_cairo_get_miter_limit +#define cairo_current_matrix cairo_current_matrix_REPLACED_BY_cairo_get_matrix +#define cairo_current_target_surface cairo_current_target_surface_REPLACED_BY_cairo_get_target +#define cairo_get_status cairo_get_status_REPLACED_BY_cairo_status +#define cairo_concat_matrix cairo_concat_matrix_REPLACED_BY_cairo_transform +#define cairo_scale_font cairo_scale_font_REPLACED_BY_cairo_set_font_size +#define cairo_select_font cairo_select_font_REPLACED_BY_cairo_select_font_face +#define cairo_transform_font cairo_transform_font_REPLACED_BY_cairo_set_font_matrix +#define cairo_transform_point cairo_transform_point_REPLACED_BY_cairo_user_to_device +#define cairo_transform_distance cairo_transform_distance_REPLACED_BY_cairo_user_to_device_distance +#define cairo_inverse_transform_point cairo_inverse_transform_point_REPLACED_BY_cairo_device_to_user +#define cairo_inverse_transform_distance cairo_inverse_transform_distance_REPLACED_BY_cairo_device_to_user_distance +#define cairo_init_clip cairo_init_clip_REPLACED_BY_cairo_reset_clip +#define cairo_surface_create_for_image cairo_surface_create_for_image_REPLACED_BY_cairo_image_surface_create_for_data +#define cairo_default_matrix cairo_default_matrix_REPLACED_BY_cairo_identity_matrix +#define cairo_matrix_set_affine cairo_matrix_set_affine_REPLACED_BY_cairo_matrix_init +#define cairo_matrix_set_identity cairo_matrix_set_identity_REPLACED_BY_cairo_matrix_init_identity +#define cairo_pattern_add_color_stop cairo_pattern_add_color_stop_REPLACED_BY_cairo_pattern_add_color_stop_rgba +#define cairo_set_rgb_color cairo_set_rgb_color_REPLACED_BY_cairo_set_source_rgb +#define cairo_set_pattern cairo_set_pattern_REPLACED_BY_cairo_set_source +#define cairo_xlib_surface_create_for_pixmap_with_visual cairo_xlib_surface_create_for_pixmap_with_visual_REPLACED_BY_cairo_xlib_surface_create +#define cairo_xlib_surface_create_for_window_with_visual cairo_xlib_surface_create_for_window_with_visual_REPLACED_BY_cairo_xlib_surface_create +#define cairo_xcb_surface_create_for_pixmap_with_visual cairo_xcb_surface_create_for_pixmap_with_visual_REPLACED_BY_cairo_xcb_surface_create +#define cairo_xcb_surface_create_for_window_with_visual cairo_xcb_surface_create_for_window_with_visual_REPLACED_BY_cairo_xcb_surface_create +#define cairo_ps_surface_set_dpi cairo_ps_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution +#define cairo_pdf_surface_set_dpi cairo_pdf_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution +#define cairo_svg_surface_set_dpi cairo_svg_surface_set_dpi_REPLACED_BY_cairo_surface_set_fallback_resolution +#define cairo_atsui_font_face_create_for_atsu_font_id cairo_atsui_font_face_create_for_atsu_font_id_REPLACED_BY_cairo_quartz_font_face_create_for_atsu_font_id + +#define cairo_current_path cairo_current_path_DEPRECATED_BY_cairo_copy_path +#define cairo_current_path_flat cairo_current_path_flat_DEPRECATED_BY_cairo_copy_path_flat +#define cairo_get_path cairo_get_path_DEPRECATED_BY_cairo_copy_path +#define cairo_get_path_flat cairo_get_path_flat_DEPRECATED_BY_cairo_get_path_flat +#define cairo_set_alpha cairo_set_alpha_DEPRECATED_BY_cairo_set_source_rgba_OR_cairo_paint_with_alpha +#define cairo_show_surface cairo_show_surface_DEPRECATED_BY_cairo_set_source_surface_AND_cairo_paint +#define cairo_copy cairo_copy_DEPRECATED_BY_cairo_create_AND_MANY_INDIVIDUAL_FUNCTIONS +#define cairo_surface_set_repeat cairo_surface_set_repeat_DEPRECATED_BY_cairo_pattern_set_extend +#define cairo_surface_set_matrix cairo_surface_set_matrix_DEPRECATED_BY_cairo_pattern_set_matrix +#define cairo_surface_get_matrix cairo_surface_get_matrix_DEPRECATED_BY_cairo_pattern_get_matrix +#define cairo_surface_set_filter cairo_surface_set_filter_DEPRECATED_BY_cairo_pattern_set_filter +#define cairo_surface_get_filter cairo_surface_get_filter_DEPRECATED_BY_cairo_pattern_get_filter +#define cairo_matrix_create cairo_matrix_create_DEPRECATED_BY_cairo_matrix_t +#define cairo_matrix_destroy cairo_matrix_destroy_DEPRECATED_BY_cairo_matrix_t +#define cairo_matrix_copy cairo_matrix_copy_DEPRECATED_BY_cairo_matrix_t +#define cairo_matrix_get_affine cairo_matrix_get_affine_DEPRECATED_BY_cairo_matrix_t +#define cairo_set_target_surface cairo_set_target_surface_DEPRECATED_BY_cairo_create +#define cairo_set_target_image cairo_set_target_image_DEPRECATED_BY_cairo_image_surface_create_for_data +#define cairo_set_target_pdf cairo_set_target_pdf_DEPRECATED_BY_cairo_pdf_surface_create +#define cairo_set_target_png cairo_set_target_png_DEPRECATED_BY_cairo_surface_write_to_png +#define cairo_set_target_ps cairo_set_target_ps_DEPRECATED_BY_cairo_ps_surface_create +#define cairo_set_target_quartz cairo_set_target_quartz_DEPRECATED_BY_cairo_quartz_surface_create +#define cairo_set_target_win32 cairo_set_target_win32_DEPRECATED_BY_cairo_win32_surface_create +#define cairo_set_target_xcb cairo_set_target_xcb_DEPRECATED_BY_cairo_xcb_surface_create +#define cairo_set_target_drawable cairo_set_target_drawable_DEPRECATED_BY_cairo_xlib_surface_create +#define cairo_get_status_string cairo_get_status_string_DEPRECATED_BY_cairo_status_AND_cairo_status_to_string +#define cairo_status_string cairo_status_string_DEPRECATED_BY_cairo_status_AND_cairo_status_to_string + +#endif /* CAIRO_DEPRECATED_H */ diff --git a/gfx/cairo/cairo/src/cairo-device-private.h b/gfx/cairo/cairo/src/cairo-device-private.h new file mode 100644 index 0000000000..6eb44f3b63 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-device-private.h @@ -0,0 +1,86 @@ +/* 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. + * + * Contributors(s): + * Chris Wilson + */ + +#ifndef _CAIRO_DEVICE_PRIVATE_H_ +#define _CAIRO_DEVICE_PRIVATE_H_ + +#include "cairo-compiler-private.h" +#include "cairo-mutex-private.h" +#include "cairo-reference-count-private.h" +#include "cairo-types-private.h" + +struct _cairo_device { + cairo_reference_count_t ref_count; + cairo_status_t status; + cairo_user_data_array_t user_data; + + const cairo_device_backend_t *backend; + + cairo_recursive_mutex_t mutex; + unsigned mutex_depth; + + cairo_bool_t finished; +}; + +struct _cairo_device_backend { + cairo_device_type_t type; + + void (*lock) (void *device); + void (*unlock) (void *device); + + cairo_warn cairo_status_t (*flush) (void *device); + void (*finish) (void *device); + void (*destroy) (void *device); +}; + +cairo_private cairo_device_t * +_cairo_device_create_in_error (cairo_status_t status); + +cairo_private void +_cairo_device_init (cairo_device_t *device, + const cairo_device_backend_t *backend); + +cairo_private cairo_status_t +_cairo_device_set_error (cairo_device_t *device, + cairo_status_t error); + +slim_hidden_proto_no_warn (cairo_device_reference); +slim_hidden_proto (cairo_device_acquire); +slim_hidden_proto (cairo_device_release); +slim_hidden_proto (cairo_device_flush); +slim_hidden_proto (cairo_device_finish); +slim_hidden_proto (cairo_device_destroy); + +#endif /* _CAIRO_DEVICE_PRIVATE_H_ */ diff --git a/gfx/cairo/cairo/src/cairo-device.c b/gfx/cairo/cairo/src/cairo-device.c new file mode 100644 index 0000000000..965c84c651 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-device.c @@ -0,0 +1,545 @@ +/* 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. + * + * Contributors(s): + * Chris Wilson + */ + +#include "cairoint.h" +#include "cairo-device-private.h" +#include "cairo-error-private.h" + +/** + * SECTION:cairo-device + * @Title: cairo_device_t + * @Short_Description: interface to underlying rendering system + * @See_Also: #cairo_surface_t + * + * Devices are the abstraction Cairo employs for the rendering system + * used by a #cairo_surface_t. You can get the device of a surface using + * cairo_surface_get_device(). + * + * Devices are created using custom functions specific to the rendering + * system you want to use. See the documentation for the surface types + * for those functions. + * + * An important function that devices fulfill is sharing access to the + * rendering system between Cairo and your application. If you want to + * access a device directly that you used to draw to with Cairo, you must + * first call cairo_device_flush() to ensure that Cairo finishes all + * operations on the device and resets it to a clean state. + * + * Cairo also provides the functions cairo_device_acquire() and + * cairo_device_release() to synchronize access to the rendering system + * in a multithreaded environment. This is done internally, but can also + * be used by applications. + * + * Putting this all together, a function that works with devices should + * look something like this: + * + * void + * my_device_modifying_function (cairo_device_t *device) + * { + * cairo_status_t status; + * + * // Ensure the device is properly reset + * cairo_device_flush (device); + * // Try to acquire the device + * status = cairo_device_acquire (device); + * if (status != CAIRO_STATUS_SUCCESS) { + * printf ("Failed to acquire the device: %s\n", cairo_status_to_string (status)); + * return; + * } + * + * // Do the custom operations on the device here. + * // But do not call any Cairo functions that might acquire devices. + * + * // Release the device when done. + * cairo_device_release (device); + * } + * + * + * Please refer to the documentation of each backend for + * additional usage requirements, guarantees provided, and + * 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, + CAIRO_STATUS_NO_MEMORY, +}; + +static const cairo_device_t _mismatch_device = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_DEVICE_TYPE_MISMATCH, +}; + +static const cairo_device_t _invalid_device = { + CAIRO_REFERENCE_COUNT_INVALID, + CAIRO_STATUS_DEVICE_ERROR, +}; + +cairo_device_t * +_cairo_device_create_in_error (cairo_status_t status) +{ + switch (status) { + case CAIRO_STATUS_NO_MEMORY: + return (cairo_device_t *) &_nil_device; + case CAIRO_STATUS_DEVICE_ERROR: + return (cairo_device_t *) &_invalid_device; + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: + return (cairo_device_t *) &_mismatch_device; + + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + /* fall-through */ + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_STATUS: + case CAIRO_STATUS_INVALID_FORMAT: + case CAIRO_STATUS_INVALID_VISUAL: + case CAIRO_STATUS_READ_ERROR: + case CAIRO_STATUS_WRITE_ERROR: + case CAIRO_STATUS_FILE_NOT_FOUND: + case CAIRO_STATUS_TEMP_FILE_ERROR: + case CAIRO_STATUS_INVALID_STRIDE: + case CAIRO_STATUS_INVALID_SIZE: + case CAIRO_STATUS_INVALID_RESTORE: + case CAIRO_STATUS_INVALID_POP_GROUP: + case CAIRO_STATUS_NO_CURRENT_POINT: + case CAIRO_STATUS_INVALID_MATRIX: + case CAIRO_STATUS_NULL_POINTER: + case CAIRO_STATUS_INVALID_STRING: + case CAIRO_STATUS_INVALID_PATH_DATA: + case CAIRO_STATUS_SURFACE_FINISHED: + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_DASH: + case CAIRO_STATUS_INVALID_DSC_COMMENT: + case CAIRO_STATUS_INVALID_INDEX: + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: + case CAIRO_STATUS_FONT_TYPE_MISMATCH: + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + case CAIRO_STATUS_USER_FONT_ERROR: + case CAIRO_STATUS_NEGATIVE_COUNT: + case CAIRO_STATUS_INVALID_CLUSTERS: + case CAIRO_STATUS_INVALID_SLANT: + 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; + } +} + +void +_cairo_device_init (cairo_device_t *device, + const cairo_device_backend_t *backend) +{ + CAIRO_REFERENCE_COUNT_INIT (&device->ref_count, 1); + device->status = CAIRO_STATUS_SUCCESS; + + device->backend = backend; + + CAIRO_RECURSIVE_MUTEX_INIT (device->mutex); + device->mutex_depth = 0; + + device->finished = FALSE; + + _cairo_user_data_array_init (&device->user_data); +} + +/** + * cairo_device_reference: + * @device: a #cairo_device_t + * + * Increases the reference count on @device by one. This prevents + * @device from being destroyed until a matching call to + * cairo_device_destroy() is made. + * + * Use cairo_device_get_reference_count() to get the number of references + * to a #cairo_device_t. + * + * Return value: the referenced #cairo_device_t. + * + * Since: 1.10 + **/ +cairo_device_t * +cairo_device_reference (cairo_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return device; + } + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); + _cairo_reference_count_inc (&device->ref_count); + + return device; +} +slim_hidden_def (cairo_device_reference); + +/** + * cairo_device_status: + * @device: a #cairo_device_t + * + * Checks whether an error has previously occurred for this + * device. + * + * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if + * the device is in an error state. + * + * Since: 1.10 + **/ +cairo_status_t +cairo_device_status (cairo_device_t *device) +{ + if (device == NULL) + return CAIRO_STATUS_NULL_POINTER; + + return device->status; +} + +/** + * cairo_device_flush: + * @device: a #cairo_device_t + * + * Finish any pending operations for the device and also restore any + * temporary modifications cairo has made to the device's state. + * This function must be called before switching from using the + * device with Cairo to operating on it directly with native APIs. + * If the device doesn't support direct access, then this function + * does nothing. + * + * This function may acquire devices. + * + * Since: 1.10 + **/ +void +cairo_device_flush (cairo_device_t *device) +{ + cairo_status_t status; + + if (device == NULL || device->status) + return; + + if (device->finished) + return; + + if (device->backend->flush != NULL) { + status = device->backend->flush (device); + if (unlikely (status)) + status = _cairo_device_set_error (device, status); + } +} +slim_hidden_def (cairo_device_flush); + +/** + * cairo_device_finish: + * @device: the #cairo_device_t to finish + * + * This function finishes the device and drops all references to + * external resources. All surfaces, fonts and other objects created + * for this @device will be finished, too. + * Further operations on the @device will not affect the @device but + * will instead trigger a %CAIRO_STATUS_DEVICE_FINISHED error. + * + * When the last call to cairo_device_destroy() decreases the + * reference count to zero, cairo will call cairo_device_finish() if + * it hasn't been called already, before freeing the resources + * associated with the device. + * + * This function may acquire devices. + * + * Since: 1.10 + **/ +void +cairo_device_finish (cairo_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return; + } + + if (device->finished) + return; + + cairo_device_flush (device); + + 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); + +/** + * cairo_device_destroy: + * @device: a #cairo_device_t + * + * Decreases the reference count on @device by one. If the result is + * zero, then @device and all associated resources are freed. See + * cairo_device_reference(). + * + * This function may acquire devices if the last reference was dropped. + * + * Since: 1.10 + **/ +void +cairo_device_destroy (cairo_device_t *device) +{ + cairo_user_data_array_t user_data; + + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return; + } + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&device->ref_count)); + if (! _cairo_reference_count_dec_and_test (&device->ref_count)) + return; + + cairo_device_finish (device); + + assert (device->mutex_depth == 0); + CAIRO_MUTEX_FINI (device->mutex); + + user_data = device->user_data; + + device->backend->destroy (device); + + _cairo_user_data_array_fini (&user_data); + +} +slim_hidden_def (cairo_device_destroy); + +/** + * cairo_device_get_type: + * @device: a #cairo_device_t + * + * This function returns the type of the device. See #cairo_device_type_t + * for available types. + * + * Return value: The type of @device. + * + * Since: 1.10 + **/ +cairo_device_type_t +cairo_device_get_type (cairo_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + { + return CAIRO_DEVICE_TYPE_INVALID; + } + + return device->backend->type; +} + +/** + * cairo_device_acquire: + * @device: a #cairo_device_t + * + * Acquires the @device for the current thread. This function will block + * until no other thread has acquired the device. + * + * If the return value is %CAIRO_STATUS_SUCCESS, you successfully acquired the + * device. From now on your thread owns the device and no other thread will be + * able to acquire it until a matching call to cairo_device_release(). It is + * allowed to recursively acquire the device multiple times from the same + * thread. + * + * You must never acquire two different devices at the same time + * unless this is explicitly allowed. Otherwise the possibility of deadlocks + * exist. + * + * As various Cairo functions can acquire devices when called, these functions + * may also cause deadlocks when you call them with an acquired device. So you + * must not have a device acquired when calling them. These functions are + * marked in the documentation. + * + * + * Return value: %CAIRO_STATUS_SUCCESS on success or an error code if + * the device is in an error state and could not be + * acquired. After a successful call to cairo_device_acquire(), + * a matching call to cairo_device_release() is required. + * + * Since: 1.10 + **/ +cairo_status_t +cairo_device_acquire (cairo_device_t *device) +{ + if (device == NULL) + return CAIRO_STATUS_SUCCESS; + + if (unlikely (device->status)) + return device->status; + + if (unlikely (device->finished)) + return _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_FINISHED); + + CAIRO_MUTEX_LOCK (device->mutex); + if (device->mutex_depth++ == 0) { + if (device->backend->lock != NULL) + device->backend->lock (device); + } + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_device_acquire); + +/** + * cairo_device_release: + * @device: a #cairo_device_t + * + * Releases a @device previously acquired using cairo_device_acquire(). See + * that function for details. + * + * Since: 1.10 + **/ +void +cairo_device_release (cairo_device_t *device) +{ + if (device == NULL) + return; + + assert (device->mutex_depth > 0); + + if (--device->mutex_depth == 0) { + if (device->backend->unlock != NULL) + device->backend->unlock (device); + } + + CAIRO_MUTEX_UNLOCK (device->mutex); +} +slim_hidden_def (cairo_device_release); + +cairo_status_t +_cairo_device_set_error (cairo_device_t *device, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; + + _cairo_status_set_error (&device->status, status); + + return _cairo_error (status); +} + +/** + * cairo_device_get_reference_count: + * @device: a #cairo_device_t + * + * Returns the current reference count of @device. + * + * Return value: the current reference count of @device. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.10 + **/ +unsigned int +cairo_device_get_reference_count (cairo_device_t *device) +{ + if (device == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&device->ref_count); +} + +/** + * cairo_device_get_user_data: + * @device: a #cairo_device_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @device using the + * specified key. If no user data has been attached with the given + * key this function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + * + * Since: 1.10 + **/ +void * +cairo_device_get_user_data (cairo_device_t *device, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&device->user_data, + key); +} + +/** + * cairo_device_set_user_data: + * @device: a #cairo_device_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the #cairo_device_t + * @destroy: a #cairo_destroy_func_t which will be called when the + * #cairo_t is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @device. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * 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_device_set_user_data (cairo_device_t *device, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count)) + return device->status; + + return _cairo_user_data_array_set_data (&device->user_data, + key, user_data, destroy); +} diff --git a/gfx/cairo/cairo/src/cairo-directfb-surface.c b/gfx/cairo/cairo/src/cairo-directfb-surface.c new file mode 100644 index 0000000000..0deedf0d54 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-directfb-surface.c @@ -0,0 +1,545 @@ +/* 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 Chris Wilson + * + * Contributor(s): + * 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 + +#include +#include +#include +#include +#include + +slim_hidden_proto(cairo_directfb_surface_create); + +typedef struct _cairo_dfb_surface { + cairo_image_surface_t image; + + IDirectFB *dfb; + IDirectFBSurface *dfb_surface; + + unsigned blit_premultiplied : 1; +} cairo_dfb_surface_t; + +static cairo_content_t +_directfb_format_to_content (DFBSurfacePixelFormat format) +{ + cairo_content_t content = 0; + + if (DFB_PIXELFORMAT_HAS_ALPHA (format)) + content |= CAIRO_CONTENT_ALPHA; + if (DFB_COLOR_BITS_PER_PIXEL (format)) + content |= CAIRO_CONTENT_COLOR_ALPHA; + + assert(content); + return content; +} + +static inline pixman_format_code_t +_directfb_to_pixman_format (DFBSurfacePixelFormat format) +{ + switch (format) { + case DSPF_UNKNOWN: return 0; + case DSPF_ARGB1555: return PIXMAN_a1r5g5b5; + case DSPF_RGB16: return PIXMAN_r5g6b5; + case DSPF_RGB24: return PIXMAN_r8g8b8; + case DSPF_RGB32: return PIXMAN_x8r8g8b8; + case DSPF_ARGB: return PIXMAN_a8r8g8b8; + case DSPF_A8: return PIXMAN_a8; + case DSPF_YUY2: return PIXMAN_yuy2; + case DSPF_RGB332: return PIXMAN_r3g3b2; + case DSPF_UYVY: return 0; + case DSPF_I420: return 0; + case DSPF_YV12: return PIXMAN_yv12; + 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 0; + case DSPF_AYUV: return 0; + case DSPF_A4: return PIXMAN_a4; + 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 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) +{ + switch ((int) format) { + case PIXMAN_a1r5g5b5: return DSPF_ARGB1555; + case PIXMAN_r5g6b5: return DSPF_RGB16; + case PIXMAN_r8g8b8: return DSPF_RGB24; + case PIXMAN_x8r8g8b8: return DSPF_RGB32; + case PIXMAN_a8r8g8b8: return DSPF_ARGB; + case PIXMAN_a8: return DSPF_A8; + case PIXMAN_yuy2: return DSPF_YUY2; + case PIXMAN_r3g3b2: return DSPF_RGB332; + case PIXMAN_yv12: return DSPF_YV12; + case PIXMAN_a1: return DSPF_A1; /* bit reversed, oops */ + case PIXMAN_a4r4g4b4: return DSPF_ARGB4444; + case PIXMAN_a4: return DSPF_A4; + case PIXMAN_x4r4g4b4: return DSPF_RGB444; + case PIXMAN_x1r5g5b5: return DSPF_RGB555; +#if DFB_NUM_PIXELFORMATS >= 29 + case PIXMAN_x1b5g5r5: return DSPF_BGR555; +#endif + default: return 0; + } +} + +static cairo_bool_t +_directfb_get_operator (cairo_operator_t operator, + DFBSurfaceBlendFunction *ret_srcblend, + DFBSurfaceBlendFunction *ret_dstblend) +{ + DFBSurfaceBlendFunction srcblend = DSBF_ONE; + DFBSurfaceBlendFunction dstblend = DSBF_ZERO; + + switch (operator) { + case CAIRO_OPERATOR_CLEAR: + srcblend = DSBF_ZERO; + dstblend = DSBF_ZERO; + break; + case CAIRO_OPERATOR_SOURCE: + srcblend = DSBF_ONE; + dstblend = DSBF_ZERO; + break; + case CAIRO_OPERATOR_OVER: + srcblend = DSBF_ONE; + dstblend = DSBF_INVSRCALPHA; + break; + case CAIRO_OPERATOR_IN: + srcblend = DSBF_DESTALPHA; + dstblend = DSBF_ZERO; + break; + case CAIRO_OPERATOR_OUT: + srcblend = DSBF_INVDESTALPHA; + dstblend = DSBF_ZERO; + break; + case CAIRO_OPERATOR_ATOP: + srcblend = DSBF_DESTALPHA; + dstblend = DSBF_INVSRCALPHA; + break; + case CAIRO_OPERATOR_DEST: + srcblend = DSBF_ZERO; + dstblend = DSBF_ONE; + break; + case CAIRO_OPERATOR_DEST_OVER: + srcblend = DSBF_INVDESTALPHA; + dstblend = DSBF_ONE; + break; + case CAIRO_OPERATOR_DEST_IN: + srcblend = DSBF_ZERO; + dstblend = DSBF_SRCALPHA; + break; + case CAIRO_OPERATOR_DEST_OUT: + srcblend = DSBF_ZERO; + dstblend = DSBF_INVSRCALPHA; + break; + case CAIRO_OPERATOR_DEST_ATOP: + srcblend = DSBF_INVDESTALPHA; + dstblend = DSBF_SRCALPHA; + break; + case CAIRO_OPERATOR_XOR: + srcblend = DSBF_INVDESTALPHA; + dstblend = DSBF_INVSRCALPHA; + break; + case CAIRO_OPERATOR_ADD: + srcblend = DSBF_ONE; + dstblend = DSBF_ONE; + break; + case CAIRO_OPERATOR_SATURATE: + /* XXX This does not work. */ +#if 0 + srcblend = DSBF_SRCALPHASAT; + dstblend = DSBF_ONE; + break; +#endif + 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 FALSE; + } + + *ret_srcblend = srcblend; + *ret_dstblend = dstblend; + + return TRUE; +} +#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_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_dfb_surface_t *dst = abstract_surface; + DFBSurfaceDrawingFlags flags; + DFBSurfaceBlendFunction sblend; + DFBSurfaceBlendFunction dblend; + DFBRectangle r[n_rects]; + int i; + + D_DEBUG_AT (CairoDFB_Render, + "%s( dst=%p, op=%d, color=%p, rects=%p, n_rects=%d ).\n", + __FUNCTION__, dst, op, color, rects, n_rects); + + if (! dst->supported_destination) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _directfb_get_operator (op, &sblend, &dblend)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (CAIRO_COLOR_IS_OPAQUE (color)) { + 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) ? DSDRAW_NOFX : DSDRAW_BLEND; + dst->dfbsurface->SetDrawingFlags (dst->dfbsurface, flags); + if (flags & DSDRAW_BLEND) { + dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend); + dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend); + } + + dst->dfbsurface->SetColor (dst->dfbsurface, + color->red_short >> 8, + color->green_short >> 8, + color->blue_short >> 8, + color->alpha_short >> 8); + + for (i = 0; i < n_rects; i++) { + r[i].x = rects[i].x; + r[i].y = rects[i].y; + r[i].w = rects[i].width; + r[i].h = rects[i].height; + } + + RUN_CLIPPED (dst, NULL, NULL, + dst->dfbsurface->FillRectangles (dst->dfbsurface, r, n_rects)); + + return CAIRO_STATUS_SUCCESS; +} +#endif + +static cairo_surface_backend_t +_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, +}; + +cairo_surface_t * +cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface) +{ + 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); + + dfbsurface->GetPixelFormat (dfbsurface, &format); + dfbsurface->GetSize (dfbsurface, &width, &height); + + 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)); + + /* 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->dfb_surface = dfbsurface; + dfbsurface->AddRef (dfbsurface); + + dfbsurface->GetCapabilities (dfbsurface, &caps); + if (caps & DSCAPS_PREMULTIPLIED) + surface->blit_premultiplied = TRUE; + + return &surface->image.base; +} +slim_hidden_def(cairo_directfb_surface_create); diff --git a/gfx/cairo/cairo/src/cairo-directfb.h b/gfx/cairo/cairo/src/cairo-directfb.h new file mode 100644 index 0000000000..e3d818c668 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-directfb.h @@ -0,0 +1,67 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2003 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 + */ + +/* + * Environment variables affecting the backend: + * + * %CAIRO_DIRECTFB_NO_ACCEL (boolean) + * if found, disables acceleration at all + * + * %CAIRO_DIRECTFB_ARGB_FONT (boolean) + * if found, enables using ARGB fonts instead of A8 + */ + +#ifndef CAIRO_DIRECTFB_H +#define CAIRO_DIRECTFB_H + +#include "cairo.h" + +#if CAIRO_HAS_DIRECTFB_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_DIRECTFB_SURFACE*/ +# error Cairo was not compiled with support for the directfb backend +#endif /*CAIRO_HAS_DIRECTFB_SURFACE*/ + +#endif /*CAIRO_DIRECTFB_H*/ diff --git a/gfx/cairo/cairo/src/cairo-drm.h b/gfx/cairo/cairo/src/cairo-drm.h new file mode 100644 index 0000000000..907610dcde --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-drm.h @@ -0,0 +1,120 @@ +/* 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. + */ + +#ifndef CAIRO_DRM_H +#define CAIRO_DRM_H + +#include "cairo.h" + +#if CAIRO_HAS_DRM_SURFACE + +CAIRO_BEGIN_DECLS + +struct udev_device; + +cairo_public cairo_device_t * +cairo_drm_device_get (struct udev_device *device); + +cairo_public cairo_device_t * +cairo_drm_device_get_for_fd (int fd); + +cairo_public cairo_device_t * +cairo_drm_device_default (void); + +cairo_public int +cairo_drm_device_get_fd (cairo_device_t *device); + +cairo_public void +cairo_drm_device_throttle (cairo_device_t *device); + +cairo_public cairo_surface_t * +cairo_drm_surface_create (cairo_device_t *device, + cairo_format_t format, + int width, int height); + +cairo_public cairo_surface_t * +cairo_drm_surface_create_for_name (cairo_device_t *device, + unsigned int name, + cairo_format_t format, + int width, int height, int stride); + +cairo_public cairo_surface_t * +cairo_drm_surface_create_from_cacheable_image (cairo_device_t *device, + cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_drm_surface_enable_scan_out (cairo_surface_t *surface); + +cairo_public unsigned int +cairo_drm_surface_get_handle (cairo_surface_t *surface); + +cairo_public unsigned int +cairo_drm_surface_get_name (cairo_surface_t *surface); + +cairo_public cairo_format_t +cairo_drm_surface_get_format (cairo_surface_t *surface); + +cairo_public int +cairo_drm_surface_get_width (cairo_surface_t *surface); + +cairo_public int +cairo_drm_surface_get_height (cairo_surface_t *surface); + +cairo_public int +cairo_drm_surface_get_stride (cairo_surface_t *surface); + +/* XXX map/unmap, general surface layer? */ + +/* Rough outline, culled from a conversation on IRC: + * map() returns an image-surface representation of the drm-surface, + * which you unmap() when you are finished, i.e. map() pulls the buffer back + * from the GPU, maps it into the CPU domain and gives you direct access to + * the pixels. With the unmap(), the buffer is ready to be used again by the + * GPU and *until* the unmap(), all operations will be done in software. + * + * (Technically calling cairo_surface_flush() on the underlying drm-surface + * will also disassociate the mapping.) +*/ +cairo_public cairo_surface_t * +cairo_drm_surface_map_to_image (cairo_surface_t *surface); + +cairo_public void +cairo_drm_surface_unmap (cairo_surface_t *drm_surface, + cairo_surface_t *image_surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_DRM_SURFACE */ +# error Cairo was not compiled with support for the DRM backend +#endif /* CAIRO_HAS_DRM_SURFACE */ + +#endif /* CAIRO_DRM_H */ 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 0000000000..bf704c630f --- /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-error-inline.h b/gfx/cairo/cairo/src/cairo-error-inline.h new file mode 100644 index 0000000000..9126c5e61e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-error-inline.h @@ -0,0 +1,52 @@ +/* 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_ERROR_INLINE_H_ +#define _CAIRO_ERROR_INLINE_H_ + +#include "cairo-error-private.h" + +CAIRO_BEGIN_DECLS + +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; +} + +#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 new file mode 100644 index 0000000000..1ab57ddf84 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-error-private.h @@ -0,0 +1,131 @@ +/* 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_ERROR_PRIVATE_H_ +#define _CAIRO_ERROR_PRIVATE_H_ + +#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) + +#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); + +/* hide compiler warnings when discarding the return value */ +#define _cairo_error_throw(status) do { \ + cairo_status_t status__ = _cairo_error (status); \ + (void) status__; \ +} while (0) + +CAIRO_END_DECLS + +#endif /* _CAIRO_ERROR_PRIVATE_H_ */ diff --git a/gfx/cairo/cairo/src/cairo-error.c b/gfx/cairo/cairo/src/cairo-error.c new file mode 100644 index 0000000000..1b9bd76be1 --- /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 0000000000..3f6199fe2b --- /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 0000000000..b9cd9d3ad2 --- /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.h b/gfx/cairo/cairo/src/cairo-features.h new file mode 100644 index 0000000000..6ccfd25b4f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-features.h @@ -0,0 +1,94 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2003 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., 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_FEATURES_H +#define CAIRO_FEATURES_H + +#include "cairo-platform.h" + +#ifdef __cplusplus +# define CAIRO_BEGIN_DECLS extern "C" { +# define CAIRO_END_DECLS } +#else +# define CAIRO_BEGIN_DECLS +# define CAIRO_END_DECLS +#endif + +#ifndef cairo_public +# define cairo_public +#endif + +#ifdef MOZ_PDF_PRINTING +#define CAIRO_HAS_PDF_SURFACE 1 +#endif + +#if defined(MOZ_X11) || defined(MOZ_WAYLAND) +#define CAIRO_HAS_PS_SURFACE 1 +#endif +#ifdef MOZ_X11 +#define CAIRO_HAS_XLIB_XRENDER_SURFACE 0 +#define CAIRO_HAS_XLIB_SURFACE 1 +#endif + +#if defined(MOZ_WIDGET_COCOA) || defined(MOZ_WIDGET_UIKIT) +#define CAIRO_HAS_QUARTZ_SURFACE 1 +#define CAIRO_HAS_QUARTZ_IMAGE_SURFACE 1 +#define CAIRO_HAS_QUARTZ_FONT 1 +#define CAIRO_HAS_QUARTZ_CORE_GRAPHICS 1 +#endif + +#if defined(MOZ_WIDGET_COCOA) +#define CAIRO_HAS_QUARTZ_ATSUFONTID 1 +#define CAIRO_HAS_QUARTZ_APPLICATION_SERVICES 1 +#endif + +#ifdef XP_WIN +#define CAIRO_HAS_DWRITE_FONT 1 +#define CAIRO_HAS_WIN32_FONT 1 +#define CAIRO_HAS_WIN32_SURFACE 1 +#endif + +#if (defined(MOZ_TREE_FREETYPE) && !defined(XP_WIN)) || defined(MOZ_HAVE_FREETYPE2) +#define CAIRO_HAS_FT_FONT 1 +#endif + +#define CAIRO_HAS_TEE_SURFACE 1 + +#ifdef USE_FC_FREETYPE +#define CAIRO_HAS_FC_FONT 1 +#endif + +#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 0000000000..9a4b657c8d --- /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 new file mode 100644 index 0000000000..5f9ce684c3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-fixed-private.h @@ -0,0 +1,395 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Mozilla Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 Foundation + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#ifndef CAIRO_FIXED_PRIVATE_H +#define CAIRO_FIXED_PRIVATE_H + +#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 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_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 i << CAIRO_FIXED_FRAC_BITS; +} + +/* This is the "magic number" approach to converting a double into fixed + * point as described here: + * + * http://www.stereopsis.com/sree/fpu2006.html (an overview) + * http://www.d6.com/users/checker/pdfs/gdmfp.pdf (in detail) + * + * The basic idea is to add a large enough number to the double that the + * literal floating point is moved up to the extent that it forces the + * double's value to be shifted down to the bottom of the mantissa (to make + * room for the large number being added in). Since the mantissa is, at a + * given moment in time, a fixed point integer itself, one can convert a + * float to various fixed point representations by moving around the point + * of a floating point number through arithmetic operations. This behavior + * is reliable on most modern platforms as it is mandated by the IEEE-754 + * standard for floating point arithmetic. + * + * For our purposes, a "magic number" must be carefully selected that is + * both large enough to produce the desired point-shifting effect, and also + * has no lower bits in its representation that would interfere with our + * value at the bottom of the mantissa. The magic number is calculated as + * follows: + * + * (2 ^ (MANTISSA_SIZE - FRACTIONAL_SIZE)) * 1.5 + * + * where in our case: + * - MANTISSA_SIZE for 64-bit doubles is 52 + * - FRACTIONAL_SIZE for 16.16 fixed point is 16 + * + * Although this approach provides a very large speedup of this function + * on a wide-array of systems, it does come with two caveats: + * + * 1) It uses banker's rounding as opposed to arithmetic rounding. + * 2) It doesn't function properly if the FPU is in single-precision + * mode. + */ + +/* The 16.16 number must always be available */ +#define CAIRO_MAGIC_NUMBER_FIXED_16_16 (103079215104.0) + +#if CAIRO_FIXED_BITS <= 32 +#define CAIRO_MAGIC_NUMBER_FIXED ((1LL << (52 - CAIRO_FIXED_FRAC_BITS)) * 1.5) + +/* For 32-bit fixed point numbers */ +static inline cairo_fixed_t +_cairo_fixed_from_double (double d) +{ + union { + double d; + int32_t i[2]; + } u; + + u.d = d + CAIRO_MAGIC_NUMBER_FIXED; +#ifdef FLOAT_WORDS_BIGENDIAN + return u.i[1]; +#else + return u.i[0]; +#endif +} + +#else +# error Please define a magic number for your fixed point type! +# error See cairo-fixed-private.h for details. +#endif + +static inline cairo_fixed_t +_cairo_fixed_from_26_6 (uint32_t i) +{ +#if CAIRO_FIXED_FRAC_BITS > 6 + return i << (CAIRO_FIXED_FRAC_BITS - 6); +#else + return i >> (6 - CAIRO_FIXED_FRAC_BITS); +#endif +} + +static inline cairo_fixed_t +_cairo_fixed_from_16_16 (uint32_t i) +{ +#if CAIRO_FIXED_FRAC_BITS > 16 + return i << (CAIRO_FIXED_FRAC_BITS - 16); +#else + return i >> (16 - CAIRO_FIXED_FRAC_BITS); +#endif +} + +static inline double +_cairo_fixed_to_double (cairo_fixed_t f) +{ + return ((double) f) / CAIRO_FIXED_ONE_DOUBLE; +} + +static inline int +_cairo_fixed_is_integer (cairo_fixed_t f) +{ + return (f & CAIRO_FIXED_FRAC_MASK) == 0; +} + +static inline cairo_fixed_t +_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) +{ + return _cairo_fixed_floor (f + (CAIRO_FIXED_FRAC_MASK+1)/2); +} + +static inline cairo_fixed_t +_cairo_fixed_round_down (cairo_fixed_t f) +{ + return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK/2); +} + +static inline int +_cairo_fixed_integer_part (cairo_fixed_t f) +{ + return f >> CAIRO_FIXED_FRAC_BITS; +} + +static inline int +_cairo_fixed_integer_round (cairo_fixed_t f) +{ + return _cairo_fixed_integer_part (f + (CAIRO_FIXED_FRAC_MASK+1)/2); +} + +static inline int +_cairo_fixed_integer_round_down (cairo_fixed_t f) +{ + return _cairo_fixed_integer_part (f + CAIRO_FIXED_FRAC_MASK/2); +} + +static inline int +_cairo_fixed_fractional_part (cairo_fixed_t f) +{ + return f & CAIRO_FIXED_FRAC_MASK; +} + +static inline int +_cairo_fixed_integer_floor (cairo_fixed_t f) +{ + if (f >= 0) + return f >> CAIRO_FIXED_FRAC_BITS; + else + return -((-f - 1) >> CAIRO_FIXED_FRAC_BITS) - 1; +} + +static inline int +_cairo_fixed_integer_ceil (cairo_fixed_t f) +{ + if (f > 0) + return ((f - 1)>>CAIRO_FIXED_FRAC_BITS) + 1; + else + return - ((cairo_fixed_t)(-(cairo_fixed_unsigned_t)f) >> CAIRO_FIXED_FRAC_BITS); +} + +/* A bunch of explicit 16.16 operators; we need these + * to interface with pixman and other backends that require + * 16.16 fixed point types. + */ +static inline cairo_fixed_16_16_t +_cairo_fixed_to_16_16 (cairo_fixed_t f) +{ +#if (CAIRO_FIXED_FRAC_BITS == 16) && (CAIRO_FIXED_BITS == 32) + return f; +#elif CAIRO_FIXED_FRAC_BITS > 16 + /* We're just dropping the low bits, so we won't ever got over/underflow here */ + return f >> (CAIRO_FIXED_FRAC_BITS - 16); +#else + cairo_fixed_16_16_t x; + + /* Handle overflow/underflow by clamping to the lowest/highest + * value representable as 16.16 + */ + if ((f >> CAIRO_FIXED_FRAC_BITS) < INT16_MIN) { + x = INT32_MIN; + } else if ((f >> CAIRO_FIXED_FRAC_BITS) > INT16_MAX) { + x = INT32_MAX; + } else { + x = f << (16 - CAIRO_FIXED_FRAC_BITS); + } + + return x; +#endif +} + +static inline cairo_fixed_16_16_t +_cairo_fixed_16_16_from_double (double d) +{ + union { + double d; + int32_t i[2]; + } u; + + u.d = d + CAIRO_MAGIC_NUMBER_FIXED_16_16; +#ifdef FLOAT_WORDS_BIGENDIAN + return u.i[1]; +#else + return u.i[0]; +#endif +} + +static inline int +_cairo_fixed_16_16_floor (cairo_fixed_16_16_t f) +{ + if (f >= 0) + return f >> 16; + else + return -((-f - 1) >> 16) - 1; +} + +static inline double +_cairo_fixed_16_16_to_double (cairo_fixed_16_16_t f) +{ + return ((double) f) / (double) (1 << 16); +} + +#if CAIRO_FIXED_BITS == 32 + +static inline cairo_fixed_t +_cairo_fixed_mul (cairo_fixed_t a, cairo_fixed_t b) +{ + cairo_int64_t temp = _cairo_int32x32_64_mul (a, b); + return _cairo_int64_to_int32(_cairo_int64_rsl (temp, CAIRO_FIXED_FRAC_BITS)); +} + +/* computes round (a * b / c) */ +static inline cairo_fixed_t +_cairo_fixed_mul_div (cairo_fixed_t a, cairo_fixed_t b, cairo_fixed_t c) +{ + cairo_int64_t ab = _cairo_int32x32_64_mul (a, b); + cairo_int64_t c64 = _cairo_int32_to_int64 (c); + return _cairo_int64_to_int32 (_cairo_int64_divrem (ab, c64).quo); +} + +/* computes floor (a * b / c) */ +static inline cairo_fixed_t +_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, + cairo_fixed_t x) +{ + cairo_fixed_t y, dx; + + if (x == p1->x) + return p1->y; + if (x == p2->x) + return p2->y; + + y = p1->y; + dx = p2->x - p1->x; + if (dx != 0) + y += _cairo_fixed_mul_div_floor (x - p1->x, p2->y - p1->y, dx); + + 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, + cairo_fixed_t y) +{ + cairo_fixed_t x, dy; + + if (y == p1->y) + return p1->x; + if (y == p2->y) + return p2->x; + + x = p1->x; + dy = p2->y - p1->y; + if (dy != 0) + x += _cairo_fixed_mul_div_floor (y - p1->y, p2->x - p1->x, dy); + + 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 + +#endif /* CAIRO_FIXED_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-fixed-type-private.h b/gfx/cairo/cairo/src/cairo-fixed-type-private.h new file mode 100644 index 0000000000..e9f26f6153 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-fixed-type-private.h @@ -0,0 +1,75 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Mozilla Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 Foundation + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#ifndef CAIRO_FIXED_TYPE_PRIVATE_H +#define CAIRO_FIXED_TYPE_PRIVATE_H + +#include "cairo-wideint-type-private.h" + +/* + * Fixed-point configuration + */ + +typedef int32_t cairo_fixed_16_16_t; +typedef cairo_int64_t cairo_fixed_32_32_t; +typedef cairo_int64_t cairo_fixed_48_16_t; +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 tessellator about the + * size of a fixed type. For now, it must be 32. + */ +#define CAIRO_FIXED_BITS 32 + +/* The number of fractional bits. Changing this involves + * making sure that you compute a double-to-fixed magic number. + * (see below). + */ +#define CAIRO_FIXED_FRAC_BITS 8 + +/* A signed type %CAIRO_FIXED_BITS in size; the main fixed point type */ +typedef int32_t cairo_fixed_t; + +/* An unsigned type of the same size as #cairo_fixed_t */ +typedef uint32_t cairo_fixed_unsigned_t; + +typedef struct _cairo_point { + cairo_fixed_t x; + cairo_fixed_t y; +} cairo_point_t; + +#endif /* CAIRO_FIXED_TYPE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-fixed.c b/gfx/cairo/cairo/src/cairo-fixed.c new file mode 100644 index 0000000000..03e0559235 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-fixed.c @@ -0,0 +1,39 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2003 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 + */ + +#include "cairoint.h" + +#include "cairo-fixed-private.h" diff --git a/gfx/cairo/cairo/src/cairo-font-face-twin-data.c b/gfx/cairo/cairo/src/cairo-font-face-twin-data.c new file mode 100644 index 0000000000..ff09cb2be1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-font-face-twin-data.c @@ -0,0 +1,1072 @@ +/* See cairo-font-face-twin.c for copyright info */ + +#include "cairoint.h" + +const int8_t _cairo_twin_outlines[] = { +/* 0x0 '\0' offset 0 */ + 0, 24, 42, 0, 2, 2, + 0, 24, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 24, -42, + 'l', 24, 0, + 'l', 0, 0, + 'e', + 'X', 'X', +/* 0x20 ' ' offset 28 */ + 0, 4, 0, 0, 0, 0, + /* snap_x */ + /* snap_y */ + 'e', + 'X', 'X', 'X', + 'X', 'X', +/* 0x21 '!' offset 40 */ + 0, 0, 42, 0, 1, 3, + 0, /* snap_x */ + -42, -14, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, -14, + 'm', 0, 0, + 'l', 0, 0, + 'e', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', 'X', 'X', 'X', + 'X', 'X', 'X', +/* 0x22 '"' offset 90 */ + 0, 16, 42, -28, 2, 2, + 0, 16, /* snap_x */ + -42, -28, /* snap_y */ + 'm', 0, -42, + 'l', 0, -28, + 'm', 16, -42, + 'l', 16, -28, + 'e', + 'X', +/* 0x23 '#' offset 114 */ + 0, 30, 50, 14, 2, 5, + 0, 30, /* snap_x */ + -24, -21, -15, -12, 0, /* snap_y */ + 'm', 16, -50, + 'l', 2, 14, + 'm', 28, -50, + 'l', 14, 14, + 'm', 2, -24, + 'l', 30, -24, + 'm', 0, -12, + 'l', 28, -12, + 'e', +/* 0x24 '$' offset 152 */ + 0, 28, 50, 8, 4, 4, + 0, 10, 18, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 10, -50, + 'l', 10, 8, + 'm', 18, -50, + 'l', 18, 8, + 'm', 28, -36, + 'c', 24, -42, 18, -42, 14, -42, + 'c', 10, -42, 0, -42, 0, -34, + 'c', 0, -25, 8, -24, 14, -22, + 'c', 20, -20, 28, -19, 28, -9, + 'c', 28, 0, 18, 0, 14, 0, + 'c', 10, 0, 4, 0, 0, -6, + 'e', +/* 0x25 '%' offset 224 */ + 0, 36, 42, 0, 4, 7, + 0, 14, 22, 36, /* snap_x */ + -42, -38, -28, -21, -15, -14, 0, /* snap_y */ + 'm', 10, -42, + 'c', 12, -41, 14, -40, 14, -36, + 'c', 14, -30, 11, -28, 6, -28, + 'c', 2, -28, 0, -30, 0, -34, + 'c', 0, -39, 3, -42, 8, -42, + 'l', 10, -42, + 'c', 18, -37, 28, -37, 36, -42, + 'l', 0, 0, + 'm', 28, -14, + 'c', 24, -14, 22, -11, 22, -6, + 'c', 22, -2, 24, 0, 28, 0, + 'c', 33, 0, 36, -2, 36, -8, + 'c', 36, -12, 34, -14, 30, -14, + 'l', 28, -14, + 'e', + 'X', 'X', 'X', +/* 0x26 '&' offset 323 */ + 0, 40, 42, 0, 4, 4, + 0, 10, 22, 40, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 40, -24, + 'c', 40, -27, 39, -28, 37, -28, + 'c', 29, -28, 32, 0, 12, 0, + 'c', 0, 0, 0, -8, 0, -10, + 'c', 0, -24, 22, -20, 22, -34, + 'c', 22, -45, 10, -45, 10, -34, + 'c', 10, -27, 25, 0, 36, 0, + 'c', 39, 0, 40, -1, 40, -4, + 'e', +/* 0x27 ''' offset 390 */ + 0, 4, 42, -30, 2, 2, + 0, 4, /* snap_x */ + -42, -28, /* snap_y */ + 'm', 2, -38, + 'c', -1, -38, -1, -42, 2, -42, + 'c', 6, -42, 5, -33, 0, -30, + 'e', + 'X', +/* 0x28 '(' offset 419 */ + 0, 14, 50, 14, 2, 2, + 0, 14, /* snap_x */ + -50, 14, /* snap_y */ + 'm', 14, -50, + 'c', -5, -32, -5, -5, 14, 14, + 'e', + 'X', +/* 0x29 ')' offset 441 */ + 0, 14, 50, 14, 2, 2, + 0, 14, /* snap_x */ + -15, 14, /* snap_y */ + 'm', 0, -50, + 'c', 19, -34, 19, -2, 0, 14, + 'e', + 'X', +/* 0x2a '*' offset 463 */ + 0, 20, 30, -6, 3, 3, + 0, 10, 20, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 10, -30, + 'l', 10, -6, + 'm', 0, -24, + 'l', 20, -12, + 'm', 20, -24, + 'l', 0, -12, + 'e', +/* 0x2b '+' offset 494 */ + 0, 36, 36, 0, 3, 4, + 0, 18, 36, /* snap_x */ + -21, -18, -15, 0, /* snap_y */ + 'm', 18, -36, + 'l', 18, 0, + 'm', 0, -18, + 'l', 36, -18, + 'e', +/* 0x2c ',' offset 520 */ + 0, 4, 4, 8, 2, 3, + 0, 4, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 4, -2, + 'c', 4, 1, 0, 1, 0, -2, + 'c', 0, -5, 4, -5, 4, -2, + 'c', 4, 4, 2, 6, 0, 8, + 'e', +/* 0x2d '-' offset 556 */ + 0, 36, 18, -18, 2, 4, + 0, 36, /* snap_x */ + -21, -18, -15, 0, /* snap_y */ + 'm', 0, -18, + 'l', 36, -18, + 'e', +/* 0x2e '.' offset 575 */ + 0, 4, 4, 0, 2, 3, + 0, 4, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 2, -4, + 'c', -1, -4, -1, 0, 2, 0, + 'c', 5, 0, 5, -4, 2, -4, + 'e', +/* 0x2f '/' offset 604 */ + 0, 36, 50, 14, 2, 3, + 0, 36, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 36, -50, + 'l', 0, 14, + 'e', +/* 0x30 '0' offset 622 */ + 0, 28, 42, 0, 2, 4, + 0, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 14, -42, + 'c', 9, -42, 0, -42, 0, -21, + 'c', 0, 0, 9, 0, 14, 0, + 'c', 19, 0, 28, 0, 28, -21, + 'c', 28, -42, 19, -42, 14, -42, + 'E', +/* 0x31 '1' offset 666 */ + 0, 28, 42, 0, 2, 3, + 0, 17, 28 /* snap_x */ + -42, -34, 0, /* snap_y */ + 'm', 7, -34, + 'c', 11, -35, 15, -38, 17, -42, + 'l', 17, 0, + 'e', +/* 0x32 '2' offset 691 */ + 0, 28, 42, 0, 4, 4, + 0, 2, 26, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 2, -32, + 'c', 2, -34, 2, -42, 14, -42, + 'c', 26, -42, 26, -34, 26, -32, + 'c', 26, -30, 25, -25, 10, -10, + 'l', 0, 0, + 'l', 28, 0, + 'e', +/* 0x33 '3' offset 736 */ + 0, 28, 42, 0, 2, 5, + 0, 28, /* snap_x */ + -42, -26, -21, -15, 0, /* snap_y */ + 'm', 4, -42, + 'l', 26, -42, + 'l', 14, -26, + 'c', 21, -26, 28, -26, 28, -14, + 'c', 28, 0, 17, 0, 13, 0, + 'c', 8, 0, 3, -1, 0, -8, + 'e', +/* 0x34 '4' offset 780 */ + 0, 28, 42, 0, 3, 3, + 0, 20, 30, /* snap_x */ + -42, -14, 0, /* snap_y */ + 'm', 20, 0, + 'l', 20, -42, + 'l', 0, -14, + 'l', 30, -14, + 'e', + 'X', 'X', 'X', + 'X', +/* 0x35 '5' offset 809 */ + 0, 28, 42, 0, 2, 5, + 0, 28, /* snap_x */ + -42, -28, -21, -15, 0, /* snap_y */ + 'm', 24, -42, + 'l', 4, -42, + 'l', 2, -24, + 'c', 5, -27, 10, -28, 13, -28, + 'c', 16, -28, 28, -28, 28, -14, + 'c', 28, 0, 16, 0, 13, 0, + 'c', 10, 0, 3, 0, 0, -8, + 'e', +/* 0x36 '6' offset 860 */ + 0, 28, 42, 0, 2, 5, + 0, 26, /* snap_x */ + -42, -26, -21, -15, 0, /* snap_y */ + 'm', 24, -36, + 'c', 22, -41, 19, -42, 14, -42, + 'c', 9, -42, 0, -41, 0, -19, + 'c', 0, -1, 9, 0, 13, 0, + 'c', 18, 0, 26, -3, 26, -13, + 'c', 26, -18, 23, -26, 13, -26, + 'c', 10, -26, 1, -24, 0, -14, + 'e', +/* 0x37 '7' offset 919 */ + 0, 28, 42, 0, 2, 4, + 0, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 0, -42, + 'l', 28, -42, + 'l', 8, 0, + 'e', + 'X', 'X', 'X', +/* 0x38 '8' offset 944 */ + 0, 28, 42, 0, 4, 4, + 0, 2, 26, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 14, -42, + 'c', 5, -42, 2, -40, 2, -34, + 'c', 2, -18, 28, -32, 28, -11, + 'c', 28, 0, 18, 0, 14, 0, + 'c', 10, 0, 0, 0, 0, -11, + 'c', 0, -32, 26, -18, 26, -34, + 'c', 26, -40, 23, -42, 14, -42, + 'E', +/* 0x39 '9' offset 1004 */ + 0, 28, 42, 0, 2, 5, + 0, 26, /* snap_x */ + -42, -21, -16, -15, 0, /* snap_y */ + 'm', 26, -28, + 'c', 25, -16, 13, -16, 13, -16, + 'c', 8, -16, 0, -19, 0, -29, + 'c', 0, -34, 3, -42, 13, -42, + 'c', 24, -42, 26, -32, 26, -23, + 'c', 26, -14, 24, 0, 12, 0, + 'c', 7, 0, 4, -2, 2, -6, + 'e', +/* 0x3a ':' offset 1063 */ + 0, 4, 28, 0, 2, 3, + 0, 4, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 2, -28, + 'c', -1, -28, -1, -24, 2, -24, + 'c', 5, -24, 5, -28, 2, -28, + 'm', 2, -4, + 'c', -1, -4, -1, 0, 2, 0, + 'c', 5, 0, 5, -4, 2, -4, + 'e', +/* 0x3b ';' offset 1109 */ + 0, 4, 28, 8, 2, 3, + 0, 4, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 2, -28, + 'c', -1, -28, -1, -24, 2, -24, + 'c', 5, -24, 5, -28, 2, -28, + 'm', 4, -2, + 'c', 4, 1, 0, 1, 0, -2, + 'c', 0, -5, 4, -5, 4, -2, + 'c', 4, 3, 2, 6, 0, 8, + 'e', +/* 0x3c '<' offset 1162 */ + 0, 32, 36, 0, 2, 3, + 0, 32, /* snap_x */ + -36, -18, 0, /* snap_y */ + 'm', 32, -36, + 'l', 0, -18, + 'l', 32, 0, + 'e', +/* 0x3d '=' offset 1183 */ + 0, 36, 24, -12, 2, 2, + 0, 36, /* snap_x */ + -24, -15, /* snap_y */ + 'm', 0, -24, + 'l', 36, -24, + 'm', 0, -12, + 'l', 36, -12, + 'e', + 'X', 'X', 'X', +/* 0x3e '>' offset 1209 */ + 0, 32, 36, 0, 2, 3, + 0, 32, /* snap_x */ + -36, -18, 0, /* snap_y */ + 'm', 0, -36, + 'l', 32, -18, + 'l', 0, 0, + 'e', +/* 0x3f '?' offset 1230 */ + 0, 24, 42, 0, 3, 4, + 0, 12, 24, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 0, -32, + 'c', 0, -34, 0, -42, 12, -42, + 'c', 24, -42, 24, -34, 24, -32, + 'c', 24, -29, 24, -24, 12, -20, + 'l', 12, -14, + 'm', 12, 0, + 'l', 12, 0, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', +/* 0x40 '@' offset 1288 */ + 0, 42, 42, 0, 1, 6, + 30, /* snap_x */ + -42, -32, -21, -15, -10, 0, /* snap_y */ + 'm', 30, -26, + 'c', 28, -31, 24, -32, 21, -32, + 'c', 10, -32, 10, -23, 10, -19, + 'c', 10, -13, 11, -10, 19, -10, + 'c', 30, -10, 28, -21, 30, -32, + 'c', 27, -10, 30, -10, 34, -10, + 'c', 41, -10, 42, -19, 42, -22, + 'c', 42, -34, 34, -42, 21, -42, + 'c', 9, -42, 0, -34, 0, -21, + 'c', 0, -9, 8, 0, 21, 0, + 'c', 30, 0, 34, -3, 36, -6, + 'e', +/* 0x41 'A' offset 1375 */ + 0, 32, 42, 0, 2, 3, + 0, 32, /* snap_x */ + -42, -14, 0, /* snap_y */ + 'm', 0, 0, + 'l', 16, -42, + 'l', 32, 0, + 'm', 6, -14, + 'l', 26, -14, + 'e', + 'X', 'X', 'X', + 'X', +/* 0x42 'B' offset 1406 */ + 0, 28, 42, 0, 2, 3, + 0, 28, /* snap_x */ + -42, -22, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 18, -42, + 'c', 32, -42, 32, -22, 18, -22, + 'l', 0, -22, + 'l', 18, -22, + 'c', 32, -22, 32, 0, 18, 0, + 'E', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', +/* 0x43 'C' offset 1455 */ + 0, 30, 42, 0, 2, 4, + 0, 30, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 30, -32, + 'c', 26, -42, 21, -42, 16, -42, + 'c', 2, -42, 0, -29, 0, -21, + 'c', 0, -13, 2, 0, 16, 0, + 'c', 21, 0, 26, 0, 30, -10, + 'e', +/* 0x44 'D' offset 1499 */ + 0, 28, 42, 0, 2, 2, + 0, 28, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 14, -42, + 'c', 33, -42, 33, 0, 14, 0, + 'E', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', +/* 0x45 'E' offset 1534 */ + 0, 26, 42, 0, 2, 3, + 0, 26, /* snap_x */ + -42, -22, 0, /* snap_y */ + 'm', 26, -42, + 'l', 0, -42, + 'l', 0, 0, + 'l', 26, 0, + 'm', 0, -22, + 'l', 16, -22, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', +/* 0x46 'F' offset 1572 */ + 0, 26, 42, 0, 2, 3, + 0, 26, /* snap_x */ + -42, -22, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 26, -42, + 'm', 0, -22, + 'l', 16, -22, + 'e', + 'X', 'X', 'X', + 'X', 'X', +/* 0x47 'G' offset 1604 */ + 0, 30, 42, 0, 2, 5, + 0, 30, /* snap_x */ + -42, -21, -16, -15, 0, /* snap_y */ + 'm', 30, -32, + 'c', 26, -42, 21, -42, 16, -42, + 'c', 2, -42, 0, -29, 0, -21, + 'c', 0, -13, 2, 0, 16, 0, + 'c', 28, 0, 30, -7, 30, -16, + 'l', 20, -16, + 'e', + 'X', 'X', 'X', +/* 0x48 'H' offset 1655 */ + 0, 28, 42, 0, 2, 3, + 0, 28, /* snap_x */ + -42, -22, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 28, -42, + 'l', 28, 0, + 'm', 0, -22, + 'l', 28, -22, + 'e', + 'X', +/* 0x49 'I' offset 1686 */ + 0, 0, 42, 0, 1, 2, + 0, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'e', + 'X', +/* 0x4a 'J' offset 1703 */ + 0, 20, 42, 0, 2, 3, + 0, 20, /* snap_x */ + -42, -15, 0, /* snap_y */ + 'm', 20, -42, + 'l', 20, -10, + 'c', 20, 3, 0, 3, 0, -10, + 'l', 0, -14, + 'e', +/* 0x4b 'K' offset 1731 */ + 0, 28, 42, 0, 2, 3, + 0, 28, /* snap_x */ + -42, -15, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 28, -42, + 'l', 0, -14, + 'm', 10, -24, + 'l', 28, 0, + 'e', +/* 0x4c 'L' offset 1761 */ + 0, 24, 42, 0, 2, 2, + 0, 24, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'l', 24, 0, + 'e', + 'X', 'X', 'X', + 'X', +/* 0x4d 'M' offset 1785 */ + 0, 32, 42, 0, 2, 2, + 0, 32, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 16, 0, + 'l', 32, -42, + 'l', 32, 0, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', +/* 0x4e 'N' offset 1821 */ + 0, 28, 42, 0, 2, 2, + 0, 28, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 28, 0, + 'l', 28, -42, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', +/* 0x4f 'O' offset 1851 */ + 0, 32, 42, 0, 2, 4, + 0, 32, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 16, -42, + 'c', 2, -42, 0, -29, 0, -21, + 'c', 0, -13, 2, 0, 16, 0, + 'c', 30, 0, 32, -13, 32, -21, + 'c', 32, -29, 30, -42, 16, -42, + 'E', +/* 0x50 'P' offset 1895 */ + 0, 28, 42, 0, 2, 5, + 0, 28, /* snap_x */ + -42, -21, -20, -15, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 18, -42, + 'c', 32, -42, 32, -20, 18, -20, + 'l', 0, -20, + 'e', + 'X', 'X', 'X', +/* 0x51 'Q' offset 1931 */ + 0, 32, 42, 4, 2, 4, + 0, 32, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 16, -42, + 'c', 2, -42, 0, -29, 0, -21, + 'c', 0, -13, 2, 0, 16, 0, + 'c', 30, 0, 32, -13, 32, -21, + 'c', 32, -29, 30, -42, 16, -42, + 'M', 18, -8, + 'l', 30, 4, + 'e', +/* 0x52 'R' offset 1981 */ + 0, 28, 42, 0, 2, 5, + 0, 28, /* snap_x */ + -42, -22, -21, -15, 0, /* snap_y */ + 'm', 0, 0, + 'l', 0, -42, + 'l', 18, -42, + 'c', 32, -42, 31, -22, 18, -22, + 'l', 0, -22, + 'm', 14, -22, + 'l', 28, 0, + 'e', + 'X', 'X', 'X', +/* 0x53 'S' offset 2023 */ + 0, 28, 42, 0, 2, 4, + 0, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 28, -36, + 'c', 25, -41, 21, -42, 14, -42, + 'c', 10, -42, 0, -42, 0, -34, + 'c', 0, -17, 28, -28, 28, -9, + 'c', 28, 0, 19, 0, 14, 0, + 'c', 7, 0, 3, -1, 0, -6, + 'e', +/* 0x54 'T' offset 2074 */ + 0, 28, 42, 0, 3, 4, + 0, 14, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 14, -42, + 'l', 14, 0, + 'm', 0, -42, + 'l', 28, -42, + 'e', +/* 0x55 'U' offset 2100 */ + 0, 28, 42, 0, 2, 2, + 0, 28, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, -12, + 'c', 0, 4, 28, 4, 28, -12, + 'l', 28, -42, + 'e', + 'X', +/* 0x56 'V' offset 2128 */ + 0, 32, 42, 0, 2, 2, + 0, 32, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 16, 0, + 'l', 32, -42, + 'e', + 'X', 'X', 'X', + 'X', +/* 0x57 'W' offset 2152 */ + 0, 40, 42, 0, 2, 2, + 0, 40, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 10, 0, + 'l', 20, -42, + 'l', 30, 0, + 'l', 40, -42, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', +/* 0x58 'X' offset 2188 */ + 0, 28, 42, 0, 2, 2, + 0, 28, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 28, 0, + 'm', 28, -42, + 'l', 0, 0, + 'e', + 'X', +/* 0x59 'Y' offset 2212 */ + 0, 32, 42, 0, 3, 3, + 0, 16, 32, /* snap_x */ + -42, -21, 0, /* snap_y */ + 'm', 0, -42, + 'l', 16, -22, + 'l', 16, 0, + 'm', 32, -42, + 'l', 16, -22, + 'e', +/* 0x5a 'Z' offset 2240 */ + 0, 28, 42, 0, 2, 4, + 0, 28, /* snap_x */ + -42, -21, -15, 0, /* snap_y */ + 'm', 28, 0, + 'l', 0, 0, + 'l', 28, -42, + 'l', 0, -42, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', +/* 0x5b '[' offset 2271 */ + 0, 14, 44, 0, 2, 4, + 0, 14, /* snap_x */ + -44, -21, -15, 0, /* snap_y */ + 'm', 14, -44, + 'l', 0, -44, + 'l', 0, 0, + 'l', 14, 0, + 'e', +/* 0x5c '\' offset 2296 */ + 0, 36, 50, 14, 2, 3, + 0, 36, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 0, -50, + 'l', 36, 14, + 'e', +/* 0x5d ']' offset 2314 */ + 0, 14, 44, 0, 2, 4, + 0, 14, /* snap_x */ + -44, -21, -15, 0, /* snap_y */ + 'm', 0, -44, + 'l', 14, -44, + 'l', 14, 0, + 'l', 0, 0, + 'e', +/* 0x5e '^' offset 2339 */ + 0, 32, 46, -18, 2, 3, + 0, 32, /* snap_x */ + -21, -15, 0, /* snap_y */ + 'm', 0, -18, + 'l', 16, -46, + 'l', 32, -18, + 'e', + 'X', 'X', 'X', +/* 0x5f '_' offset 2363 */ + 0, 36, 0, 0, 2, 1, + 0, 36, /* snap_x */ + 0, /* snap_y */ + 'm', 0, 0, + 'l', 36, 0, + 'e', + 'X', 'X', +/* 0x60 '`' offset 2381 */ + 0, 4, 42, -30, 2, 2, + 0, 4, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 4, -42, + 'c', 2, -40, 0, -39, 0, -32, + 'c', 0, -31, 1, -30, 2, -30, + 'c', 5, -30, 5, -34, 2, -34, + 'e', + 'X', +/* 0x61 'a' offset 2417 */ + 0, 24, 28, 0, 2, 4, + 0, 24, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 24, -28, + 'l', 24, 0, + 'm', 24, -22, + 'c', 21, -27, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -1, 24, -6, + 'e', +/* 0x62 'b' offset 2467 */ + 0, 24, 42, 0, 2, 4, + 0, 24, /* snap_x */ + -42, -28, -15, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 0, -22, + 'c', 3, -26, 6, -28, 11, -28, + 'c', 22, -28, 24, -19, 24, -14, + 'c', 24, -9, 22, 0, 11, 0, + 'c', 6, 0, 3, -2, 0, -6, + 'e', +/* 0x63 'c' offset 2517 */ + 0, 24, 28, 0, 2, 4, + 0, 24, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 24, -22, + 'c', 21, -26, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x64 'd' offset 2561 */ + 0, 24, 42, 0, 2, 4, + 0, 24, /* snap_x */ + -42, -28, -15, 0, /* snap_y */ + 'm', 24, -42, + 'l', 24, 0, + 'm', 24, -22, + 'c', 21, -26, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x65 'e' offset 2611 */ + 0, 24, 28, 0, 2, 5, + 0, 24, /* snap_x */ + -28, -21, -16, -15, 0, /* snap_y */ + 'm', 0, -16, + 'l', 24, -16, + 'c', 24, -20, 24, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x66 'f' offset 2659 */ + 0, 16, 42, 0, 3, 5, + 0, 6, 16, /* snap_x */ + -42, -28, -21, -15, 0, /* snap_y */ + 'm', 16, -42, + 'c', 8, -42, 6, -40, 6, -34, + 'l', 6, 0, + 'm', 0, -28, + 'l', 14, -28, + 'e', +/* 0x67 'g' offset 2693 */ + 0, 24, 28, 14, 2, 5, + 0, 24, /* snap_x */ + -28, -21, -15, 0, 14, /* snap_y */ + 'm', 24, -28, + 'l', 24, 4, + 'c', 23, 14, 16, 14, 13, 14, + 'c', 10, 14, 8, 14, 6, 12, + 'm', 24, -22, + 'c', 21, -26, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x68 'h' offset 2758 */ + 0, 22, 42, 0, 2, 4, + 0, 22, /* snap_x */ + -42, -28, -15, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 0, -20, + 'c', 8, -32, 22, -31, 22, -20, + 'l', 22, 0, + 'e', +/* 0x69 'i' offset 2790 */ + 0, 0, 44, 0, 1, 3, + 0, /* snap_x */ + -42, -28, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, -42, + 'm', 0, -28, + 'l', 0, 0, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', + 'X', 'X', +/* 0x6a 'j' offset 2826 */ + -8, 4, 44, 14, 3, 5, + -8, 2, 4, /* snap_x */ + -42, -21, -15, 0, 14, /* snap_y */ + 'm', 2, -42, + 'l', 2, -42, + 'm', 2, -28, + 'l', 2, 6, + 'c', 2, 13, -1, 14, -8, 14, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', +/* 0x6b 'k' offset 2870 */ + 0, 22, 42, 0, 2, 3, + 0, 22, /* snap_x */ + -42, -28, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'm', 20, -28, + 'l', 0, -8, + 'm', 8, -16, + 'l', 22, 0, + 'e', +/* 0x6c 'l' offset 2900 */ + 0, 0, 42, 0, 1, 2, + 0, /* snap_x */ + -42, 0, /* snap_y */ + 'm', 0, -42, + 'l', 0, 0, + 'e', + 'X', +/* 0x6d 'm' offset 2917 */ + 0, 44, 28, 0, 3, 3, + 0, 22, 44, /* snap_x */ + -28, -21, 0, /* snap_y */ + 'm', 0, -28, + 'l', 0, 0, + 'm', 0, -20, + 'c', 5, -29, 22, -33, 22, -20, + 'l', 22, 0, + 'm', 22, -20, + 'c', 27, -29, 44, -33, 44, -20, + 'l', 44, 0, + 'e', + 'X', +/* 0x6e 'n' offset 2963 */ + 0, 22, 28, 0, 2, 3, + 0, 22, /* snap_x */ + -28, -21, 0, /* snap_y */ + 'm', 0, -28, + 'l', 0, 0, + 'm', 0, -20, + 'c', 4, -28, 22, -34, 22, -20, + 'l', 22, 0, + 'e', + 'X', +/* 0x6f 'o' offset 2995 */ + 0, 26, 28, 0, 2, 4, + 0, 26, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 24, 0, 26, -9, 26, -14, + 'c', 26, -19, 24, -28, 13, -28, + 'E', +/* 0x70 'p' offset 3039 */ + 0, 24, 28, 14, 2, 4, + 0, 24, /* snap_x */ + -28, -21, 0, 14, /* snap_y */ + 'm', 0, -28, + 'l', 0, 14, + 'm', 0, -22, + 'c', 3, -26, 6, -28, 11, -28, + 'c', 22, -28, 24, -19, 24, -14, + 'c', 24, -9, 22, 0, 11, 0, + 'c', 6, 0, 3, -2, 0, -6, + 'e', +/* 0x71 'q' offset 3089 */ + 0, 24, 28, 14, 2, 4, + 0, 24, /* snap_x */ + -28, -21, 0, 14, /* snap_y */ + 'm', 24, -28, + 'l', 24, 14, + 'm', 24, -22, + 'c', 21, -26, 18, -28, 13, -28, + 'c', 2, -28, 0, -19, 0, -14, + 'c', 0, -9, 2, 0, 13, 0, + 'c', 18, 0, 21, -2, 24, -6, + 'e', +/* 0x72 'r' offset 3139 */ + 0, 16, 28, 0, 2, 4, + 0, 16, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 0, -28, + 'l', 0, 0, + 'm', 0, -16, + 'c', 2, -27, 7, -28, 16, -28, + 'e', +/* 0x73 's' offset 3168 */ + 0, 22, 28, 0, 2, 4, + 0, 22, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 22, -22, + 'c', 22, -27, 16, -28, 11, -28, + 'c', 4, -28, 0, -26, 0, -22, + 'c', 0, -11, 22, -20, 22, -7, + 'c', 22, 0, 17, 0, 11, 0, + 'c', 6, 0, 0, -1, 0, -6, + 'e', +/* 0x74 't' offset 3219 */ + 0, 16, 42, 0, 3, 4, + 0, 6, 16, /* snap_x */ + -42, -28, -21, 0, /* snap_y */ + 'm', 6, -42, + 'l', 6, -8, + 'c', 6, -2, 8, 0, 16, 0, + 'm', 0, -28, + 'l', 14, -28, + 'e', +/* 0x75 'u' offset 3252 */ + 0, 22, 28, 0, 2, 3, + 0, 22, /* snap_x */ + -28, -15, 0, /* snap_y */ + 'm', 0, -28, + 'l', 0, -8, + 'c', 0, 6, 18, 0, 22, -8, + 'm', 22, -28, + 'l', 22, 0, + 'e', +/* 0x76 'v' offset 3283 */ + 0, 24, 28, 0, 2, 3, + 0, 24, /* snap_x */ + -28, -15, 0, /* snap_y */ + 'm', 0, -28, + 'l', 12, 0, + 'l', 24, -28, + 'e', + 'X', 'X', 'X', +/* 0x77 'w' offset 3307 */ + 0, 32, 28, 0, 2, 3, + 0, 32, /* snap_x */ + -28, -15, 0, /* snap_y */ + 'm', 0, -28, + 'l', 8, 0, + 'l', 16, -28, + 'l', 24, 0, + 'l', 32, -28, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', + 'X', 'X', 'X', +/* 0x78 'x' offset 3343 */ + 0, 22, 28, 0, 2, 2, + 0, 22, /* snap_x */ + -28, 0, /* snap_y */ + 'm', 0, -28, + 'l', 22, 0, + 'm', 22, -28, + 'l', 0, 0, + 'e', + 'X', +/* 0x79 'y' offset 3367 */ + -2, 24, 28, 14, 2, 4, + 0, 24, /* snap_x */ + -28, -15, 0, 14, /* snap_y */ + 'm', 0, -28, + 'l', 12, 0, + 'm', 24, -28, + 'l', 12, 0, + 'c', 6, 13, 0, 14, -2, 14, + 'e', +/* 0x7a 'z' offset 3399 */ + 0, 22, 28, 0, 2, 4, + 0, 22, /* snap_x */ + -28, -21, -15, 0, /* snap_y */ + 'm', 22, 0, + 'l', 0, 0, + 'l', 22, -28, + 'l', 0, -28, + 'e', + 'X', 'X', 'X', + 'X', 'X', 'X', +/* 0x7b '{' offset 3430 */ + 0, 16, 44, 0, 3, 5, + 0, 6, 16, /* snap_x */ + -44, -24, -21, -15, 0, /* snap_y */ + 'm', 16, -44, + 'c', 10, -44, 6, -42, 6, -36, + 'l', 6, -24, + 'l', 0, -24, + 'l', 6, -24, + 'l', 6, -8, + 'c', 6, -2, 10, 0, 16, 0, + 'e', +/* 0x7c '|' offset 3474 */ + 0, 0, 50, 14, 1, 2, + 0, /* snap_x */ + -50, 14, /* snap_y */ + 'm', 0, -50, + 'l', 0, 14, + 'e', + 'X', +/* 0x7d '}' offset 3491 */ + 0, 16, 44, 0, 3, 5, + 0, 10, 16, /* snap_x */ + -44, -24, -21, -15, 0, /* snap_y */ + 'm', 0, -44, + 'c', 6, -44, 10, -42, 10, -36, + 'l', 10, -24, + 'l', 16, -24, + 'l', 10, -24, + 'l', 10, -8, + 'c', 10, -2, 6, 0, 0, 0, + 'e', +/* 0x7e '~' offset 3535 */ + 0, 36, 24, -12, 2, 5, + 0, 36, /* snap_x */ + -24, -21, -15, -12, 0, /* snap_y */ + 'm', 0, -14, + 'c', 1, -21, 4, -24, 8, -24, + 'c', 18, -24, 18, -12, 28, -12, + 'c', 32, -12, 35, -15, 36, -22, + 'e', +}; + +const uint16_t _cairo_twin_charmap[128] = { + 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, + 28, 40, 90, 114, 152, 224, 323, 390, + 419, 441, 463, 494, 520, 556, 575, 604, + 622, 666, 691, 736, 780, 809, 860, 919, + 944, 1004, 1063, 1109, 1162, 1183, 1209, 1230, + 1288, 1375, 1406, 1455, 1499, 1534, 1572, 1604, + 1655, 1686, 1703, 1731, 1761, 1785, 1821, 1851, + 1895, 1931, 1981, 2023, 2074, 2100, 2128, 2152, + 2188, 2212, 2240, 2271, 2296, 2314, 2339, 2363, + 2381, 2417, 2467, 2517, 2561, 2611, 2659, 2693, + 2758, 2790, 2826, 2870, 2900, 2917, 2963, 2995, + 3039, 3089, 3139, 3168, 3219, 3252, 3283, 3307, + 3343, 3367, 3399, 3430, 3474, 3491, 3535, 0, +}; + diff --git a/gfx/cairo/cairo/src/cairo-font-face-twin.c b/gfx/cairo/cairo/src/cairo-font-face-twin.c new file mode 100644 index 0000000000..a0855c9f60 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-font-face-twin.c @@ -0,0 +1,752 @@ +/* + * Copyright © 2004 Keith Packard + * Copyright © 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Keith Packard + * Behdad Esfahbod + */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +#include + +/* + * This file implements a user-font rendering the descendant of the Hershey + * font coded by Keith Packard for use in the Twin window system. + * The actual font data is in cairo-font-face-twin-data.c + * + * Ported to cairo user font and extended by Behdad Esfahbod. + */ + + + +static cairo_user_data_key_t twin_properties_key; + + +/* + * Face properties + */ + +/* We synthesize multiple faces from the twin data. Here is the parameters. */ + +/* The following tables and matching code are copied from Pango */ + +/* CSS weight */ +typedef enum { + TWIN_WEIGHT_THIN = 100, + TWIN_WEIGHT_ULTRALIGHT = 200, + TWIN_WEIGHT_LIGHT = 300, + TWIN_WEIGHT_BOOK = 380, + TWIN_WEIGHT_NORMAL = 400, + TWIN_WEIGHT_MEDIUM = 500, + TWIN_WEIGHT_SEMIBOLD = 600, + TWIN_WEIGHT_BOLD = 700, + TWIN_WEIGHT_ULTRABOLD = 800, + TWIN_WEIGHT_HEAVY = 900, + TWIN_WEIGHT_ULTRAHEAVY = 1000 +} twin_face_weight_t; + +/* CSS stretch */ +typedef enum { + TWIN_STRETCH_ULTRA_CONDENSED, + TWIN_STRETCH_EXTRA_CONDENSED, + TWIN_STRETCH_CONDENSED, + TWIN_STRETCH_SEMI_CONDENSED, + TWIN_STRETCH_NORMAL, + TWIN_STRETCH_SEMI_EXPANDED, + TWIN_STRETCH_EXPANDED, + TWIN_STRETCH_EXTRA_EXPANDED, + TWIN_STRETCH_ULTRA_EXPANDED +} twin_face_stretch_t; + +typedef struct +{ + int value; + const char str[16]; +} FieldMap; + +static const FieldMap slant_map[] = { + { CAIRO_FONT_SLANT_NORMAL, "" }, + { CAIRO_FONT_SLANT_NORMAL, "Roman" }, + { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" }, + { CAIRO_FONT_SLANT_ITALIC, "Italic" } +}; + +static const FieldMap smallcaps_map[] = { + { FALSE, "" }, + { TRUE, "Small-Caps" } +}; + +static const FieldMap weight_map[] = { + { TWIN_WEIGHT_THIN, "Thin" }, + { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" }, + { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" }, + { TWIN_WEIGHT_LIGHT, "Light" }, + { TWIN_WEIGHT_BOOK, "Book" }, + { TWIN_WEIGHT_NORMAL, "" }, + { TWIN_WEIGHT_NORMAL, "Regular" }, + { TWIN_WEIGHT_MEDIUM, "Medium" }, + { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" }, + { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" }, + { TWIN_WEIGHT_BOLD, "Bold" }, + { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" }, + { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" }, + { TWIN_WEIGHT_HEAVY, "Heavy" }, + { TWIN_WEIGHT_HEAVY, "Black" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" }, + { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" } +}; + +static const FieldMap stretch_map[] = { + { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" }, + { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" }, + { TWIN_STRETCH_CONDENSED, "Condensed" }, + { TWIN_STRETCH_SEMI_CONDENSED, "Semi-Condensed" }, + { TWIN_STRETCH_NORMAL, "" }, + { TWIN_STRETCH_SEMI_EXPANDED, "Semi-Expanded" }, + { TWIN_STRETCH_EXPANDED, "Expanded" }, + { TWIN_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" }, + { TWIN_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" } +}; + +static const FieldMap monospace_map[] = { + { FALSE, "" }, + { TRUE, "Mono" }, + { TRUE, "Monospace" } +}; + + +typedef struct _twin_face_properties { + cairo_font_slant_t slant; + twin_face_weight_t weight; + twin_face_stretch_t stretch; + + /* lets have some fun */ + cairo_bool_t monospace; + cairo_bool_t smallcaps; +} twin_face_properties_t; + +static cairo_bool_t +field_matches (const char *s1, + const char *s2, + int len) +{ + int c1, c2; + + while (len && *s1 && *s2) + { +#define TOLOWER(c) \ + (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) + + c1 = TOLOWER (*s1); + c2 = TOLOWER (*s2); + if (c1 != c2) { + if (c1 == '-') { + s1++; + continue; + } + return FALSE; + } + s1++; s2++; + len--; + } + + return len == 0 && *s1 == '\0'; +} + +static cairo_bool_t +parse_int (const char *word, + size_t wordlen, + int *out) +{ + char *end; + long val = strtol (word, &end, 10); + int i = val; + + if (end != word && (end == word + wordlen) && val >= 0 && val == i) + { + if (out) + *out = i; + + return TRUE; + } + + return FALSE; +} + +static cairo_bool_t +find_field (const char *what, + const FieldMap *map, + int n_elements, + const char *str, + int len, + int *val) +{ + int i; + cairo_bool_t had_prefix = FALSE; + + if (what) + { + i = strlen (what); + if (len > i && 0 == strncmp (what, str, i) && str[i] == '=') + { + str += i + 1; + len -= i + 1; + had_prefix = TRUE; + } + } + + for (i=0; iNAME)) \ + return; \ + + FIELD (weight); + FIELD (slant); + FIELD (stretch); + FIELD (smallcaps); + FIELD (monospace); + +#undef FIELD +} + +static void +face_props_parse (twin_face_properties_t *props, + const char *s) +{ + const char *start, *end; + + for (start = end = s; *end; end++) { + if (*end != ' ' && *end != ':') + continue; + + if (start < end) + parse_field (props, start, end - start); + start = end + 1; + } + if (start < end) + parse_field (props, start, end - start); +} + +static twin_face_properties_t * +twin_font_face_create_properties (cairo_font_face_t *twin_face) +{ + twin_face_properties_t *props; + + props = _cairo_malloc (sizeof (twin_face_properties_t)); + if (unlikely (props == NULL)) + return NULL; + + props->stretch = TWIN_STRETCH_NORMAL; + props->slant = CAIRO_FONT_SLANT_NORMAL; + props->weight = TWIN_WEIGHT_NORMAL; + props->monospace = FALSE; + props->smallcaps = FALSE; + + if (unlikely (cairo_font_face_set_user_data (twin_face, + &twin_properties_key, + props, free))) { + free (props); + return NULL; + } + + 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) +{ + twin_face_properties_t *props; + + 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 ? + TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD; + face_props_parse (props, toy_face->family); + + return CAIRO_STATUS_SUCCESS; +} + + +/* + * Scaled properties + */ + +typedef struct _twin_scaled_properties { + twin_face_properties_t *face_props; + + cairo_bool_t snap; /* hint outlines */ + + double weight; /* unhinted pen width */ + double penx, peny; /* hinted pen width */ + double marginl, marginr; /* hinted side margins */ + + double stretch; /* stretch factor */ +} twin_scaled_properties_t; + +static void +compute_hinting_scale (cairo_t *cr, + double x, double y, + double *scale, double *inv) +{ + cairo_user_to_device_distance (cr, &x, &y); + *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y); + *inv = 1 / *scale; +} + +static void +compute_hinting_scales (cairo_t *cr, + double *x_scale, double *x_scale_inv, + double *y_scale, double *y_scale_inv) +{ + double x, y; + + x = 1; y = 0; + compute_hinting_scale (cr, x, y, x_scale, x_scale_inv); + + x = 0; y = 1; + compute_hinting_scale (cr, x, y, y_scale, y_scale_inv); +} + +#define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv) +#define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv) + +/* This controls the global font size */ +#define F(g) ((g) / 72.) + +static void +twin_hint_pen_and_margins(cairo_t *cr, + double *penx, double *peny, + double *marginl, double *marginr) +{ + double x_scale, x_scale_inv; + double y_scale, y_scale_inv; + double margin; + + compute_hinting_scales (cr, + &x_scale, &x_scale_inv, + &y_scale, &y_scale_inv); + + *penx = SNAPXI (*penx); + if (*penx < x_scale_inv) + *penx = x_scale_inv; + + *peny = SNAPYI (*peny); + if (*peny < y_scale_inv) + *peny = y_scale_inv; + + margin = *marginl + *marginr; + *marginl = SNAPXI (*marginl); + if (*marginl < x_scale_inv) + *marginl = x_scale_inv; + + *marginr = margin - *marginl; + if (*marginr < 0) + *marginr = 0; + *marginr = SNAPXI (*marginr); +} + +static cairo_status_t +twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font, + cairo_t *cr) +{ + cairo_status_t status; + twin_scaled_properties_t *props; + + props = _cairo_malloc (sizeof (twin_scaled_properties_t)); + if (unlikely (props == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + + props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), + &twin_properties_key); + + props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE; + + /* weight */ + props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL); + + /* pen & margins */ + props->penx = props->peny = props->weight; + props->marginl = props->marginr = F (4); + if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT) + twin_hint_pen_and_margins(cr, + &props->penx, &props->peny, + &props->marginl, &props->marginr); + + /* stretch */ + props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL); + + + /* Save it */ + status = cairo_scaled_font_set_user_data (scaled_font, + &twin_properties_key, + props, free); + if (unlikely (status)) + goto FREE_PROPS; + + return CAIRO_STATUS_SUCCESS; + +FREE_PROPS: + free (props); + return status; +} + + +/* + * User-font implementation + */ + +static cairo_status_t +twin_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_t *cr, + cairo_font_extents_t *metrics) +{ + metrics->ascent = F (54); + metrics->descent = 1 - metrics->ascent; + + return twin_scaled_font_compute_properties (scaled_font, cr); +} + +#define TWIN_GLYPH_MAX_SNAP_X 4 +#define TWIN_GLYPH_MAX_SNAP_Y 7 + +typedef struct { + int n_snap_x; + int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X]; + double snapped_x[TWIN_GLYPH_MAX_SNAP_X]; + int n_snap_y; + int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y]; + double snapped_y[TWIN_GLYPH_MAX_SNAP_Y]; +} twin_snap_info_t; + +#define twin_glyph_left(g) ((g)[0]) +#define twin_glyph_right(g) ((g)[1]) +#define twin_glyph_ascent(g) ((g)[2]) +#define twin_glyph_descent(g) ((g)[3]) + +#define twin_glyph_n_snap_x(g) ((g)[4]) +#define twin_glyph_n_snap_y(g) ((g)[5]) +#define twin_glyph_snap_x(g) (&g[6]) +#define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g)) +#define twin_glyph_draw(g) (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g)) + +static void +twin_compute_snap (cairo_t *cr, + twin_snap_info_t *info, + const signed char *b) +{ + int s, n; + const signed char *snap; + double x_scale, x_scale_inv; + double y_scale, y_scale_inv; + + compute_hinting_scales (cr, + &x_scale, &x_scale_inv, + &y_scale, &y_scale_inv); + + snap = twin_glyph_snap_x (b); + n = twin_glyph_n_snap_x (b); + info->n_snap_x = n; + assert (n <= TWIN_GLYPH_MAX_SNAP_X); + for (s = 0; s < n; s++) { + info->snap_x[s] = snap[s]; + info->snapped_x[s] = SNAPXI (F (snap[s])); + } + + snap = twin_glyph_snap_y (b); + n = twin_glyph_n_snap_y (b); + info->n_snap_y = n; + assert (n <= TWIN_GLYPH_MAX_SNAP_Y); + for (s = 0; s < n; s++) { + info->snap_y[s] = snap[s]; + info->snapped_y[s] = SNAPYI (F (snap[s])); + } +} + +static double +twin_snap (int8_t v, int n, int8_t *snap, double *snapped) +{ + int s; + + if (!n) + return F(v); + + if (snap[0] == v) + return snapped[0]; + + for (s = 0; s < n - 1; s++) + { + if (snap[s+1] == v) + return snapped[s+1]; + + if (snap[s] <= v && v <= snap[s+1]) + { + int before = snap[s]; + int after = snap[s+1]; + int dist = after - before; + double snap_before = snapped[s]; + double snap_after = snapped[s+1]; + double dist_before = v - before; + return snap_before + (snap_after - snap_before) * dist_before / dist; + } + } + return F(v); +} + +#define SNAPX(p) twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x) +#define SNAPY(p) twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y) + +static cairo_status_t +twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *metrics) +{ + double x1, y1, x2, y2, x3, y3; + double marginl; + twin_scaled_properties_t *props; + twin_snap_info_t info; + const int8_t *b; + const int8_t *g; + int8_t w; + double gw; + + props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key); + + /* Save glyph space, we need it when stroking */ + cairo_save (cr); + + /* center the pen */ + cairo_translate (cr, props->penx * .5, -props->peny * .5); + + /* small-caps */ + if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') { + glyph += 'A' - 'a'; + /* 28 and 42 are small and capital letter heights of the glyph data */ + cairo_scale (cr, 1, 28. / 42); + } + + /* slant */ + if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) { + cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0}; + cairo_transform (cr, &shear); + } + + b = _cairo_twin_outlines + + _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph]; + g = twin_glyph_draw(b); + w = twin_glyph_right(b); + gw = F(w); + + marginl = props->marginl; + + /* monospace */ + if (props->face_props->monospace) { + double monow = F(24); + double extra = props->penx + props->marginl + props->marginr; + cairo_scale (cr, (monow + extra) / (gw + extra), 1); + gw = monow; + + /* resnap margin for new transform */ + { + double x, y, x_scale, x_scale_inv; + x = 1; y = 0; + compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv); + marginl = SNAPXI (marginl); + } + } + + cairo_translate (cr, marginl, 0); + + /* stretch */ + cairo_scale (cr, props->stretch, 1); + + if (props->snap) + twin_compute_snap (cr, &info, b); + else + info.n_snap_x = info.n_snap_y = 0; + + /* advance width */ + metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr; + + /* glyph shape */ + for (;;) { + switch (*g++) { + case 'M': + cairo_close_path (cr); + /* fall through */ + case 'm': + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); + cairo_move_to (cr, x1, y1); + continue; + case 'L': + cairo_close_path (cr); + /* fall through */ + case 'l': + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); + cairo_line_to (cr, x1, y1); + continue; + case 'C': + cairo_close_path (cr); + /* fall through */ + case 'c': + x1 = SNAPX(*g++); + y1 = SNAPY(*g++); + x2 = SNAPX(*g++); + y2 = SNAPY(*g++); + x3 = SNAPX(*g++); + y3 = SNAPY(*g++); + cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); + continue; + case 'E': + cairo_close_path (cr); + /* fall through */ + case 'e': + cairo_restore (cr); /* restore glyph space */ + cairo_set_tolerance (cr, 0.01); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width (cr, 1); + cairo_scale (cr, props->penx, props->peny); + cairo_stroke (cr); + break; + case 'X': + /* filler */ + continue; + } + break; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph) +{ + /* We use an identity charmap. Which means we could live + * with no unicode_to_glyph method too. But we define this + * to map all unknown chars to a single unknown glyph to + * reduce pressure on cache. */ + + if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap))) + *glyph = unicode; + else + *glyph = 0; + + return CAIRO_STATUS_SUCCESS; +} + + +/* + * Face constructor + */ + +static cairo_font_face_t * +_cairo_font_face_twin_create_internal (void) +{ + cairo_font_face_t *twin_font_face; + + twin_font_face = cairo_user_font_face_create (); + cairo_user_font_face_set_init_func (twin_font_face, twin_scaled_font_init); + cairo_user_font_face_set_render_glyph_func (twin_font_face, twin_scaled_font_render_glyph); + cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph); + + return twin_font_face; +} + +cairo_font_face_t * +_cairo_font_face_twin_create_fallback (void) +{ + cairo_font_face_t *twin_font_face; + + twin_font_face = _cairo_font_face_twin_create_internal (); + 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; + } + + return twin_font_face; +} + +cairo_status_t +_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + cairo_status_t status; + cairo_font_face_t *twin_font_face; + + twin_font_face = _cairo_font_face_twin_create_internal (); + status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face); + if (status) { + cairo_font_face_destroy (twin_font_face); + return status; + } + + *font_face = twin_font_face; + + return CAIRO_STATUS_SUCCESS; +} diff --git a/gfx/cairo/cairo/src/cairo-font-face.c b/gfx/cairo/cairo/src/cairo-font-face.c new file mode 100644 index 0000000000..e10a6eac2f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-font-face.c @@ -0,0 +1,345 @@ +/* -*- 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 + * Graydon Hoare + * Owen Taylor + */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +/** + * SECTION:cairo-font-face + * @Title: cairo_font_face_t + * @Short_Description: Base class for font faces + * @See_Also: #cairo_scaled_font_t + * + * #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_select_font_face(). The resulting face can be accessed using + * cairo_get_font_face(). + **/ + +/* #cairo_font_face_t */ + +const cairo_font_face_t _cairo_font_face_nil = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_NO_MEMORY, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + 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, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&font_face->status, status); + + return _cairo_error (status); +} + +void +_cairo_font_face_init (cairo_font_face_t *font_face, + const cairo_font_face_backend_t *backend) +{ + CAIRO_MUTEX_INITIALIZE (); + + font_face->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (&font_face->ref_count, 1); + font_face->backend = backend; + + _cairo_user_data_array_init (&font_face->user_data); +} + +/** + * cairo_font_face_reference: + * @font_face: a #cairo_font_face_t, (may be %NULL in which case this + * function does nothing). + * + * Increases the reference count on @font_face by one. This prevents + * @font_face from being destroyed until a matching call to + * cairo_font_face_destroy() is made. + * + * 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)) + return font_face; + + /* We would normally assert that we have a reference here but we + * can't get away with that due to the zombie case as documented + * in _cairo_ft_font_face_destroy. */ + + _cairo_reference_count_inc (&font_face->ref_count); + + return 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 + * + * 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)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count)); + + /* 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 (__put (&font_face->ref_count)) + return; + + if (! font_face->backend->destroy (font_face)) + return; + + _cairo_user_data_array_fini (&font_face->user_data); + + free (font_face); +} +slim_hidden_def (cairo_font_face_destroy); + +/** + * cairo_font_face_get_type: + * @font_face: a font face + * + * This function returns the type of the backend used to create + * a font face. See #cairo_font_type_t for available types. + * + * Return value: The type of @font_face. + * + * Since: 1.2 + **/ +cairo_font_type_t +cairo_font_face_get_type (cairo_font_face_t *font_face) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + return CAIRO_FONT_TYPE_TOY; + + return font_face->backend->type; +} + +/** + * cairo_font_face_get_reference_count: + * @font_face: a #cairo_font_face_t + * + * Returns the current reference count of @font_face. + * + * Return value: the current reference count of @font_face. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +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)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->ref_count); +} + +/** + * cairo_font_face_status: + * @font_face: a #cairo_font_face_t + * + * Checks whether an error has previously occurred for this + * 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) +{ + return font_face->status; +} + +/** + * cairo_font_face_get_user_data: + * @font_face: a #cairo_font_face_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @font_face using the specified + * key. If no user data has been attached with the given key this + * 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) +{ + return _cairo_user_data_array_get_data (&font_face->user_data, + key); +} +slim_hidden_def (cairo_font_face_get_user_data); + +/** + * cairo_font_face_set_user_data: + * @font_face: a #cairo_font_face_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the font face + * @destroy: a #cairo_destroy_func_t which will be called when the + * font face is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @font_face. To remove user data from a font face, + * call this function with the key that was used to set it and %NULL + * for @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, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count)) + return font_face->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); + +void +_cairo_unscaled_font_init (cairo_unscaled_font_t *unscaled_font, + const cairo_unscaled_font_backend_t *backend) +{ + CAIRO_REFERENCE_COUNT_INIT (&unscaled_font->ref_count, 1); + unscaled_font->backend = backend; +} + +cairo_unscaled_font_t * +_cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font) +{ + if (unscaled_font == NULL) + return NULL; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); + + _cairo_reference_count_inc (&unscaled_font->ref_count); + + return unscaled_font; +} + +void +_cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font) +{ + if (unscaled_font == NULL) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count)); + + if (__put (&unscaled_font->ref_count)) + return; + + 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 new file mode 100644 index 0000000000..7064d00acd --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-font-options.c @@ -0,0 +1,622 @@ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Owen Taylor + */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +/** + * SECTION:cairo-font-options + * @Title: cairo_font_options_t + * @Short_Description: How a font should be rendered + * @See_Also: #cairo_scaled_font_t + * + * The font options specify how fonts should be rendered. Most of the + * 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, + CAIRO_SUBPIXEL_ORDER_DEFAULT, + CAIRO_LCD_FILTER_DEFAULT, + CAIRO_HINT_STYLE_DEFAULT, + CAIRO_HINT_METRICS_DEFAULT, + CAIRO_ROUND_GLYPH_POS_DEFAULT, + NULL +}; + +/** + * _cairo_font_options_init_default: + * @options: a #cairo_font_options_t + * + * Initializes all fields of the font options object to default values. + **/ +void +_cairo_font_options_init_default (cairo_font_options_t *options) +{ + options->antialias = CAIRO_ANTIALIAS_DEFAULT; + options->subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; + options->lcd_filter = CAIRO_LCD_FILTER_DEFAULT; + 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 +_cairo_font_options_init_copy (cairo_font_options_t *options, + const cairo_font_options_t *other) +{ + options->antialias = other->antialias; + options->subpixel_order = other->subpixel_order; + 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->variations = other->variations ? strdup (other->variations) : NULL; +} + +/** + * cairo_font_options_create: + * + * Allocates a new font options object with all options initialized + * to default values. + * + * Return value: a newly allocated #cairo_font_options_t. Free with + * cairo_font_options_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_font_options_status(). + * + * Since: 1.0 + **/ +cairo_font_options_t * +cairo_font_options_create (void) +{ + cairo_font_options_t *options; + + 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; + } + + _cairo_font_options_init_default (options); + + return options; +} + +/** + * cairo_font_options_copy: + * @original: a #cairo_font_options_t + * + * Allocates a new font options object copying the option values from + * @original. + * + * Return value: a newly allocated #cairo_font_options_t. Free with + * cairo_font_options_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_font_options_status(). + * + * Since: 1.0 + **/ +cairo_font_options_t * +cairo_font_options_copy (const cairo_font_options_t *original) +{ + cairo_font_options_t *options; + + if (cairo_font_options_status ((cairo_font_options_t *) original)) + return (cairo_font_options_t *) &_cairo_font_options_nil; + + 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; + } + + _cairo_font_options_init_copy (options, 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) +{ + if (cairo_font_options_status (options)) + return; + + _cairo_font_options_fini (options); + free (options); +} + +/** + * cairo_font_options_status: + * @options: a #cairo_font_options_t + * + * Checks whether an error has previously occurred for this + * 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) +{ + if (options == NULL) + return CAIRO_STATUS_NULL_POINTER; + else if (options == (cairo_font_options_t *) &_cairo_font_options_nil) + return CAIRO_STATUS_NO_MEMORY; + else + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_font_options_status); + +/** + * cairo_font_options_merge: + * @options: a #cairo_font_options_t + * @other: another #cairo_font_options_t + * + * 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_OPERATOR_OVER. + * + * Since: 1.0 + **/ +void +cairo_font_options_merge (cairo_font_options_t *options, + const cairo_font_options_t *other) +{ + if (cairo_font_options_status (options)) + return; + + if (cairo_font_options_status ((cairo_font_options_t *) other)) + return; + + if (other->antialias != CAIRO_ANTIALIAS_DEFAULT) + options->antialias = other->antialias; + if (other->subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) + options->subpixel_order = other->subpixel_order; + if (other->lcd_filter != CAIRO_LCD_FILTER_DEFAULT) + options->lcd_filter = other->lcd_filter; + if (other->hint_style != CAIRO_HINT_STYLE_DEFAULT) + options->hint_style = other->hint_style; + if (other->hint_metrics != CAIRO_HINT_METRICS_DEFAULT) + 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); + +/** + * cairo_font_options_equal: + * @options: a #cairo_font_options_t + * @other: another #cairo_font_options_t + * + * Compares two font options objects for equality. + * + * 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, + const cairo_font_options_t *other) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return FALSE; + if (cairo_font_options_status ((cairo_font_options_t *) other)) + return FALSE; + + if (options == other) + return TRUE; + + return (options->antialias == other->antialias && + options->subpixel_order == other->subpixel_order && + 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->variations == NULL && other->variations == NULL) || + (options->variations != NULL && other->variations != NULL && + strcmp (options->variations, other->variations) == 0))); +} +slim_hidden_def (cairo_font_options_equal); + +/** + * cairo_font_options_hash: + * @options: a #cairo_font_options_t + * + * Compute a hash for the font options object; this value will + * be useful when storing an object containing a #cairo_font_options_t + * in a hash table. + * + * 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)) ^ hash; +} +slim_hidden_def (cairo_font_options_hash); + +/** + * cairo_font_options_set_antialias: + * @options: a #cairo_font_options_t + * @antialias: the new antialiasing mode + * + * 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, + cairo_antialias_t antialias) +{ + if (cairo_font_options_status (options)) + return; + + options->antialias = antialias; +} +slim_hidden_def (cairo_font_options_set_antialias); + +/** + * cairo_font_options_get_antialias: + * @options: a #cairo_font_options_t + * + * 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) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_ANTIALIAS_DEFAULT; + + return options->antialias; +} + +/** + * cairo_font_options_set_subpixel_order: + * @options: a #cairo_font_options_t + * @subpixel_order: the new subpixel order + * + * Sets the subpixel order for the font options object. 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. 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, + cairo_subpixel_order_t subpixel_order) +{ + if (cairo_font_options_status (options)) + return; + + options->subpixel_order = subpixel_order; +} +slim_hidden_def (cairo_font_options_set_subpixel_order); + +/** + * cairo_font_options_get_subpixel_order: + * @options: a #cairo_font_options_t + * + * Gets the subpixel order for the font options object. + * 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) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_SUBPIXEL_ORDER_DEFAULT; + + return options->subpixel_order; +} + +/** + * cairo_font_options_set_lcd_filter: + * @options: a #cairo_font_options_t + * @lcd_filter: the new LCD filter + * + * Sets the LCD filter for the font options object. The LCD filter + * 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. + **/ +void +cairo_font_options_set_lcd_filter (cairo_font_options_t *options, + cairo_lcd_filter_t lcd_filter) +{ + if (cairo_font_options_status (options)) + return; + + options->lcd_filter = lcd_filter; +} + +/** + * cairo_font_options_get_lcd_filter: + * @options: a #cairo_font_options_t + * + * Gets the LCD filter for the font options object. + * See the documentation for #cairo_lcd_filter_t for full details. + * + * Return value: the LCD filter for the font options object + **/ +cairo_lcd_filter_t +cairo_font_options_get_lcd_filter (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_LCD_FILTER_DEFAULT; + + return options->lcd_filter; +} + +/** + * _cairo_font_options_set_round_glyph_positions: + * @options: a #cairo_font_options_t + * @round: the new rounding value + * + * Sets the rounding options for the font options object. If rounding is set, a + * glyph's position will be rounded to integer values. + **/ +void +_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options, + cairo_round_glyph_positions_t round) +{ + if (cairo_font_options_status (options)) + return; + + options->round_glyph_positions = round; +} + +/** + * _cairo_font_options_get_round_glyph_positions: + * @options: a #cairo_font_options_t + * + * Gets the glyph position rounding option for the font options object. + * + * Return value: The round glyph posistions flag for the font options object. + **/ +cairo_round_glyph_positions_t +_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_ROUND_GLYPH_POS_DEFAULT; + + return options->round_glyph_positions; +} + +/** + * cairo_font_options_set_hint_style: + * @options: a #cairo_font_options_t + * @hint_style: the new hint style + * + * Sets the hint style for font outlines for the font options object. + * 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, + cairo_hint_style_t hint_style) +{ + if (cairo_font_options_status (options)) + return; + + options->hint_style = hint_style; +} +slim_hidden_def (cairo_font_options_set_hint_style); + +/** + * cairo_font_options_get_hint_style: + * @options: a #cairo_font_options_t + * + * Gets the hint style for font outlines for the font options object. + * 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) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_HINT_STYLE_DEFAULT; + + return options->hint_style; +} + +/** + * cairo_font_options_set_hint_metrics: + * @options: a #cairo_font_options_t + * @hint_metrics: the new metrics hinting mode + * + * Sets the metrics hinting mode for the font options object. This + * 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, + cairo_hint_metrics_t hint_metrics) +{ + if (cairo_font_options_status (options)) + return; + + options->hint_metrics = hint_metrics; +} +slim_hidden_def (cairo_font_options_set_hint_metrics); + +/** + * cairo_font_options_get_hint_metrics: + * @options: a #cairo_font_options_t + * + * Gets the metrics hinting mode for the font options object. + * 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) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return CAIRO_HINT_METRICS_DEFAULT; + + 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-fontconfig-private.h b/gfx/cairo/cairo/src/cairo-fontconfig-private.h new file mode 100644 index 0000000000..ea873abe7e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-fontconfig-private.h @@ -0,0 +1,78 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2000 Keith Packard + * Copyright © 2005 Red Hat, Inc + * 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 Red Hat, Inc. + * + * Contributor(s): + * Graydon Hoare + * Owen Taylor + * Keith Packard + * Carl Worth + * Chris Wilson + */ + +#ifndef _CAIRO_FONTCONFIG_PRIVATE_H +#define _CAIRO_FONTCONFIG_PRIVATE_H + +#include "cairo.h" + +#if CAIRO_HAS_FC_FONT +#include +#include +#endif + +/* sub-pixel order */ +#ifndef FC_RGBA_UNKNOWN +#define FC_RGBA_UNKNOWN 0 +#define FC_RGBA_RGB 1 +#define FC_RGBA_BGR 2 +#define FC_RGBA_VRGB 3 +#define FC_RGBA_VBGR 4 +#define FC_RGBA_NONE 5 +#endif + +/* hinting style */ +#ifndef FC_HINT_NONE +#define FC_HINT_NONE 0 +#define FC_HINT_SLIGHT 1 +#define FC_HINT_MEDIUM 2 +#define FC_HINT_FULL 3 +#endif + +/* LCD filter */ +#ifndef FC_LCD_NONE +#define FC_LCD_NONE 0 +#define FC_LCD_DEFAULT 1 +#define FC_LCD_LIGHT 2 +#define FC_LCD_LEGACY 3 +#endif + +#endif /* _CAIRO_FONTCONFIG_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-freed-pool-private.h b/gfx/cairo/cairo/src/cairo-freed-pool-private.h new file mode 100644 index 0000000000..8a7af523db --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-freed-pool-private.h @@ -0,0 +1,139 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_FREED_POOL_H +#define CAIRO_FREED_POOL_H + +#include "cairoint.h" +#include "cairo-atomic-private.h" + +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 16 +typedef struct { + void *pool[MAX_FREED_POOL_SIZE]; + cairo_atomic_int_t top; +} freed_pool_t; + +static cairo_always_inline void * +_atomic_fetch (void **slot) +{ + void *ptr; + + do { + ptr = _cairo_atomic_ptr_get (slot); + } while (! _cairo_atomic_ptr_cmpxchg (slot, ptr, NULL)); + + return ptr; +} + +static cairo_always_inline cairo_bool_t +_atomic_store (void **slot, void *ptr) +{ + return _cairo_atomic_ptr_cmpxchg (slot, NULL, ptr); +} + +cairo_private void * +_freed_pool_get_search (freed_pool_t *pool); + +static inline void * +_freed_pool_get (freed_pool_t *pool) +{ + void *ptr; + int i; + + i = _cairo_atomic_int_get_relaxed (&pool->top) - 1; + if (i < 0) + i = 0; + + ptr = _atomic_fetch (&pool->pool[i]); + if (likely (ptr != NULL)) { + _cairo_atomic_int_set_relaxed (&pool->top, i); + return ptr; + } + + /* either empty or contended */ + return _freed_pool_get_search (pool); +} + +cairo_private void +_freed_pool_put_search (freed_pool_t *pool, void *ptr); + +static inline void +_freed_pool_put (freed_pool_t *pool, void *ptr) +{ + int i; + + i = _cairo_atomic_int_get_relaxed (&pool->top); + if (likely (i < ARRAY_LENGTH (pool->pool) && + _atomic_store (&pool->pool[i], ptr))) + { + _cairo_atomic_int_set_relaxed (&pool->top, i + 1); + return; + } + + /* either full or contended */ + _freed_pool_put_search (pool, ptr); +} + +cairo_private void +_freed_pool_reset (freed_pool_t *pool); + +#define HAS_FREED_POOL 1 + +#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 +#define _freed_pool_put(pool, ptr) free(ptr) +#define _freed_pool_reset(ptr) + +#endif + +CAIRO_END_DECLS + +#endif /* CAIRO_FREED_POOL_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-freed-pool.c b/gfx/cairo/cairo/src/cairo-freed-pool.c new file mode 100644 index 0000000000..5b1c4c0bb2 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-freed-pool.c @@ -0,0 +1,93 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-freed-pool-private.h" + +#if HAS_FREED_POOL + +void * +_freed_pool_get_search (freed_pool_t *pool) +{ + void *ptr; + int i; + + for (i = ARRAY_LENGTH (pool->pool); i--;) { + ptr = _atomic_fetch (&pool->pool[i]); + if (ptr != NULL) { + _cairo_atomic_int_set_relaxed (&pool->top, i); + return ptr; + } + } + + /* empty */ + _cairo_atomic_int_set_relaxed (&pool->top, 0); + return NULL; +} + +void +_freed_pool_put_search (freed_pool_t *pool, void *ptr) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { + if (_atomic_store (&pool->pool[i], ptr)) { + _cairo_atomic_int_set_relaxed (&pool->top, i + 1); + return; + } + } + + /* full */ + _cairo_atomic_int_set_relaxed (&pool->top, i); + free (ptr); +} + +void +_freed_pool_reset (freed_pool_t *pool) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) { + free (pool->pool[i]); + pool->pool[i] = NULL; + } + + _cairo_atomic_int_set_relaxed (&pool->top, 0); +} + +#endif diff --git a/gfx/cairo/cairo/src/cairo-freelist-private.h b/gfx/cairo/cairo/src/cairo-freelist-private.h new file mode 100644 index 0000000000..f85f689141 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-freelist-private.h @@ -0,0 +1,139 @@ +/* + * Copyright © 2006 Joonas Pihlaja + * + * 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. + */ +#ifndef CAIRO_FREELIST_H +#define CAIRO_FREELIST_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" +#include "cairo-freelist-type-private.h" + +/* for stand-alone compilation*/ +#ifndef VG +#define VG(x) +#endif + +#ifndef NULL +#define NULL (void *) 0 +#endif + +/* Initialise a freelist that will be responsible for allocating + * nodes of size nodesize. */ +cairo_private void +_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize); + +/* Deallocate any nodes in the freelist. */ +cairo_private void +_cairo_freelist_fini (cairo_freelist_t *freelist); + +/* Allocate a new node from the freelist. If the freelist contains no + * nodes, a new one will be allocated using malloc(). The caller is + * responsible for calling _cairo_freelist_free() or free() on the + * returned node. Returns %NULL on memory allocation error. */ +cairo_private void * +_cairo_freelist_alloc (cairo_freelist_t *freelist); + +/* Allocate a new node from the freelist. If the freelist contains no + * nodes, a new one will be allocated using calloc(). The caller is + * responsible for calling _cairo_freelist_free() or free() on the + * returned node. Returns %NULL on memory allocation error. */ +cairo_private void * +_cairo_freelist_calloc (cairo_freelist_t *freelist); + +/* Return a node to the freelist. This does not deallocate the memory, + * but makes it available for later reuse by + * _cairo_freelist_alloc(). */ +cairo_private void +_cairo_freelist_free (cairo_freelist_t *freelist, void *node); + + +cairo_private void +_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize); + +cairo_private void +_cairo_freepool_fini (cairo_freepool_t *freepool); + +static inline void +_cairo_freepool_reset (cairo_freepool_t *freepool) +{ + while (freepool->pools != &freepool->embedded_pool) { + cairo_freelist_pool_t *pool = freepool->pools; + freepool->pools = pool->next; + pool->next = freepool->freepools; + freepool->freepools = pool; + } + + freepool->embedded_pool.rem = sizeof (freepool->embedded_data); + freepool->embedded_pool.data = freepool->embedded_data; +} + +cairo_private void * +_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool); + +static inline void * +_cairo_freepool_alloc_from_pool (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool; + uint8_t *ptr; + + pool = freepool->pools; + if (unlikely (freepool->nodesize > pool->rem)) + return _cairo_freepool_alloc_from_new_pool (freepool); + + ptr = pool->data; + pool->data += freepool->nodesize; + pool->rem -= freepool->nodesize; + VG (VALGRIND_MAKE_MEM_UNDEFINED (ptr, freepool->nodesize)); + return ptr; +} + +static inline void * +_cairo_freepool_alloc (cairo_freepool_t *freepool) +{ + cairo_freelist_node_t *node; + + node = freepool->first_free_node; + if (node == NULL) + return _cairo_freepool_alloc_from_pool (freepool); + + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + freepool->first_free_node = node->next; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); + + return node; +} + +cairo_private cairo_status_t +_cairo_freepool_alloc_array (cairo_freepool_t *freepool, + int count, + void **array); + +static inline void +_cairo_freepool_free (cairo_freepool_t *freepool, void *ptr) +{ + cairo_freelist_node_t *node = ptr; + + node->next = freepool->first_free_node; + freepool->first_free_node = node; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); +} + +#endif /* CAIRO_FREELIST_H */ diff --git a/gfx/cairo/cairo/src/cairo-freelist-type-private.h b/gfx/cairo/cairo/src/cairo-freelist-type-private.h new file mode 100644 index 0000000000..4dd0564610 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-freelist-type-private.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2010 Joonas Pihlaja + * + * 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. + */ +#ifndef CAIRO_FREELIST_TYPE_H +#define CAIRO_FREELIST_TYPE_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" + +typedef struct _cairo_freelist_node cairo_freelist_node_t; +struct _cairo_freelist_node { + cairo_freelist_node_t *next; +}; + +typedef struct _cairo_freelist { + cairo_freelist_node_t *first_free_node; + unsigned nodesize; +} cairo_freelist_t; + +typedef struct _cairo_freelist_pool cairo_freelist_pool_t; +struct _cairo_freelist_pool { + cairo_freelist_pool_t *next; + unsigned size, rem; + uint8_t *data; +}; + +typedef struct _cairo_freepool { + cairo_freelist_node_t *first_free_node; + cairo_freelist_pool_t *pools; + cairo_freelist_pool_t *freepools; + unsigned nodesize; + cairo_freelist_pool_t embedded_pool; + uint8_t embedded_data[1000]; +} cairo_freepool_t; + +#endif /* CAIRO_FREELIST_TYPE_H */ diff --git a/gfx/cairo/cairo/src/cairo-freelist.c b/gfx/cairo/cairo/src/cairo-freelist.c new file mode 100644 index 0000000000..7f2f3b30b8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-freelist.c @@ -0,0 +1,191 @@ +/* + * Copyright © 2006 Joonas Pihlaja + * + * 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. + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" + +void +_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize) +{ + memset (freelist, 0, sizeof (cairo_freelist_t)); + freelist->nodesize = nodesize; +} + +void +_cairo_freelist_fini (cairo_freelist_t *freelist) +{ + cairo_freelist_node_t *node = freelist->first_free_node; + while (node) { + cairo_freelist_node_t *next; + + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + next = node->next; + + free (node); + node = next; + } +} + +void * +_cairo_freelist_alloc (cairo_freelist_t *freelist) +{ + if (freelist->first_free_node) { + cairo_freelist_node_t *node; + + node = freelist->first_free_node; + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + freelist->first_free_node = node->next; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freelist->nodesize)); + + return node; + } + + return _cairo_malloc (freelist->nodesize); +} + +void * +_cairo_freelist_calloc (cairo_freelist_t *freelist) +{ + void *node = _cairo_freelist_alloc (freelist); + if (node) + memset (node, 0, freelist->nodesize); + return node; +} + +void +_cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode) +{ + cairo_freelist_node_t *node = voidnode; + if (node) { + node->next = freelist->first_free_node; + freelist->first_free_node = node; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freelist->nodesize)); + } +} + +void +_cairo_freepool_init (cairo_freepool_t *freepool, unsigned nodesize) +{ + freepool->first_free_node = NULL; + freepool->pools = &freepool->embedded_pool; + freepool->freepools = NULL; + freepool->nodesize = nodesize; + + freepool->embedded_pool.next = NULL; + freepool->embedded_pool.size = sizeof (freepool->embedded_data); + freepool->embedded_pool.rem = sizeof (freepool->embedded_data); + freepool->embedded_pool.data = freepool->embedded_data; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (freepool->embedded_data, sizeof (freepool->embedded_data))); +} + +void +_cairo_freepool_fini (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool; + + pool = freepool->pools; + while (pool != &freepool->embedded_pool) { + cairo_freelist_pool_t *next = pool->next; + free (pool); + pool = next; + } + + pool = freepool->freepools; + while (pool != NULL) { + cairo_freelist_pool_t *next = pool->next; + free (pool); + pool = next; + } + + VG (VALGRIND_MAKE_MEM_UNDEFINED (freepool, sizeof (freepool))); +} + +void * +_cairo_freepool_alloc_from_new_pool (cairo_freepool_t *freepool) +{ + cairo_freelist_pool_t *pool; + int poolsize; + + if (freepool->freepools != NULL) { + pool = freepool->freepools; + freepool->freepools = pool->next; + + poolsize = pool->size; + } else { + if (freepool->pools != &freepool->embedded_pool) + poolsize = 2 * freepool->pools->size; + else + poolsize = (128 * freepool->nodesize + 8191) & -8192; + + pool = _cairo_malloc (sizeof (cairo_freelist_pool_t) + poolsize); + if (unlikely (pool == NULL)) + return pool; + + pool->size = poolsize; + } + + pool->next = freepool->pools; + freepool->pools = pool; + + pool->rem = poolsize - freepool->nodesize; + pool->data = (uint8_t *) (pool + 1) + freepool->nodesize; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pool->data, pool->rem)); + + return pool + 1; +} + +cairo_status_t +_cairo_freepool_alloc_array (cairo_freepool_t *freepool, + int count, + void **array) +{ + int i; + + for (i = 0; i < count; i++) { + cairo_freelist_node_t *node; + + node = freepool->first_free_node; + if (likely (node != NULL)) { + VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next))); + freepool->first_free_node = node->next; + VG (VALGRIND_MAKE_MEM_UNDEFINED (node, freepool->nodesize)); + } else { + node = _cairo_freepool_alloc_from_pool (freepool); + if (unlikely (node == NULL)) + goto CLEANUP; + } + + array[i] = node; + } + + return CAIRO_STATUS_SUCCESS; + + CLEANUP: + while (i--) + _cairo_freepool_free (freepool, array[i]); + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-font.c new file mode 100644 index 0000000000..7b2afea237 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-ft-font.c @@ -0,0 +1,4088 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2000 Keith Packard + * 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): + * Graydon Hoare + * Owen Taylor + * Keith Packard + * Carl Worth + */ + +#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 + +#include "cairo-fontconfig-private.h" + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H +#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 +#endif + +#if HAVE_FT_LIBRARY_SETLCDFILTER +#include FT_LCD_FILTER_H +#endif + +#if HAVE_UNISTD_H +#include +#else +#define access(p, m) 0 +#endif +#include + +/* Fontconfig version older than 2.6 didn't have these options */ +#ifndef FC_LCD_FILTER +#define FC_LCD_FILTER "lcdfilter" +#endif +/* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */ +#ifndef FC_LCD_NONE +#define FC_LCD_NONE 0 +#define FC_LCD_DEFAULT 1 +#define FC_LCD_LIGHT 2 +#define FC_LCD_LEGACY 3 +#endif + +/* FreeType version older than 2.3.5(?) didn't have these options */ +#ifndef FT_LCD_FILTER_NONE +#define FT_LCD_FILTER_NONE 0 +#define FT_LCD_FILTER_DEFAULT 1 +#define FT_LCD_FILTER_LIGHT 2 +#define FT_LCD_FILTER_LEGACY 16 +#endif + +#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) + +/* This is the max number of FT_face objects we keep open at once + */ +#define MAX_OPEN_FACES 10 + +extern void mozilla_AddRefSharedFTFace(void* aContext); +extern void mozilla_ReleaseSharedFTFace(void* aContext, void* aOwner); +/* Returns true if the face's state has been modified 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 aLibrary); +extern void mozilla_UnlockFTLibrary(FT_Library aLibrary); + +#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)) + +/** + * Function types for FreeType symbols we'll look up at runtime, rather than + * relying on build-time checks for availability. + */ +typedef FT_Error (*GetVarFunc) (FT_Face, FT_MM_Var**); +typedef FT_Error (*DoneVarFunc) (FT_Library, FT_MM_Var*); +typedef FT_Error (*GetVarDesignCoordsFunc) (FT_Face, FT_UInt, FT_Fixed*); +typedef FT_Error (*SetVarDesignCoordsFunc) (FT_Face, FT_UInt, FT_Fixed*); +typedef FT_Error (*GetVarBlendCoordsFunc) (FT_Face, FT_UInt, FT_Fixed*); + +/** + * SECTION:cairo-ft + * @Title: FreeType Fonts + * @Short_Description: Font support for FreeType + * @See_Also: #cairo_font_face_t + * + * 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: + * + * 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 + * factors so that hinting works right + */ + +typedef struct _cairo_ft_font_transform { + double x_scale, y_scale; + double shape[2][2]; +} cairo_ft_font_transform_t; + +/* + * We create an object that corresponds to a single font on the disk; + * (identified by a filename/id pair) these are shared between all + * fonts using that file. For cairo_ft_font_face_create_for_ft_face(), we + * just create a one-off version with a permanent face value. + */ + +typedef struct _cairo_ft_font_face cairo_ft_font_face_t; + +struct _cairo_ft_unscaled_font { + cairo_unscaled_font_t base; + + 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; + int id; + + /* We temporarily scale the unscaled font as needed */ + cairo_bool_t have_scale; + cairo_matrix_t current_scale; + double x_scale; /* Extracted X scale factor */ + double y_scale; /* Extracted Y scale factor */ + cairo_bool_t have_shape; /* true if the current scale has a non-scale component*/ + 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; + + cairo_ft_font_face_t *faces; /* Linked list of faces for this font */ +}; + +static int +_cairo_ft_unscaled_font_keys_equal (const void *key_a, + const void *key_b); + +static void +_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled); + +typedef struct _cairo_ft_options { + 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; + + cairo_ft_unscaled_font_t *unscaled; + cairo_ft_options_t ft_options; + cairo_ft_font_face_t *next; + +#if CAIRO_HAS_FC_FONT + FcPattern *pattern; /* if pattern is set, the above fields will be NULL */ + cairo_font_face_t *resolved_font_face; + FcConfig *resolved_config; +#endif +}; + +static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend; + +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern); + +static cairo_font_face_t * +_cairo_ft_resolve_pattern (FcPattern *pattern, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options); + +#endif + +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 + * number of FT_Face objects we keep around; when we've exceeded that + * limit and need to create a new FT_Face, we dump the FT_Face from a + * random #cairo_ft_unscaled_font_t which has an unlocked FT_Face, (if + * there are any). + */ + +typedef struct _cairo_ft_unscaled_font_map { + cairo_hash_table_t *hash_table; + FT_Library ft_library; + int num_open_faces; +} cairo_ft_unscaled_font_map_t; + +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) +{ + if (unscaled->face) { + FT_Done_Face (unscaled->face); + unscaled->face = NULL; + unscaled->have_scale = FALSE; + + font_map->num_open_faces--; + } +} + +static cairo_status_t +_cairo_ft_unscaled_font_map_create (void) +{ + cairo_ft_unscaled_font_map_t *font_map; + + /* This function is only intended to be called from + * _cairo_ft_unscaled_font_map_lock. So we'll crash if we can + * detect some other call path. */ + assert (cairo_ft_unscaled_font_map == NULL); + + font_map = _cairo_malloc (sizeof (cairo_ft_unscaled_font_map_t)); + if (unlikely (font_map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font_map->hash_table = + _cairo_hash_table_create (_cairo_ft_unscaled_font_keys_equal); + + if (unlikely (font_map->hash_table == NULL)) + goto FAIL; + + if (unlikely (FT_Init_FreeType (&font_map->ft_library))) + goto FAIL; + + font_map->num_open_faces = 0; + + cairo_ft_unscaled_font_map = font_map; + return CAIRO_STATUS_SUCCESS; + +FAIL: + if (font_map->hash_table) + _cairo_hash_table_destroy (font_map->hash_table); + free (font_map); + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} + + +static void +_cairo_ft_unscaled_font_map_pluck_entry (void *entry, void *closure) +{ + cairo_ft_unscaled_font_t *unscaled = entry; + cairo_ft_unscaled_font_map_t *font_map = closure; + + _cairo_hash_table_remove (font_map->hash_table, + &unscaled->base.hash_entry); + + if (unscaled->from_face) + mozilla_ReleaseSharedFTFace (unscaled->face_context, unscaled); + else + _font_map_release_face_lock_held (font_map, unscaled); + + _cairo_ft_unscaled_font_fini (unscaled); + free (unscaled); +} + +static void +_cairo_ft_unscaled_font_map_destroy (void) +{ + cairo_ft_unscaled_font_map_t *font_map; + + CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex); + font_map = cairo_ft_unscaled_font_map; + cairo_ft_unscaled_font_map = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); + + if (font_map != NULL) { + _cairo_hash_table_foreach (font_map->hash_table, + _cairo_ft_unscaled_font_map_pluck_entry, + font_map); + assert (font_map->num_open_faces == 0); + + FT_Done_FreeType (font_map->ft_library); + + _cairo_hash_table_destroy (font_map->hash_table); + + free (font_map); + } +} + +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)) { + if (unlikely (_cairo_ft_unscaled_font_map_create ())) { + CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); + return NULL; + } + } + + return cairo_ft_unscaled_font_map; +} + +static void +_cairo_ft_unscaled_font_map_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex); +} + +static void +_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) +{ + unsigned long hash; + + key->from_face = from_face; + 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 */ + hash += ((unsigned long) id) * 1607; + hash += ((unsigned long) face) * 2137; + + key->base.hash_entry.hash = hash; +} + +/** + * _cairo_ft_unscaled_font_init: + * + * Initialize a #cairo_ft_unscaled_font_t. + * + * There are two basic flavors of #cairo_ft_unscaled_font_t, one + * created from an FT_Face and the other created from a filename/id + * pair. These two flavors are identified as from_face and !from_face. + * + * To initialize a from_face font, pass filename==%NULL, id=0 and the + * desired face. + * + * To initialize a !from_face font, pass the filename/id as desired + * and face==%NULL. + * + * Note that the code handles these two flavors in very distinct + * ways. For example there is a hash_table mapping + * filename/id->#cairo_unscaled_font_t in the !from_face case, but no + * parallel in the from_face case, (where the calling code would have + * to do its own mapping to ensure similar sharing). + **/ +static cairo_status_t +_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) +{ + _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, id, face, face_context); + + + unscaled->have_color = FT_HAS_COLOR (face) != 0; + unscaled->have_color_set = TRUE; + + static GetVarFunc getVar; + static DoneVarFunc doneVar; + static GetVarDesignCoordsFunc getVarDesignCoords; + + static int firstTime = 1; + if (firstTime) { + getVar = (GetVarFunc) dlsym (RTLD_DEFAULT, "FT_Get_MM_Var"); + doneVar = (DoneVarFunc) dlsym (RTLD_DEFAULT, "FT_Done_MM_Var"); + getVarDesignCoords = (GetVarDesignCoordsFunc) dlsym (RTLD_DEFAULT, "FT_Get_Var_Design_Coordinates"); + firstTime = 0; + } + + if (getVar && getVarDesignCoords) { + FT_MM_Var *ft_mm_var; + if (0 == (*getVar) (face, &ft_mm_var)) + { + unscaled->variations = calloc (ft_mm_var->num_axis, sizeof (FT_Fixed)); + if (unscaled->variations) + (*getVarDesignCoords) (face, ft_mm_var->num_axis, unscaled->variations); + if (doneVar) + (*doneVar) (face->glyph->library, ft_mm_var); + else + free (ft_mm_var); + } + } + } 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); + + unscaled->have_color_set = FALSE; + } + + unscaled->have_scale = FALSE; + CAIRO_MUTEX_INIT (unscaled->mutex); + unscaled->lock_count = 0; + + unscaled->faces = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_ft_unscaled_font_fini: + * + * Free all data associated with a #cairo_ft_unscaled_font_t. + * + * CAUTION: The unscaled->face field must be %NULL before calling this + * function. This is because the #cairo_ft_unscaled_font_t_map keeps a + * count of these faces (font_map->num_open_faces) so it maintains the + * unscaled->face field while it has its lock held. See + * _font_map_release_face_lock_held(). + **/ +static void +_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled) +{ + assert (unscaled->face == NULL); + + free (unscaled->filename); + unscaled->filename = NULL; + + free (unscaled->variations); + + CAIRO_MUTEX_FINI (unscaled->mutex); +} + +static int +_cairo_ft_unscaled_font_keys_equal (const void *key_a, + const void *key_b) +{ + const cairo_ft_unscaled_font_t *unscaled_a = key_a; + const cairo_ft_unscaled_font_t *unscaled_b = key_b; + + 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 && + unscaled_a->face_context == unscaled_b->face_context; + + if (unscaled_a->filename == NULL && unscaled_b->filename == NULL) + return TRUE; + else if (unscaled_a->filename == NULL || unscaled_b->filename == NULL) + return FALSE; + else + return (strcmp (unscaled_a->filename, unscaled_b->filename) == 0); + } + + return FALSE; +} + +/* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from + * pattern. Returns a new reference to the unscaled font. + */ +static cairo_status_t +_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; + cairo_ft_unscaled_font_map_t *font_map; + cairo_status_t status; + + font_map = _cairo_ft_unscaled_font_map_lock (); + 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); + + /* Return existing unscaled font if it exists in the hash table. */ + unscaled = _cairo_hash_table_lookup (font_map->hash_table, + &key.base.hash_entry); + if (unscaled != NULL) { + _cairo_unscaled_font_reference (&unscaled->base); + goto DONE; + } + + /* Otherwise create it and insert into hash table. */ + 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); + if (unlikely (status)) + goto UNWIND_UNSCALED_MALLOC; + + assert (unscaled->base.hash_entry.hash == key.base.hash_entry.hash); + status = _cairo_hash_table_insert (font_map->hash_table, + &unscaled->base.hash_entry); + if (unlikely (status)) + goto UNWIND_UNSCALED_FONT_INIT; + + mozilla_AddRefSharedFTFace (face_context); + +DONE: + _cairo_ft_unscaled_font_map_unlock (); + *out = unscaled; + return CAIRO_STATUS_SUCCESS; + +UNWIND_UNSCALED_FONT_INIT: + _cairo_ft_unscaled_font_fini (unscaled); +UNWIND_UNSCALED_MALLOC: + free (unscaled); +UNWIND_FONT_MAP_LOCK: + _cairo_ft_unscaled_font_map_unlock (); + return status; +} + + +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern, + cairo_ft_unscaled_font_t **out) +{ + FT_Face font_face = NULL; + char *filename = NULL; + int id = 0; + FcResult ret; + + ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face); + if (ret == FcResultMatch) + goto DONE; + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + ret = FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename); + if (ret == FcResultOutOfMemory) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (ret == FcResultMatch) { + 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; + } else + return _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND); + } + + /* The pattern contains neither a face nor a filename, resolve it later. */ + *out = NULL; + return CAIRO_STATUS_SUCCESS; + +DONE: + return _cairo_ft_unscaled_font_create_internal (font_face != NULL, + filename, id, font_face, NULL, + 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, face->face_index, face, face_context, out); +} + +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; + + 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_dec_and_test (&unscaled->base.ref_count)) { + /* somebody recreated the font whilst we waited for the lock */ + _cairo_ft_unscaled_font_map_unlock (); + return FALSE; + } + + _cairo_hash_table_remove (font_map->hash_table, + &unscaled->base.hash_entry); + + if (unscaled->from_face) { + /* See comments in _ft_font_face_destroy about the "zombie" state + * for a _ft_font_face. + */ + if (unscaled->faces && unscaled->faces->unscaled == NULL) { + assert (unscaled->faces->next == NULL); + 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 +_has_unlocked_face (const void *entry) +{ + const cairo_ft_unscaled_font_t *unscaled = entry; + + return (!unscaled->from_face && unscaled->lock_count == 0 && unscaled->face); +} + +/* Ensures that an unscaled font has a face object. If we exceed + * MAX_OPEN_FACES, try to close some. + * + * 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. + */ +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); + } + unscaled->lock_count++; + + if (unscaled->face) + return unscaled->face; + + /* If this unscaled font was created from an FT_Face then we just + * returned it above. */ + assert (!unscaled->from_face); + + font_map = _cairo_ft_unscaled_font_map_lock (); + { + assert (font_map != NULL); + + while (font_map->num_open_faces >= MAX_OPEN_FACES) + { + cairo_ft_unscaled_font_t *entry; + + entry = _cairo_hash_table_random_entry (font_map->hash_table, + _has_unlocked_face); + if (entry == NULL) + break; + + _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); + if (error) + { + unscaled->lock_count--; + CAIRO_FT_UNLOCK (unscaled); + _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; +} + + +/* Unlock unscaled font locked with _cairo_ft_unscaled_font_lock_face + */ +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); +} + + +static cairo_status_t +_compute_transform (cairo_ft_font_transform_t *sf, + cairo_matrix_t *scale, + cairo_ft_unscaled_font_t *unscaled) +{ + cairo_status_t status; + double x_scale, y_scale; + cairo_matrix_t normalized = *scale; + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. These influence the way freetype + * chooses hints, as well as selecting different bitmaps in + * hand-rendered fonts. We also copy the normalized matrix to + * freetype's transformation. + */ + + status = _cairo_matrix_compute_basis_scale_factors (scale, + &x_scale, &y_scale, + 1); + if (unlikely (status)) + return status; + + /* FreeType docs say this about x_scale and y_scale: + * "A character width or height smaller than 1pt is set to 1pt;" + * So, we cap them from below at 1.0 and let the FT transform + * take care of sub-1.0 scaling. */ + if (x_scale < 1.0) + x_scale = 1.0; + if (y_scale < 1.0) + y_scale = 1.0; + + if (unscaled && (unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) { + double min_distance = DBL_MAX; + cairo_bool_t magnify = TRUE; + int i; + double best_x_size = 0; + double best_y_size = 0; + + for (i = 0; i < unscaled->face->num_fixed_sizes; i++) { + double x_size = unscaled->face->available_sizes[i].x_ppem / 64.; + double y_size = unscaled->face->available_sizes[i].y_ppem / 64.; + double distance = y_size - y_scale; + + /* + * distance is positive if current strike is larger than desired + * size, and negative if smaller. + * + * We like to prefer down-scaling to upscaling. + */ + + if ((magnify && distance >= 0) || fabs (distance) <= min_distance) { + magnify = distance < 0; + min_distance = fabs (distance); + best_x_size = x_size; + best_y_size = y_size; + } + } + + x_scale = best_x_size; + y_scale = best_y_size; + } + + sf->x_scale = x_scale; + sf->y_scale = y_scale; + + cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale); + + _cairo_matrix_get_affine (&normalized, + &sf->shape[0][0], &sf->shape[0][1], + &sf->shape[1][0], &sf->shape[1][1], + NULL, NULL); + + return CAIRO_STATUS_SUCCESS; +} + +/* Temporarily scales an unscaled font to the give scale. We catch + * scaling to the same size, since changing a FT_Face is expensive. + */ +static cairo_status_t +_cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled, + cairo_matrix_t *scale) +{ + cairo_status_t status; + cairo_ft_font_transform_t sf; + FT_Matrix mat; + FT_Error error; + + assert (unscaled->face != NULL); + + if (unscaled->have_scale && + scale->xx == unscaled->current_scale.xx && + scale->yx == unscaled->current_scale.yx && + scale->xy == unscaled->current_scale.xy && + scale->yy == unscaled->current_scale.yy) + return CAIRO_STATUS_SUCCESS; + + unscaled->have_scale = TRUE; + unscaled->current_scale = *scale; + + status = _compute_transform (&sf, scale, unscaled); + if (unlikely (status)) + return status; + + unscaled->x_scale = sf.x_scale; + unscaled->y_scale = sf.y_scale; + + mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]); + mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]); + mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]); + mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]); + + unscaled->have_shape = (mat.xx != 0x10000 || + mat.yx != 0x00000 || + mat.xy != 0x00000 || + mat.yy != 0x10000); + + unscaled->Current_Shape = mat; + cairo_matrix_init (&unscaled->current_shape, + sf.shape[0][0], sf.shape[0][1], + sf.shape[1][0], sf.shape[1][1], + 0.0, 0.0); + + FT_Set_Transform(unscaled->face, &mat, NULL); + + error = FT_Set_Char_Size (unscaled->face, + sf.x_scale * 64.0 + .5, + sf.y_scale * 64.0 + .5, + 0, 0); + if (error) + return _cairo_error (_ft_to_cairo_error (error)); + + return CAIRO_STATUS_SUCCESS; +} + +/* we sometimes need to convert the glyph bitmap in a FT_GlyphSlot + * into a different format. For example, we want to convert a + * FT_PIXEL_MODE_LCD or FT_PIXEL_MODE_LCD_V bitmap into a 32-bit + * ARGB or ABGR bitmap. + * + * this function prepares a target descriptor for this operation. + * + * input :: target bitmap descriptor. The function will set its + * 'width', 'rows' and 'pitch' fields, and only these + * + * slot :: the glyph slot containing the source bitmap. this + * function assumes that slot->format == FT_GLYPH_FORMAT_BITMAP + * + * mode :: the requested final rendering mode. supported values are + * MONO, NORMAL (i.e. gray), LCD and LCD_V + * + * the function returns the size in bytes of the corresponding buffer, + * it's up to the caller to allocate the corresponding memory block + * before calling _fill_xrender_bitmap + * + * it also returns -1 in case of error (e.g. incompatible arguments, + * like trying to convert a gray bitmap into a monochrome one) + */ +static int +_compute_xrender_bitmap_size(FT_Bitmap *target, + FT_GlyphSlot slot, + FT_Render_Mode mode) +{ + FT_Bitmap *ftbit; + int width, height, pitch; + + if (slot->format != FT_GLYPH_FORMAT_BITMAP) + return -1; + + /* compute the size of the final bitmap */ + ftbit = &slot->bitmap; + + width = ftbit->width; + height = ftbit->rows; + pitch = (width + 3) & ~3; + + switch (ftbit->pixel_mode) { + case FT_PIXEL_MODE_MONO: + if (mode == FT_RENDER_MODE_MONO) { + pitch = (((width + 31) & ~31) >> 3); + break; + } + /* fall-through */ + + case FT_PIXEL_MODE_GRAY: + if (mode == FT_RENDER_MODE_LCD || + mode == FT_RENDER_MODE_LCD_V) + { + /* each pixel is replicated into a 32-bit ARGB value */ + pitch = width * 4; + } + break; + + case FT_PIXEL_MODE_LCD: + if (mode != FT_RENDER_MODE_LCD) + return -1; + + /* horz pixel triplets are packed into 32-bit ARGB values */ + width /= 3; + pitch = width * 4; + break; + + case FT_PIXEL_MODE_LCD_V: + if (mode != FT_RENDER_MODE_LCD_V) + return -1; + + /* vert pixel triplets are packed into 32-bit ARGB values */ + height /= 3; + 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; + } + + target->width = width; + target->rows = height; + target->pitch = pitch; + target->buffer = NULL; + + return pitch * height; +} + +/* this functions converts the glyph bitmap found in a FT_GlyphSlot + * into a different format (see _compute_xrender_bitmap_size) + * + * you should call this function after _compute_xrender_bitmap_size + * + * target :: target bitmap descriptor. Note that its 'buffer' pointer + * must point to memory allocated by the caller + * + * slot :: the glyph slot containing the source bitmap + * + * mode :: the requested final rendering mode + * + * bgr :: boolean, set if BGR or VBGR pixel ordering is needed + */ +static void +_fill_xrender_bitmap(FT_Bitmap *target, + FT_GlyphSlot slot, + FT_Render_Mode mode, + int bgr) +{ + FT_Bitmap *ftbit = &slot->bitmap; + unsigned char *srcLine = ftbit->buffer; + unsigned char *dstLine = target->buffer; + int src_pitch = ftbit->pitch; + int width = target->width; + int height = target->rows; + int pitch = target->pitch; + int subpixel; + int h; + + subpixel = (mode == FT_RENDER_MODE_LCD || + mode == FT_RENDER_MODE_LCD_V); + + if (src_pitch < 0) + srcLine -= src_pitch * (ftbit->rows - 1); + + target->pixel_mode = ftbit->pixel_mode; + + switch (ftbit->pixel_mode) { + case FT_PIXEL_MODE_MONO: + if (subpixel) { + /* convert mono to ARGB32 values */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + int x; + + for (x = 0; x < width; x++) { + if (srcLine[(x >> 3)] & (0x80 >> (x & 7))) + ((unsigned int *) dstLine)[x] = 0xffffffffU; + } + } + target->pixel_mode = FT_PIXEL_MODE_LCD; + + } else if (mode == FT_RENDER_MODE_NORMAL) { + /* convert mono to 8-bit gray */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + int x; + + for (x = 0; x < width; x++) { + if (srcLine[(x >> 3)] & (0x80 >> (x & 7))) + dstLine[x] = 0xff; + } + } + target->pixel_mode = FT_PIXEL_MODE_GRAY; + + } else { + /* copy mono to mono */ + + int bytes = (width + 7) >> 3; + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) + memcpy (dstLine, srcLine, bytes); + } + break; + + case FT_PIXEL_MODE_GRAY: + if (subpixel) { + /* convert gray to ARGB32 values */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + int x; + unsigned int *dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++) { + unsigned int pix = srcLine[x]; + + pix |= (pix << 8); + pix |= (pix << 16); + + dst[x] = pix; + } + } + target->pixel_mode = FT_PIXEL_MODE_LCD; + } else { + /* copy gray into gray */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) + memcpy (dstLine, srcLine, width); + } + break; + + case FT_PIXEL_MODE_LCD: + if (!bgr) { + /* convert horizontal RGB into ARGB32 */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + int x; + unsigned char *src = srcLine; + unsigned int *dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++, src += 3) { + unsigned int pix; + + pix = ((unsigned int)src[0] << 16) | + ((unsigned int)src[1] << 8) | + ((unsigned int)src[2] ) | + ((unsigned int)src[1] << 24) ; + + dst[x] = pix; + } + } + } else { + /* convert horizontal BGR into ARGB32 */ + + for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) { + + int x; + unsigned char *src = srcLine; + unsigned int *dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++, src += 3) { + unsigned int pix; + + pix = ((unsigned int)src[2] << 16) | + ((unsigned int)src[1] << 8) | + ((unsigned int)src[0] ) | + ((unsigned int)src[1] << 24) ; + + dst[x] = pix; + } + } + } + break; + + case FT_PIXEL_MODE_LCD_V: + /* convert vertical RGB into ARGB32 */ + if (!bgr) { + + for (h = height; h > 0; h--, srcLine += 3 * src_pitch, dstLine += pitch) { + int x; + unsigned char* src = srcLine; + unsigned int* dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++, src += 1) { + unsigned int pix; + pix = ((unsigned int)src[0] << 16) | + ((unsigned int)src[src_pitch] << 8) | + ((unsigned int)src[src_pitch*2] ) | + ((unsigned int)src[src_pitch] << 24) ; + dst[x] = pix; + } + } + } else { + + for (h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch) { + int x; + unsigned char *src = srcLine; + unsigned int *dst = (unsigned int *) dstLine; + + for (x = 0; x < width; x++, src += 1) { + unsigned int pix; + + pix = ((unsigned int)src[src_pitch * 2] << 16) | + ((unsigned int)src[src_pitch] << 8) | + ((unsigned int)src[0] ) | + ((unsigned int)src[src_pitch] << 24) ; + + dst[x] = pix; + } + } + } + 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); + } +} + + +/* Fills in val->image with an image surface created from @bitmap + */ +static cairo_status_t +_get_bitmap_surface (FT_Bitmap *bitmap, + FT_Library library, + cairo_bool_t own_buffer, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + 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; + + width = bitmap->width; + height = bitmap->rows; + + if (width == 0 || height == 0) { + *surface = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (NULL, format, 0, 0, 0); + return (*surface)->base.status; + } + + switch (bitmap->pixel_mode) { + case FT_PIXEL_MODE_MONO: + stride = (((width + 31) & ~31) >> 3); + if (own_buffer) { + data = bitmap->buffer; + assert (stride == bitmap->pitch); + } else { + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (stride == bitmap->pitch) { + memcpy (data, bitmap->buffer, stride * height); + } else { + int i; + unsigned char *source, *dest; + + source = bitmap->buffer; + dest = data; + for (i = height; i; i--) { + memcpy (dest, source, bitmap->pitch); + memset (dest + bitmap->pitch, '\0', stride - bitmap->pitch); + + source += bitmap->pitch; + dest += stride; + } + } + } + +#ifndef WORDS_BIGENDIAN + { + uint8_t *d = data; + int count = stride * height; + + while (count--) { + *d = CAIRO_BITSWAP8 (*d); + d++; + } + } +#endif + format = CAIRO_FORMAT_A1; + break; + + case FT_PIXEL_MODE_LCD: + case FT_PIXEL_MODE_LCD_V: + case FT_PIXEL_MODE_GRAY: + if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL || + bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) + { + stride = bitmap->pitch; + + /* We don't support stride not multiple of 4. */ + if (stride & 3) + { + assert (!own_buffer); + goto convert; + } + + if (own_buffer) { + data = bitmap->buffer; + } else { + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (data, bitmap->buffer, stride * height); + } + + format = CAIRO_FORMAT_A8; + } else { + data = bitmap->buffer; + stride = bitmap->pitch; + format = CAIRO_FORMAT_ARGB32; + component_alpha = TRUE; + } + break; +#ifdef FT_LOAD_COLOR + case FT_PIXEL_MODE_BGRA: + stride = width * 4; + if (own_buffer) { + data = bitmap->buffer; + } else { + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + 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 + case FT_PIXEL_MODE_GRAY2: + case FT_PIXEL_MODE_GRAY4: + convert: + if (!own_buffer && library) + { + /* This is pretty much the only case that we can get in here. */ + /* Convert to 8bit grayscale. */ + + FT_Bitmap tmp; + FT_Int align; + FT_Error error; + + format = CAIRO_FORMAT_A8; + + align = cairo_format_stride_for_width (format, bitmap->width); + + FT_Bitmap_New( &tmp ); + + 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; + + stride = bitmap->pitch; + data = _cairo_malloc_ab (height, stride); + if (!data) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (bitmap->num_grays != 256) + { + unsigned int x, y; + unsigned int mul = 255 / (bitmap->num_grays - 1); + FT_Byte *p = bitmap->buffer; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + p[x] *= mul; + p += bitmap->pitch; + } + } + + 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_INVALID_FORMAT); + } + + /* XXX */ + *surface = image = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (data, + format, + width, height, stride); + if (image->base.status) { + free (data); + return (*surface)->base.status; + } + + if (component_alpha) + pixman_image_set_component_alpha (image->pixman_image, TRUE); + + _cairo_image_surface_assume_ownership_of_data (image); + + _cairo_debug_check_image_surface_is_defined (&image->base); + + return CAIRO_STATUS_SUCCESS; +} + +/* Converts an outline FT_GlyphSlot into an image + * + * This could go through _render_glyph_bitmap as well, letting + * FreeType convert the outline to a bitmap, but doing it ourselves + * has two minor advantages: first, we save a copy of the bitmap + * buffer: we can directly use the buffer that FreeType renders + * into. + * + * Second, it may help when we add support for subpixel + * rendering: the Xft code does it this way. (Keith thinks that + * it may also be possible to get the subpixel rendering with + * FT_Render_Glyph: something worth looking into in more detail + * when we add subpixel support. If so, we may want to eliminate + * this version of the code path entirely. + */ +static cairo_status_t +_render_glyph_outline (FT_Face face, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + int rgba = FC_RGBA_UNKNOWN; + int lcd_filter = FT_LCD_FILTER_LEGACY; + FT_GlyphSlot glyphslot = face->glyph; + FT_Outline *outline = &glyphslot->outline; + FT_Bitmap bitmap; + FT_BBox cbox; + unsigned int width, height; + cairo_status_t status; + FT_Error error; + FT_Library library = glyphslot->library; + FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL; + + switch (font_options->antialias) { + case CAIRO_ANTIALIAS_NONE: + render_mode = FT_RENDER_MODE_MONO; + break; + + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: + switch (font_options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + render_mode = FT_RENDER_MODE_LCD; + break; + + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + render_mode = FT_RENDER_MODE_LCD_V; + break; + } + + switch (font_options->lcd_filter) { + case CAIRO_LCD_FILTER_NONE: + lcd_filter = FT_LCD_FILTER_NONE; + break; + case CAIRO_LCD_FILTER_DEFAULT: + case CAIRO_LCD_FILTER_INTRA_PIXEL: + lcd_filter = FT_LCD_FILTER_LEGACY; + break; + case CAIRO_LCD_FILTER_FIR3: + lcd_filter = FT_LCD_FILTER_LIGHT; + break; + case CAIRO_LCD_FILTER_FIR5: + lcd_filter = FT_LCD_FILTER_DEFAULT; + break; + } + + break; + + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_FAST: + render_mode = FT_RENDER_MODE_NORMAL; + } + + FT_Outline_Get_CBox (outline, &cbox); + + cbox.xMin &= -64; + cbox.yMin &= -64; + cbox.xMax = (cbox.xMax + 63) & -64; + cbox.yMax = (cbox.yMax + 63) & -64; + + width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6); + height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6); + + if (width * height == 0) { + cairo_format_t format; + /* Looks like fb handles zero-sized images just fine */ + switch (render_mode) { + case FT_RENDER_MODE_MONO: + format = CAIRO_FORMAT_A1; + break; + case FT_RENDER_MODE_LCD: + case FT_RENDER_MODE_LCD_V: + format= CAIRO_FORMAT_ARGB32; + break; + case FT_RENDER_MODE_LIGHT: + case FT_RENDER_MODE_NORMAL: + case FT_RENDER_MODE_MAX: + default: + format = CAIRO_FORMAT_A8; + break; + } + + (*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; + + switch (render_mode) { + case FT_RENDER_MODE_LCD: + if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) + rgba = FC_RGBA_BGR; + 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_MONO: + case FT_RENDER_MODE_LIGHT: + case FT_RENDER_MODE_NORMAL: + case FT_RENDER_MODE_MAX: + default: + break; + } + +#if HAVE_FT_LIBRARY_SETLCDFILTER + FT_Library_SetLcdFilter (library, lcd_filter); +#endif + + error = FT_Render_Glyph (face->glyph, render_mode); + +#if HAVE_FT_LIBRARY_SETLCDFILTER + FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE); +#endif + + 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_INVALID_FORMAT); + + bitmap.buffer = calloc (1, bitmap_size); + if (bitmap.buffer == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _fill_xrender_bitmap (&bitmap, face->glyph, render_mode, + (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR)); + + /* Note: + * _get_bitmap_surface will free bitmap.buffer if there is an error + */ + status = _get_bitmap_surface (&bitmap, NULL, TRUE, font_options, surface); + if (unlikely (status)) + return status; + + /* Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. Moreover, device + * offsets are position of glyph origin relative to top left while xMin + * and yMax are offsets of top left relative to origin. Another negation. + */ + cairo_surface_set_device_offset (&(*surface)->base, + (double)-glyphslot->bitmap_left, + (double)+glyphslot->bitmap_top); + } + + return CAIRO_STATUS_SUCCESS; +} + +/* Converts a bitmap (or other) FT_GlyphSlot into an image */ +static cairo_status_t +_render_glyph_bitmap (FT_Face face, + cairo_font_options_t *font_options, + cairo_image_surface_t **surface) +{ + FT_GlyphSlot glyphslot = face->glyph; + cairo_status_t status; + FT_Error error; + + /* According to the FreeType docs, glyphslot->format could be + * something other than FT_GLYPH_FORMAT_OUTLINE or + * FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType + * the opportunity to convert such to + * bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since + * we avoid the FT_LOAD_NO_RECURSE flag. + */ + error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); + /* 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); + + status = _get_bitmap_surface (&glyphslot->bitmap, + glyphslot->library, + FALSE, font_options, + surface); + if (unlikely (status)) + return status; + + /* + * Note: the font's coordinate system is upside down from ours, so the + * Y coordinate of the control box needs to be negated. Moreover, device + * offsets are position of glyph origin relative to top left while + * bitmap_left and bitmap_top are offsets of top left relative to origin. + * Another negation. + */ + cairo_surface_set_device_offset (&(*surface)->base, + -glyphslot->bitmap_left, + +glyphslot->bitmap_top); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_transform_glyph_bitmap (cairo_matrix_t * shape, + cairo_image_surface_t ** surface) +{ + cairo_matrix_t original_to_transformed; + cairo_matrix_t transformed_to_original; + cairo_image_surface_t *old_image; + cairo_surface_t *image; + double x[4], y[4]; + double origin_x, origin_y; + int orig_width, orig_height; + int i; + int x_min, y_min, x_max, y_max; + int width, height; + cairo_status_t status; + cairo_surface_pattern_t pattern; + + /* We want to compute a transform that takes the origin + * (device_x_offset, device_y_offset) to 0,0, then applies + * the "shape" portion of the font transform + */ + original_to_transformed = *shape; + + cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y); + orig_width = (*surface)->width; + orig_height = (*surface)->height; + + cairo_matrix_translate (&original_to_transformed, + -origin_x, -origin_y); + + /* Find the bounding box of the original bitmap under that + * transform + */ + x[0] = 0; y[0] = 0; + x[1] = orig_width; y[1] = 0; + x[2] = orig_width; y[2] = orig_height; + x[3] = 0; y[3] = orig_height; + + for (i = 0; i < 4; i++) + cairo_matrix_transform_point (&original_to_transformed, + &x[i], &y[i]); + + x_min = floor (x[0]); y_min = floor (y[0]); + x_max = ceil (x[0]); y_max = ceil (y[0]); + + for (i = 1; i < 4; i++) { + if (x[i] < x_min) + x_min = floor (x[i]); + else if (x[i] > x_max) + x_max = ceil (x[i]); + if (y[i] < y_min) + y_min = floor (y[i]); + else if (y[i] > y_max) + y_max = ceil (y[i]); + } + + /* Adjust the transform so that the bounding box starts at 0,0 ... + * this gives our final transform from original bitmap to transformed + * bitmap. + */ + original_to_transformed.x0 -= x_min; + original_to_transformed.y0 -= y_min; + + /* Create the transformed bitmap */ + width = x_max - x_min; + height = y_max - y_min; + + transformed_to_original = original_to_transformed; + status = cairo_matrix_invert (&transformed_to_original); + if (unlikely (status)) + return status; + + 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 + image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + if (unlikely (image->status)) + return image->status; + + /* Draw the original bitmap transformed into the new bitmap + */ + _cairo_pattern_init_for_surface (&pattern, &(*surface)->base); + cairo_pattern_set_matrix (&pattern.base, &transformed_to_original); + + 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; + } + + /* Now update the cache entry for the new bitmap, recomputing + * the origin based on the final transform. + */ + cairo_matrix_transform_point (&original_to_transformed, + &origin_x, &origin_y); + + 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, + _cairo_lround (origin_x), + _cairo_lround (origin_y)); + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = { + _cairo_ft_unscaled_font_destroy, +#if 0 + _cairo_ft_unscaled_font_create_glyph +#endif +}; + +/* #cairo_ft_scaled_font_t */ + +typedef struct _cairo_ft_scaled_font { + cairo_scaled_font_t base; + cairo_ft_unscaled_font_t *unscaled; + cairo_ft_options_t ft_options; +} cairo_ft_scaled_font_t; + +static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend; + +#if CAIRO_HAS_FC_FONT +/* The load flags passed to FT_Load_Glyph control aspects like hinting and + * antialiasing. Here we compute them from the fields of a FcPattern. + */ +static void +_get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret) +{ + FcBool antialias, vertical_layout, hinting, autohint, bitmap, embolden; + cairo_ft_options_t ft_options; + int rgba; +#ifdef FC_HINT_STYLE + int hintstyle; +#endif + char *variations; + + _cairo_font_options_init_default (&ft_options.base); + ft_options.load_flags = FT_LOAD_DEFAULT; + ft_options.synth_flags = 0; + +#ifndef FC_EMBEDDED_BITMAP +#define FC_EMBEDDED_BITMAP "embeddedbitmap" +#endif + + /* Check whether to force use of embedded bitmaps */ + if (FcPatternGetBool (pattern, + FC_EMBEDDED_BITMAP, 0, &bitmap) != FcResultMatch) + bitmap = FcFalse; + + /* disable antialiasing if requested */ + if (FcPatternGetBool (pattern, + FC_ANTIALIAS, 0, &antialias) != FcResultMatch) + antialias = FcTrue; + + if (antialias) { + cairo_subpixel_order_t subpixel_order; + int lcd_filter; + + /* disable hinting if requested */ + if (FcPatternGetBool (pattern, + FC_HINTING, 0, &hinting) != FcResultMatch) + hinting = FcTrue; + + if (FcPatternGetInteger (pattern, + FC_RGBA, 0, &rgba) != FcResultMatch) + rgba = FC_RGBA_UNKNOWN; + + switch (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; + break; + } + + if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) { + ft_options.base.subpixel_order = subpixel_order; + ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL; + } + + if (FcPatternGetInteger (pattern, + FC_LCD_FILTER, 0, &lcd_filter) == FcResultMatch) + { + switch (lcd_filter) { + case FC_LCD_NONE: + ft_options.base.lcd_filter = CAIRO_LCD_FILTER_NONE; + break; + case FC_LCD_DEFAULT: + ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR5; + break; + case FC_LCD_LIGHT: + ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR3; + break; + case FC_LCD_LEGACY: + ft_options.base.lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL; + break; + } + } + +#ifdef FC_HINT_STYLE + if (FcPatternGetInteger (pattern, + FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) + hintstyle = FC_HINT_FULL; + + if (!hinting) + hintstyle = FC_HINT_NONE; + + switch (hintstyle) { + case FC_HINT_NONE: + ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; + break; + case FC_HINT_SLIGHT: + ft_options.base.hint_style = CAIRO_HINT_STYLE_SLIGHT; + break; + case FC_HINT_MEDIUM: + default: + ft_options.base.hint_style = CAIRO_HINT_STYLE_MEDIUM; + break; + case FC_HINT_FULL: + ft_options.base.hint_style = CAIRO_HINT_STYLE_FULL; + break; + } +#else /* !FC_HINT_STYLE */ + if (!hinting) { + ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE; + } +#endif /* FC_HINT_STYLE */ + + /* Force embedded bitmaps off if no hinting requested */ + if (ft_options.base.hint_style == CAIRO_HINT_STYLE_NONE) + bitmap = FcFalse; + + if (!bitmap) + ft_options.load_flags |= FT_LOAD_NO_BITMAP; + + } else { + ft_options.base.antialias = CAIRO_ANTIALIAS_NONE; + } + + /* force autohinting if requested */ + if (FcPatternGetBool (pattern, + FC_AUTOHINT, 0, &autohint) != FcResultMatch) + autohint = FcFalse; + + if (autohint) + ft_options.load_flags |= FT_LOAD_FORCE_AUTOHINT; + + if (FcPatternGetBool (pattern, + FC_VERTICAL_LAYOUT, 0, &vertical_layout) != FcResultMatch) + vertical_layout = FcFalse; + + if (vertical_layout) + ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT; + +#ifndef FC_EMBOLDEN +#define FC_EMBOLDEN "embolden" +#endif + if (FcPatternGetBool (pattern, + FC_EMBOLDEN, 0, &embolden) != FcResultMatch) + embolden = FcFalse; + + 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 + +static void +_cairo_ft_options_merge (cairo_ft_options_t *options, + cairo_ft_options_t *other) +{ + int load_flags = other->load_flags; + int load_target = FT_LOAD_TARGET_NORMAL; + + /* clear load target mode */ + load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags))); + + if (load_flags & FT_LOAD_NO_HINTING) + other->base.hint_style = CAIRO_HINT_STYLE_NONE; + + if (other->base.antialias == CAIRO_ANTIALIAS_NONE || + options->base.antialias == CAIRO_ANTIALIAS_NONE) { + options->base.antialias = CAIRO_ANTIALIAS_NONE; + options->base.subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; + } + + 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) + options->base.hint_style = other->base.hint_style; + + if (other->base.hint_style == CAIRO_HINT_STYLE_NONE) + options->base.hint_style = CAIRO_HINT_STYLE_NONE; + + if (options->base.lcd_filter == CAIRO_LCD_FILTER_DEFAULT) + options->base.lcd_filter = other->base.lcd_filter; + + if (other->base.lcd_filter == CAIRO_LCD_FILTER_NONE) + options->base.lcd_filter = CAIRO_LCD_FILTER_NONE; + + if (options->base.antialias == CAIRO_ANTIALIAS_NONE) { + if (options->base.hint_style == CAIRO_HINT_STYLE_NONE) + load_flags |= FT_LOAD_NO_HINTING; + else + load_target = FT_LOAD_TARGET_MONO; + load_flags |= FT_LOAD_MONOCHROME; + } else { + switch (options->base.hint_style) { + case CAIRO_HINT_STYLE_NONE: + load_flags |= FT_LOAD_NO_HINTING; + break; + case CAIRO_HINT_STYLE_SLIGHT: + load_target = FT_LOAD_TARGET_LIGHT; + break; + case CAIRO_HINT_STYLE_MEDIUM: + break; + case CAIRO_HINT_STYLE_FULL: + case CAIRO_HINT_STYLE_DEFAULT: + if (options->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + switch (options->base.subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + load_target = FT_LOAD_TARGET_LCD; + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + load_target = FT_LOAD_TARGET_LCD_V; + break; + } + } + break; + } + } + + 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; +} + +static cairo_status_t +_cairo_ft_font_face_scaled_font_create (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font_out) +{ + cairo_ft_font_face_t *font_face = abstract_font_face; + cairo_ft_scaled_font_t *scaled_font; + FT_Face face; + FT_Size_Metrics *metrics; + cairo_font_extents_t fs_metrics; + cairo_status_t status; + cairo_ft_unscaled_font_t *unscaled; + + assert (font_face->unscaled); + + face = _cairo_ft_unscaled_font_lock_face (font_face->unscaled); + if (unlikely (face == NULL)) /* backend error */ + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + scaled_font = _cairo_malloc (sizeof (cairo_ft_scaled_font_t)); + if (unlikely (scaled_font == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + scaled_font->unscaled = unscaled = font_face->unscaled; + _cairo_unscaled_font_reference (&unscaled->base); + + _cairo_font_options_init_copy (&scaled_font->ft_options.base, options); + _cairo_ft_options_merge (&scaled_font->ft_options, &font_face->ft_options); + + status = _cairo_scaled_font_init (&scaled_font->base, + &font_face->base, + font_matrix, ctm, options, + &_cairo_ft_scaled_font_backend); + if (unlikely (status)) + goto CLEANUP_SCALED_FONT; + + status = _cairo_ft_unscaled_font_set_scale (unscaled, + &scaled_font->base.scale); + if (unlikely (status)) { + /* This can only fail if we encounter an error with the underlying + * font, so propagate the error back to the font-face. */ + _cairo_ft_unscaled_font_unlock_face (unscaled); + _cairo_unscaled_font_destroy (&unscaled->base); + free (scaled_font); + return status; + } + + + metrics = &face->size->metrics; + + /* + * Get to unscaled metrics so that the upper level can get back to + * user space + * + * Also use this path for bitmap-only fonts. The other branch uses + * face members that are only relevant for scalable fonts. This is + * detected by simply checking for units_per_EM==0. + */ + if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF || + face->units_per_EM == 0) { + double x_factor, y_factor; + + if (unscaled->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / unscaled->x_scale; + + if (unscaled->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / unscaled->y_scale; + + fs_metrics.ascent = DOUBLE_FROM_26_6(metrics->ascender) * y_factor; + fs_metrics.descent = DOUBLE_FROM_26_6(- metrics->descender) * y_factor; + fs_metrics.height = DOUBLE_FROM_26_6(metrics->height) * y_factor; + if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) { + fs_metrics.max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor; + fs_metrics.max_y_advance = 0; + } else { + fs_metrics.max_x_advance = 0; + fs_metrics.max_y_advance = DOUBLE_FROM_26_6(metrics->max_advance) * y_factor; + } + } else { + double scale = face->units_per_EM; + + fs_metrics.ascent = face->ascender / scale; + fs_metrics.descent = - face->descender / scale; + fs_metrics.height = face->height / scale; + if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) { + fs_metrics.max_x_advance = face->max_advance_width / scale; + fs_metrics.max_y_advance = 0; + } else { + fs_metrics.max_x_advance = 0; + fs_metrics.max_y_advance = face->max_advance_height / scale; + } + } + + status = _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics); + if (unlikely (status)) + goto CLEANUP_SCALED_FONT; + + _cairo_ft_unscaled_font_unlock_face (unscaled); + + *font_out = &scaled_font->base; + return CAIRO_STATUS_SUCCESS; + + CLEANUP_SCALED_FONT: + _cairo_unscaled_font_destroy (&unscaled->base); + free (scaled_font); + FAIL: + _cairo_ft_unscaled_font_unlock_face (font_face->unscaled); + *font_out = _cairo_scaled_font_create_in_error (status); + return CAIRO_STATUS_SUCCESS; /* non-backend error */ +} + +cairo_bool_t +_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->backend == &_cairo_ft_scaled_font_backend; +} + +static void +_cairo_ft_scaled_font_fini (void *abstract_font) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + + if (scaled_font == NULL) + return; + + _cairo_unscaled_font_destroy (&scaled_font->unscaled->base); +} + +static int +_move_to (FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x, y; + + x = _cairo_fixed_from_26_6 (to->x); + y = _cairo_fixed_from_26_6 (to->y); + + if (_cairo_path_fixed_close_path (path) != CAIRO_STATUS_SUCCESS) + return 1; + if (_cairo_path_fixed_move_to (path, x, y) != CAIRO_STATUS_SUCCESS) + return 1; + + return 0; +} + +static int +_line_to (FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x, y; + + x = _cairo_fixed_from_26_6 (to->x); + y = _cairo_fixed_from_26_6 (to->y); + + if (_cairo_path_fixed_line_to (path, x, y) != CAIRO_STATUS_SUCCESS) + return 1; + + return 0; +} + +static int +_conic_to (FT_Vector *control, FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + cairo_fixed_t x3, y3; + cairo_point_t conic; + + if (! _cairo_path_fixed_get_current_point (path, &x0, &y0)) + return 1; + + conic.x = _cairo_fixed_from_26_6 (control->x); + conic.y = _cairo_fixed_from_26_6 (control->y); + + x3 = _cairo_fixed_from_26_6 (to->x); + y3 = _cairo_fixed_from_26_6 (to->y); + + x1 = x0 + 2.0/3.0 * (conic.x - x0); + y1 = y0 + 2.0/3.0 * (conic.y - y0); + + x2 = x3 + 2.0/3.0 * (conic.x - x3); + y2 = y3 + 2.0/3.0 * (conic.y - y3); + + if (_cairo_path_fixed_curve_to (path, + x1, y1, + x2, y2, + x3, y3) != CAIRO_STATUS_SUCCESS) + return 1; + + return 0; +} + +static int +_cubic_to (FT_Vector *control1, FT_Vector *control2, + FT_Vector *to, void *closure) +{ + cairo_path_fixed_t *path = closure; + cairo_fixed_t x0, y0; + cairo_fixed_t x1, y1; + cairo_fixed_t x2, y2; + + x0 = _cairo_fixed_from_26_6 (control1->x); + y0 = _cairo_fixed_from_26_6 (control1->y); + + x1 = _cairo_fixed_from_26_6 (control2->x); + y1 = _cairo_fixed_from_26_6 (control2->y); + + x2 = _cairo_fixed_from_26_6 (to->x); + y2 = _cairo_fixed_from_26_6 (to->y); + + if (_cairo_path_fixed_curve_to (path, + x0, y0, + x1, y1, + x2, y2) != CAIRO_STATUS_SUCCESS) + return 1; + + return 0; +} + +static cairo_status_t +_decompose_glyph_outline (FT_Face face, + cairo_font_options_t *options, + cairo_path_fixed_t **pathp) +{ + static const FT_Outline_Funcs outline_funcs = { + (FT_Outline_MoveToFunc)_move_to, + (FT_Outline_LineToFunc)_line_to, + (FT_Outline_ConicToFunc)_conic_to, + (FT_Outline_CubicToFunc)_cubic_to, + 0, /* shift */ + 0, /* delta */ + }; + static const FT_Matrix invert_y = { + DOUBLE_TO_16_16 (1.0), 0, + 0, DOUBLE_TO_16_16 (-1.0), + }; + + FT_GlyphSlot glyph; + cairo_path_fixed_t *path; + cairo_status_t status; + + path = _cairo_path_fixed_create (); + if (!path) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + glyph = face->glyph; + + /* Font glyphs have an inverted Y axis compared to cairo. */ + FT_Outline_Transform (&glyph->outline, &invert_y); + if (FT_Outline_Decompose (&glyph->outline, &outline_funcs, path)) { + _cairo_path_fixed_destroy (path); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _cairo_path_fixed_close_path (path); + if (unlikely (status)) { + _cairo_path_fixed_destroy (path); + return status; + } + + *pathp = path; + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Translate glyph to match its metrics. + */ +static void +_cairo_ft_scaled_glyph_vertical_layout_bearing_fix (void *abstract_font, + FT_GlyphSlot glyph) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + FT_Vector vector; + + vector.x = glyph->metrics.vertBearingX - glyph->metrics.horiBearingX; + vector.y = -glyph->metrics.vertBearingY - glyph->metrics.horiBearingY; + + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { + FT_Vector_Transform (&vector, &scaled_font->unscaled->Current_Shape); + FT_Outline_Translate(&glyph->outline, vector.x, vector.y); + } else if (glyph->format == FT_GLYPH_FORMAT_BITMAP) { + glyph->bitmap_left += vector.x / 64; + glyph->bitmap_top += vector.y / 64; + } +} + +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; + + static GetVarFunc getVar; + static DoneVarFunc doneVar; + static GetVarDesignCoordsFunc getVarDesignCoords; + static SetVarDesignCoordsFunc setVarDesignCoords; + + static int firstTime = 1; + if (firstTime) { + getVar = (GetVarFunc) dlsym (RTLD_DEFAULT, "FT_Get_MM_Var"); + doneVar = (DoneVarFunc) dlsym (RTLD_DEFAULT, "FT_Done_MM_Var"); + getVarDesignCoords = (GetVarDesignCoordsFunc) dlsym (RTLD_DEFAULT, "FT_Get_Var_Design_Coordinates"); + setVarDesignCoords = (SetVarDesignCoordsFunc) dlsym (RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates"); + firstTime = 0; + } + + if (!getVar || !setVarDesignCoords) + return; + + ret = (*getVar) (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); + + if (getVarDesignCoords) { + ret = (*getVarDesignCoords) (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; + } + } + + (*setVarDesignCoords) (face, ft_mm_var->num_axis, coords); +done: + free (coords); + free (current_coords); + + if (doneVar) + (*doneVar) (face->glyph->library, ft_mm_var); + else + free (ft_mm_var); + } +} + +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, + cairo_scaled_glyph_info_t info) +{ + cairo_text_extents_t fs_metrics; + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_GlyphSlot glyph; + FT_Face face; + int load_flags = scaled_font->ft_options.load_flags; + FT_Glyph_Metrics *metrics; + double x_factor, y_factor; + cairo_bool_t vertical_layout = FALSE; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_bool_t scaled_glyph_loaded = FALSE; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* 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 | + 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 + * suggested by freetype people. + */ + if (load_flags & FT_LOAD_VERTICAL_LAYOUT) { + load_flags &= ~FT_LOAD_VERTICAL_LAYOUT; + vertical_layout = TRUE; + } + +#ifdef FT_LOAD_COLOR + load_flags |= FT_LOAD_COLOR; +#endif + + + 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 + */ + metrics = &glyph->metrics; + + if (unscaled->x_scale == 0) + x_factor = 0; + else + x_factor = 1 / unscaled->x_scale; + + if (unscaled->y_scale == 0) + y_factor = 0; + else + y_factor = 1 / unscaled->y_scale; + + /* + * Note: Y coordinates of the horizontal bearing need to be negated. + * + * Scale metrics back to glyph space from the scaled glyph space returned + * by FreeType + * + * If we want hinted metrics but aren't asking for hinted glyphs from + * FreeType, then we need to do the metric hinting ourselves. + */ + + if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING)) + { + FT_Pos x1, x2; + FT_Pos y1, y2; + FT_Pos advance; + + if (!vertical_layout) { + x1 = (metrics->horiBearingX) & -64; + x2 = (metrics->horiBearingX + metrics->width + 63) & -64; + y1 = (-metrics->horiBearingY) & -64; + y2 = (-metrics->horiBearingY + metrics->height + 63) & -64; + + advance = ((metrics->horiAdvance + 32) & -64); + + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor; + fs_metrics.y_advance = 0; + } else { + x1 = (metrics->vertBearingX) & -64; + x2 = (metrics->vertBearingX + metrics->width + 63) & -64; + y1 = (metrics->vertBearingY) & -64; + y2 = (metrics->vertBearingY + metrics->height + 63) & -64; + + advance = ((metrics->vertAdvance + 32) & -64); + + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor; + + fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor; + + fs_metrics.x_advance = 0; + fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor; + } + } else { + fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor; + fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor; + + if (!vertical_layout) { + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor; + + if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) + fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor; + else + fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor; + fs_metrics.y_advance = 0 * y_factor; + } else { + fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor; + fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor; + + fs_metrics.x_advance = 0 * x_factor; + if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE) + fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor; + else + fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor; + } + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &fs_metrics); + } + +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); + } else { + status = _render_glyph_bitmap (face, &scaled_font->ft_options.base, + &surface); + if (likely (status == CAIRO_STATUS_SUCCESS) && + unscaled->have_shape) + { + status = _transform_glyph_bitmap (&unscaled->current_shape, + &surface); + if (unlikely (status)) + cairo_surface_destroy (&surface->base); + } + } + if (unlikely (status)) + goto FAIL; + + 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) { + cairo_path_fixed_t *path = NULL; /* hide compiler warning */ + + /* + * A kludge -- the above code will trash the outline, + * so reload it. This will probably never occur though + */ + if ((info & (CAIRO_SCALED_GLYPH_INFO_SURFACE | CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE)) != 0) { + scaled_glyph_loaded = FALSE; + load_flags |= FT_LOAD_NO_BITMAP; + } + + if (!scaled_glyph_loaded) { + status = _cairo_ft_scaled_glyph_load_glyph (scaled_font, + scaled_glyph, + face, + load_flags, + FALSE, + vertical_layout); + if (unlikely (status)) + goto FAIL; + + glyph = face->glyph; + } + + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) + status = _decompose_glyph_outline (face, &scaled_font->ft_options.base, + &path); + else + status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (unlikely (status)) + goto FAIL; + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + } + FAIL: + _cairo_ft_unscaled_font_unlock_face (unscaled); + + return status; +} + +static unsigned long +_cairo_ft_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + FT_UInt index; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return 0; + +#if CAIRO_HAS_FC_FONT + index = FcFreeTypeCharIndex (face, ucs4); +#else + index = FT_Get_Char_Index (face, ucs4); +#endif + + _cairo_ft_unscaled_font_unlock_face (unscaled); + return index; +} + +static cairo_int_status_t +_cairo_ft_load_truetype_table (void *abstract_font, + unsigned long tag, + 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_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; + +#if HAVE_FT_LOAD_SFNT_TABLE + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + 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 + + return status; +} + +static cairo_int_status_t +_cairo_ft_index_to_ucs4(void *abstract_font, + unsigned long index, + uint32_t *ucs4) +{ + cairo_ft_scaled_font_t *scaled_font = abstract_font; + cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled; + FT_Face face; + FT_ULong charcode; + FT_UInt gindex; + + face = _cairo_ft_unscaled_font_lock_face (unscaled); + if (!face) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *ucs4 = (uint32_t) -1; + charcode = FT_Get_First_Char(face, &gindex); + while (gindex != 0) { + if (gindex == index) { + *ucs4 = charcode; + break; + } + charcode = FT_Get_Next_Char (face, charcode, &gindex); + } + + _cairo_ft_unscaled_font_unlock_face (unscaled); + + 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; + + static GetVarFunc getVar; + static DoneVarFunc doneVar; + static GetVarBlendCoordsFunc getVarBlendCoords; + + static int firstTime = 1; + if (firstTime) { + getVar = (GetVarFunc) dlsym (RTLD_DEFAULT, "FT_Get_MM_Var"); + doneVar = (DoneVarFunc) dlsym (RTLD_DEFAULT, "FT_Done_MM_Var"); + getVarBlendCoords = (GetVarBlendCoordsFunc) dlsym (RTLD_DEFAULT, "FT_Get_Var_Blend_Coordinates"); + firstTime = 0; + } + + 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 = getVar ? (*getVar) (face, &mm_var) : -1; + 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 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. + */ + if (getVarBlendCoords) { + int i; + + (*getVarBlendCoords) (face, num_axis, coords); + *is_synthetic = FALSE; + for (i = 0; i < num_axis; i++) { + if (coords[i]) { + *is_synthetic = TRUE; + break; + } + } + } + + cleanup: + free (coords); + if (doneVar) + (*doneVar) (face->glyph->library, mm_var); + else + free (mm_var); + } + + _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, + _cairo_ft_load_truetype_table, + _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_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_out) +{ + cairo_font_face_t *font_face = (cairo_font_face_t *) &_cairo_font_face_nil; + FcPattern *pattern; + int fcslant; + int fcweight; + + pattern = FcPatternCreate (); + if (!pattern) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return font_face->status; + } + + if (!FcPatternAddString (pattern, + FC_FAMILY, (unsigned char *) toy_face->family)) + { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + switch (toy_face->slant) + { + case CAIRO_FONT_SLANT_ITALIC: + fcslant = FC_SLANT_ITALIC; + break; + case CAIRO_FONT_SLANT_OBLIQUE: + fcslant = FC_SLANT_OBLIQUE; + break; + case CAIRO_FONT_SLANT_NORMAL: + default: + fcslant = FC_SLANT_ROMAN; + break; + } + + if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + switch (toy_face->weight) + { + case CAIRO_FONT_WEIGHT_BOLD: + fcweight = FC_WEIGHT_BOLD; + break; + case CAIRO_FONT_WEIGHT_NORMAL: + default: + fcweight = FC_WEIGHT_MEDIUM; + break; + } + + if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto FREE_PATTERN; + } + + font_face = _cairo_ft_font_face_create_for_pattern (pattern); + + FREE_PATTERN: + FcPatternDestroy (pattern); + + *font_face_out = font_face; + return font_face->status; +} +#endif + +static cairo_bool_t +_cairo_ft_font_face_destroy (void *abstract_face) +{ + cairo_ft_font_face_t *font_face = abstract_face; + + /* When destroying a face created by cairo_ft_font_face_create_for_ft_face, + * we have a special "zombie" state for the face when the unscaled font + * is still alive but there are no other references to a font face with + * the same FT_Face. + * + * We go from: + * + * font_face ------> unscaled + * <-....weak....../ + * + * To: + * + * font_face <------- unscaled + */ + + if (font_face->unscaled) { + CAIRO_FT_LOCK (font_face->unscaled); + + 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_FT_UNLOCK (font_face->unscaled); + _cairo_unscaled_font_destroy (&font_face->unscaled->base); + font_face->unscaled = NULL; + + return FALSE; + } + + cairo_ft_font_face_t *tmp_face = NULL; + cairo_ft_font_face_t *last_face = NULL; + + /* Remove face from linked list */ + for (tmp_face = font_face->unscaled->faces; + tmp_face; + tmp_face = tmp_face->next) + { + if (tmp_face == font_face) { + if (last_face) + last_face->next = tmp_face->next; + else + font_face->unscaled->faces = tmp_face->next; + } + + last_face = tmp_face; + } + + 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 * +_cairo_ft_font_face_get_implementation (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + cairo_ft_font_face_t *font_face = abstract_face; + + /* The handling of font options is different depending on how the + * font face was created. When the user creates a font face with + * cairo_ft_font_face_create_for_ft_face(), then the load flags + * passed in augment the load flags for the options. But for + * cairo_ft_font_face_create_for_pattern(), the load flags are + * derived from a pattern where the user has called + * cairo_ft_font_options_substitute(), so *just* use those load + * flags and ignore the options. + */ + +#if CAIRO_HAS_FC_FONT + /* If we have an unresolved pattern, resolve it and create + * unscaled font. Otherwise, use the ones stored in font_face. + */ + if (font_face->pattern) { + cairo_font_face_t *resolved; + + /* Cache the resolved font whilst the FcConfig remains consistent. */ + resolved = font_face->resolved_font_face; + if (resolved != NULL) { + if (! FcInitBringUptoDate ()) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *) &_cairo_font_face_nil; + } + + if (font_face->resolved_config == FcConfigGetCurrent ()) + return cairo_font_face_reference (resolved); + + cairo_font_face_destroy (resolved); + font_face->resolved_font_face = NULL; + } + + resolved = _cairo_ft_resolve_pattern (font_face->pattern, + font_matrix, + ctm, + options); + if (unlikely (resolved->status)) + return resolved; + + font_face->resolved_font_face = cairo_font_face_reference (resolved); + font_face->resolved_config = FcConfigGetCurrent (); + + return resolved; + } +#endif + + 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 + _cairo_ft_font_face_create_for_toy, +#else + NULL, +#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 +*/ +}; + +#if CAIRO_HAS_FC_FONT +static cairo_font_face_t * +_cairo_ft_font_face_create_for_pattern (FcPattern *pattern) +{ + cairo_ft_font_face_t *font_face; + + 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); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *) &_cairo_font_face_nil; + } + + font_face->resolved_font_face = NULL; + font_face->resolved_config = NULL; + + _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); + + return &font_face->base; +} +#endif + +static cairo_font_face_t * +_cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled, + cairo_ft_options_t *ft_options) +{ + 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; + prev_font_face = &font_face->next, font_face = font_face->next) + { + if (font_face->ft_options.load_flags == ft_options->load_flags && + font_face->ft_options.synth_flags == ft_options->synth_flags && + cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base)) + { + if (font_face->base.status) { + /* The font_face has been left in an error state, abandon it. */ + *prev_font_face = font_face->next; + break; + } + + if (font_face->unscaled == NULL) { + /* Resurrect this "zombie" font_face (from + * _cairo_ft_font_face_destroy), switching its unscaled_font + * 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; + } + } + + /* No match found, create a new one */ + 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; + } + + font_face->unscaled = unscaled; + _cairo_unscaled_font_reference (&unscaled->base); + + _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); + cairo_font_face_destroy (&unscaled->faces->base); + unscaled->faces = NULL; + } + + font_face->next = unscaled->faces; + unscaled->faces = font_face; + +#if CAIRO_HAS_FC_FONT + font_face->pattern = NULL; +#endif + + _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend); + + CAIRO_FT_UNLOCK (unscaled); + return &font_face->base; +} + +/* implement the platform-specific interface */ + +#if CAIRO_HAS_FC_FONT +static cairo_status_t +_cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern) +{ + FcValue v; + + if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) + { + if (FcPatternGet (pattern, FC_ANTIALIAS, 0, &v) == FcResultNoMatch) + { + if (! FcPatternAddBool (pattern, + FC_ANTIALIAS, + options->antialias != CAIRO_ANTIALIAS_NONE)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) { + FcPatternDel (pattern, FC_RGBA); + if (! FcPatternAddInteger (pattern, FC_RGBA, FC_RGBA_NONE)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + } + + if (options->antialias != CAIRO_ANTIALIAS_DEFAULT) + { + if (FcPatternGet (pattern, FC_RGBA, 0, &v) == FcResultNoMatch) + { + int rgba; + + if (options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) { + switch (options->subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + default: + rgba = FC_RGBA_RGB; + break; + case CAIRO_SUBPIXEL_ORDER_BGR: + rgba = FC_RGBA_BGR; + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + rgba = FC_RGBA_VRGB; + break; + case CAIRO_SUBPIXEL_ORDER_VBGR: + rgba = FC_RGBA_VBGR; + break; + } + } else { + rgba = FC_RGBA_NONE; + } + + if (! FcPatternAddInteger (pattern, FC_RGBA, rgba)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + if (options->lcd_filter != CAIRO_LCD_FILTER_DEFAULT) + { + if (FcPatternGet (pattern, FC_LCD_FILTER, 0, &v) == FcResultNoMatch) + { + int lcd_filter; + + switch (options->lcd_filter) { + case CAIRO_LCD_FILTER_NONE: + lcd_filter = FT_LCD_FILTER_NONE; + break; + case CAIRO_LCD_FILTER_DEFAULT: + case CAIRO_LCD_FILTER_INTRA_PIXEL: + lcd_filter = FT_LCD_FILTER_LEGACY; + break; + case CAIRO_LCD_FILTER_FIR3: + lcd_filter = FT_LCD_FILTER_LIGHT; + break; + default: + case CAIRO_LCD_FILTER_FIR5: + lcd_filter = FT_LCD_FILTER_DEFAULT; + break; + } + + if (! FcPatternAddInteger (pattern, FC_LCD_FILTER, lcd_filter)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT) + { + if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch) + { + if (! FcPatternAddBool (pattern, + FC_HINTING, + options->hint_style != CAIRO_HINT_STYLE_NONE)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + +#ifdef FC_HINT_STYLE + if (FcPatternGet (pattern, FC_HINT_STYLE, 0, &v) == FcResultNoMatch) + { + int hint_style; + + switch (options->hint_style) { + case CAIRO_HINT_STYLE_NONE: + hint_style = FC_HINT_NONE; + break; + case CAIRO_HINT_STYLE_SLIGHT: + hint_style = FC_HINT_SLIGHT; + break; + case CAIRO_HINT_STYLE_MEDIUM: + hint_style = FC_HINT_MEDIUM; + break; + case CAIRO_HINT_STYLE_FULL: + case CAIRO_HINT_STYLE_DEFAULT: + default: + hint_style = FC_HINT_FULL; + break; + } + + if (! FcPatternAddInteger (pattern, FC_HINT_STYLE, hint_style)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } +#endif + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_ft_font_options_substitute: + * @options: a #cairo_font_options_t object + * @pattern: an existing #FcPattern + * + * Add options to a #FcPattern based on a #cairo_font_options_t font + * options object. Options that are already in the pattern, are not overridden, + * 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, + FcPattern *pattern) +{ + if (cairo_font_options_status ((cairo_font_options_t *) options)) + return; + + _cairo_ft_font_options_substitute (options, pattern); +} + +static cairo_font_face_t * +_cairo_ft_resolve_pattern (FcPattern *pattern, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *font_options) +{ + cairo_status_t status; + + cairo_matrix_t scale; + FcPattern *resolved; + cairo_ft_font_transform_t sf; + FcResult result; + cairo_ft_unscaled_font_t *unscaled; + cairo_ft_options_t ft_options; + cairo_font_face_t *font_face; + + scale = *ctm; + scale.x0 = scale.y0 = 0; + cairo_matrix_multiply (&scale, + font_matrix, + &scale); + + status = _compute_transform (&sf, &scale, NULL); + if (unlikely (status)) + return (cairo_font_face_t *)&_cairo_font_face_nil; + + pattern = FcPatternDuplicate (pattern); + if (pattern == NULL) + return (cairo_font_face_t *)&_cairo_font_face_nil; + + if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + status = _cairo_ft_font_options_substitute (font_options, pattern); + if (status) { + font_face = (cairo_font_face_t *)&_cairo_font_face_nil; + goto FREE_PATTERN; + } + + FcDefaultSubstitute (pattern); + + 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; + } + + 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: + if (resolved != pattern) + FcPatternDestroy (resolved); + +FREE_PATTERN: + FcPatternDestroy (pattern); + + return font_face; +} + +/** + * cairo_ft_font_face_create_for_pattern: + * @pattern: A fontconfig pattern. Cairo makes a copy of the pattern + * if it needs to. You are free to modify or free @pattern after this call. + * + * Creates a new font face for the FreeType font backend based on a + * fontconfig pattern. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). The + * #cairo_scaled_font_t returned from cairo_scaled_font_create() is + * also for the FreeType backend and can be used with functions such + * as cairo_ft_scaled_font_lock_face(). + * + * Font rendering options are represented both here and when you + * call cairo_scaled_font_create(). Font options that have a representation + * in a #FcPattern must be passed in here; to modify #FcPattern + * appropriately to reflect the options in a #cairo_font_options_t, call + * cairo_ft_font_options_substitute(). + * + * The pattern's FC_FT_FACE element is inspected first and if that is set, + * that will be the FreeType font face associated with the returned cairo + * font face. Otherwise the FC_FILE element is checked. If it's set, + * that and the value of the FC_INDEX element (defaults to zero) of @pattern + * are used to load a font face from file. + * + * If both steps from the previous paragraph fails, @pattern will be passed + * to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch, + * and the resulting font pattern is used. + * + * If the FC_FT_FACE element of @pattern is set, the user is responsible + * for making sure that the referenced FT_Face remains valid for the life + * time of the returned #cairo_font_face_t. See + * 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) +{ + 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_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 */ + 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; +} +#endif + +/** + * cairo_ft_font_face_create_for_ft_face: + * @face: A FreeType face object, already opened. This must + * be kept around until the face's ref_count drops to + * zero and it is freed. Since the face may be referenced + * internally to Cairo, the best way to determine when it + * is safe to free the face is to pass a + * #cairo_destroy_func_t to cairo_font_face_set_user_data() + * @load_flags: flags to pass to FT_Load_Glyph when loading + * glyphs from the font. These flags are OR'ed together with + * the flags derived from the #cairo_font_options_t passed + * to cairo_scaled_font_create(), so only a few values such + * as %FT_LOAD_VERTICAL_LAYOUT, and %FT_LOAD_FORCE_AUTOHINT + * are useful. You should not pass any of the flags affecting + * the load target, such as %FT_LOAD_TARGET_LIGHT. + * + * Creates a new font face for the FreeType font backend from a + * pre-opened FreeType face. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). The + * #cairo_scaled_font_t returned from cairo_scaled_font_create() is + * also for the FreeType backend and can be used with functions such + * as cairo_ft_scaled_font_lock_face(). Note that Cairo may keep a reference + * to the FT_Face alive in a font-cache and the exact lifetime of the reference + * depends highly upon the exact usage pattern and is subject to external + * factors. You must not call FT_Done_Face() before the last reference to the + * #cairo_font_face_t has been dropped. + * + * As an example, below is how one might correctly couple the lifetime of + * the FreeType face object to the #cairo_font_face_t. + * + * + * static const cairo_user_data_key_t key; + * + * font_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0); + * status = cairo_font_face_set_user_data (font_face, &key, + * ft_face, (cairo_destroy_func_t) FT_Done_Face); + * if (status) { + * cairo_font_face_destroy (font_face); + * FT_Done_Face (ft_face); + * return ERROR; + * } + * + * + * 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) +{ + 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); + if (unlikely (status)) + return (cairo_font_face_t *)&_cairo_font_face_nil; + + ft_options.load_flags = load_flags; + ft_options.synth_flags = synth_flags; + _cairo_font_options_init_default (&ft_options.base); + + font_face = _cairo_ft_font_face_create (unscaled, &ft_options); + _cairo_unscaled_font_destroy (&unscaled->base); + + 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 + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * 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 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 + * lock any other font objects until you unlock this one. A count is + * kept of the number of times cairo_ft_scaled_font_lock_face() is + * called. cairo_ft_scaled_font_unlock_face() must be called the same number + * of times. + * + * You must be careful when using this function in a library or in a + * threaded application, because freetype's design makes it unsafe to + * call freetype functions simultaneously from multiple threads, (even + * if using distinct FT_Face objects). Because of this, application + * code that acquires an FT_Face object with this call must add its + * own locking to protect any use of that object, (and which also must + * protect any other calls into cairo as almost any cairo function + * might result in a call into the freetype library). + * + * 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) +{ + cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; + FT_Face face; + cairo_status_t status; + + if (! _cairo_scaled_font_is_ft (abstract_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + return NULL; + } + + if (scaled_font->base.status) + return NULL; + + face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled); + if (unlikely (face == NULL)) { + status = _cairo_scaled_font_set_error (&scaled_font->base, CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled, + &scaled_font->base.scale); + if (unlikely (status)) { + _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled); + status = _cairo_scaled_font_set_error (&scaled_font->base, status); + 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); + + return face; +} + +/** + * cairo_ft_scaled_font_unlock_face: + * @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an + * object can be created by calling cairo_scaled_font_create() on a + * FreeType backend font face (see cairo_ft_font_face_create_for_pattern(), + * 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) +{ + cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font; + + if (! _cairo_scaled_font_is_ft (abstract_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + return; + } + + if (scaled_font->base.status) + return; + + /* Note: We released the unscaled font's mutex at the end of + * 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_ft_unscaled_font_unlock_face (scaled_font->unscaled); +} + +static cairo_bool_t +_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font) +{ + cairo_ft_scaled_font_t *ft_scaled_font; + + if (!_cairo_scaled_font_is_ft (scaled_font)) + return FALSE; + + ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; + if (ft_scaled_font->ft_options.load_flags & FT_LOAD_VERTICAL_LAYOUT) + return TRUE; + return FALSE; +} + +unsigned int +_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font) +{ + cairo_ft_scaled_font_t *ft_scaled_font; + + if (! _cairo_scaled_font_is_ft (scaled_font)) + return 0; + + ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font; + return ft_scaled_font->ft_options.load_flags; +} + +void +_cairo_ft_font_reset_static_data (void) +{ + _cairo_ft_unscaled_font_map_destroy (); +} diff --git a/gfx/cairo/cairo/src/cairo-ft-private.h b/gfx/cairo/cairo/src/cairo-ft-private.h new file mode 100644 index 0000000000..0dc8114726 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-ft-private.h @@ -0,0 +1,61 @@ +/* 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): + * Graydon Hoare + * Owen Taylor + */ + +#ifndef CAIRO_FT_PRIVATE_H +#define CAIRO_FT_PRIVATE_H + +#include "cairoint.h" +#include "cairo-ft.h" + +#if CAIRO_HAS_FT_FONT + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_ft_unscaled_font cairo_ft_unscaled_font_t; + +cairo_private cairo_bool_t +_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 unsigned int +_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font); + +CAIRO_END_DECLS + +#endif /* CAIRO_HAS_FT_FONT */ +#endif /* CAIRO_FT_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-ft.h b/gfx/cairo/cairo/src/cairo-ft.h new file mode 100644 index 0000000000..f5a832b105 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-ft.h @@ -0,0 +1,120 @@ +/* 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): + * Graydon Hoare + * Owen Taylor + */ + +#ifndef CAIRO_FT_H +#define CAIRO_FT_H + +#include "cairo.h" + +#if CAIRO_HAS_FT_FONT + +/* Fontconfig/Freetype platform-specific font interface */ + +#include +#include FT_FREETYPE_H + +#if CAIRO_HAS_FC_FONT +#include +#endif + +CAIRO_BEGIN_DECLS + +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_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 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); + +cairo_public void +cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *scaled_font); + +#if CAIRO_HAS_FC_FONT + +cairo_public cairo_font_face_t * +cairo_ft_font_face_create_for_pattern (FcPattern *pattern); + +cairo_public void +cairo_ft_font_options_substitute (const cairo_font_options_t *options, + FcPattern *pattern); + +#endif + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_FT_FONT */ +# error Cairo was not compiled with support for the freetype font backend +#endif /* CAIRO_HAS_FT_FONT */ + +#endif /* CAIRO_FT_H */ 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 0000000000..5477ef0d8a --- /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 0000000000..6f4c852a45 --- /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 0000000000..cabf76f0d6 --- /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 0000000000..a49199dbb0 --- /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 0000000000..a261947bef --- /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 new file mode 100644 index 0000000000..5923af4414 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-glyphs.c @@ -0,0 +1,503 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * Copyright © 2010 Intel Corporation + * Copyright © 2010 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + * + * Contributors: + * Benjamin Otte + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-rtree-private.h" + +#define GLYPH_CACHE_WIDTH 1024 +#define GLYPH_CACHE_HEIGHT 1024 +#define GLYPH_CACHE_MIN_SIZE 4 +#define GLYPH_CACHE_MAX_SIZE 128 + +typedef struct _cairo_gl_glyph { + cairo_rtree_node_t node; + cairo_scaled_glyph_private_t base; + cairo_scaled_glyph_t *glyph; + cairo_gl_glyph_cache_t *cache; + struct { float x, y; } p1, p2; +} cairo_gl_glyph_t; + +static void +_cairo_gl_node_destroy (cairo_rtree_node_t *node) +{ + cairo_gl_glyph_t *priv = cairo_container_of (node, cairo_gl_glyph_t, node); + cairo_scaled_glyph_t *glyph; + + glyph = priv->glyph; + if (glyph == NULL) + return; + + if (glyph->dev_private_key == priv->cache) { + glyph->dev_private = NULL; + glyph->dev_private_key = NULL; + } + cairo_list_del (&priv->base.link); + priv->glyph = NULL; +} + +static void +_cairo_gl_glyph_fini (cairo_scaled_glyph_private_t *glyph_private, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_gl_glyph_t *priv = cairo_container_of (glyph_private, + cairo_gl_glyph_t, + base); + + assert (priv->glyph); + + _cairo_gl_node_destroy (&priv->node); + + /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ + if (! priv->node.pinned) + _cairo_rtree_node_remove (&priv->cache->rtree, &priv->node); + + assert (priv->glyph == NULL); +} + +static cairo_int_status_t +_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, + cairo_gl_glyph_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_image_surface_t *glyph_surface = scaled_glyph->surface; + cairo_gl_glyph_t *glyph_private; + cairo_rtree_node_t *node = NULL; + cairo_int_status_t status; + int width, height; + + width = glyph_surface->width; + if (width < GLYPH_CACHE_MIN_SIZE) + width = GLYPH_CACHE_MIN_SIZE; + height = glyph_surface->height; + if (height < GLYPH_CACHE_MIN_SIZE) + height = GLYPH_CACHE_MIN_SIZE; + + /* search for an available slot */ + status = _cairo_rtree_insert (&cache->rtree, width, height, &node); + /* search for an unlocked slot */ + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_rtree_evict_random (&cache->rtree, + width, height, &node); + if (status == CAIRO_INT_STATUS_SUCCESS) { + status = _cairo_rtree_node_insert (&cache->rtree, + node, width, height, &node); + } + } + if (status) + return status; + + /* XXX: Make sure we use the mask texture. This should work automagically somehow */ + glActiveTexture (GL_TEXTURE1); + status = _cairo_gl_surface_draw_image (cache->surface, glyph_surface, + 0, 0, + glyph_surface->width, glyph_surface->height, + node->x, node->y, FALSE); + if (unlikely (status)) + return status; + + glyph_private = (cairo_gl_glyph_t *) node; + glyph_private->cache = cache; + glyph_private->glyph = scaled_glyph; + _cairo_scaled_glyph_attach_private (scaled_glyph, + &glyph_private->base, + cache, + _cairo_gl_glyph_fini); + + scaled_glyph->dev_private = glyph_private; + scaled_glyph->dev_private_key = cache; + + /* compute tex coords */ + glyph_private->p1.x = node->x; + glyph_private->p1.y = node->y; + glyph_private->p2.x = node->x + glyph_surface->width; + glyph_private->p2.y = node->y + glyph_surface->height; + if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) { + glyph_private->p1.x /= GLYPH_CACHE_WIDTH; + glyph_private->p2.x /= GLYPH_CACHE_WIDTH; + glyph_private->p1.y /= GLYPH_CACHE_HEIGHT; + glyph_private->p2.y /= GLYPH_CACHE_HEIGHT; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_gl_glyph_t * +_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache, + cairo_scaled_glyph_t *scaled_glyph) +{ + return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private); +} + +static cairo_status_t +cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx, + cairo_format_t format, + cairo_gl_glyph_cache_t **cache_out) +{ + cairo_gl_glyph_cache_t *cache; + cairo_content_t content; + + switch (format) { + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + cache = &ctx->glyph_cache[0]; + content = CAIRO_CONTENT_COLOR_ALPHA; + break; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + cache = &ctx->glyph_cache[1]; + content = CAIRO_CONTENT_ALPHA; + break; + default: + case CAIRO_FORMAT_INVALID: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + } + + if (unlikely (cache->surface == NULL)) { + cairo_surface_t *surface; + + surface = _cairo_gl_surface_create_scratch_for_caching (ctx, + content, + GLYPH_CACHE_WIDTH, + GLYPH_CACHE_HEIGHT); + if (unlikely (surface->status)) + return surface->status; + + _cairo_surface_release_device_reference (surface); + + cache->surface = (cairo_gl_surface_t *)surface; + cache->surface->operand.texture.attributes.has_component_alpha = + content == CAIRO_CONTENT_COLOR_ALPHA; + } + + *cache_out = cache; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +render_glyphs (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + cairo_operator_t op, + cairo_surface_t *source, + cairo_composite_glyphs_info_t *info, + cairo_bool_t *has_component_alpha, + cairo_clip_t *clip) +{ + cairo_format_t last_format = CAIRO_FORMAT_INVALID; + cairo_gl_glyph_cache_t *cache = NULL; + cairo_gl_context_t *ctx; + cairo_gl_emit_glyph_t emit = NULL; + cairo_gl_composite_t setup; + cairo_int_status_t status; + int i = 0; + + TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__, + info->extents.x, info->extents.y, + info->extents.width, info->extents.height)); + + *has_component_alpha = FALSE; + + status = _cairo_gl_context_acquire (dst->base.device, &ctx); + if (unlikely (status)) + return status; + + status = _cairo_gl_composite_init (&setup, op, dst, TRUE); + if (unlikely (status)) + goto FINISH; + + if (source == NULL) { + _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE); + } else { + _cairo_gl_composite_set_source_operand (&setup, + source_to_operand (source)); + + } + + _cairo_gl_composite_set_clip (&setup, clip); + + for (i = 0; i < info->num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + cairo_gl_glyph_t *glyph; + double x_offset, y_offset; + double x1, x2, y1, y2; + + status = _cairo_scaled_glyph_lookup (info->font, + info->glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + goto FINISH; + + if (scaled_glyph->surface->width == 0 || + scaled_glyph->surface->height == 0) + { + continue; + } + if (scaled_glyph->surface->format != last_format) { + status = cairo_gl_context_get_glyph_cache (ctx, + scaled_glyph->surface->format, + &cache); + if (unlikely (status)) + goto FINISH; + + last_format = scaled_glyph->surface->format; + + _cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand); + *has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha; + + /* XXX Shoot me. */ + status = _cairo_gl_composite_begin (&setup, &ctx); + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) + goto FINISH; + + emit = _cairo_gl_context_choose_emit_glyph (ctx); + } + + if (scaled_glyph->dev_private_key != cache) { + cairo_scaled_glyph_private_t *priv; + + priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache); + if (priv) { + scaled_glyph->dev_private_key = cache; + scaled_glyph->dev_private = cairo_container_of (priv, + cairo_gl_glyph_t, + base); + } else { + status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* Cache is full, so flush existing prims and try again. */ + _cairo_gl_composite_flush (ctx); + _cairo_gl_glyph_cache_unlock (cache); + status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); + } + + if (unlikely (_cairo_int_status_is_error (status))) + goto FINISH; + } + } + + x_offset = scaled_glyph->surface->base.device_transform.x0; + y_offset = scaled_glyph->surface->base.device_transform.y0; + + x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x); + y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y); + x2 = x1 + scaled_glyph->surface->width; + y2 = y1 + scaled_glyph->surface->height; + + glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph); + assert (emit); + emit (ctx, + x1, y1, x2, y2, + glyph->p1.x, glyph->p1.y, + glyph->p2.x, glyph->p2.y); + } + + status = CAIRO_STATUS_SUCCESS; + FINISH: + status = _cairo_gl_context_release (ctx, status); + + _cairo_gl_composite_fini (&setup); + return status; +} + +static cairo_int_status_t +render_glyphs_via_mask (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + cairo_operator_t op, + cairo_surface_t *source, + cairo_composite_glyphs_info_t *info, + cairo_clip_t *clip) +{ + cairo_surface_t *mask; + cairo_status_t status; + cairo_bool_t has_component_alpha; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */ + mask = cairo_gl_surface_create (dst->base.device, + CAIRO_CONTENT_COLOR_ALPHA, + info->extents.width, + info->extents.height); + if (unlikely (mask->status)) + return mask->status; + + status = render_glyphs ((cairo_gl_surface_t *) mask, + info->extents.x, info->extents.y, + CAIRO_OPERATOR_ADD, NULL, + info, &has_component_alpha, NULL); + if (likely (status == CAIRO_STATUS_SUCCESS)) { + cairo_surface_pattern_t mask_pattern; + cairo_surface_pattern_t source_pattern; + cairo_rectangle_int_t clip_extents; + + mask->is_clear = FALSE; + _cairo_pattern_init_for_surface (&mask_pattern, mask); + mask_pattern.base.has_component_alpha = has_component_alpha; + mask_pattern.base.filter = CAIRO_FILTER_NEAREST; + mask_pattern.base.extend = CAIRO_EXTEND_NONE; + + cairo_matrix_init_translate (&mask_pattern.base.matrix, + dst_x-info->extents.x, dst_y-info->extents.y); + + _cairo_pattern_init_for_surface (&source_pattern, source); + cairo_matrix_init_translate (&source_pattern.base.matrix, + dst_x-info->extents.x, dst_y-info->extents.y); + + clip = _cairo_clip_copy (clip); + clip_extents.x = info->extents.x - dst_x; + clip_extents.y = info->extents.y - dst_y; + clip_extents.width = info->extents.width; + clip_extents.height = info->extents.height; + clip = _cairo_clip_intersect_rectangle (clip, &clip_extents); + + status = _cairo_surface_mask (&dst->base, op, + &source_pattern.base, + &mask_pattern.base, + clip); + + _cairo_clip_destroy (clip); + + _cairo_pattern_fini (&mask_pattern.base); + _cairo_pattern_fini (&source_pattern.base); + } + + cairo_surface_destroy (mask); + + return status; +} + +cairo_int_status_t +_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int *num_glyphs) +{ + if (! _cairo_gl_operator_is_supported (extents->op)) + return UNSUPPORTED ("unsupported operator"); + + /* XXX use individual masks for large glyphs? */ + if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE) + return UNSUPPORTED ("glyphs too large"); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_gl_composite_glyphs_with_clip (void *_dst, + cairo_operator_t op, + cairo_surface_t *_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + cairo_composite_glyphs_info_t *info, + cairo_clip_t *clip) +{ + cairo_gl_surface_t *dst = _dst; + cairo_bool_t has_component_alpha; + + TRACE ((stderr, "%s\n", __FUNCTION__)); + + /* If any of the glyphs require component alpha, we have to go through + * a mask, since only _cairo_gl_surface_composite() currently supports + * component alpha. + */ + if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER && + (info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL || + info->font->options.antialias == CAIRO_ANTIALIAS_BEST)) + { + info->use_mask = TRUE; + } + + if (info->use_mask) { + return render_glyphs_via_mask (dst, dst_x, dst_y, + op, _src, info, clip); + } else { + return render_glyphs (dst, dst_x, dst_y, + op, _src, info, + &has_component_alpha, + clip); + } + +} + +cairo_int_status_t +_cairo_gl_composite_glyphs (void *_dst, + cairo_operator_t op, + cairo_surface_t *_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + cairo_composite_glyphs_info_t *info) +{ + return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y, + dst_x, dst_y, info, NULL); +} + +void +_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_init (&cache->rtree, + GLYPH_CACHE_WIDTH, + GLYPH_CACHE_HEIGHT, + GLYPH_CACHE_MIN_SIZE, + sizeof (cairo_gl_glyph_t), + _cairo_gl_node_destroy); +} + +void +_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, + cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_fini (&cache->rtree); + cairo_surface_destroy (&cache->surface->base); +} diff --git a/gfx/cairo/cairo/src/cairo-gl-gradient-private.h b/gfx/cairo/cairo/src/cairo-gl-gradient-private.h new file mode 100644 index 0000000000..0d9f41f54b --- /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 0000000000..1bbd8dd0e6 --- /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 0000000000..53f5b17209 --- /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 0000000000..7a83dd2194 --- /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 0000000000..a754bde2f3 --- /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 new file mode 100644 index 0000000000..f02a58763e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-private.h @@ -0,0 +1,865 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Eric Anholt + * Copyright © 2009 Chris Wilson + * Copyright © 2005,2010 Red Hat, Inc + * Copyright © 2011 Linaro Limited + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Benjamin Otte + * Carl Worth + * 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 + +#if CAIRO_HAS_GLESV3_SURFACE +#include +#include +#elif CAIRO_HAS_GLESV2_SURFACE +#include +#include +#elif CAIRO_HAS_GL_SURFACE +#include +#include +#endif + +#include "cairo-gl-ext-def-private.h" + +#define DEBUG_GL 0 + +#if DEBUG_GL && __GNUC__ +#define UNSUPPORTED(reason) ({ \ + fprintf (stderr, \ + "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \ + __FUNCTION__, __LINE__, reason); \ + CAIRO_INT_STATUS_UNSUPPORTED; \ +}) +#else +#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED +#endif + +#define CAIRO_GL_VERSION_ENCODE(major, minor) ( \ + ((major) * 256) \ + + ((minor) * 1)) + +/* maximal number of shaders we keep in the cache. + * Random number that is hopefully big enough to not cause many cache evictions. */ +#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64 + +/* VBO size that we allocate, smaller size means we gotta flush more often, + * but larger means hogging more memory and can cause trouble for drivers + * (especially on embedded devices). Use the CAIRO_GL_VBO_SIZE environment + * variable to set this to a different size. */ +#define CAIRO_GL_VBO_SIZE_DEFAULT (1024*1024) + +typedef struct _cairo_gl_surface cairo_gl_surface_t; + +/* GL flavor is the type of GL supported by the underlying platform. */ +typedef enum cairo_gl_flavor { + CAIRO_GL_FLAVOR_NONE = 0, + CAIRO_GL_FLAVOR_DESKTOP = 1, + CAIRO_GL_FLAVOR_ES2 = 2, + CAIRO_GL_FLAVOR_ES3 = 3 +} cairo_gl_flavor_t; + +/* Indices for vertex attributes used by BindAttribLocation, etc. */ +enum { + CAIRO_GL_VERTEX_ATTRIB_INDEX = 0, + CAIRO_GL_COLOR_ATTRIB_INDEX = 1, + CAIRO_GL_TEXCOORD0_ATTRIB_INDEX = 2, + CAIRO_GL_TEXCOORD1_ATTRIB_INDEX = CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + 1 +}; + +typedef enum cairo_gl_operand_type { + CAIRO_GL_OPERAND_NONE, + CAIRO_GL_OPERAND_CONSTANT, + CAIRO_GL_OPERAND_TEXTURE, + CAIRO_GL_OPERAND_LINEAR_GRADIENT, + CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0, + CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE, + CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT, + + CAIRO_GL_OPERAND_COUNT +} cairo_gl_operand_type_t; + +/* This union structure describes a potential source or mask operand to the + * compositing equation. + */ +typedef struct cairo_gl_operand { + cairo_gl_operand_type_t type; + union { + struct { + GLuint tex; + cairo_gl_surface_t *surface; + cairo_gl_surface_t *owns_surface; + cairo_surface_attributes_t attributes; + int texgen; + } texture; + struct { + GLfloat color[4]; + } constant; + struct { + cairo_gl_gradient_t *gradient; + cairo_matrix_t m; + cairo_circle_double_t circle_d; + double radius_0, a; + cairo_extend_t extend; + int texgen; + } gradient; + }; + unsigned int vertex_offset; +} cairo_gl_operand_t; + +typedef struct cairo_gl_source { + cairo_surface_t base; + cairo_gl_operand_t operand; +} cairo_gl_source_t; + +struct _cairo_gl_surface { + cairo_surface_t base; + cairo_gl_operand_t operand; + + int width, height; + + GLuint tex; /* GL texture object containing our data. */ + GLuint fb; /* GL framebuffer object wrapping our data. */ + GLuint depth_stencil; /* GL renderbuffer object for holding stencil buffer clip. */ + +#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE + GLuint msaa_rb; /* The ARB MSAA path uses a renderbuffer. */ + GLuint msaa_fb; +#endif + GLuint msaa_depth_stencil; + + cairo_bool_t stencil_and_msaa_caps_initialized; + cairo_bool_t supports_stencil; /* Stencil support for for non-texture surfaces. */ + cairo_bool_t supports_msaa; + GLint num_samples; + cairo_bool_t msaa_active; /* Whether the multisampling + framebuffer is active or not. */ + cairo_bool_t content_in_texture; /* whether we just uploaded image + to texture, used for certain + gles2 extensions and glesv3 */ + cairo_clip_t *clip_on_stencil_buffer; + + int owns_tex; + cairo_bool_t needs_update; + + cairo_region_t *clip_region; +}; + +typedef struct cairo_gl_glyph_cache { + cairo_rtree_t rtree; + cairo_gl_surface_t *surface; +} cairo_gl_glyph_cache_t; + +typedef enum cairo_gl_tex { + CAIRO_GL_TEX_SOURCE = 0, + CAIRO_GL_TEX_MASK = 1, + CAIRO_GL_TEX_TEMP = 2 +} cairo_gl_tex_t; + +typedef struct cairo_gl_shader { + GLuint fragment_shader; + GLuint program; + GLint mvp_location; + GLint constant_location[2]; + GLint a_location[2]; + GLint circle_d_location[2]; + GLint radius_0_location[2]; + GLint texdims_location[2]; + GLint texgen_location[2]; +} cairo_gl_shader_t; + +typedef enum cairo_gl_shader_in { + CAIRO_GL_SHADER_IN_NORMAL, + CAIRO_GL_SHADER_IN_CA_SOURCE, + CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA, + + CAIRO_GL_SHADER_IN_COUNT +} cairo_gl_shader_in_t; + +typedef enum cairo_gl_var_type { + CAIRO_GL_VAR_NONE, + CAIRO_GL_VAR_TEXCOORDS, + CAIRO_GL_VAR_TEXGEN, +} cairo_gl_var_type_t; + +typedef enum cairo_gl_primitive_type { + CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES, + CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS +} cairo_gl_primitive_type_t; + +typedef void (*cairo_gl_emit_rect_t) (cairo_gl_context_t *ctx, + GLfloat x1, GLfloat y1, + GLfloat x2, GLfloat y2); + +typedef void (*cairo_gl_emit_span_t) (cairo_gl_context_t *ctx, + GLfloat x1, GLfloat y1, + GLfloat x2, GLfloat y2, + uint8_t alpha); + +typedef void (*cairo_gl_emit_glyph_t) (cairo_gl_context_t *ctx, + GLfloat x1, GLfloat y1, + GLfloat x2, GLfloat y2, + GLfloat glyph_x1, GLfloat glyph_y1, + GLfloat glyph_x2, GLfloat glyph_y2); + +#define cairo_gl_var_type_hash(src,mask,spans,dest) ((spans) << 5) | ((mask) << 3 | (src << 1) | (dest)) +#define CAIRO_GL_VAR_TYPE_MAX (1 << 6) + +typedef void (*cairo_gl_generic_func_t)(void); +typedef cairo_gl_generic_func_t (*cairo_gl_get_proc_addr_func_t)(const char *procname); + +typedef struct _cairo_gl_dispatch { + /* Buffers */ + void (*GenBuffers) (GLsizei n, GLuint *buffers); + void (*BindBuffer) (GLenum target, GLuint buffer); + void (*BufferData) (GLenum target, GLsizeiptr size, + const GLvoid* data, GLenum usage); + GLvoid *(*MapBuffer) (GLenum target, GLenum access); + GLboolean (*UnmapBuffer) (GLenum target); + + /* Shaders */ + GLuint (*CreateShader) (GLenum type); + void (*ShaderSource) (GLuint shader, GLsizei count, + const GLchar** string, const GLint* length); + void (*CompileShader) (GLuint shader); + void (*GetShaderiv) (GLuint shader, GLenum pname, GLint *params); + void (*GetShaderInfoLog) (GLuint shader, GLsizei bufSize, + GLsizei *length, GLchar *infoLog); + void (*DeleteShader) (GLuint shader); + + /* Programs */ + GLuint (*CreateProgram) (void); + void (*AttachShader) (GLuint program, GLuint shader); + void (*DeleteProgram) (GLuint program); + void (*LinkProgram) (GLuint program); + void (*UseProgram) (GLuint program); + void (*GetProgramiv) (GLuint program, GLenum pname, GLint *params); + void (*GetProgramInfoLog) (GLuint program, GLsizei bufSize, + GLsizei *length, GLchar *infoLog); + + /* Uniforms */ + GLint (*GetUniformLocation) (GLuint program, const GLchar* name); + void (*Uniform1f) (GLint location, GLfloat x); + void (*Uniform2f) (GLint location, GLfloat x, GLfloat y); + void (*Uniform3f) (GLint location, GLfloat x, GLfloat y, GLfloat z); + void (*Uniform4f) (GLint location, GLfloat x, GLfloat y, GLfloat z, + GLfloat w); + void (*UniformMatrix3fv) (GLint location, GLsizei count, + GLboolean transpose, const GLfloat *value); + void (*UniformMatrix4fv) (GLint location, GLsizei count, + GLboolean transpose, const GLfloat *value); + void (*Uniform1i) (GLint location, GLint x); + + /* Attributes */ + void (*BindAttribLocation) (GLuint program, GLuint index, + const GLchar *name); + void (*VertexAttribPointer) (GLuint index, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, + const GLvoid *pointer); + void (*EnableVertexAttribArray) (GLuint index); + void (*DisableVertexAttribArray) (GLuint index); + + /* Framebuffer objects */ + void (*GenFramebuffers) (GLsizei n, GLuint* framebuffers); + void (*BindFramebuffer) (GLenum target, GLuint framebuffer); + void (*FramebufferTexture2D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level); + GLenum (*CheckFramebufferStatus) (GLenum target); + void (*DeleteFramebuffers) (GLsizei n, const GLuint* framebuffers); + void (*GenRenderbuffers) (GLsizei n, GLuint *renderbuffers); + void (*BindRenderbuffer) (GLenum target, GLuint renderbuffer); + void (*RenderbufferStorage) (GLenum target, GLenum internal_format, + GLsizei width, GLsizei height); + void (*FramebufferRenderbuffer) (GLenum target, GLenum attachment, + GLenum renderbuffer_ttarget, GLuint renderbuffer); + void (*DeleteRenderbuffers) (GLsizei n, GLuint *renderbuffers); + void (*BlitFramebuffer) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); + void (*RenderbufferStorageMultisample) (GLenum target, GLsizei samples, + GLenum internalformat, + GLsizei width, GLsizei height); + void (*FramebufferTexture2DMultisample) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level, GLsizei samples); +} cairo_gl_dispatch_t; + +struct _cairo_gl_context { + cairo_device_t base; + + const cairo_compositor_t *compositor; + + GLuint texture_load_pbo; + GLint max_framebuffer_size; + GLint max_texture_size; + GLint max_textures; + GLenum tex_target; + + GLint num_samples; + cairo_bool_t supports_msaa; + char *vb; + + cairo_bool_t has_shader_support; + + GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX]; + cairo_gl_shader_t fill_rectangles_shader; + cairo_cache_t shaders; + + cairo_cache_t gradients; + + cairo_gl_glyph_cache_t glyph_cache[2]; + cairo_list_t fonts; + + cairo_gl_surface_t *current_target; + cairo_operator_t current_operator; + cairo_gl_shader_t *pre_shader; /* for component alpha */ + cairo_gl_shader_t *current_shader; + + cairo_gl_operand_t operands[2]; + cairo_bool_t spans; + + unsigned int vbo_size; + unsigned int vb_offset; + unsigned int vertex_size; + cairo_region_t *clip_region; + cairo_clip_t *clip; + + cairo_gl_primitive_type_t primitive_type; + cairo_array_t tristrip_indices; + + cairo_bool_t has_mesa_pack_invert; + cairo_gl_dispatch_t dispatch; + GLfloat modelviewprojection_matrix[16]; + cairo_gl_flavor_t gl_flavor; + cairo_bool_t has_map_buffer; + cairo_bool_t has_packed_depth_stencil; + cairo_bool_t has_npot_repeat; + cairo_bool_t can_read_bgra; + + cairo_bool_t thread_aware; + + void (*acquire) (void *ctx); + void (*release) (void *ctx); + + void (*make_current) (void *ctx, cairo_gl_surface_t *surface); + void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface); + void (*destroy) (void *ctx); +}; + +typedef struct _cairo_gl_composite { + cairo_gl_surface_t *dst; + cairo_operator_t op; + cairo_region_t *clip_region; + + cairo_gl_operand_t src; + cairo_gl_operand_t mask; + cairo_bool_t spans; + + cairo_clip_t *clip; + cairo_bool_t multisample; +} cairo_gl_composite_t; + +typedef struct _cairo_gl_font { + cairo_scaled_font_private_t base; + cairo_device_t *device; + cairo_list_t link; +} cairo_gl_font_t; + +static cairo_always_inline GLenum +_cairo_gl_get_error (void) +{ + GLenum err = glGetError(); + + if (unlikely (err)) + while (glGetError ()); + + return err; +} + +static inline cairo_device_t * +_cairo_gl_context_create_in_error (cairo_status_t status) +{ + return (cairo_device_t *) _cairo_device_create_in_error (status); +} + +cairo_private cairo_status_t +_cairo_gl_context_init (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_surface_init (cairo_device_t *device, + cairo_gl_surface_t *surface, + cairo_content_t content, + int width, int height); + +static cairo_always_inline cairo_bool_t cairo_warn +_cairo_gl_surface_is_texture (cairo_gl_surface_t *surface) +{ + return surface->tex != 0; +} + +cairo_private cairo_status_t +_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y, + cairo_bool_t force_flush); + +cairo_private cairo_int_status_t +_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface); + +static cairo_always_inline cairo_bool_t +_cairo_gl_device_has_glsl (cairo_device_t *device) +{ + return ((cairo_gl_context_t *) device)->has_shader_support; +} + +static cairo_always_inline cairo_bool_t +_cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device) +{ + return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE; +} + +static cairo_always_inline cairo_status_t cairo_warn +_cairo_gl_context_acquire (cairo_device_t *device, + cairo_gl_context_t **ctx) +{ + cairo_status_t status; + + status = cairo_device_acquire (device); + if (unlikely (status)) + return status; + + /* clear potential previous GL errors */ + _cairo_gl_get_error (); + + *ctx = (cairo_gl_context_t *) device; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_always_inline cairo_warn cairo_status_t +_cairo_gl_context_release (cairo_gl_context_t *ctx, cairo_status_t status) +{ + GLenum err; + + err = _cairo_gl_get_error (); + + if (unlikely (err)) { + cairo_status_t new_status; + new_status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + if (status == CAIRO_STATUS_SUCCESS) + status = new_status; + } + + cairo_device_release (&(ctx)->base); + + return status; +} + +cairo_private void +_cairo_gl_context_set_destination (cairo_gl_context_t *ctx, + cairo_gl_surface_t *surface, + cairo_bool_t multisampling); + +cairo_private void +_cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx, + cairo_gl_surface_t *surface, + cairo_bool_t multisampling); + +cairo_private cairo_gl_emit_rect_t +_cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_context_emit_rect (cairo_gl_context_t *ctx, + GLfloat x1, GLfloat y1, + GLfloat x2, GLfloat y2); + +cairo_private cairo_gl_emit_span_t +_cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx); + +cairo_private cairo_gl_emit_glyph_t +_cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_context_activate (cairo_gl_context_t *ctx, + cairo_gl_tex_t tex_unit); + +cairo_private cairo_bool_t +_cairo_gl_operator_is_supported (cairo_operator_t op); + +cairo_private cairo_bool_t +_cairo_gl_ensure_stencil (cairo_gl_context_t *ctx, + cairo_gl_surface_t *surface); + +cairo_private cairo_status_t +_cairo_gl_composite_init (cairo_gl_composite_t *setup, + cairo_operator_t op, + cairo_gl_surface_t *dst, + cairo_bool_t has_component_alpha); + +cairo_private void +_cairo_gl_composite_fini (cairo_gl_composite_t *setup); + +cairo_private cairo_status_t +_cairo_gl_composite_set_operator (cairo_gl_composite_t *setup, + cairo_operator_t op, + cairo_bool_t assume_component_alpha); + +cairo_private void +_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, + cairo_region_t *clip_region); + +cairo_private void +_cairo_gl_composite_set_clip(cairo_gl_composite_t *setup, + cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_gl_composite_set_source (cairo_gl_composite_t *setup, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *sample, + const cairo_rectangle_int_t *extents, + cairo_bool_t use_texgen); + +cairo_private void +_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup, + const cairo_color_t *color); + +cairo_private void +_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup, + const cairo_gl_operand_t *source); + +cairo_private cairo_int_status_t +_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *sample, + const cairo_rectangle_int_t *extents, + cairo_bool_t use_texgen); + +cairo_private void +_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup, + const cairo_gl_operand_t *mask); + +cairo_private void +_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup); + +cairo_private void +_cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup); + +cairo_private cairo_status_t +_cairo_gl_composite_begin (cairo_gl_composite_t *setup, + cairo_gl_context_t **ctx); + +cairo_private cairo_status_t +_cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup, + cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_composite_flush (cairo_gl_context_t *ctx); + +cairo_private cairo_int_status_t +_cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx, + cairo_gl_composite_t *setup, + const cairo_point_t quad[4]); + +cairo_private cairo_int_status_t +_cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx, + cairo_gl_composite_t *setup, + const cairo_point_t triangle[3]); + +cairo_private void +_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, + cairo_gl_tex_t tex_unit); + +cairo_private cairo_bool_t +_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor, + pixman_format_code_t pixman_format, + GLenum *internal_format, GLenum *format, + GLenum *type, cairo_bool_t *has_alpha, + cairo_bool_t *needs_swap); + +cairo_private void +_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache); + +cairo_private void +_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, + cairo_gl_glyph_cache_t *cache); + +cairo_private cairo_int_status_t +_cairo_gl_surface_show_glyphs (void *abstract_dst, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip, + int *remaining_glyphs); + +cairo_private cairo_status_t +_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx); + +cairo_private void +_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx); + +static cairo_always_inline cairo_bool_t +_cairo_gl_context_is_flushed (cairo_gl_context_t *ctx) +{ + return ctx->vb_offset == 0; +} + +cairo_private cairo_status_t +_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, + cairo_gl_operand_t *source, + cairo_gl_operand_t *mask, + cairo_bool_t use_coverage, + cairo_gl_shader_in_t in, + cairo_gl_shader_t **shader); + +cairo_private void +_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, + GLint location, + float value); + +cairo_private void +_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, + GLint location, + float value0, float value1); + +cairo_private void +_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, + GLint location, + float value0, + float value1, + float value2); + +cairo_private void +_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, + GLint location, + float value0, float value1, + float value2, float value3); + +cairo_private void +_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, + GLint location, + const cairo_matrix_t* m); + +cairo_private void +_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx, + GLint location, + GLfloat* gl_m); + +cairo_private void +_cairo_gl_set_shader (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader); + +cairo_private void +_cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader); + +cairo_private int +_cairo_gl_get_version (void); + +cairo_private cairo_gl_flavor_t +_cairo_gl_get_flavor (void); + +cairo_private unsigned long +_cairo_gl_get_vbo_size (void); + +cairo_private cairo_bool_t +_cairo_gl_has_extension (const char *ext); + +cairo_private cairo_status_t +_cairo_gl_dispatch_init(cairo_gl_dispatch_t *dispatch, + cairo_gl_get_proc_addr_func_t get_proc_addr); + +cairo_private cairo_int_status_t +_cairo_gl_operand_init (cairo_gl_operand_t *operand, + const cairo_pattern_t *pattern, + cairo_gl_surface_t *dst, + const cairo_rectangle_int_t *sample, + const cairo_rectangle_int_t *extents, + cairo_bool_t use_texgen); + +cairo_private void +_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand, + const cairo_color_t *color); + +cairo_private cairo_filter_t +_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand); + +cairo_private GLint +_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand); + +cairo_private cairo_extend_t +_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand); + +cairo_private unsigned int +_cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand); + +cairo_private cairo_bool_t +_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest, + cairo_gl_operand_t *source, + unsigned int vertex_offset); + +cairo_private void +_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, + cairo_gl_operand_t *operand, + cairo_gl_tex_t tex_unit); + +cairo_private void +_cairo_gl_operand_emit (cairo_gl_operand_t *operand, + GLfloat ** vb, + GLfloat x, + GLfloat y); + +cairo_private void +_cairo_gl_operand_copy (cairo_gl_operand_t *dst, + const cairo_gl_operand_t *src); + +cairo_private void +_cairo_gl_operand_translate (cairo_gl_operand_t *operand, + double tx, double ty); + +cairo_private void +_cairo_gl_operand_destroy (cairo_gl_operand_t *operand); + +cairo_private const cairo_compositor_t * +_cairo_gl_msaa_compositor_get (void); + +cairo_private const cairo_compositor_t * +_cairo_gl_span_compositor_get (void); + +cairo_private const cairo_compositor_t * +_cairo_gl_traps_compositor_get (void); + +cairo_private cairo_int_status_t +_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int *num_glyphs); + +cairo_private cairo_int_status_t +_cairo_gl_composite_glyphs (void *_dst, + cairo_operator_t op, + cairo_surface_t *_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + cairo_composite_glyphs_info_t *info); + +cairo_private cairo_int_status_t +_cairo_gl_composite_glyphs_with_clip (void *_dst, + cairo_operator_t op, + cairo_surface_t *_src, + int src_x, + int src_y, + int dst_x, + int dst_y, + cairo_composite_glyphs_info_t *info, + cairo_clip_t *clip); + +cairo_private void +_cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx, + cairo_gl_surface_t *surface); + +cairo_private cairo_surface_t * +_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height); + +cairo_private cairo_surface_t * +_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height); + +cairo_private cairo_surface_t * +_cairo_gl_pattern_to_source (cairo_surface_t *dst, + const cairo_pattern_t *pattern, + cairo_bool_t is_mask, + const cairo_rectangle_int_t *extents, + const cairo_rectangle_int_t *sample, + int *src_x, int *src_y); + +cairo_private cairo_int_status_t +_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx, + cairo_gl_composite_t *setup, + cairo_clip_t *clip); + +cairo_private cairo_surface_t * +_cairo_gl_white_source (void); + +cairo_private void +_cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface, + const cairo_rectangle_int_t *r); + +static inline cairo_gl_operand_t * +source_to_operand (cairo_surface_t *surface) +{ + cairo_gl_source_t *source = (cairo_gl_source_t *)surface; + return source ? &source->operand : NULL; +} + +static inline void +_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache) +{ + _cairo_rtree_unpin (&cache->rtree); +} + + +slim_hidden_proto (cairo_gl_surface_create); +slim_hidden_proto (cairo_gl_surface_create_for_texture); + +#endif /* CAIRO_GL_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-gl-shaders.c b/gfx/cairo/cairo/src/cairo-gl-shaders.c new file mode 100644 index 0000000000..b70c177f22 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-shaders.c @@ -0,0 +1,1111 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 T. Zachary Laine + * Copyright © 2010 Eric Anholt + * Copyright © 2010 Red Hat, Inc + * Copyright © 2010 Linaro Limited + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is T. Zachary Laine. + * + * Contributor(s): + * Benjamin Otte + * Eric Anholt + * T. Zachary Laine + * Alexandros Frantzis + * H. Lewin + */ + +#include "cairoint.h" +#include "cairo-gl-private.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" + +static cairo_status_t +_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader, + cairo_gl_var_type_t src, + cairo_gl_var_type_t mask, + cairo_bool_t use_coverage, + const char *fragment_text); + +typedef struct _cairo_shader_cache_entry { + cairo_cache_entry_t base; + + unsigned vertex; + + cairo_gl_operand_type_t src; + cairo_gl_operand_type_t mask; + cairo_gl_operand_type_t dest; + cairo_bool_t use_coverage; + + cairo_gl_shader_in_t in; + GLint src_gl_filter; + cairo_bool_t src_border_fade; + cairo_extend_t src_extend; + GLint mask_gl_filter; + cairo_bool_t mask_border_fade; + cairo_extend_t mask_extend; + + cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */ + cairo_gl_shader_t shader; +} cairo_shader_cache_entry_t; + +static cairo_bool_t +_cairo_gl_shader_cache_equal_desktop (const void *key_a, const void *key_b) +{ + const cairo_shader_cache_entry_t *a = key_a; + const cairo_shader_cache_entry_t *b = key_b; + cairo_bool_t both_have_npot_repeat = + a->ctx->has_npot_repeat && b->ctx->has_npot_repeat; + + return (a->vertex == b->vertex && + a->src == b->src && + a->mask == b->mask && + a->dest == b->dest && + a->use_coverage == b->use_coverage && + a->in == b->in && + (both_have_npot_repeat || a->src_extend == b->src_extend) && + (both_have_npot_repeat || a->mask_extend == b->mask_extend)); +} + +/* + * For GLES2 we use more complicated shaders to implement missing GL + * features. In this case we need more parameters to uniquely identify + * a shader (vs _cairo_gl_shader_cache_equal_desktop()). + */ +static cairo_bool_t +_cairo_gl_shader_cache_equal_gles2 (const void *key_a, const void *key_b) +{ + const cairo_shader_cache_entry_t *a = key_a; + const cairo_shader_cache_entry_t *b = key_b; + cairo_bool_t both_have_npot_repeat = + a->ctx->has_npot_repeat && b->ctx->has_npot_repeat; + + return (a->vertex == b->vertex && + a->src == b->src && + a->mask == b->mask && + a->dest == b->dest && + a->use_coverage == b->use_coverage && + a->in == b->in && + a->src_gl_filter == b->src_gl_filter && + a->src_border_fade == b->src_border_fade && + (both_have_npot_repeat || a->src_extend == b->src_extend) && + a->mask_gl_filter == b->mask_gl_filter && + a->mask_border_fade == b->mask_border_fade && + (both_have_npot_repeat || a->mask_extend == b->mask_extend)); +} + +static unsigned long +_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry) +{ + return (((uint32_t)entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in << 1) | entry->use_coverage) ^ entry->vertex; +} + +static void +_cairo_gl_shader_cache_destroy (void *data) +{ + cairo_shader_cache_entry_t *entry = data; + + _cairo_gl_shader_fini (entry->ctx, &entry->shader); + if (entry->ctx->current_shader == &entry->shader) + entry->ctx->current_shader = NULL; + free (entry); +} + +static void +_cairo_gl_shader_init (cairo_gl_shader_t *shader) +{ + shader->fragment_shader = 0; + shader->program = 0; +} + +cairo_status_t +_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) +{ + static const char *fill_fs_source = + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "uniform vec4 color;\n" + "void main()\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + cairo_status_t status; + + if (_cairo_gl_get_version () >= CAIRO_GL_VERSION_ENCODE (2, 0) || + (_cairo_gl_has_extension ("GL_ARB_shader_objects") && + _cairo_gl_has_extension ("GL_ARB_fragment_shader") && + _cairo_gl_has_extension ("GL_ARB_vertex_shader"))) { + ctx->has_shader_support = TRUE; + } else { + ctx->has_shader_support = FALSE; + fprintf (stderr, "Error: The cairo gl backend requires shader support!\n"); + return CAIRO_STATUS_DEVICE_ERROR; + } + + memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders)); + + status = _cairo_cache_init (&ctx->shaders, + ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ? + _cairo_gl_shader_cache_equal_desktop : + _cairo_gl_shader_cache_equal_gles2, + NULL, + _cairo_gl_shader_cache_destroy, + CAIRO_GL_MAX_SHADERS_PER_CONTEXT); + if (unlikely (status)) + return status; + + _cairo_gl_shader_init (&ctx->fill_rectangles_shader); + status = _cairo_gl_shader_compile_and_link (ctx, + &ctx->fill_rectangles_shader, + CAIRO_GL_VAR_NONE, + CAIRO_GL_VAR_NONE, + FALSE, + fill_fs_source); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx) +{ + int i; + + for (i = 0; i < CAIRO_GL_VAR_TYPE_MAX; i++) { + if (ctx->vertex_shaders[i]) + ctx->dispatch.DeleteShader (ctx->vertex_shaders[i]); + } + + _cairo_gl_shader_fini(ctx, &ctx->fill_rectangles_shader); + _cairo_cache_fini (&ctx->shaders); +} + +void +_cairo_gl_shader_fini (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader) +{ + if (shader->fragment_shader) + ctx->dispatch.DeleteShader (shader->fragment_shader); + + if (shader->program) + ctx->dispatch.DeleteProgram (shader->program); +} + +static const char *operand_names[] = { "source", "mask", "dest" }; + +static cairo_gl_var_type_t +cairo_gl_operand_get_var_type (cairo_gl_operand_t *operand) +{ + switch (operand->type) { + default: + case CAIRO_GL_OPERAND_COUNT: + ASSERT_NOT_REACHED; + case CAIRO_GL_OPERAND_NONE: + case CAIRO_GL_OPERAND_CONSTANT: + return CAIRO_GL_VAR_NONE; + case CAIRO_GL_OPERAND_LINEAR_GRADIENT: + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: + return operand->gradient.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS; + case CAIRO_GL_OPERAND_TEXTURE: + return operand->texture.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS; + } +} + +static void +cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, + cairo_gl_var_type_t type, + cairo_gl_tex_t name) +{ + switch (type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_GL_VAR_NONE: + break; + case CAIRO_GL_VAR_TEXCOORDS: + _cairo_output_stream_printf (stream, + "attribute vec4 MultiTexCoord%d;\n" + "varying vec2 %s_texcoords;\n", + name, + operand_names[name]); + break; + case CAIRO_GL_VAR_TEXGEN: + _cairo_output_stream_printf (stream, + "uniform mat3 %s_texgen;\n" + "varying vec2 %s_texcoords;\n", + operand_names[name], + operand_names[name]); + break; + } +} + +static void +cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream, + cairo_gl_var_type_t type, + cairo_gl_tex_t name) +{ + switch (type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_GL_VAR_NONE: + break; + case CAIRO_GL_VAR_TEXCOORDS: + _cairo_output_stream_printf (stream, + " %s_texcoords = MultiTexCoord%d.xy;\n", + operand_names[name], name); + break; + + case CAIRO_GL_VAR_TEXGEN: + _cairo_output_stream_printf (stream, + " %s_texcoords = (%s_texgen * Vertex.xyw).xy;\n", + operand_names[name], operand_names[name]); + break; + } +} + +static void +cairo_gl_shader_dcl_coverage (cairo_output_stream_t *stream) +{ + _cairo_output_stream_printf (stream, "varying float coverage;\n"); +} + +static void +cairo_gl_shader_def_coverage (cairo_output_stream_t *stream) +{ + _cairo_output_stream_printf (stream, " coverage = Color.a;\n"); +} + +static cairo_status_t +cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src, + cairo_gl_var_type_t mask, + cairo_bool_t use_coverage, + cairo_gl_var_type_t dest, + char **out) +{ + cairo_output_stream_t *stream = _cairo_memory_stream_create (); + unsigned char *source; + unsigned long length; + cairo_status_t status; + + cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE); + cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK); + if (use_coverage) + cairo_gl_shader_dcl_coverage (stream); + + _cairo_output_stream_printf (stream, + "attribute vec4 Vertex;\n" + "attribute vec4 Color;\n" + "uniform mat4 ModelViewProjectionMatrix;\n" + "void main()\n" + "{\n" + " gl_Position = ModelViewProjectionMatrix * Vertex;\n"); + + cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE); + cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK); + if (use_coverage) + cairo_gl_shader_def_coverage (stream); + + _cairo_output_stream_write (stream, + "}\n\0", 3); + + status = _cairo_memory_stream_destroy (stream, &source, &length); + if (unlikely (status)) + return status; + + *out = (char *) source; + return CAIRO_STATUS_SUCCESS; +} + +/* + * Returns whether an operand needs a special border fade fragment shader + * to simulate the GL_CLAMP_TO_BORDER wrapping method that is missing in GLES2. + */ +static cairo_bool_t +_cairo_gl_shader_needs_border_fade (cairo_gl_operand_t *operand) +{ + cairo_extend_t extend =_cairo_gl_operand_get_extend (operand); + + return extend == CAIRO_EXTEND_NONE && + (operand->type == CAIRO_GL_OPERAND_TEXTURE || + operand->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT || + operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE || + operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0); +} + +static void +cairo_gl_shader_emit_color (cairo_output_stream_t *stream, + cairo_gl_context_t *ctx, + cairo_gl_operand_t *op, + cairo_gl_tex_t name) +{ + const char *namestr = operand_names[name]; + const char *rectstr = (ctx->tex_target == GL_TEXTURE_RECTANGLE ? "Rect" : ""); + + switch (op->type) { + case CAIRO_GL_OPERAND_COUNT: + default: + ASSERT_NOT_REACHED; + break; + case CAIRO_GL_OPERAND_NONE: + _cairo_output_stream_printf (stream, + "vec4 get_%s()\n" + "{\n" + " return vec4 (0, 0, 0, 1);\n" + "}\n", + namestr); + break; + case CAIRO_GL_OPERAND_CONSTANT: + _cairo_output_stream_printf (stream, + "uniform vec4 %s_constant;\n" + "vec4 get_%s()\n" + "{\n" + " return %s_constant;\n" + "}\n", + namestr, namestr, namestr); + break; + case CAIRO_GL_OPERAND_TEXTURE: + _cairo_output_stream_printf (stream, + "uniform sampler2D%s %s_sampler;\n" + "uniform vec2 %s_texdims;\n" + "varying vec2 %s_texcoords;\n" + "vec4 get_%s()\n" + "{\n", + rectstr, namestr, namestr, namestr, namestr); + if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || + ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && + _cairo_gl_shader_needs_border_fade (op)) + { + _cairo_output_stream_printf (stream, + " vec2 border_fade = %s_border_fade (%s_texcoords, %s_texdims);\n" + " vec4 texel = texture2D%s (%s_sampler, %s_texcoords);\n" + " return texel * border_fade.x * border_fade.y;\n" + "}\n", + namestr, namestr, namestr, rectstr, namestr, namestr); + } + else + { + _cairo_output_stream_printf (stream, + " return texture2D%s (%s_sampler, %s_wrap (%s_texcoords));\n" + "}\n", + rectstr, namestr, namestr, namestr); + } + break; + case CAIRO_GL_OPERAND_LINEAR_GRADIENT: + _cairo_output_stream_printf (stream, + "varying vec2 %s_texcoords;\n" + "uniform vec2 %s_texdims;\n" + "uniform sampler2D%s %s_sampler;\n" + "\n" + "vec4 get_%s()\n" + "{\n", + namestr, namestr, rectstr, namestr, namestr); + if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || + ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && + _cairo_gl_shader_needs_border_fade (op)) + { + _cairo_output_stream_printf (stream, + " float border_fade = %s_border_fade (%s_texcoords.x, %s_texdims.x);\n" + " vec4 texel = texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n" + " return texel * border_fade;\n" + "}\n", + namestr, namestr, namestr, rectstr, namestr, namestr); + } + else + { + _cairo_output_stream_printf (stream, + " return texture2D%s (%s_sampler, %s_wrap (vec2 (%s_texcoords.x, 0.5)));\n" + "}\n", + rectstr, namestr, namestr, namestr); + } + break; + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: + _cairo_output_stream_printf (stream, + "varying vec2 %s_texcoords;\n" + "uniform vec2 %s_texdims;\n" + "uniform sampler2D%s %s_sampler;\n" + "uniform vec3 %s_circle_d;\n" + "uniform float %s_radius_0;\n" + "\n" + "vec4 get_%s()\n" + "{\n" + " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" + " \n" + " float B = dot (pos, %s_circle_d);\n" + " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" + " \n" + " float t = 0.5 * C / B;\n" + " float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n", + namestr, namestr, rectstr, namestr, namestr, namestr, namestr, + namestr, namestr, namestr, namestr, namestr); + if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || + ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && + _cairo_gl_shader_needs_border_fade (op)) + { + _cairo_output_stream_printf (stream, + " float border_fade = %s_border_fade (t, %s_texdims.x);\n" + " vec4 texel = texture2D%s (%s_sampler, vec2 (t, 0.5));\n" + " return mix (vec4 (0.0), texel * border_fade, is_valid);\n" + "}\n", + namestr, namestr, rectstr, namestr); + } + else + { + _cairo_output_stream_printf (stream, + " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2 (t, 0.5)));\n" + " return mix (vec4 (0.0), texel, is_valid);\n" + "}\n", + rectstr, namestr, namestr); + } + break; + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: + _cairo_output_stream_printf (stream, + "varying vec2 %s_texcoords;\n" + "uniform vec2 %s_texdims;\n" + "uniform sampler2D%s %s_sampler;\n" + "uniform vec3 %s_circle_d;\n" + "uniform float %s_a;\n" + "uniform float %s_radius_0;\n" + "\n" + "vec4 get_%s()\n" + "{\n" + " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" + " \n" + " float B = dot (pos, %s_circle_d);\n" + " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" + " \n" + " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n" + " float sqrtdet = sqrt (abs (det));\n" + " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n" + " \n" + " vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n" + " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n" + " \n" + " float upper_t = mix (t.y, t.x, is_valid.x);\n", + namestr, namestr, rectstr, namestr, namestr, namestr, namestr, + namestr, namestr, namestr, namestr, namestr, namestr); + if ((ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || + ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) && + _cairo_gl_shader_needs_border_fade (op)) + { + _cairo_output_stream_printf (stream, + " float border_fade = %s_border_fade (upper_t, %s_texdims.x);\n" + " vec4 texel = texture2D%s (%s_sampler, vec2 (upper_t, 0.5));\n" + " return mix (vec4 (0.0), texel * border_fade, has_color);\n" + "}\n", + namestr, namestr, rectstr, namestr); + } + else + { + _cairo_output_stream_printf (stream, + " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n" + " return mix (vec4 (0.0), texel, has_color);\n" + "}\n", + rectstr, namestr, namestr); + } + break; + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: + _cairo_output_stream_printf (stream, + "varying vec2 %s_texcoords;\n" + "uniform sampler2D%s %s_sampler;\n" + "uniform vec3 %s_circle_d;\n" + "uniform float %s_a;\n" + "uniform float %s_radius_0;\n" + "\n" + "vec4 get_%s()\n" + "{\n" + " vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n" + " \n" + " float B = dot (pos, %s_circle_d);\n" + " float C = dot (pos, vec3 (pos.xy, -pos.z));\n" + " \n" + " float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n" + " float sqrtdet = sqrt (abs (det));\n" + " vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n" + " \n" + " vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n" + " float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n" + " \n" + " float upper_t = mix (t.y, t.x, is_valid.x);\n" + " vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n" + " return mix (vec4 (0.0), texel, has_color);\n" + "}\n", + namestr, rectstr, namestr, namestr, namestr, namestr, + namestr, namestr, namestr, namestr, namestr, + namestr, namestr, namestr, rectstr, namestr, namestr); + break; + } +} + +/* + * Emits the border fade functions used by an operand. + * + * If bilinear filtering is used, the emitted function performs a linear + * fade to transparency effect in the intervals [-1/2n, 1/2n] and + * [1 - 1/2n, 1 + 1/2n] (n: texture size). + * + * If nearest filtering is used, the emitted function just returns + * 0.0 for all values outside [0, 1). + */ +static void +_cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream, + cairo_gl_operand_t *operand, + cairo_gl_tex_t name) +{ + const char *namestr = operand_names[name]; + GLint gl_filter = _cairo_gl_operand_get_gl_filter (operand); + + /* 2D version */ + _cairo_output_stream_printf (stream, + "vec2 %s_border_fade (vec2 coords, vec2 dims)\n" + "{\n", + namestr); + + if (gl_filter == GL_LINEAR) + _cairo_output_stream_printf (stream, + " return clamp(-abs(dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);\n"); + else + _cairo_output_stream_printf (stream, + " bvec2 in_tex1 = greaterThanEqual (coords, vec2 (0.0));\n" + " bvec2 in_tex2 = lessThan (coords, vec2 (1.0));\n" + " return vec2 (float (all (in_tex1) && all (in_tex2)));\n"); + + _cairo_output_stream_printf (stream, "}\n"); + + /* 1D version */ + _cairo_output_stream_printf (stream, + "float %s_border_fade (float x, float dim)\n" + "{\n", + namestr); + if (gl_filter == GL_LINEAR) + _cairo_output_stream_printf (stream, + " return clamp(-abs(dim * (x - 0.5)) + (dim + 1.0) * 0.5, 0.0, 1.0);\n"); + else + _cairo_output_stream_printf (stream, + " bool in_tex = x >= 0.0 && x < 1.0;\n" + " return float (in_tex);\n"); + + _cairo_output_stream_printf (stream, "}\n"); +} + +/* + * Emits the wrap function used by an operand. + * + * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are + * only available for NPOT textures if the GL_OES_texture_npot is supported. + * If GL_OES_texture_npot is not supported, we need to implement the wrapping + * functionality in the shader. + */ +static void +_cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx, + cairo_output_stream_t *stream, + cairo_gl_operand_t *operand, + cairo_gl_tex_t name) +{ + const char *namestr = operand_names[name]; + cairo_extend_t extend = _cairo_gl_operand_get_extend (operand); + + _cairo_output_stream_printf (stream, + "vec2 %s_wrap(vec2 coords)\n" + "{\n", + namestr); + + if (! ctx->has_npot_repeat && + (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT)) + { + if (extend == CAIRO_EXTEND_REPEAT) { + _cairo_output_stream_printf (stream, + " return fract(coords);\n"); + } else { /* CAIRO_EXTEND_REFLECT */ + _cairo_output_stream_printf (stream, + " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n"); + } + } + else + { + _cairo_output_stream_printf (stream, " return coords;\n"); + } + + _cairo_output_stream_printf (stream, "}\n"); +} + +static cairo_status_t +cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, + cairo_gl_shader_in_t in, + cairo_gl_operand_t *src, + cairo_gl_operand_t *mask, + cairo_bool_t use_coverage, + cairo_gl_operand_type_t dest_type, + char **out) +{ + cairo_output_stream_t *stream = _cairo_memory_stream_create (); + unsigned char *source; + unsigned long length; + cairo_status_t status; + const char *coverage_str; + + _cairo_output_stream_printf (stream, + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n"); + + _cairo_gl_shader_emit_wrap (ctx, stream, src, CAIRO_GL_TEX_SOURCE); + _cairo_gl_shader_emit_wrap (ctx, stream, mask, CAIRO_GL_TEX_MASK); + + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3 || + ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2) { + if (_cairo_gl_shader_needs_border_fade (src)) + _cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE); + if (_cairo_gl_shader_needs_border_fade (mask)) + _cairo_gl_shader_emit_border_fade (stream, mask, CAIRO_GL_TEX_MASK); + } + + cairo_gl_shader_emit_color (stream, ctx, src, CAIRO_GL_TEX_SOURCE); + cairo_gl_shader_emit_color (stream, ctx, mask, CAIRO_GL_TEX_MASK); + + coverage_str = ""; + if (use_coverage) { + _cairo_output_stream_printf (stream, "varying float coverage;\n"); + coverage_str = " * coverage"; + } + + _cairo_output_stream_printf (stream, + "void main()\n" + "{\n"); + switch (in) { + case CAIRO_GL_SHADER_IN_COUNT: + default: + ASSERT_NOT_REACHED; + case CAIRO_GL_SHADER_IN_NORMAL: + _cairo_output_stream_printf (stream, + " gl_FragColor = get_source() * get_mask().a%s;\n", + coverage_str); + break; + case CAIRO_GL_SHADER_IN_CA_SOURCE: + _cairo_output_stream_printf (stream, + " gl_FragColor = get_source() * get_mask()%s;\n", + coverage_str); + break; + case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA: + _cairo_output_stream_printf (stream, + " gl_FragColor = get_source().a * get_mask()%s;\n", + coverage_str); + break; + } + + _cairo_output_stream_write (stream, + "}\n\0", 3); + + status = _cairo_memory_stream_destroy (stream, &source, &length); + if (unlikely (status)) + return status; + + *out = (char *) source; + return CAIRO_STATUS_SUCCESS; +} + +static void +compile_shader (cairo_gl_context_t *ctx, + GLuint *shader, + GLenum type, + const char *source) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + GLint success, log_size, num_chars; + char *log; + + *shader = dispatch->CreateShader (type); + dispatch->ShaderSource (*shader, 1, &source, 0); + dispatch->CompileShader (*shader); + dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &success); + + if (success) + return; + + dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size); + if (log_size < 0) { + printf ("OpenGL shader compilation failed.\n"); + ASSERT_NOT_REACHED; + return; + } + + log = _cairo_malloc (log_size + 1); + dispatch->GetShaderInfoLog (*shader, log_size, &num_chars, log); + log[num_chars] = '\0'; + + printf ("OpenGL shader compilation failed. Shader:\n%s\n", source); + printf ("OpenGL compilation log:\n%s\n", log); + + free (log); + ASSERT_NOT_REACHED; +} + +static void +link_shader_program (cairo_gl_context_t *ctx, + GLuint *program, + GLuint vert, + GLuint frag) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + GLint success, log_size, num_chars; + char *log; + + *program = dispatch->CreateProgram (); + dispatch->AttachShader (*program, vert); + dispatch->AttachShader (*program, frag); + + dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX, + "Vertex"); + dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX, + "Color"); + dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX, + "MultiTexCoord0"); + dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX, + "MultiTexCoord1"); + + dispatch->LinkProgram (*program); + dispatch->GetProgramiv (*program, GL_LINK_STATUS, &success); + if (success) + return; + + dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size); + if (log_size < 0) { + printf ("OpenGL shader link failed.\n"); + ASSERT_NOT_REACHED; + return; + } + + log = _cairo_malloc (log_size + 1); + dispatch->GetProgramInfoLog (*program, log_size, &num_chars, log); + log[num_chars] = '\0'; + + printf ("OpenGL shader link failed:\n%s\n", log); + free (log); + ASSERT_NOT_REACHED; +} + +static GLint +_cairo_gl_get_op_uniform_location(cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader, + cairo_gl_tex_t tex_unit, + const char *suffix) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + char uniform_name[100]; + const char *unit_name[2] = { "source", "mask" }; + + snprintf (uniform_name, sizeof (uniform_name), "%s_%s", + unit_name[tex_unit], suffix); + + return dispatch->GetUniformLocation (shader->program, uniform_name); +} + +static cairo_status_t +_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader, + cairo_gl_var_type_t src, + cairo_gl_var_type_t mask, + cairo_bool_t use_coverage, + const char *fragment_text) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + unsigned int vertex_shader; + cairo_status_t status; + int i; + + assert (shader->program == 0); + + vertex_shader = cairo_gl_var_type_hash (src, mask, use_coverage, + CAIRO_GL_VAR_NONE); + if (ctx->vertex_shaders[vertex_shader] == 0) { + char *source; + + status = cairo_gl_shader_get_vertex_source (src, + mask, + use_coverage, + CAIRO_GL_VAR_NONE, + &source); + if (unlikely (status)) + goto FAILURE; + + compile_shader (ctx, &ctx->vertex_shaders[vertex_shader], + GL_VERTEX_SHADER, source); + free (source); + } + + compile_shader (ctx, &shader->fragment_shader, + GL_FRAGMENT_SHADER, fragment_text); + + link_shader_program (ctx, &shader->program, + ctx->vertex_shaders[vertex_shader], + shader->fragment_shader); + + shader->mvp_location = + dispatch->GetUniformLocation (shader->program, + "ModelViewProjectionMatrix"); + + for (i = 0; i < 2; i++) { + shader->constant_location[i] = + _cairo_gl_get_op_uniform_location (ctx, shader, i, "constant"); + shader->a_location[i] = + _cairo_gl_get_op_uniform_location (ctx, shader, i, "a"); + shader->circle_d_location[i] = + _cairo_gl_get_op_uniform_location (ctx, shader, i, "circle_d"); + shader->radius_0_location[i] = + _cairo_gl_get_op_uniform_location (ctx, shader, i, "radius_0"); + shader->texdims_location[i] = + _cairo_gl_get_op_uniform_location (ctx, shader, i, "texdims"); + shader->texgen_location[i] = + _cairo_gl_get_op_uniform_location (ctx, shader, i, "texgen"); + } + + return CAIRO_STATUS_SUCCESS; + + FAILURE: + _cairo_gl_shader_fini (ctx, shader); + shader->fragment_shader = 0; + shader->program = 0; + + return status; +} + +/* We always bind the source to texture unit 0 if present, and mask to + * texture unit 1 if present, so we can just initialize these once at + * compile time. + */ +static cairo_status_t +_cairo_gl_shader_set_samplers (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + GLint location; + GLint saved_program; + + /* We have to save/restore the current program because we might be + * asked for a different program while a shader is bound. This shouldn't + * be a performance issue, since this is only called once per compile. + */ + glGetIntegerv (GL_CURRENT_PROGRAM, &saved_program); + dispatch->UseProgram (shader->program); + + location = dispatch->GetUniformLocation (shader->program, "source_sampler"); + if (location != -1) { + dispatch->Uniform1i (location, CAIRO_GL_TEX_SOURCE); + } + + location = dispatch->GetUniformLocation (shader->program, "mask_sampler"); + if (location != -1) { + dispatch->Uniform1i (location, CAIRO_GL_TEX_MASK); + } + if(_cairo_gl_get_error()) return CAIRO_STATUS_DEVICE_ERROR; + dispatch->UseProgram (saved_program); + /* Pop and ignore a possible gl-error when restoring the previous program. + * It may be that being selected in the gl-context was the last reference + * to the shader. + */ + _cairo_gl_get_error(); + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx, + GLint location, + float value) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + assert (location != -1); + dispatch->Uniform1f (location, value); +} + +void +_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx, + GLint location, + float value0, + float value1) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + assert (location != -1); + dispatch->Uniform2f (location, value0, value1); +} + +void +_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx, + GLint location, + float value0, + float value1, + float value2) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + assert (location != -1); + dispatch->Uniform3f (location, value0, value1, value2); +} + +void +_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx, + GLint location, + float value0, float value1, + float value2, float value3) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + assert (location != -1); + dispatch->Uniform4f (location, value0, value1, value2, value3); +} + +void +_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx, + GLint location, + const cairo_matrix_t* m) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + float gl_m[9] = { + m->xx, m->yx, 0, + m->xy, m->yy, 0, + m->x0, m->y0, 1 + }; + assert (location != -1); + dispatch->UniformMatrix3fv (location, 1, GL_FALSE, gl_m); +} + +void +_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx, + GLint location, GLfloat* gl_m) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + assert (location != -1); + dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m); +} + +void +_cairo_gl_set_shader (cairo_gl_context_t *ctx, + cairo_gl_shader_t *shader) +{ + if (ctx->current_shader == shader) + return; + + if (shader) + ctx->dispatch.UseProgram (shader->program); + else + ctx->dispatch.UseProgram (0); + + ctx->current_shader = shader; +} + +cairo_status_t +_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, + cairo_gl_operand_t *source, + cairo_gl_operand_t *mask, + cairo_bool_t use_coverage, + cairo_gl_shader_in_t in, + cairo_gl_shader_t **shader) +{ + cairo_shader_cache_entry_t lookup, *entry; + char *fs_source; + cairo_status_t status; + + lookup.ctx = ctx; + + lookup.vertex = cairo_gl_var_type_hash (cairo_gl_operand_get_var_type (source), + cairo_gl_operand_get_var_type (mask), + use_coverage, + CAIRO_GL_VAR_NONE); + + lookup.src = source->type; + lookup.mask = mask->type; + lookup.dest = CAIRO_GL_OPERAND_NONE; + lookup.use_coverage = use_coverage; + lookup.in = in; + lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source); + lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source); + lookup.src_extend = _cairo_gl_operand_get_extend (source); + lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask); + lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask); + lookup.mask_extend = _cairo_gl_operand_get_extend (mask); + lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); + lookup.base.size = 1; + + entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base); + if (entry) { + assert (entry->shader.program); + *shader = &entry->shader; + return CAIRO_STATUS_SUCCESS; + } + + status = cairo_gl_shader_get_fragment_source (ctx, + in, + source, + mask, + use_coverage, + CAIRO_GL_OPERAND_NONE, + &fs_source); + if (unlikely (status)) + return status; + + entry = _cairo_malloc (sizeof (cairo_shader_cache_entry_t)); + if (unlikely (entry == NULL)) { + free (fs_source); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t)); + + entry->ctx = ctx; + _cairo_gl_shader_init (&entry->shader); + status = _cairo_gl_shader_compile_and_link (ctx, + &entry->shader, + cairo_gl_operand_get_var_type (source), + cairo_gl_operand_get_var_type (mask), + use_coverage, + fs_source); + free (fs_source); + + if (unlikely (status)) { + free (entry); + return status; + } + + status = _cairo_gl_shader_set_samplers (ctx, &entry->shader); + if (unlikely (status)) { + _cairo_gl_shader_fini (ctx, &entry->shader); + free (entry); + return status; + } + + status = _cairo_cache_insert (&ctx->shaders, &entry->base); + if (unlikely (status)) { + _cairo_gl_shader_fini (ctx, &entry->shader); + free (entry); + return status; + } + + *shader = &entry->shader; + + return CAIRO_STATUS_SUCCESS; +} diff --git a/gfx/cairo/cairo/src/cairo-gl-source.c b/gfx/cairo/cairo/src/cairo-gl-source.c new file mode 100644 index 0000000000..7e0ee4a826 --- /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 0000000000..0a4538a047 --- /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 0000000000..87dca2f03e --- /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 new file mode 100644 index 0000000000..b0f42d6aa1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl-surface.c @@ -0,0 +1,1550 @@ +/* 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-gl-private.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-compositor-private.h" +#include "cairo-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-surface-backend-private.h" + +static const cairo_surface_backend_t _cairo_gl_surface_backend; + +static cairo_status_t +_cairo_gl_surface_flush (void *abstract_surface, unsigned flags); + +static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface) +{ + return surface->backend == &_cairo_gl_surface_backend; +} + +static cairo_bool_t +_cairo_gl_get_image_format_and_type_gles2 (pixman_format_code_t pixman_format, + GLenum *internal_format, GLenum *format, + GLenum *type, cairo_bool_t *has_alpha, + cairo_bool_t *needs_swap) +{ + cairo_bool_t is_little_endian = _cairo_is_little_endian (); + + *has_alpha = TRUE; + + switch ((int) pixman_format) { + case PIXMAN_a8r8g8b8: + *internal_format = GL_BGRA; + *format = GL_BGRA; + *type = GL_UNSIGNED_BYTE; + *needs_swap = !is_little_endian; + return TRUE; + + case PIXMAN_x8r8g8b8: + *internal_format = GL_BGRA; + *format = GL_BGRA; + *type = GL_UNSIGNED_BYTE; + *has_alpha = FALSE; + *needs_swap = !is_little_endian; + return TRUE; + + case PIXMAN_a8b8g8r8: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + *needs_swap = !is_little_endian; + return TRUE; + + case PIXMAN_x8b8g8r8: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_BYTE; + *has_alpha = FALSE; + *needs_swap = !is_little_endian; + return TRUE; + + case PIXMAN_b8g8r8a8: + *internal_format = GL_BGRA; + *format = GL_BGRA; + *type = GL_UNSIGNED_BYTE; + *needs_swap = is_little_endian; + return TRUE; + + case PIXMAN_b8g8r8x8: + *internal_format = GL_BGRA; + *format = GL_BGRA; + *type = GL_UNSIGNED_BYTE; + *has_alpha = FALSE; + *needs_swap = is_little_endian; + return TRUE; + + case PIXMAN_r8g8b8: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE; + *needs_swap = is_little_endian; + return TRUE; + + case PIXMAN_b8g8r8: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE; + *needs_swap = !is_little_endian; + return TRUE; + + case PIXMAN_r5g6b5: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + *needs_swap = FALSE; + return TRUE; + + case PIXMAN_b5g6r5: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + *needs_swap = TRUE; + return TRUE; + + case PIXMAN_a1b5g5r5: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_5_5_5_1; + *needs_swap = TRUE; + return TRUE; + + case PIXMAN_x1b5g5r5: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_5_5_5_1; + *has_alpha = FALSE; + *needs_swap = TRUE; + return TRUE; + + case PIXMAN_a8: + *internal_format = GL_ALPHA; + *format = GL_ALPHA; + *type = GL_UNSIGNED_BYTE; + *needs_swap = FALSE; + return TRUE; + + default: + return FALSE; + } +} + +static cairo_bool_t +_cairo_gl_get_image_format_and_type_gl (pixman_format_code_t pixman_format, + GLenum *internal_format, GLenum *format, + GLenum *type, cairo_bool_t *has_alpha, + cairo_bool_t *needs_swap) +{ + *has_alpha = TRUE; + *needs_swap = FALSE; + + switch (pixman_format) { + case PIXMAN_a8r8g8b8: + *internal_format = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + return TRUE; + case PIXMAN_x8r8g8b8: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a8b8g8r8: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + return TRUE; + case PIXMAN_x8b8g8r8: + *internal_format = GL_RGB; + *format = GL_RGBA; + *type = GL_UNSIGNED_INT_8_8_8_8_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_b8g8r8a8: + *internal_format = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8; + return TRUE; + case PIXMAN_b8g8r8x8: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_INT_8_8_8_8; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_r8g8b8: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_BYTE; + return TRUE; + case PIXMAN_b8g8r8: + *internal_format = GL_RGB; + *format = GL_BGR; + *type = GL_UNSIGNED_BYTE; + return TRUE; + case PIXMAN_r5g6b5: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5; + return TRUE; + case PIXMAN_b5g6r5: + *internal_format = GL_RGB; + *format = GL_RGB; + *type = GL_UNSIGNED_SHORT_5_6_5_REV; + return TRUE; + case PIXMAN_a1r5g5b5: + *internal_format = GL_RGBA; + *format = GL_BGRA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + return TRUE; + case PIXMAN_x1r5g5b5: + *internal_format = GL_RGB; + *format = GL_BGRA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a1b5g5r5: + *internal_format = GL_RGBA; + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + return TRUE; + case PIXMAN_x1b5g5r5: + *internal_format = GL_RGB; + *format = GL_RGBA; + *type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + *has_alpha = FALSE; + return TRUE; + case PIXMAN_a8: + *internal_format = GL_ALPHA; + *format = GL_ALPHA; + *type = GL_UNSIGNED_BYTE; + return TRUE; + +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,27,2) + case PIXMAN_a8r8g8b8_sRGB: +#endif + case PIXMAN_a2b10g10r10: + case PIXMAN_x2b10g10r10: + case PIXMAN_a4r4g4b4: + case PIXMAN_x4r4g4b4: + case PIXMAN_a4b4g4r4: + case PIXMAN_x4b4g4r4: + case PIXMAN_r3g3b2: + case PIXMAN_b2g3r3: + case PIXMAN_a2r2g2b2: + case PIXMAN_a2b2g2r2: + case PIXMAN_c8: + case PIXMAN_x4a4: + /* case PIXMAN_x4c4: */ + case PIXMAN_x4g4: + case PIXMAN_a4: + case PIXMAN_r1g2b1: + case PIXMAN_b1g2r1: + case PIXMAN_a1r1g1b1: + case PIXMAN_a1b1g1r1: + case PIXMAN_c4: + case PIXMAN_g4: + case PIXMAN_a1: + case PIXMAN_g1: + case PIXMAN_yuy2: + case PIXMAN_yv12: + case PIXMAN_x2r10g10b10: + case PIXMAN_a2r10g10b10: + case PIXMAN_r8g8b8x8: + case PIXMAN_r8g8b8a8: + case PIXMAN_x14r6g6b6: + default: + return FALSE; + } +} + +/* + * Extracts pixel data from an image surface. + */ +static cairo_status_t +_cairo_gl_surface_extract_image_data (cairo_image_surface_t *image, + int x, int y, + int width, int height, + void **output) +{ + int cpp = PIXMAN_FORMAT_BPP (image->pixman_format) / 8; + char *data = _cairo_malloc_ab (width * height, cpp); + char *dst = data; + unsigned char *src = image->data + y * image->stride + x * cpp; + int i; + + if (unlikely (data == NULL)) + return CAIRO_STATUS_NO_MEMORY; + + for (i = 0; i < height; i++) { + memcpy (dst, src, width * cpp); + src += image->stride; + dst += width * cpp; + } + + *output = data; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor, + pixman_format_code_t pixman_format, + GLenum *internal_format, GLenum *format, + GLenum *type, cairo_bool_t *has_alpha, + cairo_bool_t *needs_swap) +{ + if (flavor == CAIRO_GL_FLAVOR_DESKTOP) + return _cairo_gl_get_image_format_and_type_gl (pixman_format, + internal_format, format, + type, has_alpha, + needs_swap); + else + return _cairo_gl_get_image_format_and_type_gles2 (pixman_format, + internal_format, format, + type, has_alpha, + needs_swap); + +} + +cairo_bool_t +_cairo_gl_operator_is_supported (cairo_operator_t op) +{ + return op < CAIRO_OPERATOR_SATURATE; +} + +static void +_cairo_gl_surface_embedded_operand_init (cairo_gl_surface_t *surface) +{ + cairo_gl_operand_t *operand = &surface->operand; + cairo_surface_attributes_t *attributes = &operand->texture.attributes; + + memset (operand, 0, sizeof (cairo_gl_operand_t)); + + operand->type = CAIRO_GL_OPERAND_TEXTURE; + operand->texture.surface = surface; + operand->texture.tex = surface->tex; + + if (_cairo_gl_device_requires_power_of_two_textures (surface->base.device)) { + cairo_matrix_init_identity (&attributes->matrix); + } else { + cairo_matrix_init_scale (&attributes->matrix, + 1.0 / surface->width, + 1.0 / surface->height); + } + + attributes->extend = CAIRO_EXTEND_NONE; + attributes->filter = CAIRO_FILTER_NEAREST; +} + +void +_cairo_gl_surface_init (cairo_device_t *device, + cairo_gl_surface_t *surface, + cairo_content_t content, + int width, int height) +{ + assert (width > 0 && height > 0); + + _cairo_surface_init (&surface->base, + &_cairo_gl_surface_backend, + device, + content, + FALSE); /* is_vector */ + + surface->width = width; + surface->height = height; + surface->needs_update = FALSE; + surface->content_in_texture = FALSE; + + _cairo_gl_surface_embedded_operand_init (surface); +} + +static cairo_bool_t +_cairo_gl_surface_size_valid_for_context (cairo_gl_context_t *ctx, + int width, int height) +{ + return width > 0 && height > 0 && + width <= ctx->max_framebuffer_size && + height <= ctx->max_framebuffer_size; +} + +static cairo_bool_t +_cairo_gl_surface_size_valid (cairo_gl_surface_t *surface, + int width, int height) +{ + cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; + return _cairo_gl_surface_size_valid_for_context (ctx, width, height); +} + +static cairo_surface_t * +_cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx, + cairo_content_t content, + GLuint tex, + int width, + int height) +{ + cairo_gl_surface_t *surface; + + surface = calloc (1, sizeof (cairo_gl_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->tex = tex; + _cairo_gl_surface_init (&ctx->base, surface, content, width, height); + + surface->supports_msaa = ctx->supports_msaa; + surface->num_samples = ctx->num_samples; + surface->supports_stencil = TRUE; + + /* Create the texture used to store the surface's data. */ + _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); + glBindTexture (ctx->tex_target, surface->tex); + glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return &surface->base; +} + +static cairo_surface_t * +_create_scratch_internal (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height, + cairo_bool_t for_caching) +{ + cairo_gl_surface_t *surface; + GLenum format; + GLuint tex; + + glGenTextures (1, &tex); + surface = (cairo_gl_surface_t *) + _cairo_gl_surface_create_scratch_for_texture (ctx, content, + tex, width, height); + if (unlikely (surface->base.status)) + return &surface->base; + + surface->owns_tex = TRUE; + + /* adjust the texture size after setting our real extents */ + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + switch (content) { + default: + ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: + format = GL_RGBA; + break; + case CAIRO_CONTENT_ALPHA: + /* When using GL_ALPHA, compositing doesn't work properly, but for + * caching surfaces, we are just uploading pixel data, so it isn't + * an issue. */ + if (for_caching) + format = GL_ALPHA; + else + format = GL_RGBA; + break; + case CAIRO_CONTENT_COLOR: + /* GL_RGB is almost what we want here -- sampling 1 alpha when + * texturing, using 1 as destination alpha factor in blending, + * etc. However, when filtering with GL_CLAMP_TO_BORDER, the + * alpha channel of the border color will also be clamped to + * 1, when we actually want the border color we explicitly + * specified. So, we have to store RGBA, and fill the alpha + * channel with 1 when blending. + */ + format = GL_RGBA; + break; + } + + glTexImage2D (ctx->tex_target, 0, format, width, height, 0, + format, GL_UNSIGNED_BYTE, NULL); + + return &surface->base; +} + +cairo_surface_t * +_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height) +{ + return _create_scratch_internal (ctx, content, width, height, FALSE); +} + +cairo_surface_t * +_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height) +{ + return _create_scratch_internal (ctx, content, width, height, TRUE); +} + +static cairo_status_t +_cairo_gl_surface_clear (cairo_gl_surface_t *surface, + const cairo_color_t *color) +{ + cairo_gl_context_t *ctx; + cairo_status_t status; + double r, g, b, a; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return status; + + _cairo_gl_context_set_destination (ctx, surface, surface->msaa_active); + if (surface->base.content & CAIRO_CONTENT_COLOR) { + r = color->red * color->alpha; + g = color->green * color->alpha; + b = color->blue * color->alpha; + } else { + r = g = b = 0; + } + if (surface->base.content & CAIRO_CONTENT_ALPHA) { + a = color->alpha; + } else { + a = 1.0; + } + + glDisable (GL_SCISSOR_TEST); + glClearColor (r, g, b, a); + glClear (GL_COLOR_BUFFER_BIT); + + if (a == 0) + surface->base.is_clear = TRUE; + + return _cairo_gl_context_release (ctx, status); +} + +static cairo_surface_t * +_cairo_gl_surface_create_and_clear_scratch (cairo_gl_context_t *ctx, + cairo_content_t content, + int width, + int height) +{ + cairo_gl_surface_t *surface; + cairo_int_status_t status; + + surface = (cairo_gl_surface_t *) + _cairo_gl_surface_create_scratch (ctx, content, width, height); + if (unlikely (surface->base.status)) + return &surface->base; + + /* Cairo surfaces start out initialized to transparent (black) */ + status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return _cairo_surface_create_in_error (status); + } + + return &surface->base; +} + +cairo_surface_t * +cairo_gl_surface_create (cairo_device_t *abstract_device, + cairo_content_t content, + int width, + int height) +{ + cairo_gl_context_t *ctx; + cairo_gl_surface_t *surface; + cairo_status_t status; + + if (! CAIRO_CONTENT_VALID (content)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + if (abstract_device == NULL) + return _cairo_image_surface_create_with_content (content, width, height); + + if (abstract_device->status) + return _cairo_surface_create_in_error (abstract_device->status); + + if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + status = _cairo_gl_context_acquire (abstract_device, &ctx); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (! _cairo_gl_surface_size_valid_for_context (ctx, width, height)) { + status = _cairo_gl_context_release (ctx, status); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + surface = (cairo_gl_surface_t *) + _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height); + if (unlikely (surface->base.status)) { + status = _cairo_gl_context_release (ctx, surface->base.status); + cairo_surface_destroy (&surface->base); + return _cairo_surface_create_in_error (status); + } + + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) { + cairo_surface_destroy (&surface->base); + return _cairo_surface_create_in_error (status); + } + + return &surface->base; +} +slim_hidden_def (cairo_gl_surface_create); + +/** + * cairo_gl_surface_create_for_texture: + * @content: type of content in the surface + * @tex: name of texture to use for storage of surface pixels + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a GL surface for the specified texture with the specified + * content and dimensions. The texture must be kept around until the + * #cairo_surface_t is destroyed or cairo_surface_finish() is called + * on the surface. The initial contents of @tex will be used as the + * initial image contents; you must explicitly clear the buffer, + * using, for example, cairo_rectangle() and cairo_fill() if you want + * it cleared. The format of @tex should be compatible with @content, + * in the sense that it must have the color components required by + * @content. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: TBD + **/ +cairo_surface_t * +cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, + cairo_content_t content, + unsigned int tex, + int width, + int height) +{ + cairo_gl_context_t *ctx; + cairo_gl_surface_t *surface; + cairo_status_t status; + + if (! CAIRO_CONTENT_VALID (content)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + if (abstract_device == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + + if (abstract_device->status) + return _cairo_surface_create_in_error (abstract_device->status); + + if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH)); + + status = _cairo_gl_context_acquire (abstract_device, &ctx); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (! _cairo_gl_surface_size_valid_for_context (ctx, width, height)) { + status = _cairo_gl_context_release (ctx, status); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + surface = (cairo_gl_surface_t *) + _cairo_gl_surface_create_scratch_for_texture (ctx, content, + tex, width, height); + status = _cairo_gl_context_release (ctx, status); + + return &surface->base; +} +slim_hidden_def (cairo_gl_surface_create_for_texture); + + +void +cairo_gl_surface_set_size (cairo_surface_t *abstract_surface, + int width, + int height) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (! _cairo_surface_is_gl (abstract_surface) || + _cairo_gl_surface_is_texture (surface)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (surface->width != width || surface->height != height) { + surface->needs_update = TRUE; + surface->width = width; + surface->height = height; + } +} + +int +cairo_gl_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + + if (! _cairo_surface_is_gl (abstract_surface)) + return 0; + + return surface->width; +} + +int +cairo_gl_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + + if (! _cairo_surface_is_gl (abstract_surface)) + return 0; + + return surface->height; +} + +void +cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) +{ + cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface; + + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (! _cairo_surface_is_gl (abstract_surface)) { + _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return; + } + + if (! _cairo_gl_surface_is_texture (surface)) { + cairo_gl_context_t *ctx; + cairo_status_t status; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return; + + /* For swapping on EGL, at least, we need a valid context/target. */ + _cairo_gl_context_set_destination (ctx, surface, FALSE); + /* And in any case we should flush any pending operations. */ + _cairo_gl_composite_flush (ctx); + + ctx->swap_buffers (ctx, surface); + + status = _cairo_gl_context_release (ctx, status); + if (status) + status = _cairo_surface_set_error (abstract_surface, status); + } +} + +static cairo_surface_t * +_cairo_gl_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t *surface = abstract_surface; + cairo_gl_context_t *ctx; + cairo_status_t status; + + if (! _cairo_gl_surface_size_valid (abstract_surface, width, height)) + return _cairo_image_surface_create_with_content (content, width, height); + + status = _cairo_gl_context_acquire (surface->device, &ctx); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + surface = _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height); + + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) { + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); + } + + return surface; +} + +static cairo_int_status_t +_cairo_gl_surface_fill_alpha_channel (cairo_gl_surface_t *dst, + cairo_gl_context_t *ctx, + int x, int y, + int width, int height) +{ + cairo_gl_composite_t setup; + cairo_status_t status; + + _cairo_gl_composite_flush (ctx); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + + status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, + dst, FALSE); + if (unlikely (status)) + goto CLEANUP; + + _cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_BLACK); + + status = _cairo_gl_composite_begin (&setup, &ctx); + if (unlikely (status)) + goto CLEANUP; + + _cairo_gl_context_emit_rect (ctx, x, y, x + width, y + height); + + status = _cairo_gl_context_release (ctx, status); + + CLEANUP: + _cairo_gl_composite_fini (&setup); + + _cairo_gl_composite_flush (ctx); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + return status; +} + +cairo_status_t +_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, + cairo_image_surface_t *src, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y, + cairo_bool_t force_flush) +{ + GLenum internal_format, format, type; + cairo_bool_t has_alpha, needs_swap; + cairo_image_surface_t *clone = NULL; + cairo_gl_context_t *ctx; + int cpp; + cairo_image_surface_t *rgba_clone = NULL; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + status = _cairo_gl_context_acquire (dst->base.device, &ctx); + if (unlikely (status)) + return status; + + if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES3 || + _cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES2) { + pixman_format_code_t pixman_format; + cairo_surface_pattern_t pattern; + cairo_bool_t require_conversion = FALSE; + pixman_format = _cairo_is_little_endian () ? PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8; + + if (src->base.content != CAIRO_CONTENT_ALPHA) { + if (src->pixman_format != pixman_format) + require_conversion = TRUE; + } + else if (dst->base.content != CAIRO_CONTENT_ALPHA) { + require_conversion = TRUE; + } + else if (src->pixman_format != PIXMAN_a8) { + pixman_format = PIXMAN_a8; + require_conversion = TRUE; + } + + if (require_conversion) { + rgba_clone = (cairo_image_surface_t *) + _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + src->width, + src->height, + 0); + if (unlikely (rgba_clone->base.status)) + goto FAIL; + + _cairo_pattern_init_for_surface (&pattern, &src->base); + status = _cairo_surface_paint (&rgba_clone->base, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) + goto FAIL; + + src = rgba_clone; + } + } + + if (! _cairo_gl_get_image_format_and_type (ctx->gl_flavor, + src->pixman_format, + &internal_format, + &format, + &type, + &has_alpha, + &needs_swap)) + { + cairo_bool_t is_supported; + + clone = _cairo_image_surface_coerce (src); + if (unlikely (status = clone->base.status)) + goto FAIL; + + is_supported = + _cairo_gl_get_image_format_and_type (ctx->gl_flavor, + clone->pixman_format, + &internal_format, + &format, + &type, + &has_alpha, + &needs_swap); + assert (is_supported); + assert (!needs_swap); + src = clone; + } + + cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8; + + if (force_flush) { + status = _cairo_gl_surface_flush (&dst->base, 0); + if (unlikely (status)) + goto FAIL; + } + + if (_cairo_gl_surface_is_texture (dst)) { + void *data_start = src->data + src_y * src->stride + src_x * cpp; + void *data_start_gles2 = NULL; + + /* + * Due to GL_UNPACK_ROW_LENGTH missing in GLES2 we have to extract the + * image data ourselves in some cases. In particular, we must extract + * the pixels if: + * a. we don't want full-length lines or + * b. the row stride cannot be handled by GL itself using a 4 byte + * alignment constraint + */ + if (src->stride < 0 || + (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES2 && + (src->width * cpp < src->stride - 3 || + width != src->width))) + { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + status = _cairo_gl_surface_extract_image_data (src, src_x, src_y, + width, height, + &data_start_gles2); + if (unlikely (status)) + goto FAIL; + + data_start = data_start_gles2; + } + else + { + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || + ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) + glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp); + } + + /* we must resolve the renderbuffer to texture before we + upload image */ + status = _cairo_gl_surface_resolve_multisampling (dst); + if (unlikely (status)) { + free (data_start_gles2); + goto FAIL; + } + + _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP); + glBindTexture (ctx->tex_target, dst->tex); + glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexSubImage2D (ctx->tex_target, 0, + dst_x, dst_y, width, height, + format, type, data_start); + + free (data_start_gles2); + + /* If we just treated some rgb-only data as rgba, then we have to + * go back and fix up the alpha channel where we filled in this + * texture data. + */ + if (!has_alpha) { + _cairo_gl_surface_fill_alpha_channel (dst, ctx, + dst_x, dst_y, + width, height); + } + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) + dst->content_in_texture = TRUE; + } else { + cairo_surface_t *tmp; + + tmp = _cairo_gl_surface_create_scratch (ctx, + dst->base.content, + width, height); + if (unlikely (tmp->status)) + goto FAIL; + + status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp, + src, + src_x, src_y, + width, height, + 0, 0, force_flush); + if (status == CAIRO_INT_STATUS_SUCCESS) { + cairo_surface_pattern_t tmp_pattern; + cairo_rectangle_int_t r; + cairo_clip_t *clip; + + _cairo_pattern_init_for_surface (&tmp_pattern, tmp); + cairo_matrix_init_translate (&tmp_pattern.base.matrix, + -dst_x, -dst_y); + tmp_pattern.base.filter = CAIRO_FILTER_NEAREST; + tmp_pattern.base.extend = CAIRO_EXTEND_NONE; + + r.x = dst_x; + r.y = dst_y; + r.width = width; + r.height = height; + clip = _cairo_clip_intersect_rectangle (NULL, &r); + status = _cairo_surface_paint (&dst->base, + CAIRO_OPERATOR_SOURCE, + &tmp_pattern.base, + clip); + _cairo_clip_destroy (clip); + _cairo_pattern_fini (&tmp_pattern.base); + } + + cairo_surface_destroy (tmp); + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) + dst->content_in_texture = TRUE; + } + +FAIL: + status = _cairo_gl_context_release (ctx, status); + + if (clone) + cairo_surface_destroy (&clone->base); + + if (rgba_clone) + cairo_surface_destroy (&rgba_clone->base); + + return status; +} + +static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface) +{ + cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; + return ctx->gl_flavor; +} + +static cairo_status_t +_cairo_gl_surface_finish (void *abstract_surface) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_gl_context_t *ctx; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return status; + + if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && + ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) + _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE); + if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && + ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) + _cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK); + if (ctx->current_target == surface) + ctx->current_target = NULL; + + if (surface->fb) + ctx->dispatch.DeleteFramebuffers (1, &surface->fb); + if (surface->depth_stencil) + ctx->dispatch.DeleteRenderbuffers (1, &surface->depth_stencil); + if (surface->owns_tex) + glDeleteTextures (1, &surface->tex); + + if (surface->msaa_depth_stencil) + ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_depth_stencil); + +#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV3_SURFACE + if (surface->msaa_fb) + ctx->dispatch.DeleteFramebuffers (1, &surface->msaa_fb); + if (surface->msaa_rb) + ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb); +#endif + + _cairo_clip_destroy (surface->clip_on_stencil_buffer); + + return _cairo_gl_context_release (ctx, status); +} + +static cairo_image_surface_t * +_cairo_gl_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_image_surface_t *image; + cairo_gl_context_t *ctx; + GLenum format, type; + pixman_format_code_t pixman_format; + unsigned int cpp; + cairo_bool_t flipped, mesa_invert; + cairo_status_t status; + int y; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) { + return _cairo_image_surface_create_in_error (status); + } + + /* Want to use a switch statement here but the compiler gets whiny. */ + if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) { + format = GL_BGRA; + pixman_format = PIXMAN_a8r8g8b8; + type = GL_UNSIGNED_INT_8_8_8_8_REV; + cpp = 4; + } else if (surface->base.content == CAIRO_CONTENT_COLOR) { + format = GL_BGRA; + pixman_format = PIXMAN_x8r8g8b8; + type = GL_UNSIGNED_INT_8_8_8_8_REV; + cpp = 4; + } else if (surface->base.content == CAIRO_CONTENT_ALPHA) { + format = GL_ALPHA; + pixman_format = PIXMAN_a8; + type = GL_UNSIGNED_BYTE; + cpp = 1; + } else { + ASSERT_NOT_REACHED; + return NULL; + } + + if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES3 || + _cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES2) { + /* If only RGBA is supported, we must download data in a compatible + * format. This means that pixman will convert the data on the CPU when + * interacting with other image surfaces. For ALPHA, GLES2 does not + * support GL_PACK_ROW_LENGTH anyway, and this makes sure that the + * pixman image that is created has row_stride = row_width * bpp. */ + if (surface->base.content == CAIRO_CONTENT_ALPHA || !ctx->can_read_bgra) { + cairo_bool_t little_endian = _cairo_is_little_endian (); + format = GL_RGBA; + + if (surface->base.content == CAIRO_CONTENT_COLOR) { + pixman_format = little_endian ? + PIXMAN_x8b8g8r8 : PIXMAN_r8g8b8x8; + } else { + pixman_format = little_endian ? + PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8; + } + } + + /* GLES2 only supports GL_UNSIGNED_BYTE. */ + type = GL_UNSIGNED_BYTE; + cpp = 4; + } + + image = (cairo_image_surface_t*) + _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + extents->width, + extents->height, + -1); + if (unlikely (image->base.status)) { + status = _cairo_gl_context_release (ctx, status); + return image; + } + + cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y); + + /* If the original surface has not been modified or + * is clear, we can avoid downloading data. */ + if (surface->base.is_clear || surface->base.serial == 0) { + status = _cairo_gl_context_release (ctx, status); + return image; + } + + /* This is inefficient, as we'd rather just read the thing without making + * it the destination. But then, this is the fallback path, so let's not + * fall back instead. + */ + _cairo_gl_composite_flush (ctx); + + if (ctx->gl_flavor != CAIRO_GL_FLAVOR_ES3) { + _cairo_gl_context_set_destination (ctx, surface, FALSE); + } else { + if (surface->content_in_texture) { + _cairo_gl_ensure_framebuffer (ctx, surface); + ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb); + } else { + status = _cairo_gl_surface_resolve_multisampling (surface); + if (unlikely (status)) { + status = _cairo_gl_context_release (ctx, status); + cairo_surface_destroy (&image->base); + return _cairo_image_surface_create_in_error (status); + } + } + } + + flipped = ! _cairo_gl_surface_is_texture (surface); + mesa_invert = flipped && ctx->has_mesa_pack_invert; + + glPixelStorei (GL_PACK_ALIGNMENT, 4); + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP || + ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) + glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp); + if (mesa_invert) + glPixelStorei (GL_PACK_INVERT_MESA, 1); + + y = extents->y; + if (flipped) + y = surface->height - extents->y - extents->height; + + glReadPixels (extents->x, y, + extents->width, extents->height, + format, type, image->data); + if (mesa_invert) + glPixelStorei (GL_PACK_INVERT_MESA, 0); + + status = _cairo_gl_context_release (ctx, status); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return _cairo_image_surface_create_in_error (status); + } + + /* We must invert the image manually if we lack GL_MESA_pack_invert */ + if (flipped && ! mesa_invert) { + uint8_t stack[1024], *row = stack; + uint8_t *top = image->data; + uint8_t *bot = image->data + (image->height-1)*image->stride; + + if (image->stride > (int)sizeof(stack)) { + row = _cairo_malloc (image->stride); + if (unlikely (row == NULL)) { + cairo_surface_destroy (&image->base); + return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + while (top < bot) { + memcpy (row, top, image->stride); + memcpy (top, bot, image->stride); + memcpy (bot, row, image->stride); + top += image->stride; + bot -= image->stride; + } + + if (row != stack) + free(row); + } + + image->base.is_clear = FALSE; + return image; +} + +static cairo_surface_t * +_cairo_gl_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_gl_surface_t *surface = abstract_surface; + + if (extents) { + extents->x = extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + } + + return &surface->base; +} + +static cairo_status_t +_cairo_gl_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_rectangle_int_t extents; + + *image_extra = NULL; + + extents.x = extents.y = 0; + extents.width = surface->width; + extents.height = surface->height; + + *image_out = (cairo_image_surface_t *) + _cairo_gl_surface_map_to_image (surface, &extents); + return (*image_out)->base.status; +} + +static void +_cairo_gl_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_int_status_t +_cairo_gl_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_int_status_t status; + + status = _cairo_gl_surface_draw_image (abstract_surface, image, + 0, 0, + image->width, image->height, + image->base.device_transform_inverse.x0, + image->base.device_transform_inverse.y0, + TRUE); + + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return status; +} + +static cairo_bool_t +_cairo_gl_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_gl_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static cairo_status_t +_cairo_gl_surface_flush (void *abstract_surface, unsigned flags) +{ + cairo_gl_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_gl_context_t *ctx; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return status; + + if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE && + ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) || + (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE && + ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) || + (ctx->current_target == surface)) + _cairo_gl_composite_flush (ctx); + + status = _cairo_gl_surface_resolve_multisampling (surface); + + return _cairo_gl_context_release (ctx, status); +} + +cairo_int_status_t +_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface) +{ + cairo_gl_context_t *ctx; + cairo_int_status_t status; + + if (! surface->msaa_active) + return CAIRO_INT_STATUS_SUCCESS; + + if (surface->base.device == NULL) + return CAIRO_INT_STATUS_SUCCESS; + + /* GLES surfaces do not need explicit resolution. */ + if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES2) + return CAIRO_INT_STATUS_SUCCESS; + else if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES3 && + surface->content_in_texture) + return CAIRO_INT_STATUS_SUCCESS; + + if (! _cairo_gl_surface_is_texture (surface)) + return CAIRO_INT_STATUS_SUCCESS; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) + return status; + +#if CAIRO_HAS_GLESV3_SURFACE + _cairo_gl_composite_flush (ctx); + ctx->current_target = NULL; + _cairo_gl_context_bind_framebuffer (ctx, surface, FALSE); + if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES3) + surface->content_in_texture = TRUE; + +#elif CAIRO_HAS_GL_SURFACE + ctx->current_target = surface; + _cairo_gl_context_bind_framebuffer (ctx, surface, FALSE); + +#else + ctx->current_target = surface; + +#endif + + status = _cairo_gl_context_release (ctx, status); + return status; +} + +static const cairo_compositor_t * +get_compositor (cairo_gl_surface_t *surface) +{ + cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device; + return ctx->compositor; +} + +static cairo_int_status_t +_cairo_gl_surface_paint (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + /* simplify the common case of clearing the surface */ + if (clip == NULL) { + if (op == CAIRO_OPERATOR_CLEAR) + return _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT); + else if (source->type == CAIRO_PATTERN_TYPE_SOLID && + (op == CAIRO_OPERATOR_SOURCE || + (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) { + return _cairo_gl_surface_clear (surface, + &((cairo_solid_pattern_t *) source)->color); + } + } + + return _cairo_compositor_paint (get_compositor (surface), surface, + op, source, clip); +} + +static cairo_int_status_t +_cairo_gl_surface_mask (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + return _cairo_compositor_mask (get_compositor (surface), surface, + op, source, mask, clip); +} + +static cairo_int_status_t +_cairo_gl_surface_stroke (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + return _cairo_compositor_stroke (get_compositor (surface), surface, + op, source, path, style, + ctm, ctm_inverse, tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_gl_surface_fill (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t*path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + return _cairo_compositor_fill (get_compositor (surface), surface, + op, source, path, + fill_rule, tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_gl_surface_glyphs (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *font, + const cairo_clip_t *clip) +{ + return _cairo_compositor_glyphs (get_compositor (surface), surface, + op, source, glyphs, num_glyphs, font, + clip); +} + +static const cairo_surface_backend_t _cairo_gl_surface_backend = { + CAIRO_SURFACE_TYPE_GL, + _cairo_gl_surface_finish, + _cairo_default_context_create, + + _cairo_gl_surface_create_similar, + NULL, /* similar image */ + _cairo_gl_surface_map_to_image, + _cairo_gl_surface_unmap_image, + + _cairo_gl_surface_source, + _cairo_gl_surface_acquire_source_image, + _cairo_gl_surface_release_source_image, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_gl_surface_get_extents, + _cairo_image_surface_get_font_options, + + _cairo_gl_surface_flush, + NULL, /* mark_dirty_rectangle */ + + _cairo_gl_surface_paint, + _cairo_gl_surface_mask, + _cairo_gl_surface_stroke, + _cairo_gl_surface_fill, + NULL, /* fill/stroke */ + _cairo_gl_surface_glyphs, +}; diff --git a/gfx/cairo/cairo/src/cairo-gl-traps-compositor.c b/gfx/cairo/cairo/src/cairo-gl-traps-compositor.c new file mode 100644 index 0000000000..7938c5b20a --- /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 new file mode 100644 index 0000000000..7cd869c76b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gl.h @@ -0,0 +1,155 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Eric Anholt + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Eric Anholt. + */ + +/* + * cairo-gl.h: + * + * The cairo-gl backend provides an implementation of possibly + * hardware-accelerated cairo rendering by targeting the OpenGL API. + * The goal of the cairo-gl backend is to provide better performance + * with equal functionality to cairo-image where possible. It does + * not directly provide for applying additional OpenGL effects to + * cairo surfaces. + * + * Cairo-gl allows interoperability with other GL rendering through GL + * context sharing. Cairo-gl surfaces are created in reference to a + * #cairo_device_t, which represents a GL context created by the user. + * When that GL context is created with its sharePtr set to another + * context (or vice versa), its objects (textures backing cairo-gl + * surfaces) can be accessed in the other OpenGL context. This allows + * cairo-gl to maintain its drawing state in one context while the + * user's 3D rendering occurs in the user's other context. + * + * However, as only one context can be current to a thread at a time, + * cairo-gl may make its context current to the thread on any cairo + * call which interacts with a cairo-gl surface or the cairo-gl + * device. As a result, the user must make their own context current + * between any cairo calls and their own OpenGL rendering. + **/ + +#ifndef CAIRO_GL_H +#define CAIRO_GL_H + +#include "cairo.h" + +#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE || CAIRO_HAS_GLESV3_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_gl_surface_create (cairo_device_t *device, + cairo_content_t content, + int width, int height); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, + cairo_content_t content, + unsigned int tex, + int width, int height); +cairo_public void +cairo_gl_surface_set_size (cairo_surface_t *surface, int width, int height); + +cairo_public int +cairo_gl_surface_get_width (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_gl_surface_get_height (cairo_surface_t *abstract_surface); + +cairo_public void +cairo_gl_surface_swapbuffers (cairo_surface_t *surface); + +cairo_public void +cairo_gl_device_set_thread_aware (cairo_device_t *device, + cairo_bool_t thread_aware); + +#if CAIRO_HAS_GLX_FUNCTIONS +#include + +cairo_public cairo_device_t * +cairo_glx_device_create (Display *dpy, GLXContext gl_ctx); + +cairo_public Display * +cairo_glx_device_get_display (cairo_device_t *device); + +cairo_public GLXContext +cairo_glx_device_get_context (cairo_device_t *device); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_window (cairo_device_t *device, + Window win, + int width, int height); +#endif + +#if CAIRO_HAS_WGL_FUNCTIONS +#include + +cairo_public cairo_device_t * +cairo_wgl_device_create (HGLRC rc); + +cairo_public HGLRC +cairo_wgl_device_get_context (cairo_device_t *device); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_dc (cairo_device_t *device, + HDC dc, + int width, + int height); +#endif + +#if CAIRO_HAS_EGL_FUNCTIONS +#include + +cairo_public cairo_device_t * +cairo_egl_device_create (EGLDisplay dpy, EGLContext egl); + +cairo_public cairo_surface_t * +cairo_gl_surface_create_for_egl (cairo_device_t *device, + EGLSurface egl, + int width, + int height); + +cairo_public EGLDisplay +cairo_egl_device_get_display (cairo_device_t *device); + +cairo_public EGLSurface +cairo_egl_device_get_context (cairo_device_t *device); + +#endif + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_GL_SURFACE */ +# error Cairo was not compiled with support for the GL backend +#endif /* CAIRO_HAS_GL_SURFACE */ + +#endif /* CAIRO_GL_H */ diff --git a/gfx/cairo/cairo/src/cairo-glx-context.c b/gfx/cairo/cairo/src/cairo-glx-context.c new file mode 100644 index 0000000000..66f5a0d1b4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-glx-context.c @@ -0,0 +1,324 @@ +/* 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" + +#include + +/* XXX needs hooking into XCloseDisplay() */ + +typedef struct _cairo_glx_context { + cairo_gl_context_t base; + + Display *display; + Window dummy_window; + GLXContext context; + + GLXDrawable previous_drawable; + GLXContext previous_context; + + cairo_bool_t has_multithread_makecurrent; +} cairo_glx_context_t; + +typedef struct _cairo_glx_surface { + cairo_gl_surface_t base; + + Window win; +} cairo_glx_surface_t; + +static cairo_bool_t +_context_acquisition_changed_glx_state (cairo_glx_context_t *ctx, + GLXDrawable current_drawable) +{ + return ctx->previous_drawable != current_drawable || + ctx->previous_context != ctx->context; +} + +static GLXDrawable +_glx_get_current_drawable (cairo_glx_context_t *ctx) +{ + if (ctx->base.current_target == NULL || + _cairo_gl_surface_is_texture (ctx->base.current_target)) { + return ctx->dummy_window; + } + + return ((cairo_glx_surface_t *) ctx->base.current_target)->win; +} + +static void +_glx_query_current_state (cairo_glx_context_t * ctx) +{ + ctx->previous_drawable = glXGetCurrentDrawable (); + ctx->previous_context = glXGetCurrentContext (); + + /* If any of the values were none, assume they are all none. Not all + drivers seem well behaved when it comes to using these values across + multiple threads. */ + if (ctx->previous_drawable == None || + ctx->previous_context == None) { + ctx->previous_drawable = None; + ctx->previous_context = None; + } +} + +static void +_glx_acquire (void *abstract_ctx) +{ + cairo_glx_context_t *ctx = abstract_ctx; + GLXDrawable current_drawable = _glx_get_current_drawable (ctx); + + _glx_query_current_state (ctx); + if (!_context_acquisition_changed_glx_state (ctx, current_drawable)) + return; + + glXMakeCurrent (ctx->display, current_drawable, ctx->context); +} + +static void +_glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface) +{ + cairo_glx_context_t *ctx = abstract_ctx; + cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; + + /* Set the window as the target of our context. */ + glXMakeCurrent (ctx->display, surface->win, ctx->context); +} + +static void +_glx_release (void *abstract_ctx) +{ + cairo_glx_context_t *ctx = abstract_ctx; + + if (ctx->has_multithread_makecurrent || !ctx->base.thread_aware || + !_context_acquisition_changed_glx_state (ctx, + _glx_get_current_drawable (ctx))) { + return; + } + + glXMakeCurrent (ctx->display, None, None); +} + +static void +_glx_swap_buffers (void *abstract_ctx, + cairo_gl_surface_t *abstract_surface) +{ + cairo_glx_context_t *ctx = abstract_ctx; + cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface; + + glXSwapBuffers (ctx->display, surface->win); +} + +static void +_glx_destroy (void *abstract_ctx) +{ + cairo_glx_context_t *ctx = abstract_ctx; + + if (ctx->dummy_window != None) + XDestroyWindow (ctx->display, ctx->dummy_window); + + glXMakeCurrent (ctx->display, None, None); +} + +static cairo_status_t +_glx_dummy_window (Display *dpy, GLXContext gl_ctx, Window *dummy) +{ + int attr[3] = { GLX_FBCONFIG_ID, 0, None }; + GLXFBConfig *config; + XVisualInfo *vi; + Colormap cmap; + XSetWindowAttributes swa; + Window win = None; + int cnt; + + /* Create a dummy window created for the target GLX context that we can + * use to query the available GL/GLX extensions. + */ + glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]); + + cnt = 0; + config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt); + if (unlikely (cnt == 0)) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + + vi = glXGetVisualFromFBConfig (dpy, config[0]); + XFree (config); + + if (unlikely (vi == NULL)) + return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + + cmap = XCreateColormap (dpy, + RootWindow (dpy, vi->screen), + vi->visual, + AllocNone); + swa.colormap = cmap; + swa.border_pixel = 0; + win = XCreateWindow (dpy, RootWindow (dpy, vi->screen), + -1, -1, 1, 1, 0, + vi->depth, + InputOutput, + vi->visual, + CWBorderPixel | CWColormap, &swa); + XFreeColormap (dpy, cmap); + XFree (vi); + + XFlush (dpy); + if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) { + XDestroyWindow (dpy, win); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + *dummy = win; + return CAIRO_STATUS_SUCCESS; +} + +cairo_device_t * +cairo_glx_device_create (Display *dpy, GLXContext gl_ctx) +{ + cairo_glx_context_t *ctx; + cairo_status_t status; + Window dummy = None; + const char *glx_extensions; + + ctx = calloc (1, sizeof (cairo_glx_context_t)); + if (unlikely (ctx == NULL)) + return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY); + + /* glx_dummy_window will call glXMakeCurrent, so we need to + * query the current state of the context now. */ + _glx_query_current_state (ctx); + + status = _glx_dummy_window (dpy, gl_ctx, &dummy); + if (unlikely (status)) { + free (ctx); + return _cairo_gl_context_create_in_error (status); + } + + ctx->display = dpy; + ctx->dummy_window = dummy; + ctx->context = gl_ctx; + + ctx->base.acquire = _glx_acquire; + ctx->base.release = _glx_release; + ctx->base.make_current = _glx_make_current; + ctx->base.swap_buffers = _glx_swap_buffers; + ctx->base.destroy = _glx_destroy; + + status = _cairo_gl_dispatch_init (&ctx->base.dispatch, + (cairo_gl_get_proc_addr_func_t) glXGetProcAddress); + if (unlikely (status)) { + free (ctx); + return _cairo_gl_context_create_in_error (status); + } + + status = _cairo_gl_context_init (&ctx->base); + if (unlikely (status)) { + free (ctx); + return _cairo_gl_context_create_in_error (status); + } + + glx_extensions = glXQueryExtensionsString (dpy, DefaultScreen (dpy)); + if (strstr(glx_extensions, "GLX_MESA_multithread_makecurrent")) { + ctx->has_multithread_makecurrent = TRUE; + } + + ctx->base.release (ctx); + + return &ctx->base.base; +} + +Display * +cairo_glx_device_get_display (cairo_device_t *device) +{ + cairo_glx_context_t *ctx; + + if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { + _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + return NULL; + } + + ctx = (cairo_glx_context_t *) device; + + return ctx->display; +} + +GLXContext +cairo_glx_device_get_context (cairo_device_t *device) +{ + cairo_glx_context_t *ctx; + + if (device->backend->type != CAIRO_DEVICE_TYPE_GL) { + _cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + return NULL; + } + + ctx = (cairo_glx_context_t *) device; + + return ctx->context; +} + +cairo_surface_t * +cairo_gl_surface_create_for_window (cairo_device_t *device, + Window win, + int width, + int height) +{ + cairo_glx_surface_t *surface; + + if (unlikely (device->status)) + return _cairo_surface_create_in_error (device->status); + + if (device->backend->type != CAIRO_DEVICE_TYPE_GL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + if (width <= 0 || height <= 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + surface = calloc (1, sizeof (cairo_glx_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_gl_surface_init (device, &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, width, height); + surface->win = win; + + return &surface->base.base; +} diff --git a/gfx/cairo/cairo/src/cairo-gstate-private.h b/gfx/cairo/cairo/src/cairo-gstate-private.h new file mode 100644 index 0000000000..198c669985 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gstate-private.h @@ -0,0 +1,396 @@ +/* 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_GSTATE_PRIVATE_H +#define CAIRO_GSTATE_PRIVATE_H + +#include "cairo-clip-private.h" + +struct _cairo_gstate { + cairo_operator_t op; + + double opacity; + double tolerance; + cairo_antialias_t antialias; + + cairo_stroke_style_t stroke_style; + + cairo_fill_rule_t fill_rule; + + cairo_font_face_t *font_face; + cairo_scaled_font_t *scaled_font; /* Specific to the current CTM */ + cairo_scaled_font_t *previous_scaled_font; /* holdover */ + cairo_matrix_t font_matrix; + cairo_font_options_t font_options; + + 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 */ + cairo_surface_t *original_target; /* The original target the initial gstate was created with */ + + /* the user is allowed to update the device after we have cached the matrices... */ + cairo_observer_t device_transform_observer; + + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + cairo_matrix_t source_ctm_inverse; /* At the time ->source was set */ + cairo_bool_t is_identity; + + cairo_pattern_t *source; + + struct _cairo_gstate *next; +}; + +/* cairo-gstate.c */ +cairo_private cairo_status_t +_cairo_gstate_init (cairo_gstate_t *gstate, + cairo_surface_t *target); + +cairo_private void +_cairo_gstate_fini (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist); + +cairo_private cairo_status_t +_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist); + +cairo_private cairo_bool_t +_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); + +cairo_private cairo_surface_t * +_cairo_gstate_get_target (cairo_gstate_t *gstate); + +cairo_private cairo_surface_t * +_cairo_gstate_get_original_target (cairo_gstate_t *gstate); + +cairo_private cairo_clip_t * +_cairo_gstate_get_clip (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_source (cairo_gstate_t *gstate, cairo_pattern_t *source); + +cairo_private cairo_pattern_t * +_cairo_gstate_get_source (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_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); + +cairo_private double +_cairo_gstate_get_tolerance (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule); + +cairo_private cairo_fill_rule_t +_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width); + +cairo_private double +_cairo_gstate_get_line_width (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap); + +cairo_private cairo_line_cap_t +_cairo_gstate_get_line_cap (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join); + +cairo_private cairo_line_join_t +_cairo_gstate_get_line_join (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset); + +cairo_private void +_cairo_gstate_get_dash (cairo_gstate_t *gstate, double *dash, int *num_dashes, double *offset); + +cairo_private cairo_status_t +_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit); + +cairo_private double +_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate); + +cairo_private void +_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix); + +cairo_private cairo_status_t +_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty); + +cairo_private cairo_status_t +_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy); + +cairo_private cairo_status_t +_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle); + +cairo_private cairo_status_t +_cairo_gstate_transform (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); + +cairo_private cairo_status_t +_cairo_gstate_set_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); + +cairo_private void +_cairo_gstate_identity_matrix (cairo_gstate_t *gstate); + +cairo_private void +_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y); + +cairo_private void +_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, double *dx, double *dy); + +cairo_private void +_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y); + +cairo_private void +_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy); + +cairo_private void +_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y); + +static inline void +_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) +{ + if (! gstate->is_identity) + _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); + +static inline void +_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y) +{ + if (! gstate->is_identity) + _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, + double *x2, double *y2, + cairo_bool_t *is_tight); + +cairo_private void +_cairo_gstate_path_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private cairo_status_t +_cairo_gstate_paint (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_mask (cairo_gstate_t *gstate, + cairo_pattern_t *mask); + +cairo_private cairo_status_t +_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_gstate_copy_page (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_show_page (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private cairo_status_t +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private cairo_status_t +_cairo_gstate_in_stroke (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y, + cairo_bool_t *inside_ret); + +cairo_private cairo_bool_t +_cairo_gstate_in_fill (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y); + +cairo_private cairo_bool_t +_cairo_gstate_in_clip (cairo_gstate_t *gstate, + double x, + double y); + +cairo_private cairo_status_t +_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_gstate_reset_clip (cairo_gstate_t *gstate); + +cairo_private cairo_bool_t +_cairo_gstate_clip_extents (cairo_gstate_t *gstate, + double *x1, + double *y1, + double *x2, + double *y2); + +cairo_private cairo_rectangle_list_t* +_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate); + +cairo_private cairo_status_t +_cairo_gstate_show_surface (cairo_gstate_t *gstate, + cairo_surface_t *surface, + double x, + double y, + double width, + double height); + +cairo_private cairo_status_t +_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, + double size); + +cairo_private void +_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, + cairo_matrix_t *matrix); + +cairo_private cairo_status_t +_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix); + +cairo_private void +_cairo_gstate_get_font_options (cairo_gstate_t *gstate, + cairo_font_options_t *options); + +cairo_private void +_cairo_gstate_set_font_options (cairo_gstate_t *gstate, + const cairo_font_options_t *options); + +cairo_private cairo_status_t +_cairo_gstate_get_font_face (cairo_gstate_t *gstate, + cairo_font_face_t **font_face); + +cairo_private cairo_status_t +_cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, + cairo_scaled_font_t **scaled_font); + +cairo_private cairo_status_t +_cairo_gstate_get_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents); + +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_glyph_extents (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); + +cairo_private cairo_status_t +_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_glyph_text_info_t *info); + +cairo_private cairo_status_t +_cairo_gstate_glyph_path (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_gstate_set_antialias (cairo_gstate_t *gstate, + cairo_antialias_t antialias); + +cairo_private cairo_antialias_t +_cairo_gstate_get_antialias (cairo_gstate_t *gstate); + +#endif /* CAIRO_GSTATE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-gstate.c b/gfx/cairo/cairo/src/cairo-gstate.c new file mode 100644 index 0000000000..4b31dca3b6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-gstate.c @@ -0,0 +1,2361 @@ +/* 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-clip-inline.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-list-inline.h" +#include "cairo-gstate-private.h" +#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); + +static cairo_status_t +_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate); + +static cairo_status_t +_cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate); + +static void +_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate); + +static void +_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, + 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_t *transformed_glyphs, + int *num_transformed_glyphs, + cairo_text_cluster_t *transformed_clusters); + +static void +_cairo_gstate_update_device_transform (cairo_observer_t *observer, + void *arg) +{ + cairo_gstate_t *gstate = cairo_container_of (observer, + cairo_gstate_t, + device_transform_observer); + + gstate->is_identity = (_cairo_matrix_is_identity (&gstate->ctm) && + _cairo_matrix_is_identity (&gstate->target->device_transform)); +} + +cairo_status_t +_cairo_gstate_init (cairo_gstate_t *gstate, + cairo_surface_t *target) +{ + 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; + + _cairo_stroke_style_init (&gstate->stroke_style); + + gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; + + gstate->font_face = NULL; + gstate->scaled_font = NULL; + gstate->previous_scaled_font = NULL; + + cairo_matrix_init_scale (&gstate->font_matrix, + CAIRO_GSTATE_DEFAULT_FONT_SIZE, + CAIRO_GSTATE_DEFAULT_FONT_SIZE); + + _cairo_font_options_init_default (&gstate->font_options); + + gstate->clip = NULL; + + gstate->target = cairo_surface_reference (target); + gstate->parent_target = NULL; + gstate->original_target = cairo_surface_reference (target); + + gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform; + cairo_list_add (&gstate->device_transform_observer.link, + &gstate->target->device_transform_observers); + + gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform); + cairo_matrix_init_identity (&gstate->ctm); + gstate->ctm_inverse = gstate->ctm; + gstate->source_ctm_inverse = gstate->ctm; + + gstate->source = (cairo_pattern_t *) &_cairo_pattern_black.base; + + /* Now that the gstate is fully initialized and ready for the eventual + * _cairo_gstate_fini(), we can check for errors (and not worry about + * the resource deallocation). */ + return target->status; +} + +/** + * _cairo_gstate_init_copy: + * + * Initialize @gstate by performing a deep copy of state fields from + * @other. Note that gstate->next is not copied but is set to %NULL by + * this function. + **/ +static cairo_status_t +_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) +{ + cairo_status_t status; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); + + gstate->op = other->op; + gstate->opacity = other->opacity; + + gstate->tolerance = other->tolerance; + gstate->antialias = other->antialias; + + status = _cairo_stroke_style_init_copy (&gstate->stroke_style, + &other->stroke_style); + if (unlikely (status)) + return status; + + gstate->fill_rule = other->fill_rule; + + gstate->font_face = cairo_font_face_reference (other->font_face); + gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font); + gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font); + + gstate->font_matrix = other->font_matrix; + + _cairo_font_options_init_copy (&gstate->font_options , &other->font_options); + + 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 */ + gstate->parent_target = NULL; + gstate->original_target = cairo_surface_reference (other->original_target); + + gstate->device_transform_observer.callback = _cairo_gstate_update_device_transform; + cairo_list_add (&gstate->device_transform_observer.link, + &gstate->target->device_transform_observers); + + gstate->is_identity = other->is_identity; + gstate->ctm = other->ctm; + gstate->ctm_inverse = other->ctm_inverse; + gstate->source_ctm_inverse = other->source_ctm_inverse; + + gstate->source = cairo_pattern_reference (other->source); + + gstate->next = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gstate_fini (cairo_gstate_t *gstate) +{ + _cairo_stroke_style_fini (&gstate->stroke_style); + + cairo_font_face_destroy (gstate->font_face); + gstate->font_face = NULL; + + cairo_scaled_font_destroy (gstate->previous_scaled_font); + gstate->previous_scaled_font = NULL; + + cairo_scaled_font_destroy (gstate->scaled_font); + gstate->scaled_font = NULL; + + _cairo_clip_destroy (gstate->clip); + + cairo_list_del (&gstate->device_transform_observer.link); + + cairo_surface_destroy (gstate->target); + gstate->target = NULL; + + cairo_surface_destroy (gstate->parent_target); + gstate->parent_target = NULL; + + cairo_surface_destroy (gstate->original_target); + gstate->original_target = NULL; + + cairo_pattern_destroy (gstate->source); + gstate->source = NULL; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t))); +} + +/** + * _cairo_gstate_save: + * @gstate: input/output gstate pointer + * + * Makes a copy of the current state of @gstate and saves it + * to @gstate->next, then put the address of the newly allcated + * copy into @gstate. _cairo_gstate_restore() reverses this. + **/ +cairo_status_t +_cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist) +{ + cairo_gstate_t *top; + cairo_status_t status; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + top = *freelist; + if (top == NULL) { + top = _cairo_malloc (sizeof (cairo_gstate_t)); + if (unlikely (top == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + *freelist = top->next; + + status = _cairo_gstate_init_copy (top, *gstate); + if (unlikely (status)) { + top->next = *freelist; + *freelist = top; + return status; + } + + top->next = *gstate; + *gstate = top; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_gstate_restore: + * @gstate: input/output gstate pointer + * + * Reverses the effects of one _cairo_gstate_save() call. + **/ +cairo_status_t +_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist) +{ + cairo_gstate_t *top; + + top = *gstate; + if (top->next == NULL) + return _cairo_error (CAIRO_STATUS_INVALID_RESTORE); + + *gstate = top->next; + + _cairo_gstate_fini (top); + VG (VALGRIND_MAKE_MEM_UNDEFINED (&top->next, sizeof (cairo_gstate_t *))); + top->next = *freelist; + *freelist = top; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_gstate_redirect_target: + * @gstate: a #cairo_gstate_t + * @child: the new child target + * + * 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(). + **/ +cairo_status_t +_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child) +{ + /* 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); + + /* Set up our new parent_target based on our current target; + * gstate->parent_target will take the ref that is held by gstate->target + */ + gstate->parent_target = gstate->target; + + /* Now set up our new target; we overwrite gstate->target directly, + * since its ref is now owned by gstate->parent_target */ + gstate->target = cairo_surface_reference (child); + gstate->is_identity &= _cairo_matrix_is_identity (&child->device_transform); + cairo_list_move (&gstate->device_transform_observer.link, + &gstate->target->device_transform_observers); + + /* The clip is in surface backend coordinates for the previous target; + * translate it into the child's backend coordinates. */ + _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_group: + * @gstate: a #cairo_gstate_t + * + * Check if _cairo_gstate_redirect_target has been called on the head + * of the stack. + * + * 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_group (cairo_gstate_t *gstate) +{ + return gstate->parent_target != NULL; +} + +/** + * _cairo_gstate_get_target: + * @gstate: a #cairo_gstate_t + * + * Return the current drawing target; if drawing is not redirected, + * this will be the same as _cairo_gstate_get_original_target(). + * + * Return value: the current target surface + **/ +cairo_surface_t * +_cairo_gstate_get_target (cairo_gstate_t *gstate) +{ + return gstate->target; +} + +/** + * _cairo_gstate_get_original_target: + * @gstate: a #cairo_gstate_t + * + * Return the original target with which @gstate was created. This + * function always returns the original target independent of any + * child target that may have been set with + * _cairo_gstate_redirect_target. + * + * Return value: the original target surface + **/ +cairo_surface_t * +_cairo_gstate_get_original_target (cairo_gstate_t *gstate) +{ + return gstate->original_target; +} + +/** + * _cairo_gstate_get_clip: + * @gstate: a #cairo_gstate_t + * + * 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; +} + +cairo_status_t +_cairo_gstate_set_source (cairo_gstate_t *gstate, + cairo_pattern_t *source) +{ + if (source->status) + return source->status; + + source = cairo_pattern_reference (source); + cairo_pattern_destroy (gstate->source); + gstate->source = source; + gstate->source_ctm_inverse = gstate->ctm_inverse; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_pattern_t * +_cairo_gstate_get_source (cairo_gstate_t *gstate) +{ + if (gstate->source == &_cairo_pattern_black.base) { + /* do not expose the static object to the user */ + gstate->source = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); + } + + return gstate->source; +} + +cairo_status_t +_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t op) +{ + gstate->op = op; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_operator_t +_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) +{ + gstate->tolerance = tolerance; + + return CAIRO_STATUS_SUCCESS; +} + +double +_cairo_gstate_get_tolerance (cairo_gstate_t *gstate) +{ + return gstate->tolerance; +} + +cairo_status_t +_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule) +{ + gstate->fill_rule = fill_rule; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_fill_rule_t +_cairo_gstate_get_fill_rule (cairo_gstate_t *gstate) +{ + return gstate->fill_rule; +} + +cairo_status_t +_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width) +{ + gstate->stroke_style.line_width = width; + + return CAIRO_STATUS_SUCCESS; +} + +double +_cairo_gstate_get_line_width (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.line_width; +} + +cairo_status_t +_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap) +{ + gstate->stroke_style.line_cap = line_cap; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_line_cap_t +_cairo_gstate_get_line_cap (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.line_cap; +} + +cairo_status_t +_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join) +{ + gstate->stroke_style.line_join = line_join; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_line_join_t +_cairo_gstate_get_line_join (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.line_join; +} + +cairo_status_t +_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset) +{ + double dash_total, on_total, off_total; + int i, j; + + free (gstate->stroke_style.dash); + + gstate->stroke_style.num_dashes = num_dashes; + + if (gstate->stroke_style.num_dashes == 0) { + gstate->stroke_style.dash = NULL; + gstate->stroke_style.dash_offset = 0.0; + return CAIRO_STATUS_SUCCESS; + } + + gstate->stroke_style.dash = _cairo_malloc_ab (gstate->stroke_style.num_dashes, sizeof (double)); + if (unlikely (gstate->stroke_style.dash == NULL)) { + gstate->stroke_style.num_dashes = 0; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + 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); + + 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) + return _cairo_error (CAIRO_STATUS_INVALID_DASH); + + /* An odd dash value indicate symmetric repeating, so the total + * is twice as long. */ + 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 + * positive and less than twice the pattern length. */ + offset = fmod (offset, dash_total); + if (offset < 0.0) + offset += dash_total; + if (offset <= 0.0) /* Take care of -0 */ + offset = 0.0; + gstate->stroke_style.dash_offset = offset; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gstate_get_dash (cairo_gstate_t *gstate, + double *dashes, + int *num_dashes, + double *offset) +{ + if (dashes) { + memcpy (dashes, + gstate->stroke_style.dash, + sizeof (double) * gstate->stroke_style.num_dashes); + } + + if (num_dashes) + *num_dashes = gstate->stroke_style.num_dashes; + + if (offset) + *offset = gstate->stroke_style.dash_offset; +} + +cairo_status_t +_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit) +{ + gstate->stroke_style.miter_limit = limit; + + return CAIRO_STATUS_SUCCESS; +} + +double +_cairo_gstate_get_miter_limit (cairo_gstate_t *gstate) +{ + return gstate->stroke_style.miter_limit; +} + +void +_cairo_gstate_get_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) +{ + *matrix = gstate->ctm; +} + +cairo_status_t +_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) +{ + cairo_matrix_t tmp; + + if (! ISFINITE (tx) || ! ISFINITE (ty)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_translate (&tmp, tx, ty); + cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); + gstate->is_identity = FALSE; + + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&gstate->ctm)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_init_translate (&tmp, -tx, -ty); + cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) +{ + cairo_matrix_t tmp; + + if (sx * sy == 0.) /* either sx or sy is 0, or det == 0 due to underflow */ + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + if (! ISFINITE (sx) || ! ISFINITE (sy)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_scale (&tmp, sx, sy); + cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); + gstate->is_identity = FALSE; + + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&gstate->ctm)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_init_scale (&tmp, 1/sx, 1/sy); + cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) +{ + cairo_matrix_t tmp; + + if (angle == 0.) + return CAIRO_STATUS_SUCCESS; + + if (! ISFINITE (angle)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_rotate (&tmp, angle); + cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); + gstate->is_identity = FALSE; + + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&gstate->ctm)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + cairo_matrix_init_rotate (&tmp, -angle); + cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_transform (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) +{ + cairo_matrix_t tmp; + cairo_status_t status; + + if (! _cairo_matrix_is_invertible (matrix)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + if (_cairo_matrix_is_identity (matrix)) + return CAIRO_STATUS_SUCCESS; + + tmp = *matrix; + status = cairo_matrix_invert (&tmp); + if (unlikely (status)) + return status; + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_multiply (&gstate->ctm, matrix, &gstate->ctm); + cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); + gstate->is_identity = FALSE; + + /* paranoid check against gradual numerical instability */ + if (! _cairo_matrix_is_invertible (&gstate->ctm)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_set_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + + if (memcmp (matrix, &gstate->ctm, sizeof (cairo_matrix_t)) == 0) + return CAIRO_STATUS_SUCCESS; + + if (! _cairo_matrix_is_invertible (matrix)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + if (_cairo_matrix_is_identity (matrix)) { + _cairo_gstate_identity_matrix (gstate); + return CAIRO_STATUS_SUCCESS; + } + + _cairo_gstate_unset_scaled_font (gstate); + + gstate->ctm = *matrix; + gstate->ctm_inverse = *matrix; + status = cairo_matrix_invert (&gstate->ctm_inverse); + assert (status == CAIRO_STATUS_SUCCESS); + gstate->is_identity = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gstate_identity_matrix (cairo_gstate_t *gstate) +{ + if (_cairo_matrix_is_identity (&gstate->ctm)) + return; + + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_identity (&gstate->ctm); + cairo_matrix_init_identity (&gstate->ctm_inverse); + gstate->is_identity = _cairo_matrix_is_identity (&gstate->target->device_transform); +} + +void +_cairo_gstate_user_to_device (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_point (&gstate->ctm, x, y); +} + +void +_cairo_gstate_user_to_device_distance (cairo_gstate_t *gstate, + double *dx, double *dy) +{ + cairo_matrix_transform_distance (&gstate->ctm, dx, dy); +} + +void +_cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); +} + +void +_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, + double *dx, double *dy) +{ + cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy); +} + +void +_do_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y) +{ + cairo_matrix_transform_point (&gstate->ctm, x, 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) +{ + cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, 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, + double *x2, double *y2, + cairo_bool_t *is_tight) +{ + cairo_matrix_t matrix_inverse; + + 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 +cairo_status_t +_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate) +{ + cairo_status_t status; + + _cairo_pen_init (&gstate); + return CAIRO_STATUS_SUCCESS; +} +*/ + +void +_cairo_gstate_path_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_box_t box; + double px1, py1, px2, py2; + + if (_cairo_path_fixed_extents (path, &box)) { + px1 = _cairo_fixed_to_double (box.p1.x); + py1 = _cairo_fixed_to_double (box.p1.y); + px2 = _cairo_fixed_to_double (box.p2.x); + py2 = _cairo_fixed_to_double (box.p2.y); + + _cairo_gstate_backend_to_user_rectangle (gstate, + &px1, &py1, &px2, &py2, + NULL); + } else { + px1 = 0.0; + py1 = 0.0; + px2 = 0.0; + py2 = 0.0; + } + + if (x1) + *x1 = px1; + if (y1) + *y1 = py1; + if (x2) + *x2 = px2; + if (y2) + *y2 = py2; +} + +static void +_cairo_gstate_copy_pattern (cairo_pattern_t *pattern, + const cairo_pattern_t *original) +{ + /* First check if the we can replace the original with a much simpler + * pattern. For example, gradients that are uniform or just have a single + * stop can sometimes be replaced with a solid. + */ + + if (_cairo_pattern_is_clear (original)) { + _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, + CAIRO_COLOR_TRANSPARENT); + return; + } + + if (original->type == CAIRO_PATTERN_TYPE_LINEAR || + original->type == CAIRO_PATTERN_TYPE_RADIAL) + { + cairo_color_t color; + if (_cairo_gradient_pattern_is_solid ((cairo_gradient_pattern_t *) original, + NULL, + &color)) + { + _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, + &color); + return; + } + } + + _cairo_pattern_init_static_copy (pattern, original); +} + +static void +_cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, + cairo_pattern_t *pattern, + const cairo_pattern_t *original, + const cairo_matrix_t *ctm_inverse) +{ + _cairo_gstate_copy_pattern (pattern, original); + + /* apply device_transform first so that it is transformed by ctm_inverse */ + if (original->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern; + cairo_surface_t *surface; + + surface_pattern = (cairo_surface_pattern_t *) original; + surface = surface_pattern->surface; + + if (_cairo_surface_has_device_transform (surface)) + _cairo_pattern_pretransform (pattern, &surface->device_transform); + } + + if (! _cairo_matrix_is_identity (ctm_inverse)) + _cairo_pattern_transform (pattern, ctm_inverse); + + if (_cairo_surface_has_device_transform (gstate->target)) { + _cairo_pattern_transform (pattern, + &gstate->target->device_transform_inverse); + } +} + +static void +_cairo_gstate_copy_transformed_source (cairo_gstate_t *gstate, + cairo_pattern_t *pattern) +{ + _cairo_gstate_copy_transformed_pattern (gstate, pattern, + gstate->source, + &gstate->source_ctm_inverse); +} + +static void +_cairo_gstate_copy_transformed_mask (cairo_gstate_t *gstate, + cairo_pattern_t *pattern, + cairo_pattern_t *mask) +{ + _cairo_gstate_copy_transformed_pattern (gstate, pattern, + mask, + &gstate->ctm_inverse); +} + +static cairo_operator_t +_reduce_op (cairo_gstate_t *gstate) +{ + cairo_operator_t op; + const cairo_pattern_t *pattern; + + op = gstate->op; + if (op != CAIRO_OPERATOR_SOURCE) + return op; + + pattern = gstate->source; + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + if (solid->color.alpha_short <= 0x00ff) { + op = CAIRO_OPERATOR_CLEAR; + } else if ((gstate->target->content & CAIRO_CONTENT_ALPHA) == 0) { + if ((solid->color.red_short | + solid->color.green_short | + solid->color.blue_short) <= 0x00ff) + { + op = CAIRO_OPERATOR_CLEAR; + } + } + } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern; + if (surface->surface->is_clear && + surface->surface->content & CAIRO_CONTENT_ALPHA) + { + op = CAIRO_OPERATOR_CLEAR; + } + } else { + const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; + if (gradient->n_stops == 0) + op = CAIRO_OPERATOR_CLEAR; + } + + 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_status_t status; + cairo_operator_t op; + + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; + + if (gstate->op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_clip_is_all_clipped (gstate->clip)) + return CAIRO_STATUS_SUCCESS; + + op = _reduce_op (gstate); + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = &_cairo_pattern_clear.base; + } else { + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + pattern = &source_pattern.base; + } + + return _cairo_surface_paint (gstate->target, + op, pattern, + gstate->clip); +} + +cairo_status_t +_cairo_gstate_mask (cairo_gstate_t *gstate, + cairo_pattern_t *mask) +{ + cairo_pattern_union_t source_pattern, mask_pattern; + const cairo_pattern_t *source; + cairo_operator_t op; + cairo_status_t status; + + status = _cairo_gstate_get_pattern_status (mask); + if (unlikely (status)) + return 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 (_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); + + if (_cairo_pattern_is_clear (mask) && + _cairo_operator_bounded_by_mask (gstate->op)) + { + return CAIRO_STATUS_SUCCESS; + } + + op = _reduce_op (gstate); + if (op == CAIRO_OPERATOR_CLEAR) { + source = &_cairo_pattern_clear.base; + } else { + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + source = &source_pattern.base; + } + _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); + + if (source->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; + cairo_color_t combined; + + if (mask_pattern.base.has_component_alpha) { +#define M(R, A, B, c) R.c = A.c * B.c + M(combined, solid->color, mask_pattern.solid.color, red); + M(combined, solid->color, mask_pattern.solid.color, green); + M(combined, solid->color, mask_pattern.solid.color, blue); + M(combined, solid->color, mask_pattern.solid.color, alpha); +#undef M + } else { + combined = solid->color; + _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha); + } + + _cairo_pattern_init_solid (&source_pattern.solid, &combined); + + status = _cairo_surface_paint (gstate->target, op, + &source_pattern.base, + gstate->clip); + } + else + { + status = _cairo_surface_mask (gstate->target, op, + source, + &mask_pattern.base, + gstate->clip); + } + + return status; +} + +cairo_status_t +_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_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; + + if (gstate->op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (gstate->stroke_style.line_width <= 0.0) + return CAIRO_STATUS_SUCCESS; + + 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, &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_stroke (gstate->target, + gstate->op, + &source_pattern.base, + path, + &style, + &aggregate_transform, + &aggregate_transform_inverse, + gstate->tolerance, + gstate->antialias, + gstate->clip); +} + +cairo_status_t +_cairo_gstate_in_stroke (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y, + cairo_bool_t *inside_ret) +{ + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_box_t limit; + cairo_traps_t traps; + + if (gstate->stroke_style.line_width <= 0.0) { + *inside_ret = FALSE; + return CAIRO_STATUS_SUCCESS; + } + + _cairo_gstate_user_to_backend (gstate, &x, &y); + + /* Before we perform the expensive stroke analysis, + * check whether the point is within the extents of the path. + */ + _cairo_path_fixed_approximate_stroke_extents (path, + &gstate->stroke_style, + &gstate->ctm, + gstate->target->is_vector, + &extents); + if (x < extents.x || x > extents.x + extents.width || + y < extents.y || y > extents.y + extents.height) + { + *inside_ret = FALSE; + return CAIRO_STATUS_SUCCESS; + } + + limit.p1.x = _cairo_fixed_from_double (x) - 1; + limit.p1.y = _cairo_fixed_from_double (y) - 1; + limit.p2.x = limit.p1.x + 2; + limit.p2.y = limit.p1.y + 2; + + _cairo_traps_init (&traps); + _cairo_traps_limit (&traps, &limit, 1); + + 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; + + *inside_ret = _cairo_traps_contain (&traps, x, y); + +BAIL: + _cairo_traps_fini (&traps); + + return status; +} + +cairo_status_t +_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path) +{ + cairo_status_t 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 (_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; + + status = _cairo_surface_paint (gstate->target, + CAIRO_OPERATOR_CLEAR, + &_cairo_pattern_clear.base, + gstate->clip); + } else { + cairo_pattern_union_t source_pattern; + const cairo_pattern_t *pattern; + cairo_operator_t op; + cairo_rectangle_int_t extents; + cairo_box_t box; + + op = _reduce_op (gstate); + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = &_cairo_pattern_clear.base; + } else { + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + pattern = &source_pattern.base; + } + + /* Toolkits often paint the entire background with a fill */ + if (_cairo_surface_get_extents (gstate->target, &extents) && + _cairo_path_fixed_is_box (path, &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)) + { + status = _cairo_surface_paint (gstate->target, op, pattern, + gstate->clip); + } + else + { + status = _cairo_surface_fill (gstate->target, op, pattern, + path, + gstate->fill_rule, + gstate->tolerance, + gstate->antialias, + gstate->clip); + } + } + + return status; +} + +cairo_bool_t +_cairo_gstate_in_fill (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double x, + double y) +{ + _cairo_gstate_user_to_backend (gstate, &x, &y); + + return _cairo_path_fixed_in_fill (path, + gstate->fill_rule, + gstate->tolerance, + x, y); +} + +cairo_bool_t +_cairo_gstate_in_clip (cairo_gstate_t *gstate, + double x, + double y) +{ + cairo_clip_t *clip = gstate->clip; + int i; + + if (_cairo_clip_is_all_clipped (clip)) + return FALSE; + + if (clip == NULL) + return TRUE; + + _cairo_gstate_user_to_backend (gstate, &x, &y); + + 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; + } + + 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; + } + + 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; +} + +cairo_status_t +_cairo_gstate_copy_page (cairo_gstate_t *gstate) +{ + cairo_surface_copy_page (gstate->target); + return cairo_surface_status (gstate->target); +} + +cairo_status_t +_cairo_gstate_show_page (cairo_gstate_t *gstate) +{ + cairo_surface_show_page (gstate->target); + return cairo_surface_status (gstate->target); +} + +static void +_cairo_gstate_extents_to_user_rectangle (cairo_gstate_t *gstate, + const cairo_box_t *extents, + double *x1, double *y1, + double *x2, double *y2) +{ + 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_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 +_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_int_status_t status; + cairo_box_t extents; + cairo_bool_t empty; + + 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); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_polygon_t polygon; + + _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); + } + + return status; +} + +cairo_status_t +_cairo_gstate_fill_extents (cairo_gstate_t *gstate, + cairo_path_fixed_t *path, + double *x1, double *y1, + double *x2, double *y2) +{ + cairo_status_t status; + cairo_box_t extents; + cairo_bool_t empty; + + 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); + } + + if (! empty) { + _cairo_gstate_extents_to_user_rectangle (gstate, &extents, + x1, y1, x2, y2); + } + + return status; +} + +cairo_status_t +_cairo_gstate_reset_clip (cairo_gstate_t *gstate) +{ + _cairo_clip_destroy (gstate->clip); + gstate->clip = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path) +{ + 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) +{ + cairo_bool_t is_bounded; + + is_bounded = _cairo_surface_get_extents (gstate->target, extents); + + if (gstate->clip) { + _cairo_rectangle_intersect (extents, + _cairo_clip_get_extents (gstate->clip)); + is_bounded = TRUE; + } + + return is_bounded; +} + +cairo_bool_t +_cairo_gstate_clip_extents (cairo_gstate_t *gstate, + double *x1, + double *y1, + double *x2, + double *y2) +{ + cairo_rectangle_int_t extents; + double px1, py1, px2, py2; + + if (! _cairo_gstate_int_clip_extents (gstate, &extents)) + return FALSE; + + px1 = extents.x; + py1 = extents.y; + px2 = extents.x + (int) extents.width; + py2 = extents.y + (int) extents.height; + + _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; + + return TRUE; +} + +cairo_rectangle_list_t* +_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate) +{ + cairo_rectangle_int_t extents; + cairo_rectangle_list_t *list; + cairo_clip_t *clip; + + if (_cairo_surface_get_extents (gstate->target, &extents)) + clip = _cairo_clip_copy_intersect_rectangle (gstate->clip, &extents); + else + clip = gstate->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) +{ + return _cairo_surface_tag (gstate->target, + TRUE, /* begin */ + tag_name, + attributes ? attributes : ""); +} + +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 */ +} + +static void +_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate) +{ + if (gstate->scaled_font == NULL) + return; + + if (gstate->previous_scaled_font != NULL) + cairo_scaled_font_destroy (gstate->previous_scaled_font); + + gstate->previous_scaled_font = gstate->scaled_font; + gstate->scaled_font = NULL; +} + +cairo_status_t +_cairo_gstate_set_font_size (cairo_gstate_t *gstate, + double size) +{ + _cairo_gstate_unset_scaled_font (gstate); + + cairo_matrix_init_scale (&gstate->font_matrix, size, size); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_set_font_matrix (cairo_gstate_t *gstate, + const cairo_matrix_t *matrix) +{ + if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0) + return CAIRO_STATUS_SUCCESS; + + _cairo_gstate_unset_scaled_font (gstate); + + gstate->font_matrix = *matrix; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_gstate_get_font_matrix (cairo_gstate_t *gstate, + cairo_matrix_t *matrix) +{ + *matrix = gstate->font_matrix; +} + +void +_cairo_gstate_set_font_options (cairo_gstate_t *gstate, + const cairo_font_options_t *options) +{ + if (memcmp (options, &gstate->font_options, sizeof (cairo_font_options_t)) == 0) + return; + + _cairo_gstate_unset_scaled_font (gstate); + + _cairo_font_options_init_copy (&gstate->font_options, options); +} + +void +_cairo_gstate_get_font_options (cairo_gstate_t *gstate, + cairo_font_options_t *options) +{ + *options = gstate->font_options; +} + +cairo_status_t +_cairo_gstate_get_font_face (cairo_gstate_t *gstate, + cairo_font_face_t **font_face) +{ + cairo_status_t status; + + status = _cairo_gstate_ensure_font_face (gstate); + if (unlikely (status)) + return status; + + *font_face = gstate->font_face; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_get_scaled_font (cairo_gstate_t *gstate, + cairo_scaled_font_t **scaled_font) +{ + cairo_status_t status; + + status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + *scaled_font = gstate->scaled_font; + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Like everything else in this file, fonts involve Too Many Coordinate Spaces; + * it is easy to get confused about what's going on. + * + * The user's view + * --------------- + * + * Users ask for things in user space. When cairo starts, a user space unit + * is about 1/96 inch, which is similar to (but importantly different from) + * the normal "point" units most users think in terms of. When a user + * selects a font, its scale is set to "one user unit". The user can then + * independently scale the user coordinate system *or* the font matrix, in + * order to adjust the rendered size of the font. + * + * Metrics are returned in user space, whether they are obtained from + * the currently selected font in a #cairo_t or from a #cairo_scaled_font_t + * which is a font specialized to a particular scale matrix, CTM, and target + * surface. + * + * The font's view + * --------------- + * + * Fonts are designed and stored (in say .ttf files) in "font space", which + * describes an "EM Square" (a design tile) and has some abstract number + * such as 1000, 1024, or 2048 units per "EM". This is basically an + * uninteresting space for us, but we need to remember that it exists. + * + * Font resources (from libraries or operating systems) render themselves + * to a particular device. Since they do not want to make most programmers + * worry about the font design space, the scaling API is simplified to + * involve just telling the font the required pixel size of the EM square + * (that is, in device space). + * + * + * Cairo's gstate view + * ------------------- + * + * In addition to the CTM and CTM inverse, we keep a matrix in the gstate + * called the "font matrix" which describes the user's most recent + * font-scaling or font-transforming request. This is kept in terms of an + * abstract scale factor, composed with the CTM and used to set the font's + * pixel size. So if the user asks to "scale the font by 12", the matrix + * is: + * + * [ 12.0, 0.0, 0.0, 12.0, 0.0, 0.0 ] + * + * It is an affine matrix, like all cairo matrices, where its tx and ty + * components are used to "nudging" fonts around and are handled in gstate + * and then ignored by the "scaled-font" layer. + * + * In order to perform any action on a font, we must build an object + * called a #cairo_font_scale_t; this contains the central 2x2 matrix + * resulting from "font matrix * CTM" (sans the font matrix translation + * components as stated in the previous paragraph). + * + * We pass this to the font when making requests of it, which causes it to + * reply for a particular [user request, device] combination, under the CTM + * (to accommodate the "zoom in" == "bigger fonts" issue above). + * + * The other terms in our communication with the font are therefore in + * device space. When we ask it to perform text->glyph conversion, it will + * produce a glyph string in device space. Glyph vectors we pass to it for + * measuring or rendering should be in device space. The metrics which we + * get back from the font will be in device space. The contents of the + * global glyph image cache will be in device space. + * + * + * Cairo's public view + * ------------------- + * + * Since the values entering and leaving via public API calls are in user + * space, the gstate functions typically need to multiply arguments by the + * CTM (for user-input glyph vectors), and return values by the CTM inverse + * (for font responses such as metrics or glyph vectors). + * + */ + +static cairo_status_t +_cairo_gstate_ensure_font_face (cairo_gstate_t *gstate) +{ + cairo_font_face_t *font_face; + + if (gstate->font_face != NULL) + return gstate->font_face->status; + + + font_face = cairo_toy_font_face_create (CAIRO_FONT_FAMILY_DEFAULT, + CAIRO_FONT_SLANT_DEFAULT, + CAIRO_FONT_WEIGHT_DEFAULT); + if (font_face->status) + return font_face->status; + + gstate->font_face = font_face; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_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; + + status = _cairo_gstate_ensure_font_face (gstate); + if (unlikely (status)) + return status; + + 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, + &font_ctm, + &options); + + status = cairo_scaled_font_status (scaled_font); + if (unlikely (status)) + return status; + + gstate->scaled_font = scaled_font; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_get_font_extents (cairo_gstate_t *gstate, + cairo_font_extents_t *extents) +{ + cairo_status_t status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + cairo_scaled_font_extents (gstate->scaled_font, extents); + + return cairo_scaled_font_status (gstate->scaled_font); +} + +cairo_status_t +_cairo_gstate_set_font_face (cairo_gstate_t *gstate, + cairo_font_face_t *font_face) +{ + if (font_face && font_face->status) + return _cairo_error (font_face->status); + + if (font_face == gstate->font_face) + return CAIRO_STATUS_SUCCESS; + + cairo_font_face_destroy (gstate->font_face); + gstate->font_face = cairo_font_face_reference (font_face); + + _cairo_gstate_unset_scaled_font (gstate); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + + status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + cairo_scaled_font_glyph_extents (gstate->scaled_font, + glyphs, num_glyphs, + extents); + + return cairo_scaled_font_status (gstate->scaled_font); +} + +cairo_status_t +_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_glyph_text_info_t *info) +{ + cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + 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; + + status = _cairo_gstate_get_pattern_status (gstate->source); + if (unlikely (status)) + return status; + + if (gstate->op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_clip_is_all_clipped (gstate->clip)) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_gstate_ensure_scaled_font (gstate); + if (unlikely (status)) + return status; + + transformed_glyphs = stack_transformed_glyphs; + transformed_clusters = stack_transformed_clusters; + + if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) { + transformed_glyphs = cairo_glyph_allocate (num_glyphs); + if (unlikely (transformed_glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + 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); + } + + if (num_glyphs == 0) + goto CLEANUP_GLYPHS; + + op = _reduce_op (gstate); + if (op == CAIRO_OPERATOR_CLEAR) { + pattern = &_cairo_pattern_clear.base; + } else { + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); + pattern = &source_pattern.base; + } + + /* 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, + * 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. 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) <= 10240) + { + + 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 + { + cairo_path_fixed_t path; + + _cairo_path_fixed_init (&path); + + status = _cairo_scaled_font_glyph_path (gstate->scaled_font, + transformed_glyphs, num_glyphs, + &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->clip); + } + + _cairo_path_fixed_fini (&path); + } + +CLEANUP_GLYPHS: + if (transformed_glyphs != stack_transformed_glyphs) + cairo_glyph_free (transformed_glyphs); + if (transformed_clusters != stack_transformed_clusters) + cairo_text_cluster_free (transformed_clusters); + + return status; +} + +cairo_status_t +_cairo_gstate_glyph_path (cairo_gstate_t *gstate, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) +{ + 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; + } else { + transformed_glyphs = cairo_glyph_allocate (num_glyphs); + if (unlikely (transformed_glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _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); + + if (transformed_glyphs != stack_transformed_glyphs) + cairo_glyph_free (transformed_glyphs); + + return status; +} + +cairo_status_t +_cairo_gstate_set_antialias (cairo_gstate_t *gstate, + cairo_antialias_t antialias) +{ + gstate->antialias = antialias; + + return CAIRO_STATUS_SUCCESS; +} + +cairo_antialias_t +_cairo_gstate_get_antialias (cairo_gstate_t *gstate) +{ + return gstate->antialias; +} + +/** + * _cairo_gstate_transform_glyphs_to_backend: + * @gstate: a #cairo_gstate_t + * @glyphs: the array of #cairo_glyph_t objects to be transformed + * @num_glyphs: the number of elements in @glyphs + * @transformed_glyphs: a pre-allocated array of at least @num_glyphs + * #cairo_glyph_t objects + * @num_transformed_glyphs: the number of elements in @transformed_glyphs + * after dropping out of bounds glyphs, or %NULL if glyphs shouldn't be + * dropped + * + * Transform an array of glyphs to backend space by first adding the offset + * of the font matrix, then transforming from user space to backend space. + * The result of the transformation is placed in @transformed_glyphs. + * + * This also uses information from the scaled font and the surface to + * cull/drop glyphs that will not be visible. + **/ +static void +_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, + 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_t *transformed_glyphs, + int *num_transformed_glyphs, + cairo_text_cluster_t *transformed_clusters) +{ + 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; + + 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; + +#define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2) + + j = 0; + if (_cairo_matrix_is_identity (ctm) && + _cairo_matrix_is_identity (device_transform) && + font_matrix->x0 == 0 && font_matrix->y0 == 0) + { + 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++) { + transformed_glyphs[j].index = glyphs[i].index; + transformed_glyphs[j].x = glyphs[i].x; + transformed_glyphs[j].y = glyphs[i].y; + if (KEEP_GLYPH (transformed_glyphs[j])) + j++; + } + } else { + const cairo_glyph_t *cur_glyph; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph = glyphs + num_glyphs - 1; + else + cur_glyph = glyphs; + + for (i = 0; i < num_clusters; i++) { + cairo_bool_t cluster_visible = FALSE; + + for (k = 0; k < clusters[i].num_glyphs; k++) { + transformed_glyphs[j+k].index = cur_glyph->index; + transformed_glyphs[j+k].x = cur_glyph->x; + transformed_glyphs[j+k].y = cur_glyph->y; + if (KEEP_GLYPH (transformed_glyphs[j+k])) + cluster_visible = TRUE; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph--; + else + cur_glyph++; + } + + transformed_clusters[i] = clusters[i]; + if (cluster_visible) + j += k; + else + transformed_clusters[i].num_glyphs = 0; + } + } + } + else if (_cairo_matrix_is_translation (ctm) && + _cairo_matrix_is_translation (device_transform)) + { + double tx = font_matrix->x0 + ctm->x0 + device_transform->x0; + double ty = font_matrix->y0 + ctm->y0 + device_transform->y0; + + if (! drop || num_clusters == 0) { + for (i = 0; i < num_glyphs; i++) { + transformed_glyphs[j].index = glyphs[i].index; + transformed_glyphs[j].x = glyphs[i].x + tx; + transformed_glyphs[j].y = glyphs[i].y + ty; + 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; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph = glyphs + num_glyphs - 1; + else + cur_glyph = glyphs; + + for (i = 0; i < num_clusters; i++) { + cairo_bool_t cluster_visible = FALSE; + + for (k = 0; k < clusters[i].num_glyphs; k++) { + transformed_glyphs[j+k].index = cur_glyph->index; + transformed_glyphs[j+k].x = cur_glyph->x + tx; + transformed_glyphs[j+k].y = cur_glyph->y + ty; + if (KEEP_GLYPH (transformed_glyphs[j+k])) + cluster_visible = TRUE; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph--; + else + cur_glyph++; + } + + transformed_clusters[i] = clusters[i]; + if (cluster_visible) + j += k; + else + transformed_clusters[i].num_glyphs = 0; + } + } + } + else + { + cairo_matrix_t aggregate_transform; + + cairo_matrix_init_translate (&aggregate_transform, + gstate->font_matrix.x0, + gstate->font_matrix.y0); + cairo_matrix_multiply (&aggregate_transform, + &aggregate_transform, ctm); + cairo_matrix_multiply (&aggregate_transform, + &aggregate_transform, device_transform); + + if (! drop || num_clusters == 0) { + for (i = 0; i < num_glyphs; i++) { + transformed_glyphs[j] = glyphs[i]; + cairo_matrix_transform_point (&aggregate_transform, + &transformed_glyphs[j].x, + &transformed_glyphs[j].y); + 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; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph = glyphs + num_glyphs - 1; + else + cur_glyph = glyphs; + + for (i = 0; i < num_clusters; i++) { + cairo_bool_t cluster_visible = FALSE; + for (k = 0; k < clusters[i].num_glyphs; k++) { + transformed_glyphs[j+k] = *cur_glyph; + cairo_matrix_transform_point (&aggregate_transform, + &transformed_glyphs[j+k].x, + &transformed_glyphs[j+k].y); + if (KEEP_GLYPH (transformed_glyphs[j+k])) + cluster_visible = TRUE; + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph--; + else + cur_glyph++; + } + + transformed_clusters[i] = clusters[i]; + if (cluster_visible) + j += k; + else + transformed_clusters[i].num_glyphs = 0; + } + } + } + *num_transformed_glyphs = j; + + if (num_clusters != 0 && cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) { + for (i = 0; i < --j; i++) { + cairo_glyph_t tmp; + + tmp = transformed_glyphs[i]; + transformed_glyphs[i] = transformed_glyphs[j]; + transformed_glyphs[j] = tmp; + } + } +} diff --git a/gfx/cairo/cairo/src/cairo-hash-private.h b/gfx/cairo/cairo/src/cairo-hash-private.h new file mode 100644 index 0000000000..30e51ffe66 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-hash-private.h @@ -0,0 +1,87 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc. + * 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): + * Keith Packard + * Graydon Hoare + * Carl Worth + */ + +#ifndef CAIRO_HASH_PRIVATE_H +#define CAIRO_HASH_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +/* XXX: I'd like this file to be self-contained in terms of + * includeability, but that's not really possible with the current + * monolithic cairoint.h. So, for now, just include cairoint.h instead + * if you want to include this file. */ + +typedef cairo_bool_t +(*cairo_hash_keys_equal_func_t) (const void *key_a, const void *key_b); + +typedef cairo_bool_t +(*cairo_hash_predicate_func_t) (const void *entry); + +typedef void +(*cairo_hash_callback_func_t) (void *entry, + void *closure); + +cairo_private cairo_hash_table_t * +_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal); + +cairo_private void +_cairo_hash_table_destroy (cairo_hash_table_t *hash_table); + +cairo_private void * +_cairo_hash_table_lookup (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key); + +cairo_private void * +_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, + cairo_hash_predicate_func_t predicate); + +cairo_private cairo_status_t +_cairo_hash_table_insert (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *entry); + +cairo_private void +_cairo_hash_table_remove (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key); + +cairo_private void +_cairo_hash_table_foreach (cairo_hash_table_t *hash_table, + cairo_hash_callback_func_t hash_callback, + void *closure); + +#endif diff --git a/gfx/cairo/cairo/src/cairo-hash.c b/gfx/cairo/cairo/src/cairo-hash.c new file mode 100644 index 0000000000..151842eb6d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-hash.c @@ -0,0 +1,578 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc. + * 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): + * Keith Packard + * Graydon Hoare + * Carl Worth + */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +/* + * An entry can be in one of three states: + * + * FREE: Entry has never been used, terminates all searches. + * Appears in the table as a %NULL pointer. + * + * DEAD: Entry had been live in the past. A dead entry can be reused + * but does not terminate a search for an exact entry. + * Appears in the table as a pointer to DEAD_ENTRY. + * + * LIVE: Entry is currently being used. + * Appears in the table as any non-%NULL, non-DEAD_ENTRY pointer. + */ + +#define DEAD_ENTRY ((cairo_hash_entry_t *) 0x1) + +#define ENTRY_IS_FREE(entry) ((entry) == NULL) +#define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY) +#define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY) + +/* + * 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. + * + * 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. + * + * 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 + * permission (and suggested modification of doubling sizes) by Keith + * Packard. + */ + +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 +}; + +struct _cairo_hash_table { + cairo_hash_keys_equal_func_t keys_equal; + + 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 + * + * Creates a new hash table which will use the keys_equal() function + * to compare hash keys. Data is provided to the hash table in the + * form of user-derived versions of #cairo_hash_entry_t. A hash entry + * must be able to hold both a key (including a hash code) and a + * value. Sometimes only the key will be necessary, (as in + * _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. + **/ +cairo_hash_table_t * +_cairo_hash_table_create (cairo_hash_keys_equal_func_t keys_equal) +{ + cairo_hash_table_t *hash_table; + + hash_table = _cairo_malloc (sizeof (cairo_hash_table_t)); + if (unlikely (hash_table == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + if (keys_equal == NULL) + hash_table->keys_equal = _cairo_hash_table_uid_keys_equal; + else + hash_table->keys_equal = keys_equal; + + memset (&hash_table->cache, 0, sizeof (hash_table->cache)); + hash_table->table_size = &hash_table_sizes[0]; + + 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); + return NULL; + } + + hash_table->live_entries = 0; + hash_table->free_entries = *hash_table->table_size; + hash_table->iterating = 0; + + return hash_table; +} + +/** + * _cairo_hash_table_destroy: + * @hash_table: an empty hash table to destroy + * + * Immediately destroys the given hash table, freeing all resources + * associated with it. + * + * WARNING: The hash_table must have no live entries in it before + * _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 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, + * and this function will halt. + **/ +void +_cairo_hash_table_destroy (cairo_hash_table_t *hash_table) +{ + /* The hash table must be empty. Otherwise, halt. */ + assert (hash_table->live_entries == 0); + /* No iterators can be running. Otherwise, halt. */ + assert (hash_table->iterating == 0); + + free (hash_table->entries); + free (hash_table); +} + +static cairo_hash_entry_t ** +_cairo_hash_table_lookup_unique_key (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + unsigned long table_size, i, idx, step; + cairo_hash_entry_t **entry; + + table_size = *hash_table->table_size; + idx = key->hash % table_size; + + entry = &hash_table->entries[idx]; + if (! ENTRY_IS_LIVE (*entry)) + return entry; + + i = 1; + step = 1 + key->hash % (table_size - 2); + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = &hash_table->entries[idx]; + if (! ENTRY_IS_LIVE (*entry)) + return entry; + } while (++i < table_size); + + ASSERT_NOT_REACHED; + return NULL; +} + +/** + * _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 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_manage (cairo_hash_table_t *hash_table) +{ + cairo_hash_table_t tmp; + unsigned long new_size, i; + + /* 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 > live_high) + { + tmp.table_size = hash_table->table_size + 1; + /* This code is being abused if we can't make a table big enough. */ + assert (tmp.table_size - hash_table_sizes < + ARRAY_LENGTH (hash_table_sizes)); + } + else if (hash_table->live_entries < live_low) + { + /* Can't shrink if we're at the smallest size */ + 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; + } + + 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->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]; + } + } + + free (hash_table->entries); + hash_table->entries = tmp.entries; + hash_table->table_size = tmp.table_size; + hash_table->free_entries = new_size - hash_table->live_entries; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_hash_table_lookup: + * @hash_table: a hash table + * @key: the key of interest + * + * Performs a lookup in @hash_table looking for an entry which has a + * key that matches @key, (as determined by the keys_equal() function + * passed to _cairo_hash_table_create). + * + * Return value: the matching entry, of %NULL if no match was found. + **/ +void * +_cairo_hash_table_lookup (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + cairo_hash_entry_t *entry; + unsigned long table_size, i, idx, step; + unsigned long hash = key->hash; + + 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 (entry->hash == hash && hash_table->keys_equal (key, entry)) + goto insert_cache; + } else if (ENTRY_IS_FREE (entry)) + return NULL; + + i = 1; + step = 1 + hash % (table_size - 2); + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (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; +} + +/** + * _cairo_hash_table_random_entry: + * @hash_table: a hash table + * @predicate: a predicate function. + * + * Find a random entry in the hash table satisfying the given + * @predicate. + * + * We use the same algorithm as the lookup algorithm to walk over the + * entries in the hash table in a pseudo-random order. Walking + * linearly would favor entries following gaps in the hash table. We + * could also call rand() repeatedly, which works well for almost-full + * tables, but degrades when the table is almost empty, or predicate + * returns %TRUE for most entries. + * + * Return value: a random live entry or %NULL if there are no entries + * that match the given predicate. In particular, if predicate is + * %NULL, a %NULL return value indicates that the table is empty. + **/ +void * +_cairo_hash_table_random_entry (cairo_hash_table_t *hash_table, + cairo_hash_predicate_func_t predicate) +{ + cairo_hash_entry_t *entry; + unsigned long hash; + unsigned long table_size, i, idx, step; + + assert (predicate != NULL); + + table_size = *hash_table->table_size; + hash = rand (); + idx = hash % table_size; + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry) && predicate (entry)) + return entry; + + i = 1; + step = 1 + hash % (table_size - 2); + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = hash_table->entries[idx]; + if (ENTRY_IS_LIVE (entry) && predicate (entry)) + return entry; + } while (++i < table_size); + + return NULL; +} + +/** + * _cairo_hash_table_insert: + * @hash_table: a hash table + * @key_and_value: an entry to be inserted + * + * Insert the entry #key_and_value into the hash table. + * + * WARNING: There must not be an existing entry in the hash table + * with a matching key. + * + * WARNING: It is a fatal error to insert an element while + * an iterator is running + * + * Instead of using insert to replace an entry, consider just editing + * the entry obtained with _cairo_hash_table_lookup. Or if absolutely + * necessary, use _cairo_hash_table_remove first. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if insufficient memory is available. + **/ +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); + + status = _cairo_hash_table_manage (hash_table); + if (unlikely (status)) + return status; + + 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; +} + +static cairo_hash_entry_t ** +_cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + unsigned long table_size, i, idx, step; + cairo_hash_entry_t **entry; + + table_size = *hash_table->table_size; + idx = key->hash % table_size; + + entry = &hash_table->entries[idx]; + if (*entry == key) + return entry; + + i = 1; + step = 1 + key->hash % (table_size - 2); + do { + idx += step; + if (idx >= table_size) + idx -= table_size; + + entry = &hash_table->entries[idx]; + if (*entry == key) + return entry; + } while (++i < table_size); + + ASSERT_NOT_REACHED; + return NULL; +} +/** + * _cairo_hash_table_remove: + * @hash_table: a hash table + * @key: key of entry to be removed + * + * Remove an entry from the hash table which points to @key. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful or + * %CAIRO_STATUS_NO_MEMORY if out of memory. + **/ +void +_cairo_hash_table_remove (cairo_hash_table_t *hash_table, + cairo_hash_entry_t *key) +{ + *_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 + * skip some elements. */ + if (hash_table->iterating == 0) { + /* This call _can_ fail, but only in failing to allocate new + * memory to shrink the hash table. It does leave the table in a + * consistent state, and we've already succeeded in removing the + * entry, so we don't examine the failure status of this call. */ + _cairo_hash_table_manage (hash_table); + } +} + +/** + * _cairo_hash_table_foreach: + * @hash_table: a hash table + * @hash_callback: function to be called for each live entry + * @closure: additional argument to be passed to @hash_callback + * + * Call @hash_callback for each live entry in the hash table, in a + * non-specified order. + * + * Entries in @hash_table may be removed by code executed from @hash_callback. + * + * Entries may not be inserted to @hash_table, nor may @hash_table + * be destroyed by code executed from @hash_callback. The relevant + * functions will halt in these cases. + **/ +void +_cairo_hash_table_foreach (cairo_hash_table_t *hash_table, + cairo_hash_callback_func_t hash_callback, + void *closure) +{ + unsigned long i; + cairo_hash_entry_t *entry; + + /* Mark the table for iteration */ + ++hash_table->iterating; + for (i = 0; i < *hash_table->table_size; i++) { + entry = hash_table->entries[i]; + if (ENTRY_IS_LIVE(entry)) + hash_callback (entry, closure); + } + /* If some elements were deleted during the iteration, + * the table may need resizing. Just do this every time + * as the check is inexpensive. + */ + if (--hash_table->iterating == 0) { + /* 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_manage (hash_table); + } +} diff --git a/gfx/cairo/cairo/src/cairo-hull.c b/gfx/cairo/cairo/src/cairo-hull.c new file mode 100644 index 0000000000..c65593327f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-hull.c @@ -0,0 +1,235 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2003 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 + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-slope-private.h" + +typedef struct cairo_hull { + cairo_point_t point; + cairo_slope_t slope; + int discard; + int id; +} cairo_hull_t; + +static void +_cairo_hull_init (cairo_hull_t *hull, + cairo_pen_vertex_t *vertices, + int num_vertices) +{ + cairo_point_t *p, *extremum, tmp; + int i; + + extremum = &vertices[0].point; + for (i = 1; i < num_vertices; i++) { + p = &vertices[i].point; + if (p->y < extremum->y || (p->y == extremum->y && p->x < extremum->x)) + extremum = p; + } + /* Put the extremal point at the beginning of the array */ + tmp = *extremum; + *extremum = vertices[0].point; + vertices[0].point = tmp; + + for (i = 0; i < num_vertices; i++) { + hull[i].point = vertices[i].point; + _cairo_slope_init (&hull[i].slope, &hull[0].point, &hull[i].point); + + /* give each point a unique id for later comparison */ + hull[i].id = i; + + /* Don't discard by default */ + hull[i].discard = 0; + + /* Discard all points coincident with the extremal point */ + if (i != 0 && hull[i].slope.dx == 0 && hull[i].slope.dy == 0) + hull[i].discard = 1; + } +} + +static inline cairo_int64_t +_slope_length (cairo_slope_t *slope) +{ + return _cairo_int64_add (_cairo_int32x32_64_mul (slope->dx, slope->dx), + _cairo_int32x32_64_mul (slope->dy, slope->dy)); +} + +static int +_cairo_hull_vertex_compare (const void *av, const void *bv) +{ + cairo_hull_t *a = (cairo_hull_t *) av; + cairo_hull_t *b = (cairo_hull_t *) bv; + int ret; + + /* Some libraries are reported to actually compare identical + * pointers and require the result to be 0. This is the crazy world we + * have to live in. + */ + if (a == b) + return 0; + + ret = _cairo_slope_compare (&a->slope, &b->slope); + + /* + * In the case of two vertices with identical slope from the + * extremal point discard the nearer point. + */ + if (ret == 0) { + int cmp; + + cmp = _cairo_int64_cmp (_slope_length (&a->slope), + _slope_length (&b->slope)); + + /* + * Use the points' ids to ensure a well-defined ordering, + * and avoid setting discard on both points. + */ + if (cmp < 0 || (cmp == 0 && a->id < b->id)) { + a->discard = 1; + ret = -1; + } else { + b->discard = 1; + ret = 1; + } + } + + return ret; +} + +static int +_cairo_hull_prev_valid (cairo_hull_t *hull, int num_hull, int index) +{ + /* hull[0] is always valid, and we never need to wraparound, (if + * we are passed an index of 0 here, then the calling loop is just + * about to terminate). */ + if (index == 0) + return 0; + + do { + index--; + } while (hull[index].discard); + + return index; +} + +static int +_cairo_hull_next_valid (cairo_hull_t *hull, int num_hull, int index) +{ + do { + index = (index + 1) % num_hull; + } while (hull[index].discard); + + return index; +} + +static void +_cairo_hull_eliminate_concave (cairo_hull_t *hull, int num_hull) +{ + int i, j, k; + cairo_slope_t slope_ij, slope_jk; + + i = 0; + j = _cairo_hull_next_valid (hull, num_hull, i); + k = _cairo_hull_next_valid (hull, num_hull, j); + + do { + _cairo_slope_init (&slope_ij, &hull[i].point, &hull[j].point); + _cairo_slope_init (&slope_jk, &hull[j].point, &hull[k].point); + + /* Is the angle formed by ij and jk concave? */ + if (_cairo_slope_compare (&slope_ij, &slope_jk) >= 0) { + if (i == k) + return; + hull[j].discard = 1; + j = i; + i = _cairo_hull_prev_valid (hull, num_hull, j); + } else { + i = j; + j = k; + k = _cairo_hull_next_valid (hull, num_hull, j); + } + } while (j != 0); +} + +static void +_cairo_hull_to_pen (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int *num_vertices) +{ + int i, j = 0; + + for (i = 0; i < *num_vertices; i++) { + if (hull[i].discard) + continue; + vertices[j++].point = hull[i].point; + } + + *num_vertices = j; +} + +/* Given a set of vertices, compute the convex hull using the Graham + scan algorithm. */ +cairo_status_t +_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices) +{ + cairo_hull_t hull_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_hull_t)]; + cairo_hull_t *hull; + int num_hull = *num_vertices; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (num_hull > ARRAY_LENGTH (hull_stack)) { + hull = _cairo_malloc_ab (num_hull, sizeof (cairo_hull_t)); + if (unlikely (hull == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + hull = hull_stack; + } + + _cairo_hull_init (hull, vertices, num_hull); + + qsort (hull + 1, num_hull - 1, + sizeof (cairo_hull_t), _cairo_hull_vertex_compare); + + _cairo_hull_eliminate_concave (hull, num_hull); + + _cairo_hull_to_pen (hull, vertices, num_vertices); + + if (hull != hull_stack) + free (hull); + + return CAIRO_STATUS_SUCCESS; +} 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 0000000000..8bf3fd4b1e --- /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 new file mode 100644 index 0000000000..e64928e40e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-image-info-private.h @@ -0,0 +1,68 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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_IMAGE_INFO_PRIVATE_H +#define CAIRO_IMAGE_INFO_PRIVATE_H + +#include "cairoint.h" + +typedef struct _cairo_image_info { + int width; + int height; + int num_components; + int bits_per_component; +} cairo_image_info_t; + +cairo_private cairo_int_status_t +_cairo_image_info_get_jpeg_info (cairo_image_info_t *info, + const unsigned char *data, + long length); + +cairo_private cairo_int_status_t +_cairo_image_info_get_jpx_info (cairo_image_info_t *info, + const unsigned char *data, + unsigned long length); + +cairo_private cairo_int_status_t +_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 new file mode 100644 index 0000000000..d147e37239 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-image-info.c @@ -0,0 +1,421 @@ +/* -*- 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 + * + * 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-error-private.h" +#include "cairo-image-info-private.h" + +/* JPEG (image/jpeg) + * + * http://www.w3.org/Graphics/JPEG/itu-t81.pdf + */ + +/* Markers with no parameters. All other markers are followed by a two + * byte length of the parameters. */ +#define TEM 0x01 +#define RST_begin 0xd0 +#define RST_end 0xd7 +#define SOI 0xd8 +#define EOI 0xd9 + +/* Start of frame markers. */ +#define SOF0 0xc0 +#define SOF1 0xc1 +#define SOF2 0xc2 +#define SOF3 0xc3 +#define SOF5 0xc5 +#define SOF6 0xc6 +#define SOF7 0xc7 +#define SOF9 0xc9 +#define SOF10 0xca +#define SOF11 0xcb +#define SOF13 0xcd +#define SOF14 0xce +#define SOF15 0xcf + +static const unsigned char * +_jpeg_skip_segment (const unsigned char *p) +{ + int len; + + p++; + len = (p[0] << 8) | p[1]; + + return p + len; +} + +static void +_jpeg_extract_info (cairo_image_info_t *info, const unsigned char *p) +{ + info->width = (p[6] << 8) + p[7]; + info->height = (p[4] << 8) + p[5]; + info->num_components = p[8]; + info->bits_per_component = p[3]; +} + +cairo_int_status_t +_cairo_image_info_get_jpeg_info (cairo_image_info_t *info, + const unsigned char *data, + long length) +{ + const unsigned char *p = data; + + while (p + 1 < data + length) { + if (*p != 0xff) + return CAIRO_INT_STATUS_UNSUPPORTED; + p++; + + switch (*p) { + /* skip fill bytes */ + case 0xff: + p++; + break; + + case TEM: + case SOI: + case EOI: + p++; + break; + + case SOF0: + case SOF1: + case SOF2: + case SOF3: + case SOF5: + case SOF6: + case SOF7: + case SOF9: + case SOF10: + case SOF11: + case SOF13: + case SOF14: + case SOF15: + /* Start of frame found. Extract the image parameters. */ + if (p + 8 > data + length) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _jpeg_extract_info (info, p); + return CAIRO_STATUS_SUCCESS; + + default: + if (*p >= RST_begin && *p <= RST_end) { + p++; + break; + } + + if (p + 3 > data + length) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p = _jpeg_skip_segment (p); + break; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +/* JPEG 2000 (image/jp2) + * + * http://www.jpeg.org/public/15444-1annexi.pdf + */ + +#define JPX_FILETYPE 0x66747970 +#define JPX_JP2_HEADER 0x6A703268 +#define JPX_IMAGE_HEADER 0x69686472 + +static const unsigned char _jpx_signature[] = { + 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a +}; + +static const unsigned char * +_jpx_next_box (const unsigned char *p) +{ + return p + get_unaligned_be32 (p); +} + +static const unsigned char * +_jpx_get_box_contents (const unsigned char *p) +{ + return p + 8; +} + +static cairo_bool_t +_jpx_match_box (const unsigned char *p, const unsigned char *end, uint32_t type) +{ + uint32_t length; + + if (p + 8 < end) { + length = get_unaligned_be32 (p); + if (get_unaligned_be32 (p + 4) == type && p + length < end) + return TRUE; + } + + return FALSE; +} + +static const unsigned char * +_jpx_find_box (const unsigned char *p, const unsigned char *end, uint32_t type) +{ + while (p < end) { + if (_jpx_match_box (p, end, type)) + return p; + p = _jpx_next_box (p); + } + + return NULL; +} + +static void +_jpx_extract_info (const unsigned char *p, cairo_image_info_t *info) +{ + info->height = get_unaligned_be32 (p); + info->width = get_unaligned_be32 (p + 4); + info->num_components = (p[8] << 8) + p[9]; + info->bits_per_component = p[10]; +} + +cairo_int_status_t +_cairo_image_info_get_jpx_info (cairo_image_info_t *info, + const unsigned char *data, + unsigned long length) +{ + const unsigned char *p = data; + const unsigned char *end = data + length; + + /* First 12 bytes must be the JPEG 2000 signature box. */ + if (length < ARRAY_LENGTH(_jpx_signature) || + memcmp(p, _jpx_signature, ARRAY_LENGTH(_jpx_signature)) != 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += ARRAY_LENGTH(_jpx_signature); + + /* Next box must be a File Type Box */ + if (! _jpx_match_box (p, end, JPX_FILETYPE)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p = _jpx_next_box (p); + + /* Locate the JP2 header box. */ + p = _jpx_find_box (p, end, JPX_JP2_HEADER); + if (!p) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Step into the JP2 header box. First box must be the Image + * Header */ + p = _jpx_get_box_contents (p); + if (! _jpx_match_box (p, end, JPX_IMAGE_HEADER)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Get the image info */ + p = _jpx_get_box_contents (p); + _jpx_extract_info (p, info); + + return CAIRO_STATUS_SUCCESS; +} + +/* PNG (image/png) + * + * http://www.w3.org/TR/2003/REC-PNG-20031110/ + */ + +#define PNG_IHDR 0x49484452 + +static const unsigned char _png_magic[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + +cairo_int_status_t +_cairo_image_info_get_png_info (cairo_image_info_t *info, + const unsigned char *data, + unsigned long length) +{ + const unsigned char *p = data; + const unsigned char *end = data + length; + + if (length < 8 || memcmp (data, _png_magic, 8) != 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 8; + + /* The first chunk must be IDHR. IDHR has 13 bytes of data plus + * the 12 bytes of overhead for the chunk. */ + if (p + 13 + 12 > end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 4; + if (get_unaligned_be32 (p) != PNG_IHDR) + return CAIRO_INT_STATUS_UNSUPPORTED; + + p += 4; + info->width = get_unaligned_be32 (p); + p += 4; + 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 0000000000..4934216f87 --- /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 0000000000..c56845ab2d --- /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 0000000000..743d5fd3e7 --- /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 0000000000..2b7921133f --- /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 new file mode 100644 index 0000000000..3b11eb9815 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-image-surface.c @@ -0,0 +1,1363 @@ +/* -*- 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 + */ + +#include "cairoint.h" + +#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" + +/* Limit on the width / height of an image surface in pixels. This is + * mainly determined by coordinates of things sent to pixman at the + * moment being in 16.16 format. */ +#define MAX_IMAGE_SIZE 32767 + +/** + * SECTION:cairo-image + * @Title: Image Surfaces + * @Short_Description: Rendering to memory buffers + * @See_Also: #cairo_surface_t + * + * 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: + * + * Defined if the image surface backend is available. + * The image surface backend is always built in. + * This macro was added for completeness in cairo 1.8. + * + * Since: 1.8 + **/ + +static cairo_bool_t +_cairo_image_surface_is_size_valid (int width, int height) +{ + return 0 <= width && width <= MAX_IMAGE_SIZE && + 0 <= height && height <= MAX_IMAGE_SIZE; +} + +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: + return CAIRO_FORMAT_A8; + case PIXMAN_a1: + 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: + case PIXMAN_x1b5g5r5: case PIXMAN_a4r4g4b4: case PIXMAN_x4r4g4b4: + case PIXMAN_a4b4g4r4: case PIXMAN_x4b4g4r4: case PIXMAN_r3g3b2: + case PIXMAN_b2g3r3: case PIXMAN_a2r2g2b2: case PIXMAN_a2b2g2r2: + case PIXMAN_c8: case PIXMAN_g8: case PIXMAN_x4a4: + case PIXMAN_a4: case PIXMAN_r1g2b1: case PIXMAN_b1g2r1: + case PIXMAN_a1r1g1b1: case PIXMAN_a1b1g1r1: case PIXMAN_c4: + case PIXMAN_g4: case PIXMAN_g1: + case PIXMAN_yuy2: case PIXMAN_yv12: + case PIXMAN_b8g8r8x8: + case PIXMAN_b8g8r8a8: + case PIXMAN_a2b10g10r10: + case PIXMAN_x2b10g10r10: + case PIXMAN_a2r10g10b10: +#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0) + case PIXMAN_x14r6g6b6: +#endif + default: + return CAIRO_FORMAT_INVALID; + } + + return CAIRO_FORMAT_INVALID; +} + +cairo_content_t +_cairo_content_from_pixman_format (pixman_format_code_t pixman_format) +{ + cairo_content_t content; + + content = 0; + if (PIXMAN_FORMAT_RGB (pixman_format)) + content |= CAIRO_CONTENT_COLOR; + if (PIXMAN_FORMAT_A (pixman_format)) + content |= CAIRO_CONTENT_ALPHA; + + return content; +} + +void +_cairo_image_surface_init (cairo_image_surface_t *surface, + pixman_image_t *pixman_image, + pixman_format_code_t pixman_format) +{ + surface->parent = NULL; + surface->pixman_image = pixman_image; + + surface->pixman_format = pixman_format; + surface->format = _cairo_format_from_pixman_format (pixman_format); + 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 = 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; +} + +cairo_bool_t +_pixman_format_from_masks (cairo_format_masks_t *masks, + pixman_format_code_t *format_ret) +{ + pixman_format_code_t format; + int format_type; + int a, r, g, b; + cairo_format_masks_t format_masks; + + a = _cairo_popcount (masks->alpha_mask); + r = _cairo_popcount (masks->red_mask); + g = _cairo_popcount (masks->green_mask); + b = _cairo_popcount (masks->blue_mask); + + if (masks->red_mask) { + if (masks->red_mask > masks->blue_mask) + format_type = PIXMAN_TYPE_ARGB; + else + format_type = PIXMAN_TYPE_ABGR; + } else if (masks->alpha_mask) { + format_type = PIXMAN_TYPE_A; + } else { + return FALSE; + } + + format = PIXMAN_FORMAT (masks->bpp, format_type, a, r, g, b); + + if (! pixman_format_supported_destination (format)) + return FALSE; + + /* Sanity check that we got out of PIXMAN_FORMAT exactly what we + * expected. This avoid any problems from something bizarre like + * alpha in the least-significant bits, or insane channel order, + * or whatever. */ + if (!_pixman_format_to_masks (format, &format_masks) || + masks->bpp != format_masks.bpp || + masks->red_mask != format_masks.red_mask || + masks->green_mask != format_masks.green_mask || + masks->blue_mask != format_masks.blue_mask) + { + return FALSE; + } + + *format_ret = format; + return TRUE; +} + +/* A mask consisting of N bits set to 1. */ +#define MASK(N) ((1UL << (N))-1) + +cairo_bool_t +_pixman_format_to_masks (pixman_format_code_t format, + cairo_format_masks_t *masks) +{ + int a, r, g, b; + + masks->bpp = PIXMAN_FORMAT_BPP (format); + + /* Number of bits in each channel */ + a = PIXMAN_FORMAT_A (format); + r = PIXMAN_FORMAT_R (format); + g = PIXMAN_FORMAT_G (format); + b = PIXMAN_FORMAT_B (format); + + switch (PIXMAN_FORMAT_TYPE (format)) { + case PIXMAN_TYPE_ARGB: + masks->alpha_mask = MASK (a) << (r + g + b); + masks->red_mask = MASK (r) << (g + b); + masks->green_mask = MASK (g) << (b); + masks->blue_mask = MASK (b); + return TRUE; + case PIXMAN_TYPE_ABGR: + masks->alpha_mask = MASK (a) << (b + g + r); + masks->blue_mask = MASK (b) << (g + r); + masks->green_mask = MASK (g) << (r); + masks->red_mask = MASK (r); + return TRUE; +#ifdef PIXMAN_TYPE_BGRA + case PIXMAN_TYPE_BGRA: + masks->blue_mask = MASK (b) << (masks->bpp - b); + masks->green_mask = MASK (g) << (masks->bpp - b - g); + masks->red_mask = MASK (r) << (masks->bpp - b - g - r); + masks->alpha_mask = MASK (a); + return TRUE; +#endif + case PIXMAN_TYPE_A: + masks->alpha_mask = MASK (a); + masks->red_mask = 0; + masks->green_mask = 0; + masks->blue_mask = 0; + return TRUE; + case PIXMAN_TYPE_OTHER: + case PIXMAN_TYPE_COLOR: + case PIXMAN_TYPE_GRAY: + case PIXMAN_TYPE_YUY2: + case PIXMAN_TYPE_YV12: + default: + masks->alpha_mask = 0; + masks->red_mask = 0; + masks->green_mask = 0; + masks->blue_mask = 0; + return FALSE; + } +} + +pixman_format_code_t +_cairo_format_to_pixman_format_code (cairo_format_t format) +{ + pixman_format_code_t ret; + switch (format) { + case CAIRO_FORMAT_A1: + ret = PIXMAN_a1; + break; + case CAIRO_FORMAT_A8: + ret = PIXMAN_a8; + break; + 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: + ret = PIXMAN_a8r8g8b8; + break; + } + return ret; +} + +cairo_surface_t * +_cairo_image_surface_create_with_pixman_format (unsigned char *data, + pixman_format_code_t pixman_format, + int width, + int height, + int stride) +{ + cairo_surface_t *surface; + pixman_image_t *pixman_image; + + if (! _cairo_image_surface_is_size_valid (width, height)) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + pixman_image = pixman_image_create_bits (pixman_format, width, height, + (uint32_t *) data, stride); + + if (unlikely (pixman_image == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = _cairo_image_surface_create_for_pixman_image (pixman_image, + pixman_format); + if (unlikely (surface->status)) { + pixman_image_unref (pixman_image); + return surface; + } + + /* we can not make any assumptions about the initial state of user data */ + surface->is_clear = data == NULL; + return surface; +} + +/** + * cairo_image_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 + * + * Creates an image surface of the specified format and + * 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). + * + * 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.0 + **/ +cairo_surface_t * +cairo_image_surface_create (cairo_format_t format, + int width, + int height) +{ + pixman_format_code_t pixman_format; + + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + pixman_format = _cairo_format_to_pixman_format_code (format); + + return _cairo_image_surface_create_with_pixman_format (NULL, pixman_format, + width, height, -1); +} +slim_hidden_def (cairo_image_surface_create); + + cairo_surface_t * +_cairo_image_surface_create_with_content (cairo_content_t content, + int width, + int height) +{ + return cairo_image_surface_create (_cairo_format_from_content (content), + width, height); +} + +/** + * cairo_format_stride_for_width: + * @format: A #cairo_format_t value + * @width: The desired width of an image surface to be created. + * + * This function provides a stride value that will respect all + * alignment requirements of the accelerated image-rendering code + * within cairo. Typical usage will be of the form: + * + * + * int stride; + * unsigned char *data; + * cairo_surface_t *surface; + * + * stride = cairo_format_stride_for_width (format, width); + * data = malloc (stride * height); + * surface = cairo_image_surface_create_for_data (data, format, + * width, height, + * stride); + * + * + * Return value: the appropriate stride to use given the desired + * format and width, or -1 if either the format is invalid or the width + * too large. + * + * Since: 1.6 + **/ + int +cairo_format_stride_for_width (cairo_format_t format, + int width) +{ + int bpp; + + if (! CAIRO_FORMAT_VALID (format)) { + _cairo_error_throw (CAIRO_STATUS_INVALID_FORMAT); + return -1; + } + + bpp = _cairo_format_bits_per_pixel (format); + if ((unsigned) (width) >= (INT32_MAX - 7) / (unsigned) (bpp)) + return -1; + + return CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp); +} +slim_hidden_def (cairo_format_stride_for_width); + +/** + * cairo_image_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: the format of pixels in the buffer + * @width: the width of the image to be stored in the buffer + * @height: the height of the image to be stored in the buffer + * @stride: the number of bytes between the start of rows in the + * buffer as allocated. This value should always be computed by + * cairo_format_stride_for_width() before allocating the data + * buffer. + * + * Creates an image surface for the provided pixel data. The output + * buffer must be kept around until the #cairo_surface_t is destroyed + * or cairo_surface_finish() is called on the surface. The initial + * contents of @data will be used as the initial image contents; you + * must explicitly clear the buffer, using, for example, + * cairo_rectangle() and cairo_fill() if you want it cleared. + * + * Note that the stride may be larger than + * width*bytes_per_pixel to provide proper alignment for each pixel + * and row. This alignment is required to allow high-performance rendering + * within cairo. The correct way to obtain a legal stride value is to + * call cairo_format_stride_for_width() with the desired format and + * maximum image width value, and then use the resulting stride value + * to allocate the data and to create the image surface. See + * cairo_format_stride_for_width() for example code. + * + * 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 in the case of an error such as out of + * memory or an invalid stride value. In case of invalid stride value + * the error status of the returned surface will be + * %CAIRO_STATUS_INVALID_STRIDE. You can use + * cairo_surface_status() to check for this. + * + * 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_image_surface_create_for_data (unsigned char *data, + cairo_format_t format, + int width, + int height, + int stride) +{ + pixman_format_code_t pixman_format; + int minstride; + + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + if ((stride & (CAIRO_STRIDE_ALIGNMENT-1)) != 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + + if (! _cairo_image_surface_is_size_valid (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + minstride = cairo_format_stride_for_width (format, width); + if (stride < 0) { + if (stride > -minstride) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + } + } else { + if (stride < minstride) { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + } + } + + pixman_format = _cairo_format_to_pixman_format_code (format); + return _cairo_image_surface_create_with_pixman_format (data, + pixman_format, + width, height, + stride); +} +slim_hidden_def (cairo_image_surface_create_for_data); + +/** + * cairo_image_surface_get_data: + * @surface: a #cairo_image_surface_t + * + * 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. + * + * Since: 1.2 + **/ +unsigned char * +cairo_image_surface_get_data (cairo_surface_t *surface) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return image_surface->data; +} +slim_hidden_def (cairo_image_surface_get_data); + +/** + * cairo_image_surface_get_format: + * @surface: a #cairo_image_surface_t + * + * Get the format of the surface. + * + * Return value: the format of the surface + * + * Since: 1.2 + **/ +cairo_format_t +cairo_image_surface_get_format (cairo_surface_t *surface) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return CAIRO_FORMAT_INVALID; + } + + return image_surface->format; +} +slim_hidden_def (cairo_image_surface_get_format); + +/** + * cairo_image_surface_get_width: + * @surface: a #cairo_image_surface_t + * + * 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) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return image_surface->width; +} +slim_hidden_def (cairo_image_surface_get_width); + +/** + * cairo_image_surface_get_height: + * @surface: a #cairo_image_surface_t + * + * 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) +{ + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return image_surface->height; +} +slim_hidden_def (cairo_image_surface_get_height); + +/** + * cairo_image_surface_get_stride: + * @surface: a #cairo_image_surface_t + * + * Get the stride of the image surface in bytes + * + * Return value: the stride of the image surface in bytes (or 0 if + * @surface is not an image surface). The stride is the distance in + * bytes from the beginning of one row of the image data to the + * beginning of the next row. + * + * Since: 1.2 + **/ +int +cairo_image_surface_get_stride (cairo_surface_t *surface) +{ + + cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; + + if (! _cairo_surface_is_image (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return image_surface->stride; +} +slim_hidden_def (cairo_image_surface_get_stride); + + cairo_format_t +_cairo_format_from_content (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_COLOR: + return CAIRO_FORMAT_RGB24; + case CAIRO_CONTENT_ALPHA: + return CAIRO_FORMAT_A8; + case CAIRO_CONTENT_COLOR_ALPHA: + return CAIRO_FORMAT_ARGB32; + } + + ASSERT_NOT_REACHED; + return CAIRO_FORMAT_INVALID; +} + + 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: + return CAIRO_CONTENT_COLOR; + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + return CAIRO_CONTENT_ALPHA; + case CAIRO_FORMAT_INVALID: + break; + } + + ASSERT_NOT_REACHED; + return CAIRO_CONTENT_COLOR_ALPHA; +} + + 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: + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB24: + return 32; + case CAIRO_FORMAT_RGB16_565: + return 16; + case CAIRO_FORMAT_A8: + return 8; + case CAIRO_FORMAT_A1: + return 1; + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +cairo_surface_t * +_cairo_image_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height) +{ + 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)); + + if (content == other->base.content) { + return _cairo_image_surface_create_with_pixman_format (NULL, + other->pixman_format, + width, height, + 0); + } + + return _cairo_image_surface_create_with_content (content, + width, height); +} + +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; + + if (surface->pixman_image) { + pixman_image_unref (surface->pixman_image); + surface->pixman_image = NULL; + } + + if (surface->owns_data) { + free (surface->data); + surface->data = NULL; + } + + if (surface->parent) { + cairo_surface_t *parent = surface->parent; + surface->parent = NULL; + cairo_surface_destroy (parent); + } + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) +{ + surface->owns_data = TRUE; +} + +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) +{ + *image_out = abstract_surface; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_image_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ +} + +/* high level image interface */ +cairo_bool_t +_cairo_image_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_image_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +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) +{ + _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); +} + +const cairo_surface_backend_t _cairo_image_surface_backend = { + CAIRO_SURFACE_TYPE_IMAGE, + _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_snapshot, + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_image_surface_get_extents, + _cairo_image_surface_get_font_options, + + NULL, /* 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, +}; + +/* A convenience function for when one needs to coerce an image + * surface to an alternate format. */ +cairo_image_surface_t * +_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 + * surface to an alternate format. */ +cairo_image_surface_t * +_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, + cairo_format_t format) +{ + cairo_image_surface_t *clone; + cairo_status_t status; + + status = surface->base.status; + if (unlikely (status)) + return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); + + if (surface->format == format) + return (cairo_image_surface_t *)cairo_surface_reference(&surface->base); + + clone = (cairo_image_surface_t *) + cairo_image_surface_create (format, surface->width, surface->height); + if (unlikely (clone->base.status)) + return clone; + + pixman_image_composite32 (PIXMAN_OP_SRC, + surface->pixman_image, NULL, clone->pixman_image, + 0, 0, + 0, 0, + 0, 0, + surface->width, surface->height); + clone->base.is_clear = FALSE; + + clone->base.device_transform = + surface->base.device_transform; + clone->base.device_transform_inverse = + surface->base.device_transform_inverse; + + return clone; +} + +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; + cairo_image_transparency_t transparency; + + if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0) + 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 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) { + return CAIRO_IMAGE_IS_OPAQUE; + } + + if (image->format != CAIRO_FORMAT_ARGB32) + return CAIRO_IMAGE_HAS_ALPHA; + + 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 CAIRO_IMAGE_HAS_ALPHA; + } else if (a == 0) { + transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + } + } + } + + 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->width == 0 || image->height == 0) + return CAIRO_IMAGE_IS_MONOCHROME; + + 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 0000000000..71cc5e7ebf --- /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/cairo-line-private.h b/gfx/cairo/cairo/src/cairo-line-private.h new file mode 100644 index 0000000000..6ddcae18d6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-line-private.h @@ -0,0 +1,51 @@ +/* cairo - a vector graphics library with display and print output + * + * 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 + * 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. + * + */ + +#ifndef CAIRO_LINE_PRIVATE_H +#define CAIRO_LINE_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-error-private.h" +#include "cairo-compiler-private.h" + +CAIRO_BEGIN_DECLS + +cairo_private int +_cairo_lines_compare_at_y (const cairo_line_t *a, + const cairo_line_t *b, + int y); + +CAIRO_END_DECLS + +#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 0000000000..2c1768eeb1 --- /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 0000000000..0955178d24 --- /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 new file mode 100644 index 0000000000..9f39b668f8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-list-private.h @@ -0,0 +1,48 @@ +/* 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_PRIVATE_H +#define CAIRO_LIST_PRIVATE_H + +#include "cairo-compiler-private.h" + +/* Basic circular, doubly linked list implementation */ + +typedef struct _cairo_list { + struct _cairo_list *next, *prev; +} cairo_list_t; + +#endif /* CAIRO_LIST_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-lzw.c b/gfx/cairo/cairo/src/cairo-lzw.c new file mode 100644 index 0000000000..f27b3c3384 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-lzw.c @@ -0,0 +1,404 @@ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +typedef struct _lzw_buf { + cairo_status_t status; + + unsigned char *data; + int data_size; + int num_data; + uint32_t pending; + unsigned int pending_bits; +} lzw_buf_t; + +/* An lzw_buf_t is a simple, growable chunk of memory for holding + * variable-size objects of up to 16 bits each. + * + * Initialize an lzw_buf_t to the given size in bytes. + * + * To store objects into the lzw_buf_t, call _lzw_buf_store_bits and + * when finished, call _lzw_buf_store_pending, (which flushes out the + * last few bits that hadn't yet made a complete byte yet). + * + * Instead of returning failure from any functions, lzw_buf_t provides + * a status value that the caller can query, (and should query at + * least once when done with the object). The status value will be + * either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY; + */ +static void +_lzw_buf_init (lzw_buf_t *buf, int size) +{ + if (size == 0) + size = 16; + + buf->status = CAIRO_STATUS_SUCCESS; + buf->data_size = size; + buf->num_data = 0; + buf->pending = 0; + buf->pending_bits = 0; + + buf->data = _cairo_malloc (size); + if (unlikely (buf->data == NULL)) { + buf->data_size = 0; + buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return; + } +} + +/* Increase the buffer size by doubling. + * + * Returns %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + */ +static cairo_status_t +_lzw_buf_grow (lzw_buf_t *buf) +{ + int new_size = buf->data_size * 2; + unsigned char *new_data; + + if (buf->status) + return buf->status; + + new_data = NULL; + /* check for integer overflow */ + if (new_size / 2 == buf->data_size) + new_data = realloc (buf->data, new_size); + + if (unlikely (new_data == NULL)) { + free (buf->data); + buf->data_size = 0; + buf->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return buf->status; + } + + buf->data = new_data; + buf->data_size = new_size; + + return CAIRO_STATUS_SUCCESS; +} + +/* Store the lowest num_bits bits of values into buf. + * + * Note: The bits of value above size_in_bits must be 0, (so don't lie + * about the size). + * + * See also _lzw_buf_store_pending which must be called after the last + * call to _lzw_buf_store_bits. + * + * Sets buf->status to either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. + */ +static void +_lzw_buf_store_bits (lzw_buf_t *buf, uint16_t value, int num_bits) +{ + cairo_status_t status; + + assert (value <= (1 << num_bits) - 1); + + if (buf->status) + return; + + buf->pending = (buf->pending << num_bits) | value; + buf->pending_bits += num_bits; + + while (buf->pending_bits >= 8) { + if (buf->num_data >= buf->data_size) { + status = _lzw_buf_grow (buf); + if (unlikely (status)) + return; + } + buf->data[buf->num_data++] = buf->pending >> (buf->pending_bits - 8); + buf->pending_bits -= 8; + } +} + +/* Store the last remaining pending bits into the buffer. + * + * Note: This function must be called after the last call to + * _lzw_buf_store_bits. + * + * Sets buf->status to either %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY. + */ +static void +_lzw_buf_store_pending (lzw_buf_t *buf) +{ + cairo_status_t status; + + if (buf->status) + return; + + if (buf->pending_bits == 0) + return; + + assert (buf->pending_bits < 8); + + if (buf->num_data >= buf->data_size) { + status = _lzw_buf_grow (buf); + if (unlikely (status)) + return; + } + + buf->data[buf->num_data++] = buf->pending << (8 - buf->pending_bits); + buf->pending_bits = 0; +} + +/* LZW defines a few magic code values */ +#define LZW_CODE_CLEAR_TABLE 256 +#define LZW_CODE_EOD 257 +#define LZW_CODE_FIRST 258 + +/* We pack three separate values into a symbol as follows: + * + * 12 bits (31 down to 20): CODE: code value used to represent this symbol + * 12 bits (19 down to 8): PREV: previous code value in chain + * 8 bits ( 7 down to 0): NEXT: next byte value in chain + */ +typedef uint32_t lzw_symbol_t; + +#define LZW_SYMBOL_SET(sym, prev, next) ((sym) = ((prev) << 8)|(next)) +#define LZW_SYMBOL_SET_CODE(sym, code, prev, next) ((sym) = ((code << 20)|(prev) << 8)|(next)) +#define LZW_SYMBOL_GET_CODE(sym) (((sym) >> 20)) +#define LZW_SYMBOL_GET_PREV(sym) (((sym) >> 8) & 0x7ff) +#define LZW_SYMBOL_GET_BYTE(sym) (((sym) >> 0) & 0x0ff) + +/* The PREV+NEXT fields can be seen as the key used to fetch values + * from the hash table, while the code is the value fetched. + */ +#define LZW_SYMBOL_KEY_MASK 0x000fffff + +/* Since code values are only stored starting with 258 we can safely + * use a zero value to represent free slots in the hash table. */ +#define LZW_SYMBOL_FREE 0x00000000 + +/* These really aren't very free for modifying. First, the PostScript + * specification sets the 9-12 bit range. Second, the encoding of + * lzw_symbol_t above also relies on 2 of LZW_BITS_MAX plus one byte + * fitting within 32 bits. + * + * But other than that, the LZW compression scheme could function with + * more bits per code. + */ +#define LZW_BITS_MIN 9 +#define LZW_BITS_MAX 12 +#define LZW_BITS_BOUNDARY(bits) ((1<<(bits))-1) +#define LZW_MAX_SYMBOLS (1<table, 0, LZW_SYMBOL_TABLE_SIZE * sizeof (lzw_symbol_t)); +} + +/* Lookup a symbol in the symbol table. The PREV and NEXT fields of + * symbol form the key for the lookup. + * + * If successful, then this function returns %TRUE and slot_ret will be + * left pointing at the result that will have the CODE field of + * interest. + * + * If the lookup fails, then this function returns %FALSE and slot_ret + * will be pointing at the location in the table to which a new CODE + * value should be stored along with PREV and NEXT. + */ +static cairo_bool_t +_lzw_symbol_table_lookup (lzw_symbol_table_t *table, + lzw_symbol_t symbol, + lzw_symbol_t **slot_ret) +{ + /* The algorithm here is identical to that in cairo-hash.c. We + * copy it here to allow for a rather more efficient + * implementation due to several circumstances that do not apply + * to the more general case: + * + * 1) We have a known bound on the total number of symbols, so we + * have a fixed-size table without any copying when growing + * + * 2) We never delete any entries, so we don't need to + * support/check for DEAD entries during lookup. + * + * 3) The object fits in 32 bits so we store each object in its + * entirety within the table rather than storing objects + * externally and putting pointers in the table, (which here + * would just double the storage requirements and have negative + * impacts on memory locality). + */ + int i, idx, step, hash = symbol & LZW_SYMBOL_KEY_MASK; + lzw_symbol_t candidate; + + idx = hash % LZW_SYMBOL_MOD1; + step = 0; + + *slot_ret = NULL; + for (i = 0; i < LZW_SYMBOL_TABLE_SIZE; i++) + { + candidate = table->table[idx]; + if (candidate == LZW_SYMBOL_FREE) + { + *slot_ret = &table->table[idx]; + return FALSE; + } + else /* candidate is LIVE */ + { + if ((candidate & LZW_SYMBOL_KEY_MASK) == + (symbol & LZW_SYMBOL_KEY_MASK)) + { + *slot_ret = &table->table[idx]; + return TRUE; + } + } + + if (step == 0) { + step = hash % LZW_SYMBOL_MOD2; + if (step == 0) + step = 1; + } + + idx += step; + if (idx >= LZW_SYMBOL_TABLE_SIZE) + idx -= LZW_SYMBOL_TABLE_SIZE; + } + + return FALSE; +} + +/* Compress a bytestream using the LZW algorithm. + * + * This is an original implementation based on reading the + * specification of the LZWDecode filter in the PostScript Language + * Reference. The free parameters in the LZW algorithm are set to the + * values mandated by PostScript, (symbols encoded with widths from 9 + * to 12 bits). + * + * This function returns a pointer to a newly allocated buffer holding + * the compressed data, or %NULL if an out-of-memory situation + * occurs. + * + * Notice that any one of the _lzw_buf functions called here could + * trigger an out-of-memory condition. But lzw_buf_t uses cairo's + * shutdown-on-error idiom, so it's safe to continue to call into + * lzw_buf without having to check for errors, (until a final check at + * the end). + */ +unsigned char * +_cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out) +{ + int bytes_remaining = *size_in_out; + lzw_buf_t buf; + lzw_symbol_table_t table; + lzw_symbol_t symbol, *slot = NULL; /* just to squelch a warning */ + int code_next = LZW_CODE_FIRST; + int code_bits = LZW_BITS_MIN; + int prev, next = 0; /* just to squelch a warning */ + + if (*size_in_out == 0) + return NULL; + + _lzw_buf_init (&buf, *size_in_out); + + _lzw_symbol_table_init (&table); + + /* The LZW header is a clear table code. */ + _lzw_buf_store_bits (&buf, LZW_CODE_CLEAR_TABLE, code_bits); + + while (1) { + + /* Find the longest existing code in the symbol table that + * matches the current input, if any. */ + prev = *data++; + bytes_remaining--; + if (bytes_remaining) { + do + { + next = *data++; + bytes_remaining--; + LZW_SYMBOL_SET (symbol, prev, next); + if (_lzw_symbol_table_lookup (&table, symbol, &slot)) + prev = LZW_SYMBOL_GET_CODE (*slot); + } while (bytes_remaining && *slot != LZW_SYMBOL_FREE); + if (*slot == LZW_SYMBOL_FREE) { + data--; + bytes_remaining++; + } + } + + /* Write the code into the output. This is either a byte read + * directly from the input, or a code from the last successful + * lookup. */ + _lzw_buf_store_bits (&buf, prev, code_bits); + + if (bytes_remaining == 0) + break; + + LZW_SYMBOL_SET_CODE (*slot, code_next++, prev, next); + + if (code_next > LZW_BITS_BOUNDARY(code_bits)) + { + code_bits++; + if (code_bits > LZW_BITS_MAX) { + _lzw_symbol_table_init (&table); + _lzw_buf_store_bits (&buf, LZW_CODE_CLEAR_TABLE, code_bits - 1); + code_bits = LZW_BITS_MIN; + code_next = LZW_CODE_FIRST; + } + } + } + + /* The LZW footer is an end-of-data code. */ + _lzw_buf_store_bits (&buf, LZW_CODE_EOD, code_bits); + + _lzw_buf_store_pending (&buf); + + /* See if we ever ran out of memory while writing to buf. */ + if (buf.status == CAIRO_STATUS_NO_MEMORY) { + *size_in_out = 0; + return NULL; + } + + assert (buf.status == CAIRO_STATUS_SUCCESS); + + *size_in_out = buf.num_data; + return buf.data; +} diff --git a/gfx/cairo/cairo/src/cairo-malloc-private.h b/gfx/cairo/cairo/src/cairo-malloc-private.h new file mode 100644 index 0000000000..570f7cb0ee --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-malloc-private.h @@ -0,0 +1,149 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Mozilla Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 Foundation + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#ifndef CAIRO_MALLOC_PRIVATE_H +#define CAIRO_MALLOC_PRIVATE_H + +#include "cairo-wideint-private.h" +#include + +#if HAVE_MEMFAULT +#include +#define CAIRO_INJECT_FAULT() MEMFAULT_INJECT_FAULT() +#else +#define CAIRO_INJECT_FAULT() 0 +#endif + +/** + * _cairo_malloc: + * @size: size in bytes + * + * Allocate @size memory using malloc(). + * The memory should be freed using free(). + * malloc is skipped, if 0 bytes are requested, and %NULL will be returned. + * + * 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) > 0 ? malloc((unsigned) (size)) : NULL) + +/** + * _cairo_malloc_ab: + * @a: number of elements to allocate + * @size: size of each element + * + * 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(). + * + * @size should be a constant so that the compiler can optimize + * out a constant division. + * + * 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 : \ + _cairo_malloc((unsigned) (a) * (unsigned) (size))) + +/** + * _cairo_realloc_ab: + * @ptr: original pointer to block of memory to be resized + * @a: number of elements to allocate + * @size: size of each element + * + * 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(). + * + * @size should be a constant so that the compiler can optimize + * out a constant division. + * + * 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 : \ + realloc(ptr, (unsigned) (a) * (unsigned) (size))) + +/** + * _cairo_malloc_abc: + * @a: first factor of number of elements to allocate + * @b: second factor of number of elements to allocate + * @size: size of each element + * + * 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(). + * + * @size should be a constant so that the compiler can optimize + * out a constant division. + * + * 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 : \ + (size) && (unsigned) ((a)*(b)) >= INT32_MAX / (unsigned) (size) ? NULL : \ + _cairo_malloc((unsigned) (a) * (unsigned) (b) * (unsigned) (size))) + +/** + * _cairo_malloc_ab_plus_c: + * @a: number of elements to allocate + * @size: size of each element + * @c: additional size to allocate + * + * 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(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 0000000000..4d6b118dda --- /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 new file mode 100644 index 0000000000..f3cf684c9d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-matrix.c @@ -0,0 +1,1206 @@ +/* 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 + */ + +#include "cairoint.h" +#include "cairo-error-private.h" +#include + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + +/** + * SECTION:cairo-matrix + * @Title: cairo_matrix_t + * @Short_Description: Generic matrix operations + * @See_Also: #cairo_t + * + * #cairo_matrix_t is used throughout cairo to convert between different + * coordinate spaces. A #cairo_matrix_t holds an affine transformation, + * such as a scale, rotation, shear, or a combination of these. + * The transformation of a point (x,y) + * is given by: + * + * + * x_new = xx * x + xy * y + x0; + * y_new = yx * x + yy * y + y0; + * + * + * The current transformation matrix of a #cairo_t, represented as a + * #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); + +static void +_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix); + +/** + * cairo_matrix_init_identity: + * @matrix: a #cairo_matrix_t + * + * Modifies @matrix to be an identity transformation. + * + * Since: 1.0 + **/ +void +cairo_matrix_init_identity (cairo_matrix_t *matrix) +{ + cairo_matrix_init (matrix, + 1, 0, + 0, 1, + 0, 0); +} +slim_hidden_def(cairo_matrix_init_identity); + +/** + * cairo_matrix_init: + * @matrix: a #cairo_matrix_t + * @xx: xx component of the affine transformation + * @yx: yx component of the affine transformation + * @xy: xy component of the affine transformation + * @yy: yy component of the affine transformation + * @x0: X translation component of the affine transformation + * @y0: Y translation component of the affine transformation + * + * Sets @matrix to be the affine transformation given by + * @xx, @yx, @xy, @yy, @x0, @y0. The transformation is given + * by: + * + * 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, + double xx, double yx, + + double xy, double yy, + double x0, double y0) +{ + matrix->xx = xx; matrix->yx = yx; + matrix->xy = xy; matrix->yy = yy; + matrix->x0 = x0; matrix->y0 = y0; +} +slim_hidden_def(cairo_matrix_init); + +/** + * _cairo_matrix_get_affine: + * @matrix: a #cairo_matrix_t + * @xx: location to store xx component of matrix + * @yx: location to store yx component of matrix + * @xy: location to store xy component of matrix + * @yy: location to store yy component of matrix + * @x0: location to store x0 (X-translation component) of matrix, or %NULL + * @y0: location to store y0 (Y-translation component) of matrix, or %NULL + * + * Gets the matrix values for the affine transformation that @matrix represents. + * See cairo_matrix_init(). + * + * + * This function is a leftover from the old public API, but is still + * mildly useful as an internal means for getting at the matrix + * members in a positional way. For example, when reassigning to some + * external matrix type, or when renaming members to more meaningful + * names (such as a,b,c,d,e,f) for particular manipulations. + **/ +void +_cairo_matrix_get_affine (const cairo_matrix_t *matrix, + double *xx, double *yx, + double *xy, double *yy, + double *x0, double *y0) +{ + *xx = matrix->xx; + *yx = matrix->yx; + + *xy = matrix->xy; + *yy = matrix->yy; + + if (x0) + *x0 = matrix->x0; + if (y0) + *y0 = matrix->y0; +} + +/** + * cairo_matrix_init_translate: + * @matrix: a #cairo_matrix_t + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction + * + * 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, + double tx, double ty) +{ + cairo_matrix_init (matrix, + 1, 0, + 0, 1, + tx, ty); +} +slim_hidden_def(cairo_matrix_init_translate); + +/** + * cairo_matrix_translate: + * @matrix: a #cairo_matrix_t + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction + * + * Applies a translation by @tx, @ty to the transformation in + * @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) +{ + cairo_matrix_t tmp; + + cairo_matrix_init_translate (&tmp, tx, ty); + + cairo_matrix_multiply (matrix, &tmp, matrix); +} +slim_hidden_def (cairo_matrix_translate); + +/** + * cairo_matrix_init_scale: + * @matrix: a #cairo_matrix_t + * @sx: scale factor in the X direction + * @sy: scale factor in the Y direction + * + * 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, + double sx, double sy) +{ + cairo_matrix_init (matrix, + sx, 0, + 0, sy, + 0, 0); +} +slim_hidden_def(cairo_matrix_init_scale); + +/** + * cairo_matrix_scale: + * @matrix: a #cairo_matrix_t + * @sx: scale factor in the X direction + * @sy: scale factor in the Y direction + * + * 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) +{ + cairo_matrix_t tmp; + + cairo_matrix_init_scale (&tmp, sx, sy); + + cairo_matrix_multiply (matrix, &tmp, matrix); +} +slim_hidden_def(cairo_matrix_scale); + +/** + * cairo_matrix_init_rotate: + * @matrix: a #cairo_matrix_t + * @radians: angle of rotation, in radians. The direction of rotation + * is defined such that positive angles rotate in the direction from + * the positive X axis toward the positive Y axis. With the default + * axis orientation of cairo, positive angles rotate in a clockwise + * direction. + * + * Initialized @matrix to a transformation that rotates by @radians. + * + * Since: 1.0 + **/ +void +cairo_matrix_init_rotate (cairo_matrix_t *matrix, + double radians) +{ + double s; + double c; + + s = sin (radians); + c = cos (radians); + + cairo_matrix_init (matrix, + c, s, + -s, c, + 0, 0); +} +slim_hidden_def(cairo_matrix_init_rotate); + +/** + * cairo_matrix_rotate: + * @matrix: a #cairo_matrix_t + * @radians: angle of rotation, in radians. The direction of rotation + * is defined such that positive angles rotate in the direction from + * the positive X axis toward the positive Y axis. With the default + * axis orientation of cairo, positive angles rotate in a clockwise + * direction. + * + * Applies rotation by @radians to the transformation in + * @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) +{ + cairo_matrix_t tmp; + + cairo_matrix_init_rotate (&tmp, radians); + + cairo_matrix_multiply (matrix, &tmp, matrix); +} + +/** + * cairo_matrix_multiply: + * @result: a #cairo_matrix_t in which to store the result + * @a: a #cairo_matrix_t + * @b: a #cairo_matrix_t + * + * Multiplies the affine transformations in @a and @b together + * and stores the result in @result. The effect of the resulting + * transformation is to first apply the transformation in @a to the + * coordinates and then apply the transformation in @b to the + * 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 + * to [row_vector]*A*B. If we want to use column vectors instead, + * then we need to switch the two arguments and fix up all + * uses. + */ +void +cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b) +{ + cairo_matrix_t r; + + 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; + + *result = r; +} +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 + * @dx: X component of a distance vector. An in/out parameter + * @dy: Y component of a distance vector. An in/out parameter + * + * Transforms the distance vector (@dx,@dy) by @matrix. This is + * similar to cairo_matrix_transform_point() except that the translation + * components of the transformation are ignored. The calculation of + * the returned vector is as follows: + * + * + * dx2 = dx1 * a + dy1 * c; + * dy2 = dx1 * b + dy1 * d; + * + * + * Affine transformations are position invariant, so the same vector + * always transforms to the same vector. If (@x1,@y1) transforms + * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to + * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2. + * + * Since: 1.0 + **/ +void +cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy) +{ + double new_x, new_y; + + new_x = (matrix->xx * *dx + matrix->xy * *dy); + new_y = (matrix->yx * *dx + matrix->yy * *dy); + + *dx = new_x; + *dy = new_y; +} +slim_hidden_def(cairo_matrix_transform_distance); + +/** + * cairo_matrix_transform_point: + * @matrix: a #cairo_matrix_t + * @x: X position. An in/out parameter + * @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) +{ + cairo_matrix_transform_distance (matrix, x, y); + + *x += matrix->x0; + *y += matrix->y0; +} +slim_hidden_def(cairo_matrix_transform_point); + +void +_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, + double *x1, double *y1, + double *x2, double *y2, + cairo_bool_t *is_tight) +{ + int i; + double quad_x[4], quad_y[4]; + double min_x, max_x; + double min_y, max_y; + + if (matrix->xy == 0. && matrix->yx == 0.) { + /* non-rotation/skew matrix, just map the two extreme points */ + + if (matrix->xx != 1.) { + quad_x[0] = *x1 * matrix->xx; + quad_x[1] = *x2 * matrix->xx; + if (quad_x[0] < quad_x[1]) { + *x1 = quad_x[0]; + *x2 = quad_x[1]; + } else { + *x1 = quad_x[1]; + *x2 = quad_x[0]; + } + } + if (matrix->x0 != 0.) { + *x1 += matrix->x0; + *x2 += matrix->x0; + } + + if (matrix->yy != 1.) { + quad_y[0] = *y1 * matrix->yy; + quad_y[1] = *y2 * matrix->yy; + if (quad_y[0] < quad_y[1]) { + *y1 = quad_y[0]; + *y2 = quad_y[1]; + } else { + *y1 = quad_y[1]; + *y2 = quad_y[0]; + } + } + if (matrix->y0 != 0.) { + *y1 += matrix->y0; + *y2 += matrix->y0; + } + + if (is_tight) + *is_tight = TRUE; + + return; + } + + /* general matrix */ + quad_x[0] = *x1; + quad_y[0] = *y1; + cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]); + + quad_x[1] = *x2; + quad_y[1] = *y1; + cairo_matrix_transform_point (matrix, &quad_x[1], &quad_y[1]); + + quad_x[2] = *x1; + quad_y[2] = *y2; + cairo_matrix_transform_point (matrix, &quad_x[2], &quad_y[2]); + + quad_x[3] = *x2; + quad_y[3] = *y2; + cairo_matrix_transform_point (matrix, &quad_x[3], &quad_y[3]); + + min_x = max_x = quad_x[0]; + min_y = max_y = quad_y[0]; + + for (i=1; i < 4; i++) { + if (quad_x[i] < min_x) + min_x = quad_x[i]; + if (quad_x[i] > max_x) + max_x = quad_x[i]; + + if (quad_y[i] < min_y) + min_y = quad_y[i]; + if (quad_y[i] > max_y) + max_y = quad_y[i]; + } + + *x1 = min_x; + *y1 = min_y; + *x2 = max_x; + *y2 = max_y; + + if (is_tight) { + /* it's tight if and only if the four corner points form an axis-aligned + rectangle. + And that's true if and only if we can derive corners 0 and 3 from + corners 1 and 2 in one of two straightforward ways... + We could use a tolerance here but for now we'll fall back to FALSE in the case + of floating point error. + */ + *is_tight = + (quad_x[1] == quad_x[0] && quad_y[1] == quad_y[3] && + quad_x[2] == quad_x[3] && quad_y[2] == quad_y[0]) || + (quad_x[1] == quad_x[3] && quad_y[1] == quad_y[0] && + quad_x[2] == quad_x[0] && quad_y[2] == quad_y[3]); + } +} + +cairo_private void +_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, + cairo_box_t *bbox, + cairo_bool_t *is_tight) +{ + double x1, y1, x2, y2; + + _cairo_box_to_doubles (bbox, &x1, &y1, &x2, &y2); + _cairo_matrix_transform_bounding_box (matrix, &x1, &y1, &x2, &y2, is_tight); + _cairo_box_from_doubles (bbox, &x1, &y1, &x2, &y2); +} + +static void +_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar) +{ + matrix->xx *= scalar; + matrix->yx *= scalar; + + matrix->xy *= scalar; + matrix->yy *= scalar; + + matrix->x0 *= scalar; + matrix->y0 *= scalar; +} + +/* This function isn't a correct adjoint in that the implicit 1 in the + homogeneous result should actually be ad-bc instead. But, since this + adjoint is only used in the computation of the inverse, which + divides by det (A)=ad-bc anyway, everything works out in the end. */ +static void +_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix) +{ + /* adj (A) = transpose (C:cofactor (A,i,j)) */ + double a, b, c, d, tx, ty; + + _cairo_matrix_get_affine (matrix, + &a, &b, + &c, &d, + &tx, &ty); + + cairo_matrix_init (matrix, + d, -b, + -c, a, + c*ty - d*tx, b*tx - a*ty); +} + +/** + * cairo_matrix_invert: + * @matrix: a #cairo_matrix_t + * + * Changes @matrix to be the inverse of its original value. Not + * all transformation matrices have inverses; if the matrix + * collapses points together (it is degenerate), + * then it has no inverse and this function will fail. + * + * 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) +{ + double det; + + /* Simple scaling|translation matrices are quite common... */ + if (matrix->xy == 0. && matrix->yx == 0.) { + matrix->x0 = -matrix->x0; + matrix->y0 = -matrix->y0; + + if (matrix->xx != 1.) { + if (matrix->xx == 0.) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + matrix->xx = 1. / matrix->xx; + matrix->x0 *= matrix->xx; + } + + if (matrix->yy != 1.) { + if (matrix->yy == 0.) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + matrix->yy = 1. / matrix->yy; + matrix->y0 *= matrix->yy; + } + + return CAIRO_STATUS_SUCCESS; + } + + /* inv (A) = 1/det (A) * adj (A) */ + det = _cairo_matrix_compute_determinant (matrix); + + if (! ISFINITE (det)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + if (det == 0) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + _cairo_matrix_compute_adjoint (matrix); + _cairo_matrix_scalar_multiply (matrix, 1 / det); + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def(cairo_matrix_invert); + +cairo_bool_t +_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) +{ + double det; + + det = _cairo_matrix_compute_determinant (matrix); + + return ISFINITE (det) && det != 0.; +} + +cairo_bool_t +_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) +{ + return matrix->xx == 0. && + matrix->xy == 0. && + matrix->yx == 0. && + matrix->yy == 0.; +} + +double +_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) +{ + double a, b, c, d; + + a = matrix->xx; b = matrix->yx; + c = matrix->xy; d = matrix->yy; + + return a*d - b*c; +} + +/** + * _cairo_matrix_compute_basis_scale_factors: + * @matrix: a matrix + * @basis_scale: the scale factor in the direction of basis + * @normal_scale: the scale factor in the direction normal to the basis + * @x_basis: basis to use. X basis if true, Y basis otherwise. + * + * Computes |Mv| and det(M)/|Mv| for v=[1,0] if x_basis is true, and v=[0,1] + * otherwise, and M is @matrix. + * + * Return value: the scale factor of @matrix on the height of the font, + * or 1.0 if @matrix is %NULL. + **/ +cairo_status_t +_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, + double *basis_scale, double *normal_scale, + cairo_bool_t x_basis) +{ + double det; + + det = _cairo_matrix_compute_determinant (matrix); + + if (! ISFINITE (det)) + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + + if (det == 0) + { + *basis_scale = *normal_scale = 0; + } + else + { + double x = x_basis != 0; + double y = x == 0; + double major, minor; + + cairo_matrix_transform_distance (matrix, &x, &y); + major = hypot (x, y); + /* + * ignore mirroring + */ + if (det < 0) + det = -det; + if (major) + minor = det / major; + else + minor = 0.0; + if (x_basis) + { + *basis_scale = major; + *normal_scale = minor; + } + else + { + *basis_scale = minor; + *normal_scale = major; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, + int *itx, int *ity) +{ + if (_cairo_matrix_is_translation (matrix)) + { + cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0); + cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0); + + if (_cairo_fixed_is_integer (x0_fixed) && + _cairo_fixed_is_integer (y0_fixed)) + { + if (itx) + *itx = _cairo_fixed_integer_part (x0_fixed); + if (ity) + *ity = _cairo_fixed_integer_part (y0_fixed); + + return TRUE; + } + } + + 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) +{ + /* 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 + * 90 degree rotations, flips, and integer translations and produces a 1:1 + * mapping between source and destination pixels. If we transform an image + * with a pixel-exact matrix, filtering is not useful. + */ +cairo_bool_t +_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) +{ + cairo_fixed_t x0_fixed, y0_fixed; + + if (! _cairo_matrix_has_unity_scale (matrix)) + return FALSE; + + x0_fixed = _cairo_fixed_from_double (matrix->x0); + y0_fixed = _cairo_fixed_from_double (matrix->y0); + + return _cairo_fixed_is_integer (x0_fixed) && _cairo_fixed_is_integer (y0_fixed); +} + +/* + A circle in user space is transformed into an ellipse in device space. + + The following is a derivation of a formula to calculate the length of the + major axis for this ellipse; this is useful for error bounds calculations. + + Thanks to Walter Brisken for this derivation: + + 1. First some notation: + + All capital letters represent vectors in two dimensions. A prime ' + represents a transformed coordinate. Matrices are written in underlined + form, ie _R_. Lowercase letters represent scalar real values. + + 2. The question has been posed: What is the maximum expansion factor + achieved by the linear transformation + + X' = X _R_ + + where _R_ is a real-valued 2x2 matrix with entries: + + _R_ = [a b] + [c d] . + + In other words, what is the maximum radius, MAX[ |X'| ], reached for any + X on the unit circle ( |X| = 1 ) ? + + 3. Some useful formulae + + (A) through (C) below are standard double-angle formulae. (D) is a lesser + known result and is derived below: + + (A) sin²(θ) = (1 - cos(2*θ))/2 + (B) cos²(θ) = (1 + cos(2*θ))/2 + (C) sin(θ)*cos(θ) = sin(2*θ)/2 + (D) MAX[a*cos(θ) + b*sin(θ)] = sqrt(a² + b²) + + Proof of (D): + + find the maximum of the function by setting the derivative to zero: + + -a*sin(θ)+b*cos(θ) = 0 + + From this it follows that + + tan(θ) = b/a + + and hence + + sin(θ) = b/sqrt(a² + b²) + + and + + cos(θ) = a/sqrt(a² + b²) + + Thus the maximum value is + + MAX[a*cos(θ) + b*sin(θ)] = (a² + b²)/sqrt(a² + b²) + = sqrt(a² + b²) + + 4. Derivation of maximum expansion + + To find MAX[ |X'| ] we search brute force method using calculus. The unit + circle on which X is constrained is to be parameterized by t: + + X(θ) = (cos(θ), sin(θ)) + + Thus + + X'(θ) = X(θ) * _R_ = (cos(θ), sin(θ)) * [a b] + [c d] + = (a*cos(θ) + c*sin(θ), b*cos(θ) + d*sin(θ)). + + Define + + r(θ) = |X'(θ)| + + Thus + + r²(θ) = (a*cos(θ) + c*sin(θ))² + (b*cos(θ) + d*sin(θ))² + = (a² + b²)*cos²(θ) + (c² + d²)*sin²(θ) + + 2*(a*c + b*d)*cos(θ)*sin(θ) + + Now apply the double angle formulae (A) to (C) from above: + + r²(θ) = (a² + b² + c² + d²)/2 + + (a² + b² - c² - d²)*cos(2*θ)/2 + + (a*c + b*d)*sin(2*θ) + = f + g*cos(φ) + h*sin(φ) + + Where + + f = (a² + b² + c² + d²)/2 + g = (a² + b² - c² - d²)/2 + h = (a*c + d*d) + φ = 2*θ + + It is clear that MAX[ |X'| ] = sqrt(MAX[ r² ]). Here we determine MAX[ r² ] + using (D) from above: + + MAX[ r² ] = f + sqrt(g² + h²) + + And finally + + MAX[ |X'| ] = sqrt( f + sqrt(g² + h²) ) + + Which is the solution to this problem. + + Walter Brisken + 2004/10/08 + + (Note that the minor axis length is at the minimum of the above solution, + which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)). + + + For another derivation of the same result, using Singular Value Decomposition, + see doc/tutorial/src/singular.c. +*/ + +/* 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; + + if (_cairo_matrix_has_unity_scale (matrix)) + return radius; + + _cairo_matrix_get_affine (matrix, + &a, &b, + &c, &d, + NULL, NULL); + + i = a*a + b*b; + j = c*c + d*d; + + f = 0.5 * (i + j); + g = 0.5 * (i - j); + h = a*c + b*d; + + 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)); + */ +} + +static const pixman_transform_t pixman_identity_transform = {{ + {1 << 16, 0, 0}, + { 0, 1 << 16, 0}, + { 0, 0, 1 << 16} + }}; + +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[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; + + /* 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 (dx == 0 && dy == 0) + return CAIRO_STATUS_SUCCESS; + } while (--max_iterations); + + /* We didn't find an exact match between cairo and pixman, but + * the matrix should be mostly correct */ + return CAIRO_STATUS_SUCCESS; +} + +static inline double +_pixman_nearest_sample (double d) +{ + return ceil (d - .5); +} + +/** + * _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 (!_cairo_matrix_is_translation (matrix)) + return FALSE; + + if (matrix->x0 == 0. && matrix->y0 == 0.) + return TRUE; + + 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. + */ + tx = m.x0; + ty = m.y0; + norm = MAX (fabs (tx), fabs (ty)); + + 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 0000000000..a09f6ce51f --- /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 0000000000..ce40ce521f --- /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 0000000000..e7f0db666a --- /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-misc.c b/gfx/cairo/cairo/src/cairo-misc.c new file mode 100644 index 0000000000..ac4d446224 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-misc.c @@ -0,0 +1,1123 @@ +/* -*- 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 © 2007 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Adrian Johnson + */ + +#define _GNU_SOURCE 1 /* strtod_l() */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +#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); + +/** + * SECTION:cairo-status + * @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_region_status() + * + * Cairo uses a single status type to represent all kinds of errors. A status + * value of %CAIRO_STATUS_SUCCESS represents no error and has an integer value + * of zero. All other status values represent an error. + * + * Cairo's error handling is designed to be easy to use and safe. All major + * cairo objects retain an error status internally which + * can be queried anytime by the users using cairo*_status() calls. In + * 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 */ + +/** + * cairo_status_to_string: + * @status: a cairo status + * + * 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) +{ + switch (status) { + case CAIRO_STATUS_SUCCESS: + return "no error has occurred"; + case CAIRO_STATUS_NO_MEMORY: + return "out of memory"; + case CAIRO_STATUS_INVALID_RESTORE: + return "cairo_restore() without matching cairo_save()"; + case CAIRO_STATUS_INVALID_POP_GROUP: + return "no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()"; + case CAIRO_STATUS_NO_CURRENT_POINT: + return "no current point defined"; + case CAIRO_STATUS_INVALID_MATRIX: + return "invalid matrix (not invertible)"; + case CAIRO_STATUS_INVALID_STATUS: + return "invalid value for an input cairo_status_t"; + case CAIRO_STATUS_NULL_POINTER: + return "NULL pointer"; + case CAIRO_STATUS_INVALID_STRING: + return "input string not valid UTF-8"; + case CAIRO_STATUS_INVALID_PATH_DATA: + return "input path data not valid"; + case CAIRO_STATUS_READ_ERROR: + return "error while reading from input stream"; + case CAIRO_STATUS_WRITE_ERROR: + return "error while writing to output stream"; + case CAIRO_STATUS_SURFACE_FINISHED: + return "the target surface has been finished"; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + return "the surface type is not appropriate for the operation"; + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: + return "the pattern type is not appropriate for the operation"; + case CAIRO_STATUS_INVALID_CONTENT: + return "invalid value for an input cairo_content_t"; + case CAIRO_STATUS_INVALID_FORMAT: + return "invalid value for an input cairo_format_t"; + case CAIRO_STATUS_INVALID_VISUAL: + return "invalid value for an input Visual*"; + case CAIRO_STATUS_FILE_NOT_FOUND: + return "file not found"; + case CAIRO_STATUS_INVALID_DASH: + return "invalid value for a dash setting"; + case CAIRO_STATUS_INVALID_DSC_COMMENT: + return "invalid value for a DSC comment"; + case CAIRO_STATUS_INVALID_INDEX: + return "invalid index passed to getter"; + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: + return "clip region not representable in desired format"; + case CAIRO_STATUS_TEMP_FILE_ERROR: + return "error creating or writing to a temporary file"; + case CAIRO_STATUS_INVALID_STRIDE: + return "invalid value for stride"; + case CAIRO_STATUS_FONT_TYPE_MISMATCH: + return "the font type is not appropriate for the operation"; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + return "the user-font is immutable"; + case CAIRO_STATUS_USER_FONT_ERROR: + return "error occurred in a user-font callback function"; + case CAIRO_STATUS_NEGATIVE_COUNT: + return "negative number used where it is not allowed"; + case CAIRO_STATUS_INVALID_CLUSTERS: + return "input clusters do not represent the accompanying text and glyph arrays"; + case CAIRO_STATUS_INVALID_SLANT: + return "invalid value for an input cairo_font_slant_t"; + case CAIRO_STATUS_INVALID_WEIGHT: + return "invalid value for an input cairo_font_weight_t"; + case CAIRO_STATUS_INVALID_SIZE: + return "invalid value (typically too big) for the size of the input (surface, pattern, etc.)"; + case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: + return "user-font method not implemented"; + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: + 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 ""; + } +} + + +/** + * cairo_glyph_allocate: + * @num_glyphs: number of glyphs to allocate + * + * Allocates an array of #cairo_glyph_t's. + * This function is only useful in implementations of + * #cairo_user_scaled_font_text_to_glyphs_func_t where the user + * needs to allocate an array of glyphs that cairo will free. + * For all other uses, user can use their own allocation method + * for glyphs. + * + * This function returns %NULL if @num_glyphs is not positive, + * or if out of memory. That means, the %NULL return value + * signals out-of-memory only if @num_glyphs was positive. + * + * Returns: the newly allocated array of glyphs that should be + * freed using cairo_glyph_free() + * + * Since: 1.8 + **/ +cairo_glyph_t * +cairo_glyph_allocate (int num_glyphs) +{ + if (num_glyphs <= 0) + return NULL; + + return _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); +} +slim_hidden_def (cairo_glyph_allocate); + +/** + * cairo_glyph_free: + * @glyphs: array of glyphs to free, or %NULL + * + * Frees an array of #cairo_glyph_t's allocated using cairo_glyph_allocate(). + * This function is only useful to free glyph array returned + * by cairo_scaled_font_text_to_glyphs() where cairo returns + * an array of glyphs that the user will free. + * For all other uses, user can use their own allocation method + * for glyphs. + * + * Since: 1.8 + **/ +void +cairo_glyph_free (cairo_glyph_t *glyphs) +{ + free (glyphs); +} +slim_hidden_def (cairo_glyph_free); + +/** + * cairo_text_cluster_allocate: + * @num_clusters: number of text_clusters to allocate + * + * Allocates an array of #cairo_text_cluster_t's. + * This function is only useful in implementations of + * #cairo_user_scaled_font_text_to_glyphs_func_t where the user + * needs to allocate an array of text clusters that cairo will free. + * For all other uses, user can use their own allocation method + * for text clusters. + * + * This function returns %NULL if @num_clusters is not positive, + * or if out of memory. That means, the %NULL return value + * signals out-of-memory only if @num_clusters was positive. + * + * Returns: the newly allocated array of text clusters that should be + * freed using cairo_text_cluster_free() + * + * Since: 1.8 + **/ +cairo_text_cluster_t * +cairo_text_cluster_allocate (int num_clusters) +{ + if (num_clusters <= 0) + return NULL; + + return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); +} +slim_hidden_def (cairo_text_cluster_allocate); + +/** + * cairo_text_cluster_free: + * @clusters: array of text clusters to free, or %NULL + * + * Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate(). + * This function is only useful to free text cluster array returned + * by cairo_scaled_font_text_to_glyphs() where cairo returns + * an array of text clusters that the user will free. + * For all other uses, user can use their own allocation method + * for text clusters. + * + * Since: 1.8 + **/ +void +cairo_text_cluster_free (cairo_text_cluster_t *clusters) +{ + free (clusters); +} +slim_hidden_def (cairo_text_cluster_free); + + +/* Private stuff */ + +/** + * _cairo_validate_text_clusters: + * @utf8: UTF-8 text + * @utf8_len: length of @utf8 in bytes + * @glyphs: array of glyphs + * @num_glyphs: number of glyphs + * @clusters: array of cluster mapping information + * @num_clusters: number of clusters in the mapping + * @cluster_flags: cluster flags + * + * Check that clusters cover the entire glyphs and utf8 arrays, + * and that cluster boundaries are UTF-8 boundaries. + * + * Return value: %CAIRO_STATUS_SUCCESS upon success, or + * %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, + 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_status_t status; + unsigned int n_bytes = 0; + unsigned int n_glyphs = 0; + int i; + + for (i = 0; i < num_clusters; i++) { + int cluster_bytes = clusters[i].num_bytes; + int cluster_glyphs = clusters[i].num_glyphs; + + if (cluster_bytes < 0 || cluster_glyphs < 0) + goto BAD; + + /* A cluster should cover at least one character or glyph. + * I can't see any use for a 0,0 cluster. + * I can't see an immediate use for a zero-text cluster + * right now either, but they don't harm. + * Zero-glyph clusters on the other hand are useful for + * things like U+200C ZERO WIDTH NON-JOINER */ + if (cluster_bytes == 0 && cluster_glyphs == 0) + goto BAD; + + /* Since n_bytes and n_glyphs are unsigned, but the rest of + * values involved are signed, we can detect overflow easily */ + if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs) + goto BAD; + + /* Make sure we've got valid UTF-8 for the cluster */ + status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL); + if (unlikely (status)) + return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); + + n_bytes += cluster_bytes ; + n_glyphs += cluster_glyphs; + } + + if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) { + BAD: + return _cairo_error (CAIRO_STATUS_INVALID_CLUSTERS); + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_operator_bounded_by_mask: + * @op: a #cairo_operator_t + * + * A bounded operator is one where mask pixel + * of zero results in no effect on the destination image. + * + * Unbounded operators often require special handling; if you, for + * example, draw trapezoids with an unbounded operator, the effect + * extends past the bounding box of the trapezoids. + * + * Return value: %TRUE if the operator is bounded by the mask operand + **/ +cairo_bool_t +_cairo_operator_bounded_by_mask (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_OUT: + 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: + return TRUE; + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_ATOP: + return FALSE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +/** + * _cairo_operator_bounded_by_source: + * @op: a #cairo_operator_t + * + * A bounded operator is one where source pixels of zero + * (in all four components, r, g, b and a) effect no change + * in the resulting destination image. + * + * Unbounded operators often require special handling; if you, for + * example, copy a surface with the SOURCE operator, the effect + * extends past the bounding box of the source surface. + * + * Return value: %TRUE if the operator is bounded by the source operand + **/ +cairo_bool_t +_cairo_operator_bounded_by_source (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_OUT: + 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: + return TRUE; + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_ATOP: + return FALSE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +uint32_t +_cairo_operator_bounded_by_either (cairo_operator_t op) +{ + switch (op) { + default: + ASSERT_NOT_REACHED; + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_ATOP: + case CAIRO_OPERATOR_DEST: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_OUT: + 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: + return CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE; + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + return CAIRO_OPERATOR_BOUND_BY_MASK; + case CAIRO_OPERATOR_OUT: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_DEST_ATOP: + return 0; + } + +} + +#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 + * [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems + * than other commonly used methods for rounding (lround, round, rint, lrint + * or float (d + 0.5)). + * + * The reason why this function is much faster on x86 than other + * methods is due to the fact that it avoids the fldcw instruction. + * This instruction incurs a large performance penalty on modern Intel + * processors due to how it prevents efficient instruction pipelining. + * + * The reason why this function is much faster on FPU-less systems is for + * an entirely different reason. All common rounding methods involve multiple + * floating-point operations. Each one of these operations has to be + * emulated in software, which adds up to be a large performance penalty. + * This function doesn't perform any floating-point calculations, and thus + * avoids this penalty. + */ +int +_cairo_lround (double d) +{ + uint32_t top, shift_amount, output; + union { + double d; + uint64_t ui64; + uint32_t ui32[2]; + } u; + + u.d = d; + + /* If the integer word order doesn't match the float word order, we swap + * the words of the input double. This is needed because we will be + * treating the whole double as a 64-bit unsigned integer. Notice that we + * use WORDS_BIGENDIAN to detect the integer word order, which isn't + * exactly correct because WORDS_BIGENDIAN refers to byte order, not word + * order. Thus, we are making the assumption that the byte order is the + * same as the integer word order which, on the modern machines that we + * care about, is OK. + */ +#if ( defined(FLOAT_WORDS_BIGENDIAN) && !defined(WORDS_BIGENDIAN)) || \ + (!defined(FLOAT_WORDS_BIGENDIAN) && defined(WORDS_BIGENDIAN)) + { + uint32_t temp = u.ui32[0]; + u.ui32[0] = u.ui32[1]; + u.ui32[1] = temp; + } +#endif + +#ifdef WORDS_BIGENDIAN + #define MSW (0) /* Most Significant Word */ + #define LSW (1) /* Least Significant Word */ +#else + #define MSW (1) + #define LSW (0) +#endif + + /* By shifting the most significant word of the input double to the + * right 20 places, we get the very "top" of the double where the exponent + * and sign bit lie. + */ + top = u.ui32[MSW] >> 20; + + /* Here, we calculate how much we have to shift the mantissa to normalize + * it to an integer value. We extract the exponent "top" by masking out the + * sign bit, then we calculate the shift amount by subtracting the exponent + * from the bias. Notice that the correct bias for 64-bit doubles is + * actually 1075, but we use 1053 instead for two reasons: + * + * 1) To perform rounding later on, we will first need the target + * value in a 31.1 fixed-point format. Thus, the bias needs to be one + * less: (1075 - 1: 1074). + * + * 2) To avoid shifting the mantissa as a full 64-bit integer (which is + * costly on certain architectures), we break the shift into two parts. + * First, the upper and lower parts of the mantissa are shifted + * individually by a constant amount that all valid inputs will require + * at the very least. This amount is chosen to be 21, because this will + * allow the two parts of the mantissa to later be combined into a + * single 32-bit representation, on which the remainder of the shift + * will be performed. Thus, we decrease the bias by an additional 21: + * (1074 - 21: 1053). + */ + shift_amount = 1053 - (top & 0x7FF); + + /* We are done with the exponent portion in "top", so here we shift it off + * the end. + */ + top >>= 11; + + /* Before we perform any operations on the mantissa, we need to OR in + * the implicit 1 at the top (see the IEEE-754 spec). We needn't mask + * off the sign bit nor the exponent bits because these higher bits won't + * make a bit of difference in the rest of our calculations. + */ + u.ui32[MSW] |= 0x100000; + + /* If the input double is negative, we have to decrease the mantissa + * by a hair. This is an important part of performing arithmetic rounding, + * as negative numbers must round towards positive infinity in the + * halfwase case of -x.5. Since "top" contains only the sign bit at this + * point, we can just decrease the mantissa by the value of "top". + */ + u.ui64 -= top; + + /* By decrementing "top", we create a bitmask with a value of either + * 0x0 (if the input was negative) or 0xFFFFFFFF (if the input was positive + * and thus the unsigned subtraction underflowed) that we'll use later. + */ + top--; + + /* Here, we shift the mantissa by the constant value as described above. + * We can emulate a 64-bit shift right by 21 through shifting the top 32 + * bits left 11 places and ORing in the bottom 32 bits shifted 21 places + * to the right. Both parts of the mantissa are now packed into a single + * 32-bit integer. Although we severely truncate the lower part in the + * process, we still have enough significant bits to perform the conversion + * without error (for all valid inputs). + */ + output = (u.ui32[MSW] << 11) | (u.ui32[LSW] >> 21); + + /* Next, we perform the shift that converts the X.Y fixed-point number + * currently found in "output" to the desired 31.1 fixed-point format + * needed for the following rounding step. It is important to consider + * all possible values for "shift_amount" at this point: + * + * - {shift_amount < 0} Since shift_amount is an unsigned integer, it + * really can't have a value less than zero. But, if the shift_amount + * calculation above caused underflow (which would happen with + * input > INT_MAX or input <= INT_MIN) then shift_amount will now be + * a very large number, and so this shift will result in complete + * garbage. But that's OK, as the input was out of our range, so our + * output is undefined. + * + * - {shift_amount > 31} If the magnitude of the input was very small + * (i.e. |input| << 1.0), shift_amount will have a value greater than + * 31. Thus, this shift will also result in garbage. After performing + * the shift, we will zero-out "output" if this is the case. + * + * - {0 <= shift_amount < 32} In this case, the shift will properly convert + * the mantissa into a 31.1 fixed-point number. + */ + output >>= shift_amount; + + /* This is where we perform rounding with the 31.1 fixed-point number. + * Since what we're after is arithmetic rounding, we simply add the single + * fractional bit into the integer part of "output", and just keep the + * integer part. + */ + output = (output >> 1) + (output & 1); + + /* Here, we zero-out the result if the magnitude if the input was very small + * (as explained in the section above). Notice that all input out of the + * valid range is also caught by this condition, which means we produce 0 + * for all invalid input, which is a nice side effect. + * + * The most straightforward way to do this would be: + * + * if (shift_amount > 31) + * output = 0; + * + * But we can use a little trick to avoid the potential branch. The + * expression (shift_amount > 31) will be either 1 or 0, which when + * decremented will be either 0x0 or 0xFFFFFFFF (unsigned underflow), + * which can be used to conditionally mask away all the bits in "output" + * (in the 0x0 case), effectively zeroing it out. Certain, compilers would + * have done this for us automatically. + */ + output &= ((shift_amount > 31) - 1); + + /* If the input double was a negative number, then we have to negate our + * output. The most straightforward way to do this would be: + * + * if (!top) + * output = -output; + * + * as "top" at this point is either 0x0 (if the input was negative) or + * 0xFFFFFFFF (if the input was positive). But, we can use a trick to + * avoid the branch. Observe that the following snippet of code has the + * same effect as the reference snippet above: + * + * if (!top) + * output = 0 - output; + * else + * output = output - 0; + * + * Armed with the bitmask found in "top", we can condense the two statements + * into the following: + * + * output = (output & top) - (output & ~top); + * + * where, in the case that the input double was negative, "top" will be 0, + * and the statement will be equivalent to: + * + * output = (0) - (output); + * + * and if the input double was positive, "top" will be 0xFFFFFFFF, and the + * statement will be equivalent to: + * + * output = (output) - (0); + * + * Which, as pointed out earlier, is equivalent to the original reference + * snippet. + */ + output = (output & top) - (output & ~top); + + return output; +#undef MSW +#undef LSW +} +#endif + +/* Convert a 32-bit IEEE single precision floating point number to a + * 'half' representation (s10.5) + */ +uint16_t +_cairo_half_from_float (float f) +{ + union { + uint32_t ui; + float f; + } u; + int s, e, m; + + u.f = f; + s = (u.ui >> 16) & 0x00008000; + e = ((u.ui >> 23) & 0x000000ff) - (127 - 15); + m = u.ui & 0x007fffff; + if (e <= 0) { + if (e < -10) { + /* underflow */ + return 0; + } + + m = (m | 0x00800000) >> (1 - e); + + /* round to nearest, round 0.5 up. */ + if (m & 0x00001000) + m += 0x00002000; + return s | (m >> 13); + } else if (e == 0xff - (127 - 15)) { + if (m == 0) { + /* infinity */ + return s | 0x7c00; + } else { + /* nan */ + m >>= 13; + return s | 0x7c00 | m | (m == 0); + } + } else { + /* round to nearest, round 0.5 up. */ + if (m & 0x00001000) { + m += 0x00002000; + + if (m & 0x00800000) { + m = 0; + e += 1; + } + } + + if (e > 30) { + /* overflow -> infinity */ + return s | 0x7c00; + } + + return s | (e << 10) | (m >> 13); + } +} + +#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 if (bufptr == buf && (*p == '-' || *p == '+')) { + *bufptr++ = *p; + } 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 + +#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 +#include + +#if !_WIN32_WCE +/* tmpfile() replacement for Windows. + * + * On Windows tmpfile() creates the file in the root directory. This + * may fail due to insufficient privileges. However, this isn't a + * problem on Windows CE so we don't use it there. + */ +FILE * +_cairo_win32_tmpfile (void) +{ + DWORD path_len; + WCHAR path_name[MAX_PATH + 1]; + WCHAR file_name[MAX_PATH + 1]; + HANDLE handle; + int fd; + FILE *fp; + + path_len = GetTempPathW (MAX_PATH, path_name); + if (path_len <= 0 || path_len >= MAX_PATH) + return NULL; + + if (GetTempFileNameW (path_name, L"ps_", 0, file_name) == 0) + return NULL; + + handle = CreateFileW (file_name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, + NULL); + if (handle == INVALID_HANDLE_VALUE) { + DeleteFileW (file_name); + return NULL; + } + + fd = _open_osfhandle((intptr_t) handle, 0); + if (fd < 0) { + CloseHandle (handle); + return NULL; + } + + fp = fdopen(fd, "w+b"); + if (fp == NULL) { + _close(fd); + return NULL; + } + + return fp; +} +#endif /* !_WIN32_WCE */ + +#endif /* _WIN32 */ + +typedef struct _cairo_intern_string { + cairo_hash_entry_t hash_entry; + int len; + char *string; +} cairo_intern_string_t; + +static cairo_hash_table_t *_cairo_intern_string_ht; + +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 > 0; len--, p++) + h = (h << 5) - h + *p; + + return h; +} + +static cairo_bool_t +_intern_string_equal (const void *_a, const void *_b) +{ + const cairo_intern_string_t *a = _a; + const cairo_intern_string_t *b = _b; + + if (a->len != b->len) + return FALSE; + + return memcmp (a->string, b->string, a->len) == 0; +} + +cairo_status_t +_cairo_intern_string (const char **str_inout, int len) +{ + char *str = (char *) *str_inout; + cairo_intern_string_t tmpl, *istring; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (len < 0) + len = strlen (str); + tmpl.hash_entry.hash = _cairo_string_hash (str, len); + tmpl.len = len; + tmpl.string = (char *) str; + + CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex); + if (_cairo_intern_string_ht == NULL) { + _cairo_intern_string_ht = _cairo_hash_table_create (_intern_string_equal); + if (unlikely (_cairo_intern_string_ht == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + } + + istring = _cairo_hash_table_lookup (_cairo_intern_string_ht, + &tmpl.hash_entry); + if (istring == NULL) { + istring = _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; + istring->string = (char *) (istring + 1); + memcpy (istring->string, str, len); + istring->string[len] = '\0'; + + status = _cairo_hash_table_insert (_cairo_intern_string_ht, + &istring->hash_entry); + if (unlikely (status)) { + free (istring); + goto BAIL; + } + } else { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + } + + *str_inout = istring->string; + + BAIL: + CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex); + return status; +} + +static void +_intern_string_pluck (void *entry, void *closure) +{ + _cairo_hash_table_remove (closure, entry); + free (entry); +} + +void +_cairo_intern_string_reset_static_data (void) +{ + CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex); + if (_cairo_intern_string_ht != NULL) { + _cairo_hash_table_foreach (_cairo_intern_string_ht, + _intern_string_pluck, + _cairo_intern_string_ht); + _cairo_hash_table_destroy(_cairo_intern_string_ht); + _cairo_intern_string_ht = NULL; + } + CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex); +} diff --git a/gfx/cairo/cairo/src/cairo-mono-scan-converter.c b/gfx/cairo/cairo/src/cairo-mono-scan-converter.c new file mode 100644 index 0000000000..891f435c9e --- /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 new file mode 100644 index 0000000000..25223f3eac --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mutex-impl-private.h @@ -0,0 +1,278 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005,2007 Red Hat, Inc. + * Copyright © 2007 Mathias Hasselmann + * + * 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 + * Mathias Hasselmann + * Behdad Esfahbod + */ + +#ifndef CAIRO_MUTEX_IMPL_PRIVATE_H +#define CAIRO_MUTEX_IMPL_PRIVATE_H + +#include "cairo.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_LOCKDEP +#include +#endif + +/* A fully qualified no-operation statement */ +#define CAIRO_MUTEX_IMPL_NOOP do {/*no-op*/} while (0) +/* And one that evaluates its argument once */ +#define CAIRO_MUTEX_IMPL_NOOP1(expr) do { (void)(expr); } while (0) +/* Note: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the + * result of __attribute__((warn_used_result)) functions. */ + +/* Cairo mutex implementation: + * + * Any new mutex implementation needs to do the following: + * + * - Condition on the right header or feature. Headers are + * preferred as eg. you still can use win32 mutex implementation + * on a win32 system even if you do not compile the win32 + * surface/backend. + * + * - typedef #cairo_mutex_impl_t to the proper mutex type on your target + * system. Note that you may or may not need to use a pointer, + * depending on what kinds of initialization your mutex + * implementation supports. No trailing semicolon needed. + * You should be able to compile the following snippet (don't try + * running it): + * + * + * cairo_mutex_impl_t _cairo_some_mutex; + * + * + * - #define %CAIRO_MUTEX_IMPL_ 1 with suitable name for your platform. You + * can later use this symbol in cairo-system.c. + * + * - #define CAIRO_MUTEX_IMPL_LOCK(mutex) and CAIRO_MUTEX_IMPL_UNLOCK(mutex) to + * proper statement to lock/unlock the mutex object passed in. + * You can (and should) assume that the mutex is already + * initialized, and is-not-already-locked/is-locked, + * respectively. Use the "do { ... } while (0)" idiom if necessary. + * No trailing semicolons are needed (in any macro you define here). + * You should be able to compile the following snippet: + * + * + * cairo_mutex_impl_t _cairo_some_mutex; + * + * if (1) + * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); + * else + * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); + * + * + * - #define %CAIRO_MUTEX_IMPL_NIL_INITIALIZER to something that can + * initialize the #cairo_mutex_impl_t type you defined. Most of the + * time one of 0, %NULL, or {} works. At this point + * you should be able to compile the following snippet: + * + * + * cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; + * + * if (1) + * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); + * else + * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); + * + * + * - If the above code is not enough to initialize a mutex on + * your platform, #define CAIRO_MUTEX_IMPL_INIT(mutex) to statement + * to initialize the mutex (allocate resources, etc). Such that + * you should be able to compile AND RUN the following snippet: + * + * + * cairo_mutex_impl_t _cairo_some_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; + * + * CAIRO_MUTEX_IMPL_INIT (_cairo_some_mutex); + * + * if (1) + * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex); + * else + * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex); + * + * + * - If you define CAIRO_MUTEX_IMPL_INIT(mutex), cairo will use it to + * initialize all static mutex'es. If for any reason that should + * not happen (eg. %CAIRO_MUTEX_IMPL_INIT is just a faster way than + * what cairo does using %CAIRO_MUTEX_IMPL_NIL_INITIALIZER), then + * + * #define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP + * + * + * - If your system supports freeing a mutex object (deallocating + * resources, etc), then #define CAIRO_MUTEX_IMPL_FINI(mutex) to do + * that. + * + * - If you define CAIRO_MUTEX_IMPL_FINI(mutex), cairo will use it to + * define a finalizer function to finalize all static mutex'es. + * However, it's up to you to call CAIRO_MUTEX_IMPL_FINALIZE() at + * proper places, eg. when the system is unloading the cairo library. + * So, if for any reason finalizing static mutex'es is not needed + * (eg. you never call CAIRO_MUTEX_IMPL_FINALIZE()), then + * + * #define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP + * + * + * - That is all. If for any reason you think the above API is + * not enough to implement #cairo_mutex_impl_t on your system, please + * stop and write to the cairo mailing list about it. DO NOT + * poke around cairo-mutex-private.h for possible solutions. + */ + +#if CAIRO_NO_MUTEX + +/* No mutexes */ + + typedef int cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_NO 1 +# define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP +# define CAIRO_MUTEX_IMPL_LOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 + +# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 + + typedef int cairo_recursive_mutex_impl_t; + +# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) +# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER 0 + +#elif defined(_WIN32) /******************************************************/ + +#define WIN32_LEAN_AND_MEAN +/* We require Windows 2000 features such as ETO_PDY */ +#if !defined(WINVER) || (WINVER < 0x0500) +# define WINVER 0x0500 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) +# define _WIN32_WINNT 0x0500 +#endif + +# include + + typedef CRITICAL_SECTION cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_WIN32 1 +# define CAIRO_MUTEX_IMPL_LOCK(mutex) EnterCriticalSection (&(mutex)) +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) LeaveCriticalSection (&(mutex)) +# define CAIRO_MUTEX_IMPL_INIT(mutex) InitializeCriticalSection (&(mutex)) +# define CAIRO_MUTEX_IMPL_FINI(mutex) DeleteCriticalSection (&(mutex)) +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER { NULL, 0, 0, NULL, NULL, 0 } + +#elif defined __OS2__ /******************************************************/ + +# define INCL_BASE +# define INCL_PM +# include + + typedef HMTX cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_OS2 1 +# define CAIRO_MUTEX_IMPL_LOCK(mutex) DosRequestMutexSem(mutex, SEM_INDEFINITE_WAIT) +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) DosReleaseMutexSem(mutex) +# define CAIRO_MUTEX_IMPL_INIT(mutex) DosCreateMutexSem (NULL, &(mutex), 0L, FALSE) +# define CAIRO_MUTEX_IMPL_FINI(mutex) DosCloseMutexSem (mutex) +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER 0 + +#elif CAIRO_HAS_BEOS_SURFACE /***********************************************/ + + typedef BLocker* cairo_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_BEOS 1 +# define CAIRO_MUTEX_IMPL_LOCK(mutex) (mutex)->Lock() +# define CAIRO_MUTEX_IMPL_UNLOCK(mutex) (mutex)->Unlock() +# define CAIRO_MUTEX_IMPL_INIT(mutex) (mutex) = new BLocker() +# define CAIRO_MUTEX_IMPL_FINI(mutex) delete (mutex) +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER NULL + +#elif CAIRO_HAS_PTHREAD /* and finally if there are no native mutexes ********/ + +# include + + typedef pthread_mutex_t cairo_mutex_impl_t; + typedef pthread_mutex_t cairo_recursive_mutex_impl_t; + +# define CAIRO_MUTEX_IMPL_PTHREAD 1 +#if HAVE_LOCKDEP +/* expose all mutexes to the validator */ +# 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_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) +#if HAVE_LOCKDEP +# define CAIRO_MUTEX_IS_LOCKED(mutex) LOCKDEP_IS_LOCKED (&(mutex)) +# define CAIRO_MUTEX_IS_UNLOCKED(mutex) LOCKDEP_IS_UNLOCKED (&(mutex)) +#endif +# define CAIRO_MUTEX_IMPL_FINI(mutex) pthread_mutex_destroy (&(mutex)) +#if ! HAVE_LOCKDEP +# define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP +#endif +# define CAIRO_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 +# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) do { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init (&attr); \ + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init (&(mutex), &attr); \ + pthread_mutexattr_destroy (&attr); \ +} while (0) +# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + +#else /**********************************************************************/ + +# error "XXX: No mutex implementation found. Cairo will not work with multiple threads. Define CAIRO_NO_MUTEX to 1 to acknowledge and accept this limitation and compile cairo without thread-safety support." + +#endif + +/* By default mutex implementations are assumed to be recursive */ +#if ! CAIRO_MUTEX_HAS_RECURSIVE_IMPL + +# define CAIRO_MUTEX_HAS_RECURSIVE_IMPL 1 + + typedef cairo_mutex_impl_t cairo_recursive_mutex_impl_t; + +# define CAIRO_RECURSIVE_MUTEX_IMPL_INIT(mutex) CAIRO_MUTEX_IMPL_INIT(mutex) +# define CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER CAIRO_MUTEX_IMPL_NIL_INITIALIZER + +#endif + +#endif diff --git a/gfx/cairo/cairo/src/cairo-mutex-list-private.h b/gfx/cairo/cairo/src/cairo-mutex-list-private.h new file mode 100644 index 0000000000..ca74030062 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mutex-list-private.h @@ -0,0 +1,79 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Mathias Hasselmann + * + * 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): + * Mathias Hasselmann + */ + +#ifndef CAIRO_FEATURES_H +/* This block is to just make this header file standalone */ +#define CAIRO_MUTEX_DECLARE(mutex) +#endif + +CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock) + +CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_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) +#endif + +#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 +CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex) +#endif + +#if CAIRO_HAS_XCB_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_xcb_connections_mutex) +#endif + +#if CAIRO_HAS_GL_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_gl_context_mutex) +#endif + +#if !defined (HAS_ATOMIC_OPS) || defined (ATOMIC_OP_NEEDS_MEMORY_BARRIER) +CAIRO_MUTEX_DECLARE (_cairo_atomic_mutex) +#endif + +#if CAIRO_HAS_DRM_SURFACE +CAIRO_MUTEX_DECLARE (_cairo_drm_device_mutex) +#endif +/* Undefine, to err on unintended inclusion */ +#undef CAIRO_MUTEX_DECLARE diff --git a/gfx/cairo/cairo/src/cairo-mutex-private.h b/gfx/cairo/cairo/src/cairo-mutex-private.h new file mode 100644 index 0000000000..61a7160a06 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mutex-private.h @@ -0,0 +1,67 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005,2007 Red Hat, Inc. + * Copyright © 2007 Mathias Hasselmann + * + * 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 + * Mathias Hasselmann + * Behdad Esfahbod + */ + +#ifndef CAIRO_MUTEX_PRIVATE_H +#define CAIRO_MUTEX_PRIVATE_H + +#include "cairo-mutex-type-private.h" + +CAIRO_BEGIN_DECLS + +#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER +cairo_private void _cairo_mutex_initialize (void); +#endif +#if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER +cairo_private void _cairo_mutex_finalize (void); +#endif +/* only if using static initializer and/or finalizer define the boolean */ +#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER + cairo_private extern cairo_bool_t _cairo_mutex_initialized; +#endif + +/* Finally, extern the static mutexes and undef */ + +#define CAIRO_MUTEX_DECLARE(mutex) cairo_private extern cairo_mutex_t mutex; +#include "cairo-mutex-list-private.h" +#undef CAIRO_MUTEX_DECLARE + +CAIRO_END_DECLS + +#endif diff --git a/gfx/cairo/cairo/src/cairo-mutex-type-private.h b/gfx/cairo/cairo/src/cairo-mutex-type-private.h new file mode 100644 index 0000000000..e8c493985c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mutex-type-private.h @@ -0,0 +1,194 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005,2007 Red Hat, Inc. + * Copyright © 2007 Mathias Hasselmann + * + * 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 + * Mathias Hasselmann + * Behdad Esfahbod + */ + +#ifndef CAIRO_MUTEX_TYPE_PRIVATE_H +#define CAIRO_MUTEX_TYPE_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-mutex-impl-private.h" + +/* Only the following four are mandatory at this point */ +#ifndef CAIRO_MUTEX_IMPL_LOCK +# error "CAIRO_MUTEX_IMPL_LOCK not defined. Check cairo-mutex-impl-private.h." +#endif +#ifndef CAIRO_MUTEX_IMPL_UNLOCK +# error "CAIRO_MUTEX_IMPL_UNLOCK not defined. Check cairo-mutex-impl-private.h." +#endif +#ifndef CAIRO_MUTEX_IMPL_NIL_INITIALIZER +# error "CAIRO_MUTEX_IMPL_NIL_INITIALIZER not defined. Check cairo-mutex-impl-private.h." +#endif +#ifndef CAIRO_RECURSIVE_MUTEX_IMPL_INIT +# error "CAIRO_RECURSIVE_MUTEX_IMPL_INIT not defined. Check cairo-mutex-impl-private.h." +#endif + + +/* make sure implementations don't fool us: we decide these ourself */ +#undef _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER +#undef _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER + + +#ifdef CAIRO_MUTEX_IMPL_INIT + +/* If %CAIRO_MUTEX_IMPL_INIT is defined, we may need to initialize all + * static mutex'es. */ +# ifndef CAIRO_MUTEX_IMPL_INITIALIZE +# define CAIRO_MUTEX_IMPL_INITIALIZE() do { \ + if (!_cairo_mutex_initialized) \ + _cairo_mutex_initialize (); \ + } while(0) + +/* and make sure we implement the above */ +# define _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER 1 +# endif /* CAIRO_MUTEX_IMPL_INITIALIZE */ + +#else /* no CAIRO_MUTEX_IMPL_INIT */ + +/* Otherwise we probably don't need to initialize static mutex'es, */ +# ifndef CAIRO_MUTEX_IMPL_INITIALIZE +# define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP +# endif /* CAIRO_MUTEX_IMPL_INITIALIZE */ + +/* and dynamic ones can be initialized using the static initializer. */ +# define CAIRO_MUTEX_IMPL_INIT(mutex) do { \ + cairo_mutex_t _tmp_mutex = CAIRO_MUTEX_IMPL_NIL_INITIALIZER; \ + memcpy (&(mutex), &_tmp_mutex, sizeof (_tmp_mutex)); \ + } while (0) + +#endif /* CAIRO_MUTEX_IMPL_INIT */ + +#ifdef CAIRO_MUTEX_IMPL_FINI + +/* If %CAIRO_MUTEX_IMPL_FINI is defined, we may need to finalize all + * static mutex'es. */ +# ifndef CAIRO_MUTEX_IMPL_FINALIZE +# define CAIRO_MUTEX_IMPL_FINALIZE() do { \ + if (_cairo_mutex_initialized) \ + _cairo_mutex_finalize (); \ + } while(0) + +/* and make sure we implement the above */ +# define _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER 1 +# endif /* CAIRO_MUTEX_IMPL_FINALIZE */ + +#else /* no CAIRO_MUTEX_IMPL_FINI */ + +/* Otherwise we probably don't need to finalize static mutex'es, */ +# ifndef CAIRO_MUTEX_IMPL_FINALIZE +# define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP +# endif /* CAIRO_MUTEX_IMPL_FINALIZE */ + +/* neither do the dynamic ones. */ +# define CAIRO_MUTEX_IMPL_FINI(mutex) CAIRO_MUTEX_IMPL_NOOP1(mutex) + +#endif /* CAIRO_MUTEX_IMPL_FINI */ + + +#ifndef _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER +#define _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER 0 +#endif +#ifndef _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER +#define _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER 0 +#endif + + +/* Make sure everything we want is defined */ +#ifndef CAIRO_MUTEX_IMPL_INITIALIZE +# error "CAIRO_MUTEX_IMPL_INITIALIZE not defined" +#endif +#ifndef CAIRO_MUTEX_IMPL_FINALIZE +# error "CAIRO_MUTEX_IMPL_FINALIZE not defined" +#endif +#ifndef CAIRO_MUTEX_IMPL_LOCK +# error "CAIRO_MUTEX_IMPL_LOCK not defined" +#endif +#ifndef CAIRO_MUTEX_IMPL_UNLOCK +# error "CAIRO_MUTEX_IMPL_UNLOCK not defined" +#endif +#ifndef CAIRO_MUTEX_IMPL_INIT +# error "CAIRO_MUTEX_IMPL_INIT not defined" +#endif +#ifndef CAIRO_MUTEX_IMPL_FINI +# error "CAIRO_MUTEX_IMPL_FINI not defined" +#endif +#ifndef CAIRO_MUTEX_IMPL_NIL_INITIALIZER +# error "CAIRO_MUTEX_IMPL_NIL_INITIALIZER not defined" +#endif + + +/* Public interface. */ + +/* By default it simply uses the implementation provided. + * But we can provide for debugging features by overriding them */ + +#ifndef CAIRO_MUTEX_DEBUG +typedef cairo_mutex_impl_t cairo_mutex_t; +typedef cairo_recursive_mutex_impl_t cairo_recursive_mutex_t; +#else +# define cairo_mutex_t cairo_mutex_impl_t +#endif + +#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_UNLOCK CAIRO_MUTEX_IMPL_UNLOCK +#define CAIRO_MUTEX_INIT CAIRO_MUTEX_IMPL_INIT +#define CAIRO_MUTEX_FINI CAIRO_MUTEX_IMPL_FINI +#define CAIRO_MUTEX_NIL_INITIALIZER CAIRO_MUTEX_IMPL_NIL_INITIALIZER + +#define CAIRO_RECURSIVE_MUTEX_INIT CAIRO_RECURSIVE_MUTEX_IMPL_INIT +#define CAIRO_RECURSIVE_MUTEX_NIL_INITIALIZER CAIRO_RECURSIVE_MUTEX_IMPL_NIL_INITIALIZER + +#ifndef CAIRO_MUTEX_IS_LOCKED +# define CAIRO_MUTEX_IS_LOCKED(name) 1 +#endif +#ifndef CAIRO_MUTEX_IS_UNLOCKED +# define CAIRO_MUTEX_IS_UNLOCKED(name) 1 +#endif + + +/* Debugging support */ + +#ifdef CAIRO_MUTEX_DEBUG + +/* TODO add mutex debugging facilities here (eg deadlock detection) */ + +#endif /* CAIRO_MUTEX_DEBUG */ + +#endif diff --git a/gfx/cairo/cairo/src/cairo-mutex.c b/gfx/cairo/cairo/src/cairo-mutex.c new file mode 100644 index 0000000000..0a31dced3e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-mutex.c @@ -0,0 +1,82 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Mathias Hasselmann + * + * 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): + * Mathias Hasselmann + */ + +#include "cairoint.h" + +#include "cairo-mutex-private.h" + +#define CAIRO_MUTEX_DECLARE(mutex) cairo_mutex_t mutex = CAIRO_MUTEX_NIL_INITIALIZER; +#include "cairo-mutex-list-private.h" +#undef CAIRO_MUTEX_DECLARE + +#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER || _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER + +# if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER +# define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE FALSE +# else +# define _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE TRUE +# endif + +cairo_bool_t _cairo_mutex_initialized = _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE; + +# undef _CAIRO_MUTEX_IMPL_INITIALIZED_DEFAULT_VALUE + +#endif + +#if _CAIRO_MUTEX_IMPL_USE_STATIC_INITIALIZER +void _cairo_mutex_initialize (void) +{ + if (_cairo_mutex_initialized) + return; + + _cairo_mutex_initialized = TRUE; + +#define CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_INIT (mutex); +#include "cairo-mutex-list-private.h" +#undef CAIRO_MUTEX_DECLARE +} +#endif + +#if _CAIRO_MUTEX_IMPL_USE_STATIC_FINALIZER +void _cairo_mutex_finalize (void) +{ + if (!_cairo_mutex_initialized) + return; + + _cairo_mutex_initialized = FALSE; + +#define CAIRO_MUTEX_DECLARE(mutex) CAIRO_MUTEX_FINI (mutex); +#include "cairo-mutex-list-private.h" +#undef CAIRO_MUTEX_DECLARE +} +#endif 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 0000000000..e86e3021cf --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-no-compositor.c @@ -0,0 +1,108 @@ +/* -*- 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, + cairo_bool_t permit_subpixel_antialiasing) +{ + 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-observer.c b/gfx/cairo/cairo/src/cairo-observer.c new file mode 100644 index 0000000000..36d6b93bd6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-observer.c @@ -0,0 +1,52 @@ +/* -*- 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 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-list-inline.h" + +void +_cairo_observers_notify (cairo_list_t *observers, void *arg) +{ + cairo_observer_t *obs, *next; + + cairo_list_foreach_entry_safe (obs, next, + cairo_observer_t, + observers, link) + { + obs->callback (obs, arg); + } +} diff --git a/gfx/cairo/cairo/src/cairo-os2-private.h b/gfx/cairo/cairo/src/cairo-os2-private.h new file mode 100644 index 0000000000..829dd3c8d4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-os2-private.h @@ -0,0 +1,67 @@ +/* vim: set sw=4 sts=4 et cin: */ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2005-2006 netlabs.org + * + * 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 + * Doodle + * + * Contributor(s): + * Peter Weilbacher + */ + +#ifndef CAIRO_OS2_PRIVATE_H +#define CAIRO_OS2_PRIVATE_H + +#include "cairo-os2.h" +#include "cairoint.h" + +typedef struct _cairo_os2_surface +{ + cairo_surface_t base; + + /* Mutex semaphore to protect private fields from concurrent access */ + HMTX hmtx_use_private_fields; + /* Private fields: */ + HPS hps_client_window; + HWND hwnd_client_window; + BITMAPINFO2 bitmap_info; + unsigned char *pixels; + cairo_image_surface_t *image_surface; + int pixel_array_lend_count; + HEV hev_pixel_array_came_back; + + RECTL rcl_dirty_area; + cairo_bool_t dirty_area_present; + + /* General flags: */ + cairo_bool_t blit_as_changes; + cairo_bool_t use_24bpp; +} cairo_os2_surface_t; + +#endif /* CAIRO_OS2_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-os2-surface.c b/gfx/cairo/cairo/src/cairo-os2-surface.c new file mode 100644 index 0000000000..84f08c8077 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-os2-surface.c @@ -0,0 +1,1416 @@ +/* vim: set sw=4 sts=4 et cin: */ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2005-2006 netlabs.org + * + * 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 + * Doodle + * + * Contributor(s): + * Peter Weilbacher + * Rich Walsh + */ + +#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 +#endif + +#include +#ifdef BUILD_CAIRO_DLL +# include "cairo-os2.h" +# ifndef __WATCOMC__ +# include +# endif +#endif + +/* + * Here comes the extra API for the OS/2 platform. Currently it consists + * of two extra functions, the cairo_os2_init() and the + * cairo_os2_fini(). Both of them are called automatically if + * Cairo is compiled to be a DLL file, but you have to call them before + * using the Cairo API if you link to Cairo statically! + * + * You'll also find the code in here which deals with DLL initialization + * and termination, if the code is built to be a DLL. + * (if BUILD_CAIRO_DLL is defined) + */ + +/* Initialization counter: */ +static int cairo_os2_initialization_count = 0; + +static inline void +DisableFPUException (void) +{ + unsigned short usCW; + + /* Some OS/2 PM API calls modify the FPU Control Word, + * but forget to restore it. + * + * This can result in XCPT_FLOAT_INVALID_OPCODE exceptions, + * so to be sure, we disable Invalid Opcode FPU exception + * before using FPU stuffs. + */ + usCW = _control87 (0, 0); + usCW = usCW | EM_INVALID | 0x80; + _control87 (usCW, MCW_EM | 0x80); +} + +/** + * cairo_os2_init: + * + * Initializes the Cairo library. This function is automatically called if + * Cairo was compiled to be a DLL (however it's not a problem if it's called + * multiple times). But if you link to Cairo statically, you have to call it + * once to set up Cairo's internal structures and mutexes. + * + * Since: 1.4 + **/ +cairo_public void +cairo_os2_init (void) +{ + /* This may initialize some stuffs, like create mutex semaphores etc.. */ + + cairo_os2_initialization_count++; + if (cairo_os2_initialization_count > 1) return; + + DisableFPUException (); + +#if CAIRO_HAS_FC_FONT + /* Initialize FontConfig */ + FcInit (); +#endif + + CAIRO_MUTEX_INITIALIZE (); +} + +/** + * cairo_os2_fini: + * + * Uninitializes the Cairo library. This function is automatically called if + * Cairo was compiled to be a DLL (however it's not a problem if it's called + * multiple times). But if you link to Cairo statically, you have to call it + * once to shut down Cairo, to let it free all the resources it has allocated. + * + * Since: 1.4 + **/ +cairo_public void +cairo_os2_fini (void) +{ + /* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */ + + if (cairo_os2_initialization_count <= 0) return; + cairo_os2_initialization_count--; + if (cairo_os2_initialization_count > 0) return; + + DisableFPUException (); + + cairo_debug_reset_static_data (); + +#if CAIRO_HAS_FC_FONT +# if HAVE_FCFINI + /* Uninitialize FontConfig */ + FcFini (); +# endif +#endif + +#ifdef __WATCOMC__ + /* It can happen that the libraries we use have memory leaks, + * so there are still memory chunks allocated at this point. + * In these cases, Watcom might still have a bigger memory chunk, + * called "the heap" allocated from the OS. + * As we want to minimize the memory we lose from the point of + * view of the OS, we call this function to shrink that heap + * as much as possible. + */ + _heapshrink (); +#else + /* GCC has a heapmin function that approximately corresponds to + * what the Watcom function does + */ + _heapmin (); +#endif +} + +/* + * This function calls the allocation function depending on which + * method was compiled into the library: it can be native allocation + * (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free). + * Actually, for pixel buffers that we use this function for, cairo + * uses _cairo_malloc_abc, so we use that here, too. And use the + * change to check the size argument + */ +void *_buffer_alloc (size_t a, size_t b, const unsigned int size) +{ + size_t nbytes; + void *buffer = NULL; + + if (!a || !b || !size || + a >= INT32_MAX / b || a*b >= INT32_MAX / size) { + return NULL; + } + nbytes = a * b * size; + +#ifdef OS2_USE_PLATFORM_ALLOC + /* Using OBJ_ANY on a machine that isn't configured for hi-mem + * will cause ERROR_INVALID_PARAMETER. If this occurs, or this + * build doesn't have hi-mem enabled, fall back to using lo-mem. + */ +#ifdef OS2_HIGH_MEMORY + if (!DosAllocMem (&buffer, nbytes, + OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT)) + return buffer; +#endif + if (DosAllocMem (&buffer, nbytes, + PAG_READ | PAG_WRITE | PAG_COMMIT)) + return NULL; +#else + /* Clear the malloc'd buffer the way DosAllocMem() does. */ + buffer = _cairo_malloc (nbytes); + if (buffer) { + memset (buffer, 0, nbytes); + } +#endif + return buffer; +} + +/* + * This function selects the free function depending on which + * allocation method was compiled into the library + */ +void _buffer_free (void *buffer) +{ +#ifdef OS2_USE_PLATFORM_ALLOC + DosFreeMem (buffer); +#else + free (buffer); +#endif +} + +/* XXX + * The cairo_os2_ini() and cairo_os2_fini() functions should be removed and + * the LibMain code moved to cairo-system.c. It should also call + * cairo_debug_reset_static_data() instead of duplicating its logic... + */ + +#ifdef BUILD_CAIRO_DLL +/* The main DLL entry for DLL initialization and uninitialization */ +/* Only include this code if we're about to build a DLL. */ + +#ifdef __WATCOMC__ +unsigned _System +LibMain (unsigned hmod, + unsigned termination) +#else +unsigned long _System +_DLL_InitTerm (unsigned long hModule, + unsigned long termination) +#endif +{ + if (termination) { + /* Unloading the DLL */ + cairo_os2_fini (); + +#ifndef __WATCOMC__ + /* Uninitialize RTL of GCC */ + __ctordtorTerm (); + _CRT_term (); +#endif + return 1; + } else { + /* Loading the DLL */ +#ifndef __WATCOMC__ + /* Initialize RTL of GCC */ + if (_CRT_init () != 0) + return 0; + __ctordtorInit (); +#endif + + cairo_os2_init (); + return 1; + } +} + +#endif /* BUILD_CAIRO_DLL */ + +/* + * The following part of the source file contains the code which might + * be called the "core" of the OS/2 backend support. This contains the + * OS/2 surface support functions and structures. + */ + +/* Forward declaration */ +static const cairo_surface_backend_t cairo_os2_surface_backend; + +/* Unpublished API: + * GpiEnableYInversion = PMGPI.723 + * GpiQueryYInversion = PMGPI.726 + * BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight); + * LONG APIENTRY GpiQueryYInversion (HPS hps); + */ +BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight); +LONG APIENTRY GpiQueryYInversion (HPS hps); + +#ifdef __WATCOMC__ +/* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */ +LONG APIENTRY GpiDrawBits (HPS hps, + PVOID pBits, + PBITMAPINFO2 pbmiInfoTable, + LONG lCount, + PPOINTL aptlPoints, + LONG lRop, + ULONG flOptions); +#endif + +static void +_cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface, + HPS hps_begin_paint, + PRECTL prcl_begin_paint_rect) +{ + POINTL aptlPoints[4]; + LONG lOldYInversion; + LONG rc = GPI_OK; + + /* Check the limits (may not be necessary) */ + if (prcl_begin_paint_rect->xLeft < 0) + prcl_begin_paint_rect->xLeft = 0; + if (prcl_begin_paint_rect->yBottom < 0) + prcl_begin_paint_rect->yBottom = 0; + if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx) + prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx; + if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy) + prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy; + + /* Exit if the rectangle is empty */ + if (prcl_begin_paint_rect->xLeft >= prcl_begin_paint_rect->xRight || + prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop) + return; + + /* Set the Target & Source coordinates */ + *((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect; + *((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect; + + /* Make the Target coordinates non-inclusive */ + aptlPoints[1].x -= 1; + aptlPoints[1].y -= 1; + + /* Enable Y Inversion for the HPS, so GpiDrawBits will + * work with upside-top image, not with upside-down image! + */ + lOldYInversion = GpiQueryYInversion (hps_begin_paint); + GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1); + + /* Debug code to draw rectangle limits */ +#if 0 + { + int x, y; + unsigned char *pixels; + + pixels = surface->pixels; + for (x = 0; x < surface->bitmap_info.cx; x++) { + for (y = 0; y < surface->bitmap_info.cy; y++) { + if ((x == 0) || + (y == 0) || + (x == y) || + (x >= surface->bitmap_info.cx-1) || + (y >= surface->bitmap_info.cy-1)) + { + pixels[y*surface->bitmap_info.cx*4+x*4] = 255; + } + } + } + } +#endif + if (!surface->use_24bpp) { + rc = GpiDrawBits (hps_begin_paint, + surface->pixels, + &(surface->bitmap_info), + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + if (rc != GPI_OK) + surface->use_24bpp = TRUE; + } + + if (surface->use_24bpp) { + /* If GpiDrawBits () failed then this is most likely because the + * display driver could not handle a 32bit bitmap. So we need to + * - create a buffer that only contains 3 bytes per pixel + * - change the bitmap info header to contain 24bit + * - pass the new buffer to GpiDrawBits () again + * - clean up the new buffer + */ + BITMAPINFO2 bmpinfo; + unsigned char *pchPixBuf; + unsigned char *pchTarget; + ULONG *pulSource; + ULONG ulX; + ULONG ulY; + ULONG ulPad; + + /* Set up the bitmap header, but this time for 24bit depth. */ + bmpinfo = surface->bitmap_info; + bmpinfo.cBitCount = 24; + + /* The start of each row has to be DWORD aligned. Calculate the + * of number aligned bytes per row, the total size of the bitmap, + * and the number of padding bytes at the end of each row. + */ + ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4; + bmpinfo.cbImage = ulX * bmpinfo.cy; + ulPad = ulX - bmpinfo.cx * 3; + + /* Allocate temporary pixel buffer. If the rows don't need + * padding, it has to be 1 byte larger than the size of the + * bitmap or else the high-order byte from the last source + * row will end up in unallocated memory. + */ + pchPixBuf = (unsigned char *)_buffer_alloc (1, 1, + bmpinfo.cbImage + (ulPad ? 0 : 1)); + + if (pchPixBuf) { + /* Copy 4 bytes from the source but advance the target ptr only + * 3 bytes, so the high-order alpha byte will be overwritten by + * the next copy. At the end of each row, skip over the padding. + */ + pchTarget = pchPixBuf; + pulSource = (ULONG*)surface->pixels; + for (ulY = bmpinfo.cy; ulY; ulY--) { + for (ulX = bmpinfo.cx; ulX; ulX--) { + *((ULONG*)pchTarget) = *pulSource++; + pchTarget += 3; + } + pchTarget += ulPad; + } + + rc = GpiDrawBits (hps_begin_paint, + pchPixBuf, + &bmpinfo, + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + if (rc != GPI_OK) + surface->use_24bpp = FALSE; + + _buffer_free (pchPixBuf); + } + } + + /* Restore Y inversion */ + GpiEnableYInversion (hps_begin_paint, lOldYInversion); +} + +static void +_cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface, + HPS hps_begin_paint, + PRECTL prcl_begin_paint_rect) +{ + HPS hps; + HDC hdc; + SIZEL sizlTemp; + HBITMAP hbmpTemp; + BITMAPINFO2 bmi2Temp; + POINTL aptlPoints[4]; + int y; + unsigned char *pchTemp; + + /* To copy pixels from screen to our buffer, we do the following steps: + * + * - Blit pixels from screen to a HBITMAP: + * -- Create Memory Device Context + * -- Create a PS into it + * -- Create a HBITMAP + * -- Select HBITMAP into memory PS + * -- Blit dirty pixels from screen to HBITMAP + * - Copy HBITMAP lines (pixels) into our buffer + * - Free resources + */ + + /* Create a memory device context */ + hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE); + if (!hdc) { + return; + } + + /* Create a memory PS */ + sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft; + sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom; + hps = GpiCreatePS (0, + hdc, + &sizlTemp, + PU_PELS | GPIT_NORMAL | GPIA_ASSOC); + if (!hps) { + DevCloseDC (hdc); + return; + } + + /* Create an uninitialized bitmap. */ + /* Prepare BITMAPINFO2 structure for our buffer */ + memset (&bmi2Temp, 0, sizeof (bmi2Temp)); + bmi2Temp.cbFix = sizeof (BITMAPINFOHEADER2); + bmi2Temp.cx = sizlTemp.cx; + bmi2Temp.cy = sizlTemp.cy; + bmi2Temp.cPlanes = 1; + bmi2Temp.cBitCount = 32; + + hbmpTemp = GpiCreateBitmap (hps, + (PBITMAPINFOHEADER2) &bmi2Temp, + 0, + NULL, + NULL); + + if (!hbmpTemp) { + GpiDestroyPS (hps); + DevCloseDC (hdc); + return; + } + + /* Select the bitmap into the memory device context. */ + GpiSetBitmap (hps, hbmpTemp); + + /* Target coordinates (Noninclusive) */ + aptlPoints[0].x = 0; + aptlPoints[0].y = 0; + + aptlPoints[1].x = sizlTemp.cx; + aptlPoints[1].y = sizlTemp.cy; + + /* Source coordinates (Inclusive) */ + aptlPoints[2].x = prcl_begin_paint_rect->xLeft; + aptlPoints[2].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom; + + aptlPoints[3].x = prcl_begin_paint_rect->xRight; + aptlPoints[3].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yTop; + + /* Blit pixels from screen to bitmap */ + GpiBitBlt (hps, + hps_begin_paint, + 4, + aptlPoints, + ROP_SRCCOPY, + BBO_IGNORE); + + /* Now we have to extract the pixels from the bitmap. */ + pchTemp = + surface->pixels + + (prcl_begin_paint_rect->yBottom)*surface->bitmap_info.cx*4 + + prcl_begin_paint_rect->xLeft*4; + for (y = 0; y < sizlTemp.cy; y++) { + /* Get one line of pixels */ + GpiQueryBitmapBits (hps, + sizlTemp.cy - y - 1, /* lScanStart */ + 1, /* lScans */ + (PBYTE)pchTemp, + &bmi2Temp); + + /* Go for next line */ + pchTemp += surface->bitmap_info.cx*4; + } + + /* Clean up resources */ + GpiSetBitmap (hps, (HBITMAP) NULL); + GpiDeleteBitmap (hbmpTemp); + GpiDestroyPS (hps); + DevCloseDC (hdc); +} + +static cairo_status_t +_cairo_os2_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; + + DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + + /* Increase lend counter */ + surface->pixel_array_lend_count++; + + *image_out = surface->image_surface; + *image_extra = NULL; + + DosReleaseMutexSem (surface->hmtx_use_private_fields); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_os2_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; + + /* Decrease Lend counter! */ + DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + + if (surface->pixel_array_lend_count > 0) + surface->pixel_array_lend_count--; + DosPostEventSem (surface->hev_pixel_array_came_back); + + DosReleaseMutexSem (surface->hmtx_use_private_fields); +} + +static cairo_image_surface_t * +_cairo_os2_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface; + + DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + /* Increase lend counter */ + 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 cairo_int_status_t +_cairo_os2_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + 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; + + /* Get mutex, we'll work with the pixel array! */ + if (DosRequestMutexSem (surface->hmtx_use_private_fields, + SEM_INDEFINITE_WAIT) != NO_ERROR) + { + /* Could not get mutex! */ + return; + } + + 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.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 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! + */ + _cairo_os2_surface_blit_pixels (surface, + surface->hps_client_window, + &rclToBlit); + } + + DosReleaseMutexSem (surface->hmtx_use_private_fields); + } + /* Also decrease Lend counter! */ + DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT); + + if (surface->pixel_array_lend_count > 0) + surface->pixel_array_lend_count--; + DosPostEventSem (surface->hev_pixel_array_came_back); + + 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 *surface = (cairo_os2_surface_t *) abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->bitmap_info.cx; + rectangle->height = surface->bitmap_info.cy; + + return TRUE; +} + +/** + * cairo_os2_surface_create: + * @hps_client_window: the presentation handle to bind the surface to + * @width: the width of the surface + * @height: the height of the surface + * + * Create a Cairo surface which is bound to a given presentation space (HPS). + * The caller retains ownership of the HPS and must dispose of it after the + * the surface has been destroyed. The surface will be created to have the + * given size. By default every change to the surface will be made visible + * immediately by blitting it into the window. This can be changed with + * cairo_os2_surface_set_manual_window_refresh(). + * Note that the surface will contain garbage when created, so the pixels + * have to be initialized by hand first. You can use the Cairo functions to + * fill it with black, or use cairo_surface_mark_dirty() to fill the surface + * with pixels from the window/HPS. + * + * Return value: the newly created surface + * + * Since: 1.4 + **/ +cairo_surface_t * +cairo_os2_surface_create (HPS hps_client_window, + int width, + int height) +{ + cairo_os2_surface_t *local_os2_surface = 0; + cairo_status_t status; + int rc; + + /* Check the size of the window */ + if ((width <= 0) || (height <= 0)) { + status = _cairo_error (CAIRO_STATUS_INVALID_SIZE); + goto error_exit; + } + + /* Allocate an OS/2 surface structure. */ + 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; + } + + memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t)); + + /* Allocate resources: mutex & event semaphores and the pixel buffer */ + if (DosCreateMutexSem (NULL, + &(local_os2_surface->hmtx_use_private_fields), + 0, + FALSE)) + { + status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + goto error_exit; + } + + if (DosCreateEventSem (NULL, + &(local_os2_surface->hev_pixel_array_came_back), + 0, + FALSE)) + { + status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); + goto error_exit; + } + + local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4); + if (!local_os2_surface->pixels) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto error_exit; + } + + /* Create image surface from pixel array */ + local_os2_surface->image_surface = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (local_os2_surface->pixels, + CAIRO_FORMAT_ARGB32, + width, /* Width */ + height, /* Height */ + width * 4); /* Rowstride */ + status = local_os2_surface->image_surface->base.status; + if (status) + goto error_exit; + + /* Set values for OS/2-specific data that aren't zero/NULL/FALSE. + * Note: hps_client_window may be null if this was called by + * cairo_os2_surface_create_for_window(). + */ + local_os2_surface->hps_client_window = hps_client_window; + local_os2_surface->blit_as_changes = TRUE; + + /* Prepare BITMAPINFO2 structure for our buffer */ + local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2); + local_os2_surface->bitmap_info.cx = width; + local_os2_surface->bitmap_info.cy = height; + local_os2_surface->bitmap_info.cPlanes = 1; + local_os2_surface->bitmap_info.cBitCount = 32; + + /* Initialize base surface */ + _cairo_surface_init (&local_os2_surface->base, + &cairo_os2_surface_backend, + NULL, /* device */ + _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 occurred */ + + if (local_os2_surface) { + if (local_os2_surface->pixels) + _buffer_free (local_os2_surface->pixels); + if (local_os2_surface->hev_pixel_array_came_back) + DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); + if (local_os2_surface->hmtx_use_private_fields) + DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); + free (local_os2_surface); + } + + return _cairo_surface_create_in_error (status); +} + +/** + * cairo_os2_surface_create_for_window: + * @hwnd_client_window: the window handle to bind the surface to + * @width: the width of the surface + * @height: the height of the surface + * + * Create a Cairo surface which is bound to a given window; the caller retains + * ownership of the window. This is a convenience function for use with + * windows that will only be updated when cairo_os2_surface_refresh_window() + * is called (usually in response to a WM_PAINT message). It avoids the need + * to create a persistent HPS for every window and assumes that one will be + * supplied by the caller when a cairo function needs one. If it isn't, an + * HPS will be created on-the-fly and released before the function which needs + * it returns. + * + * Return value: the newly created surface + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_os2_surface_create_for_window (HWND hwnd_client_window, + int width, + int height) +{ + cairo_os2_surface_t *local_os2_surface; + + /* A window handle must be provided. */ + if (!hwnd_client_window) { + return _cairo_surface_create_in_error ( + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + /* Create the surface. */ + local_os2_surface = (cairo_os2_surface_t *) + cairo_os2_surface_create (0, width, height); + + /* If successful, save the hwnd & turn off automatic repainting. */ + if (!local_os2_surface->image_surface->base.status) { + local_os2_surface->hwnd_client_window = hwnd_client_window; + local_os2_surface->blit_as_changes = FALSE; + } + + return (cairo_surface_t *)local_os2_surface; +} + +/** + * cairo_os2_surface_set_size: + * @surface: the cairo surface to resize + * @new_width: the new width of the surface + * @new_height: the new height of the surface + * @timeout: timeout value in milliseconds + * + * When the client window is resized, call this API to set the new size in the + * underlying surface accordingly. This function will reallocate everything, + * so you'll have to redraw everything in the surface after this call. + * The surface will contain garbage after the resizing. So the notes of + * cairo_os2_surface_create() apply here, too. + * + * The timeout value specifies how long the function should wait on other parts + * of the program to release the buffers. It is necessary, because it can happen + * that Cairo is just drawing something into the surface while we want to + * destroy and recreate it. + * + * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, + * %CAIRO_STATUS_INVALID_SIZE for invalid sizes + * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the + * timeout happened before all the buffers were released + * + * Since: 1.4 + **/ +int +cairo_os2_surface_set_size (cairo_surface_t *surface, + int new_width, + int new_height, + int timeout) +{ + cairo_os2_surface_t *local_os2_surface; + unsigned char *pchNewPixels; + int rc; + cairo_image_surface_t *pNewImageSurface; + + local_os2_surface = (cairo_os2_surface_t *) 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); + } + + if ((new_width <= 0) || + (new_height <= 0)) + { + /* Invalid size! */ + return _cairo_error (CAIRO_STATUS_INVALID_SIZE); + } + + /* Allocate memory for new stuffs */ + pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4); + if (!pchNewPixels) { + /* Not enough memory for the pixels! + * Everything remains the same! + */ + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* Create image surface from new pixel array */ + pNewImageSurface = (cairo_image_surface_t *) + cairo_image_surface_create_for_data (pchNewPixels, + CAIRO_FORMAT_ARGB32, + new_width, /* Width */ + new_height, /* Height */ + new_width * 4); /* Rowstride */ + + if (pNewImageSurface->base.status) { + /* Could not create image surface! + * Everything remains the same! + */ + _buffer_free (pchNewPixels); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* Okay, new memory allocated, so it's time to swap old buffers + * to new ones! + */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) { + /* Could not get mutex! + * Everything remains the same! + */ + cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); + _buffer_free (pchNewPixels); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + /* We have to make sure that we won't destroy a surface which + * is lent to some other code (Cairo is drawing into it)! + */ + while (local_os2_surface->pixel_array_lend_count > 0) { + ULONG ulPostCount; + DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount); + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + /* Wait for somebody to return the pixels! */ + rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout); + if (rc != NO_ERROR) { + /* Either timeout or something wrong... Exit. */ + cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); + _buffer_free (pchNewPixels); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + /* Okay, grab mutex and check counter again! */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) + != NO_ERROR) + { + /* Could not get mutex! + * Everything remains the same! + */ + cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface); + _buffer_free (pchNewPixels); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + /* Destroy old image surface */ + cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); + /* Destroy old pixel buffer */ + _buffer_free (local_os2_surface->pixels); + /* Set new image surface */ + local_os2_surface->image_surface = pNewImageSurface; + /* Set new pixel buffer */ + local_os2_surface->pixels = pchNewPixels; + /* Change bitmap2 structure */ + local_os2_surface->bitmap_info.cx = new_width; + local_os2_surface->bitmap_info.cy = new_height; + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_os2_surface_refresh_window: + * @surface: the cairo surface to refresh + * @hps_begin_paint: the presentation handle of the window to refresh + * @prcl_begin_paint_rect: the rectangle to redraw + * + * This function can be used to force a repaint of a given area of the client + * window. It should usually be called from the WM_PAINT processing of the + * window procedure. However, it can be called any time a given part of the + * window has to be updated. + * + * The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call + * of the window procedure, but you can also get the HPS using WinGetPS, and you + * can assemble your own update rectangle by hand. + * If hps_begin_paint is %NULL, the function will use the HPS passed into + * cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function + * will query the current window size and repaint the whole window. + * + * Cairo assumes that if you set the HWND to the surface using + * cairo_os2_surface_set_hwnd(), this function will be called by the application + * every time it gets a WM_PAINT for that HWND. If the HWND is set in the + * surface, Cairo uses this function to handle dirty areas too. + * + * Since: 1.4 + **/ +void +cairo_os2_surface_refresh_window (cairo_surface_t *surface, + HPS hps_begin_paint, + PRECTL prcl_begin_paint_rect) +{ + cairo_os2_surface_t *local_os2_surface; + RECTL rclTemp; + HPS hpsTemp = 0; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return; + } + + /* If an HPS wasn't provided, see if we can get one. */ + if (!hps_begin_paint) { + hps_begin_paint = local_os2_surface->hps_client_window; + if (!hps_begin_paint) { + if (local_os2_surface->hwnd_client_window) { + hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window); + hps_begin_paint = hpsTemp; + } + /* No HPS & no way to get one, so exit */ + if (!hps_begin_paint) + return; + } + } + + if (prcl_begin_paint_rect == NULL) { + /* Update the whole window! */ + rclTemp.xLeft = 0; + rclTemp.xRight = local_os2_surface->bitmap_info.cx; + rclTemp.yTop = local_os2_surface->bitmap_info.cy; + rclTemp.yBottom = 0; + } else { + /* Use the rectangle we got passed as parameter! */ + rclTemp.xLeft = prcl_begin_paint_rect->xLeft; + rclTemp.xRight = prcl_begin_paint_rect->xRight; + rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom; + rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ; + } + + /* Get mutex, we'll work with the pixel array! */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) + != NO_ERROR) + { + /* Could not get mutex! */ + if (hpsTemp) + WinReleasePS(hpsTemp); + return; + } + + if ((local_os2_surface->dirty_area_present) && + (local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) && + (local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) && + (local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) && + (local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom)) + { + /* Aha, this call was because of a dirty area, so in this case we + * have to blit the pixels from the screen to the surface! + */ + local_os2_surface->dirty_area_present = FALSE; + _cairo_os2_surface_get_pixels_from_screen (local_os2_surface, + hps_begin_paint, + &rclTemp); + } else { + /* Okay, we have the surface, have the HPS + * (might be from WinBeginPaint () or from WinGetPS () ) + * Now blit there the stuffs! + */ + _cairo_os2_surface_blit_pixels (local_os2_surface, + hps_begin_paint, + &rclTemp); + } + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + + if (hpsTemp) + WinReleasePS(hpsTemp); +} + +static cairo_status_t +_cairo_os2_surface_finish (void *abstract_surface) +{ + 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); + + /* Destroy old image surface */ + cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface)); + /* Destroy old pixel buffer */ + _buffer_free (local_os2_surface->pixels); + DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields); + DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back); + + /* The memory itself will be free'd by the cairo_surface_destroy () + * who called us. + */ + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_os2_surface_set_hwnd: + * @surface: the cairo surface to associate with the window handle + * @hwnd_client_window: the window handle of the client window + * + * Sets window handle for surface; the caller retains ownership of the window. + * If Cairo wants to blit into the window because it is set to blit as the + * surface changes (see cairo_os2_surface_set_manual_window_refresh()), then + * there are two ways it can choose: + * If it knows the HWND of the surface, then it invalidates that area, so the + * application will get a WM_PAINT message and it can call + * cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself + * will use the HPS it got at surface creation time, and blit the pixels itself. + * It's also a solution, but experience shows that if this happens from a non-PM + * thread, then it can screw up PM internals. + * + * So, best solution is to set the HWND for the surface after the surface + * creation, so every blit will be done from application's message processing + * loop, which is the safest way to do. + * + * Since: 1.4 + **/ +void +cairo_os2_surface_set_hwnd (cairo_surface_t *surface, + HWND hwnd_client_window) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return; + } + + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) + != NO_ERROR) + { + /* Could not get mutex! */ + return; + } + + local_os2_surface->hwnd_client_window = hwnd_client_window; + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); +} + +/** + * cairo_os2_surface_set_manual_window_refresh: + * @surface: the cairo surface to set the refresh mode for + * @manual_refresh: the switch for manual surface refresh + * + * This API can tell Cairo if it should show every change to this surface + * immediately in the window or if it should be cached and will only be visible + * once the user calls cairo_os2_surface_refresh_window() explicitly. If the + * HWND was not set in the cairo surface, then the HPS will be used to blit the + * graphics. Otherwise it will invalidate the given window region so the user + * will get the WM_PAINT message to redraw that area of the window. + * + * So, if you're only interested in displaying the final result after several + * drawing operations, you might get better performance if you put the surface + * into manual refresh mode by passing a true value to this function. Then call + * cairo_os2_surface_refresh() whenever desired. + * + * Since: 1.4 + **/ +void +cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, + cairo_bool_t manual_refresh) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return; + } + + local_os2_surface->blit_as_changes = !manual_refresh; +} + +/** + * cairo_os2_surface_get_manual_window_refresh: + * @surface: the cairo surface to query the refresh mode from + * + * This space left intentionally blank. + * + * Return value: current refresh mode of the surface (true by default) + * + * Since: 1.4 + **/ +cairo_bool_t +cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) surface; + if ((!local_os2_surface) || + (local_os2_surface->base.backend != &cairo_os2_surface_backend)) + { + /* Invalid parameter (wrong surface)! */ + return FALSE; + } + + return !(local_os2_surface->blit_as_changes); +} + +/** + * cairo_os2_surface_get_hps: + * @surface: the cairo surface to be querued + * @hps: HPS currently associated with the surface (if any) + * + * This API retrieves the HPS associated with the surface. + * + * Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, + * %CAIRO_STATUS_NULL_POINTER if the hps argument is null + * + * Since: 1.10 + **/ +cairo_status_t +cairo_os2_surface_get_hps (cairo_surface_t *surface, + HPS *hps) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) 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); + } + if (!hps) + { + return _cairo_error (CAIRO_STATUS_NULL_POINTER); + } + *hps = local_os2_surface->hps_client_window; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_os2_surface_set_hps: + * @surface: the cairo surface to associate with the HPS + * @hps: new HPS to be associated with the surface (the HPS may be null) + * + * This API replaces the HPS associated with the surface with a new one. + * The caller retains ownership of the HPS and must dispose of it after + * the surface has been destroyed or it has been replaced by another + * call to this function. + * + * Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced, + * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface, + * + * Since: 1.10 + **/ +cairo_status_t +cairo_os2_surface_set_hps (cairo_surface_t *surface, + HPS hps) +{ + cairo_os2_surface_t *local_os2_surface; + + local_os2_surface = (cairo_os2_surface_t *) 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); + } + local_os2_surface->hps_client_window = hps; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_os2_surface_mark_dirty_rectangle (void *surface, + int x, + int y, + int width, + int height) +{ + cairo_os2_surface_t *local_os2_surface; + RECTL rclToBlit; + + local_os2_surface = (cairo_os2_surface_t *) 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); + } + + /* Get mutex, we'll work with the pixel array! */ + if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT) + != NO_ERROR) + { + /* Could not get mutex! */ + return CAIRO_STATUS_NO_MEMORY; + } + + /* Check for defaults */ + if (width < 0) + width = local_os2_surface->bitmap_info.cx; + if (height < 0) + height = local_os2_surface->bitmap_info.cy; + + if (local_os2_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. + * From that function we'll note that it's not a redraw but a + * dirty-rectangle deal stuff, so we'll handle the things from + * there. + * + * This is the safe method, which should be preferred every time. + */ + rclToBlit.xLeft = x; + rclToBlit.xRight = x + width; /* Noninclusive */ + rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y); + rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */ + +#if 0 + if (local_os2_surface->dirty_area_present) { + /* Yikes, there is already a dirty area which should be + * cleaned up, but we'll overwrite it. Sorry. + * TODO: Something clever should be done here. + */ + } +#endif + + /* Set up dirty area reminder stuff */ + memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL)); + local_os2_surface->dirty_area_present = TRUE; + + /* Invalidate window area */ + WinInvalidateRect (local_os2_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. + * 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 = x; + rclToBlit.xRight = x + width; /* Noninclusive */ + rclToBlit.yBottom = y; + rclToBlit.yTop = y + height; /* Noninclusive */ + /* Now get the pixels from the screen! */ + _cairo_os2_surface_get_pixels_from_screen (local_os2_surface, + local_os2_surface->hps_client_window, + &rclToBlit); + } + + DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_os2_surface_backend = { + CAIRO_SURFACE_TYPE_OS2, + _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, + NULL, /* snapshot */ + + _cairo_os2_surface_get_extents, + NULL, /* get_font_options */ + + NULL, /* flush */ + _cairo_os2_surface_mark_dirty_rectangle, + + _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 new file mode 100644 index 0000000000..d23f2dec42 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-os2.h @@ -0,0 +1,110 @@ +/* vim: set sw=4 sts=4 et cin: */ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2005-2006 netlabs.org + * + * 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 + * Doodle + * + * Contributor(s): + * Peter Weilbacher + * Rich Walsh + */ + +#ifndef _CAIRO_OS2_H_ +#define _CAIRO_OS2_H_ + +#define INCL_DOS +#define INCL_DOSSEMAPHORES +#define INCL_DOSERRORS +#define INCL_WIN +#define INCL_GPI + +#include "cairo.h" + +#include + +CAIRO_BEGIN_DECLS + +/* The OS/2 Specific Cairo API */ + +cairo_public void +cairo_os2_init (void); + +cairo_public void +cairo_os2_fini (void); + +#if CAIRO_HAS_OS2_SURFACE + +cairo_public cairo_surface_t * +cairo_os2_surface_create (HPS hps_client_window, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_os2_surface_create_for_window (HWND hwnd_client_window, + int width, + int height); + +cairo_public void +cairo_os2_surface_set_hwnd (cairo_surface_t *surface, + HWND hwnd_client_window); + +cairo_public int +cairo_os2_surface_set_size (cairo_surface_t *surface, + int new_width, + int new_height, + int timeout); + +cairo_public void +cairo_os2_surface_refresh_window (cairo_surface_t *surface, + HPS hps_begin_paint, + PRECTL prcl_begin_paint_rect); + +cairo_public void +cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface, + cairo_bool_t manual_refresh); + +cairo_public cairo_bool_t +cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_os2_surface_get_hps (cairo_surface_t *surface, + HPS *hps); + +cairo_public cairo_status_t +cairo_os2_surface_set_hps (cairo_surface_t *surface, + HPS hps); + +#else /* CAIRO_HAS_OS2_SURFACE */ +# error Cairo was not compiled with support for the OS/2 backend +#endif /* CAIRO_HAS_OS2_SURFACE */ + +CAIRO_END_DECLS + +#endif /* _CAIRO_OS2_H_ */ diff --git a/gfx/cairo/cairo/src/cairo-output-stream-private.h b/gfx/cairo/cairo/src/cairo-output-stream-private.h new file mode 100644 index 0000000000..2542646b86 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-output-stream-private.h @@ -0,0 +1,201 @@ +/* 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. + * + * Author(s): + * Kristian Høgsberg + */ + +#ifndef CAIRO_OUTPUT_STREAM_PRIVATE_H +#define CAIRO_OUTPUT_STREAM_PRIVATE_H + +#include "cairo-compiler-private.h" +#include "cairo-types-private.h" + +#include +#include +#include + +typedef cairo_status_t +(*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream, + const unsigned char *data, + unsigned int length); + +typedef cairo_status_t +(*cairo_output_stream_flush_func_t) (cairo_output_stream_t *output_stream); + +typedef cairo_status_t +(*cairo_output_stream_close_func_t) (cairo_output_stream_t *output_stream); + +struct _cairo_output_stream { + cairo_output_stream_write_func_t write_func; + cairo_output_stream_flush_func_t flush_func; + cairo_output_stream_close_func_t close_func; + unsigned long position; + cairo_status_t status; + cairo_bool_t closed; +}; + +extern const cairo_private cairo_output_stream_t _cairo_output_stream_nil; + +cairo_private void +_cairo_output_stream_init (cairo_output_stream_t *stream, + cairo_output_stream_write_func_t write_func, + cairo_output_stream_flush_func_t flush_func, + cairo_output_stream_close_func_t close_func); + +cairo_private cairo_status_t +_cairo_output_stream_fini (cairo_output_stream_t *stream); + + +/* We already have the following declared in cairo.h: + +typedef cairo_status_t (*cairo_write_func_t) (void *closure, + const unsigned char *data, + unsigned int length); +*/ +typedef cairo_status_t (*cairo_close_func_t) (void *closure); + + +/* This function never returns %NULL. If an error occurs (NO_MEMORY) + * while trying to create the output stream this function returns a + * valid pointer to a nil output stream. + * + * Note that even with a nil surface, the close_func callback will be + * called by a call to _cairo_output_stream_close or + * _cairo_output_stream_destroy. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create (cairo_write_func_t write_func, + cairo_close_func_t close_func, + void *closure); + +cairo_private cairo_output_stream_t * +_cairo_output_stream_create_in_error (cairo_status_t status); + +/* Tries to flush any buffer maintained by the stream or its delegates. */ +cairo_private cairo_status_t +_cairo_output_stream_flush (cairo_output_stream_t *stream); + +/* Returns the final status value associated with this object, just + * before its last gasp. This final status value will capture any + * status failure returned by the stream's close_func as well. */ +cairo_private cairo_status_t +_cairo_output_stream_close (cairo_output_stream_t *stream); + +/* Returns the final status value associated with this object, just + * before its last gasp. This final status value will capture any + * status failure returned by the stream's close_func as well. */ +cairo_private cairo_status_t +_cairo_output_stream_destroy (cairo_output_stream_t *stream); + +cairo_private void +_cairo_output_stream_write (cairo_output_stream_t *stream, + const void *data, size_t length); + +cairo_private void +_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, + const unsigned char *data, + size_t length); + +cairo_private void +_cairo_output_stream_vprintf (cairo_output_stream_t *stream, + const char *fmt, + va_list ap) CAIRO_PRINTF_FORMAT ( 2, 0); + +cairo_private void +_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); + +cairo_private cairo_status_t +_cairo_output_stream_get_status (cairo_output_stream_t *stream); + +/* This function never returns %NULL. If an error occurs (NO_MEMORY or + * WRITE_ERROR) while trying to create the output stream this function + * returns a valid pointer to a nil output stream. + * + * Note: Even if a nil surface is returned, the caller should still + * call _cairo_output_stream_destroy (or _cairo_output_stream_close at + * least) in order to ensure that everything is properly cleaned up. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create_for_filename (const char *filename); + +/* This function never returns %NULL. If an error occurs (NO_MEMORY or + * WRITE_ERROR) while trying to create the output stream this function + * returns a valid pointer to a nil output stream. + * + * The caller still "owns" file and is responsible for calling fclose + * on it when finished. The stream will not do this itself. + */ +cairo_private cairo_output_stream_t * +_cairo_output_stream_create_for_file (FILE *file); + +cairo_private cairo_output_stream_t * +_cairo_memory_stream_create (void); + +cairo_private void +_cairo_memory_stream_copy (cairo_output_stream_t *base, + cairo_output_stream_t *dest); + +cairo_private int +_cairo_memory_stream_length (cairo_output_stream_t *stream); + +cairo_private cairo_status_t +_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, + unsigned char **data_out, + unsigned long *length_out); + +cairo_private cairo_output_stream_t * +_cairo_null_stream_create (void); + +/* cairo-base85-stream.c */ +cairo_private cairo_output_stream_t * +_cairo_base85_stream_create (cairo_output_stream_t *output); + +/* cairo-base64-stream.c */ +cairo_private cairo_output_stream_t * +_cairo_base64_stream_create (cairo_output_stream_t *output); + +/* cairo-deflate-stream.c */ +cairo_private cairo_output_stream_t * +_cairo_deflate_stream_create (cairo_output_stream_t *output); + + +#endif /* CAIRO_OUTPUT_STREAM_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-output-stream.c b/gfx/cairo/cairo/src/cairo-output-stream.c new file mode 100644 index 0000000000..935fa44c33 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-output-stream.c @@ -0,0 +1,811 @@ +/* cairo-output-stream.c: Output stream abstraction + * + * Copyright © 2005 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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. + * + * Author(s): + * Kristian Høgsberg + */ + +#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 + +/* Numbers printed with %f are printed with this number of significant + * digits after the decimal. + */ +#define SIGNIFICANT_DIGITS_AFTER_DECIMAL 6 + +/* Numbers printed with %g are assumed to only have %CAIRO_FIXED_FRAC_BITS + * bits of precision available after the decimal point. + * + * FIXED_POINT_DECIMAL_DIGITS specifies the minimum number of decimal + * digits after the decimal point required to preserve the available + * precision. + * + * The conversion is: + * + * + * FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) ) + * + * + * We can replace ceil(x) with (int)(x+1) since x will never be an + * integer for any likely value of %CAIRO_FIXED_FRAC_BITS. + */ +#define FIXED_POINT_DECIMAL_DIGITS ((int)(CAIRO_FIXED_FRAC_BITS*0.301029996 + 1)) + +void +_cairo_output_stream_init (cairo_output_stream_t *stream, + cairo_output_stream_write_func_t write_func, + cairo_output_stream_flush_func_t flush_func, + cairo_output_stream_close_func_t close_func) +{ + stream->write_func = write_func; + stream->flush_func = flush_func; + stream->close_func = close_func; + stream->position = 0; + stream->status = CAIRO_STATUS_SUCCESS; + stream->closed = FALSE; +} + +cairo_status_t +_cairo_output_stream_fini (cairo_output_stream_t *stream) +{ + return _cairo_output_stream_close (stream); +} + +const cairo_output_stream_t _cairo_output_stream_nil = { + NULL, /* write_func */ + NULL, /* flush_func */ + NULL, /* close_func */ + 0, /* position */ + CAIRO_STATUS_NO_MEMORY, + FALSE /* closed */ +}; + +static const cairo_output_stream_t _cairo_output_stream_nil_write_error = { + NULL, /* write_func */ + NULL, /* flush_func */ + NULL, /* close_func */ + 0, /* position */ + CAIRO_STATUS_WRITE_ERROR, + FALSE /* closed */ +}; + +typedef struct _cairo_output_stream_with_closure { + cairo_output_stream_t base; + cairo_write_func_t write_func; + cairo_close_func_t close_func; + void *closure; +} cairo_output_stream_with_closure_t; + + +static cairo_status_t +closure_write (cairo_output_stream_t *stream, + const unsigned char *data, unsigned int length) +{ + cairo_output_stream_with_closure_t *stream_with_closure = + (cairo_output_stream_with_closure_t *) stream; + + if (stream_with_closure->write_func == NULL) + return CAIRO_STATUS_SUCCESS; + + return stream_with_closure->write_func (stream_with_closure->closure, + data, length); +} + +static cairo_status_t +closure_close (cairo_output_stream_t *stream) +{ + cairo_output_stream_with_closure_t *stream_with_closure = + (cairo_output_stream_with_closure_t *) stream; + + if (stream_with_closure->close_func != NULL) + return stream_with_closure->close_func (stream_with_closure->closure); + else + return CAIRO_STATUS_SUCCESS; +} + +cairo_output_stream_t * +_cairo_output_stream_create (cairo_write_func_t write_func, + cairo_close_func_t close_func, + void *closure) +{ + cairo_output_stream_with_closure_t *stream; + + 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; + } + + _cairo_output_stream_init (&stream->base, + closure_write, NULL, closure_close); + stream->write_func = write_func; + stream->close_func = close_func; + stream->closure = closure; + + return &stream->base; +} + +cairo_output_stream_t * +_cairo_output_stream_create_in_error (cairo_status_t status) +{ + cairo_output_stream_t *stream; + + /* check for the common ones */ + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + if (status == CAIRO_STATUS_WRITE_ERROR) + return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; + + 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; + } + + _cairo_output_stream_init (stream, NULL, NULL, NULL); + stream->status = status; + + return stream; +} + +cairo_status_t +_cairo_output_stream_flush (cairo_output_stream_t *stream) +{ + cairo_status_t status; + + if (stream->closed) + return stream->status; + + if (stream == &_cairo_output_stream_nil || + stream == &_cairo_output_stream_nil_write_error) + { + return stream->status; + } + + if (stream->flush_func) { + status = stream->flush_func (stream); + /* Don't overwrite a pre-existing status failure. */ + if (stream->status == CAIRO_STATUS_SUCCESS) + stream->status = status; + } + + return stream->status; +} + +cairo_status_t +_cairo_output_stream_close (cairo_output_stream_t *stream) +{ + cairo_status_t status; + + if (stream->closed) + return stream->status; + + if (stream == &_cairo_output_stream_nil || + stream == &_cairo_output_stream_nil_write_error) + { + return stream->status; + } + + if (stream->close_func) { + status = stream->close_func (stream); + /* Don't overwrite a pre-existing status failure. */ + if (stream->status == CAIRO_STATUS_SUCCESS) + stream->status = status; + } + + stream->closed = TRUE; + + return stream->status; +} + +cairo_status_t +_cairo_output_stream_destroy (cairo_output_stream_t *stream) +{ + cairo_status_t status; + + assert (stream != NULL); + + if (stream == &_cairo_output_stream_nil || + stream == &_cairo_output_stream_nil_write_error) + { + return stream->status; + } + + status = _cairo_output_stream_fini (stream); + free (stream); + + return status; +} + +void +_cairo_output_stream_write (cairo_output_stream_t *stream, + const void *data, size_t length) +{ + if (length == 0) + return; + + if (stream->status) + return; + + stream->status = stream->write_func (stream, data, length); + stream->position += length; +} + +void +_cairo_output_stream_write_hex_string (cairo_output_stream_t *stream, + const unsigned char *data, + size_t length) +{ + const char hex_chars[] = "0123456789abcdef"; + char buffer[2]; + unsigned int i, column; + + if (stream->status) + return; + + for (i = 0, column = 0; i < length; i++, column++) { + if (column == 38) { + _cairo_output_stream_write (stream, "\n", 1); + column = 0; + } + buffer[0] = hex_chars[(data[i] >> 4) & 0x0f]; + buffer[1] = hex_chars[data[i] & 0x0f]; + _cairo_output_stream_write (stream, buffer, 2); + } +} + +/* Format a double in a locale independent way and trim trailing + * zeros. Based on code from Alex Larson . + * 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 + * into cairo (see COPYING). -- Kristian Høgsberg + */ +static void +_cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision) +{ + const char *decimal_point; + int decimal_point_len; + char *p; + int decimal_len; + int num_zeros, decimal_digits; + + /* Omit the minus sign from negative zero. */ + if (d == 0.0) + d = 0.0; + + decimal_point = _cairo_get_locale_decimal_point (); + decimal_point_len = strlen (decimal_point); + + assert (decimal_point_len != 0); + + if (limited_precision) { + snprintf (buffer, size, "%.*f", FIXED_POINT_DECIMAL_DIGITS, d); + } else { + /* Using "%f" to print numbers less than 0.1 will result in + * reduced precision due to the default 6 digits after the + * decimal point. + * + * For numbers is < 0.1, we print with maximum precision and count + * the number of zeros between the decimal point and the first + * significant digit. We then print the number again with the + * number of decimal places that gives us the required number of + * significant digits. This ensures the number is correctly + * rounded. + */ + if (fabs (d) >= 0.1) { + snprintf (buffer, size, "%f", d); + } else { + snprintf (buffer, size, "%.18f", d); + p = buffer; + + if (*p == '+' || *p == '-') + p++; + + while (_cairo_isdigit (*p)) + p++; + + if (strncmp (p, decimal_point, decimal_point_len) == 0) + p += decimal_point_len; + + num_zeros = 0; + while (*p++ == '0') + num_zeros++; + + decimal_digits = num_zeros + SIGNIFICANT_DIGITS_AFTER_DECIMAL; + + if (decimal_digits < 18) + snprintf (buffer, size, "%.*f", decimal_digits, d); + } + } + p = buffer; + + if (*p == '+' || *p == '-') + p++; + + while (_cairo_isdigit (*p)) + p++; + + if (strncmp (p, decimal_point, decimal_point_len) == 0) { + *p = '.'; + decimal_len = strlen (p + decimal_point_len); + memmove (p + 1, p + decimal_point_len, decimal_len); + p[1 + decimal_len] = 0; + + /* Remove trailing zeros and decimal point if possible. */ + for (p = p + decimal_len; *p == '0'; p--) + *p = 0; + + if (*p == '.') { + *p = 0; + p--; + } + } +} + +enum { + LENGTH_MODIFIER_LONG = 0x100 +}; + +/* Here's a limited reimplementation of printf. The reason for doing + * this is primarily to special case handling of doubles. We want + * locale independent formatting of doubles and we want to trim + * trailing zeros. This is handled by dtostr() above, and the code + * below handles everything else by calling snprintf() to do the + * formatting. This functionality is only for internal use and we + * only implement the formats we actually use. + */ +void +_cairo_output_stream_vprintf (cairo_output_stream_t *stream, + const char *fmt, va_list ap) +{ +#define SINGLE_FMT_BUFFER_SIZE 32 + char buffer[512], single_fmt[SINGLE_FMT_BUFFER_SIZE]; + int single_fmt_length; + char *p; + const char *f, *start; + int length_modifier, width; + cairo_bool_t var_width; + + if (stream->status) + return; + + f = fmt; + p = buffer; + while (*f != '\0') { + if (p == buffer + sizeof (buffer)) { + _cairo_output_stream_write (stream, buffer, sizeof (buffer)); + p = buffer; + } + + if (*f != '%') { + *p++ = *f++; + continue; + } + + start = f; + f++; + + if (*f == '0') + f++; + + var_width = FALSE; + if (*f == '*') { + var_width = TRUE; + f++; + } + + while (_cairo_isdigit (*f)) + f++; + + length_modifier = 0; + if (*f == 'l') { + length_modifier = LENGTH_MODIFIER_LONG; + f++; + } + + /* The only format strings exist in the cairo implementation + * itself. So there's an internal consistency problem if any + * of them is larger than our format buffer size. */ + single_fmt_length = f - start + 1; + assert (single_fmt_length + 1 <= SINGLE_FMT_BUFFER_SIZE); + + /* Reuse the format string for this conversion. */ + memcpy (single_fmt, start, single_fmt_length); + single_fmt[single_fmt_length] = '\0'; + + /* Flush contents of buffer before snprintf()'ing into it. */ + _cairo_output_stream_write (stream, buffer, p - buffer); + + /* We group signed and unsigned together in this switch, the + * only thing that matters here is the size of the arguments, + * since we're just passing the data through to sprintf(). */ + switch (*f | length_modifier) { + case '%': + buffer[0] = *f; + buffer[1] = 0; + break; + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + if (var_width) { + width = va_arg (ap, int); + snprintf (buffer, sizeof buffer, + single_fmt, width, va_arg (ap, int)); + } else { + snprintf (buffer, sizeof buffer, single_fmt, va_arg (ap, int)); + } + break; + case 'd' | LENGTH_MODIFIER_LONG: + case 'u' | LENGTH_MODIFIER_LONG: + case 'o' | LENGTH_MODIFIER_LONG: + case 'x' | LENGTH_MODIFIER_LONG: + case 'X' | LENGTH_MODIFIER_LONG: + if (var_width) { + width = va_arg (ap, int); + snprintf (buffer, sizeof buffer, + single_fmt, width, va_arg (ap, long int)); + } else { + snprintf (buffer, sizeof buffer, + single_fmt, va_arg (ap, long int)); + } + break; + 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); + break; + case 'g': + _cairo_dtostr (buffer, sizeof buffer, va_arg (ap, double), TRUE); + break; + case 'c': + buffer[0] = va_arg (ap, int); + buffer[1] = 0; + break; + default: + ASSERT_NOT_REACHED; + } + p = buffer + strlen (buffer); + f++; + } + + _cairo_output_stream_write (stream, buffer, p - buffer); +} + +void +_cairo_output_stream_printf (cairo_output_stream_t *stream, + const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + + _cairo_output_stream_vprintf (stream, fmt, ap); + + 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) +{ + return stream->position; +} + +cairo_status_t +_cairo_output_stream_get_status (cairo_output_stream_t *stream) +{ + return stream->status; +} + +/* Maybe this should be a configure time option, so embedded targets + * don't have to pull in stdio. */ + + +typedef struct _stdio_stream { + cairo_output_stream_t base; + FILE *file; +} stdio_stream_t; + +static cairo_status_t +stdio_write (cairo_output_stream_t *base, + const unsigned char *data, unsigned int length) +{ + stdio_stream_t *stream = (stdio_stream_t *) base; + + if (fwrite (data, 1, length, stream->file) != length) + return _cairo_error (CAIRO_STATUS_WRITE_ERROR); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +stdio_flush (cairo_output_stream_t *base) +{ + stdio_stream_t *stream = (stdio_stream_t *) base; + + fflush (stream->file); + + if (ferror (stream->file)) + return _cairo_error (CAIRO_STATUS_WRITE_ERROR); + else + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +stdio_close (cairo_output_stream_t *base) +{ + cairo_status_t status; + stdio_stream_t *stream = (stdio_stream_t *) base; + + status = stdio_flush (base); + + fclose (stream->file); + + return status; +} + +cairo_output_stream_t * +_cairo_output_stream_create_for_file (FILE *file) +{ + stdio_stream_t *stream; + + if (file == NULL) { + _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); + return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; + } + + 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; + } + + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_flush); + stream->file = file; + + return &stream->base; +} + +cairo_output_stream_t * +_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 (); + + 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: + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + default: + _cairo_error_throw (CAIRO_STATUS_WRITE_ERROR); + return (cairo_output_stream_t *) &_cairo_output_stream_nil_write_error; + } + } + + stream = _cairo_malloc (sizeof *stream); + if (unlikely (stream == NULL)) { + fclose (file); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_output_stream_t *) &_cairo_output_stream_nil; + } + + _cairo_output_stream_init (&stream->base, + stdio_write, stdio_flush, stdio_close); + stream->file = file; + + return &stream->base; +} + + +typedef struct _memory_stream { + cairo_output_stream_t base; + cairo_array_t array; +} memory_stream_t; + +static cairo_status_t +memory_write (cairo_output_stream_t *base, + const unsigned char *data, unsigned int length) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + return _cairo_array_append_multiple (&stream->array, data, length); +} + +static cairo_status_t +memory_close (cairo_output_stream_t *base) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + _cairo_array_fini (&stream->array); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_output_stream_t * +_cairo_memory_stream_create (void) +{ + memory_stream_t *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; + } + + _cairo_output_stream_init (&stream->base, memory_write, NULL, memory_close); + _cairo_array_init (&stream->array, 1); + + return &stream->base; +} + +cairo_status_t +_cairo_memory_stream_destroy (cairo_output_stream_t *abstract_stream, + unsigned char **data_out, + unsigned long *length_out) +{ + memory_stream_t *stream; + cairo_status_t status; + + status = abstract_stream->status; + if (unlikely (status)) + return _cairo_output_stream_destroy (abstract_stream); + + stream = (memory_stream_t *) abstract_stream; + + *length_out = _cairo_array_num_elements (&stream->array); + *data_out = _cairo_malloc (*length_out); + if (unlikely (*data_out == NULL)) { + status = _cairo_output_stream_destroy (abstract_stream); + assert (status == CAIRO_STATUS_SUCCESS); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + memcpy (*data_out, _cairo_array_index (&stream->array, 0), *length_out); + + return _cairo_output_stream_destroy (abstract_stream); +} + +void +_cairo_memory_stream_copy (cairo_output_stream_t *base, + cairo_output_stream_t *dest) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + if (dest->status) + return; + + if (base->status) { + dest->status = base->status; + return; + } + + _cairo_output_stream_write (dest, + _cairo_array_index (&stream->array, 0), + _cairo_array_num_elements (&stream->array)); +} + +int +_cairo_memory_stream_length (cairo_output_stream_t *base) +{ + memory_stream_t *stream = (memory_stream_t *) base; + + return _cairo_array_num_elements (&stream->array); +} + +static cairo_status_t +null_write (cairo_output_stream_t *base, + const unsigned char *data, unsigned int length) +{ + return CAIRO_STATUS_SUCCESS; +} + +cairo_output_stream_t * +_cairo_null_stream_create (void) +{ + cairo_output_stream_t *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; + } + + _cairo_output_stream_init (stream, null_write, NULL, NULL); + + return stream; +} diff --git a/gfx/cairo/cairo/src/cairo-paginated-private.h b/gfx/cairo/cairo/src/cairo-paginated-private.h new file mode 100644 index 0000000000..89f1c99a25 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-paginated-private.h @@ -0,0 +1,184 @@ +/* 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 + */ + +#ifndef CAIRO_PAGINATED_H +#define CAIRO_PAGINATED_H + +#include "cairoint.h" + +struct _cairo_paginated_surface_backend { + /* Optional. Will be called once for each page. + * + * Note: With respect to the order of drawing operations as seen + * by the target, this call will occur before any drawing + * operations for the relevant page. However, with respect to the + * function calls as made by the user, this call will be *after* + * any drawing operations for the page, (that is, it will occur + * during the user's call to cairo_show_page or cairo_copy_page). + */ + cairo_warn cairo_int_status_t + (*start_page) (void *surface); + + /* Required. Will be called twice for each page, once with an + * argument of CAIRO_PAGINATED_MODE_ANALYZE and once with + * CAIRO_PAGINATED_MODE_RENDER. See more details in the + * documentation for _cairo_paginated_surface_create below. + */ + cairo_warn cairo_int_status_t + (*set_paginated_mode) (void *surface, + cairo_paginated_mode_t mode); + + /* Optional. Specifies the smallest box that encloses all objects + * on the page. Will be called at the end of the ANALYZE phase but + * before the mode is changed to RENDER. + */ + cairo_warn cairo_int_status_t + (*set_bounding_box) (void *surface, + cairo_box_t *bbox); + + /* Optional. Indicates whether the page requires fallback images. + * Will be called at the end of the ANALYZE phase but before the + * mode is changed to RENDER. + */ + cairo_warn cairo_int_status_t + (*set_fallback_images_required) (void *surface, + cairo_bool_t fallbacks_required); + + cairo_bool_t + (*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 + * is well-suited for doing the analysis common to most surfaces that + * have paginated output, (that is, things directed at printers, or + * for saving content in files such as PostScript or PDF files). + * + * To use the paginated surface, you'll first need to create your + * 'real' surface using _cairo_surface_init() and the standard + * #cairo_surface_backend_t. Then you also call + * _cairo_paginated_surface_create which takes its own, much simpler, + * #cairo_paginated_surface_backend_t. You are free to return the result + * of _cairo_paginated_surface_create() from your public + * cairo__surface_create(). The paginated backend will be careful + * to not let the user see that they really got a "wrapped" + * surface. See test-paginated-surface.c for a fairly minimal example + * of a paginated-using surface. That should be a reasonable example + * to follow. + * + * What the paginated surface does is first save all drawing + * operations for a page into a recording-surface. Then when the user calls + * cairo_show_page(), the paginated surface performs the following + * sequence of operations (using the backend functions passed to + * cairo_paginated_surface_create()): + * + * 1. Calls start_page() (if not %NULL). At this point, it is appropriate + * for the target to emit any page-specific header information into + * its output. + * + * 2. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_ANALYZE + * + * 3. Replays the recording-surface to the target surface, (with an + * analysis surface inserted between which watches the return value + * from each operation). This analysis stage is used to decide which + * operations will require fallbacks. + * + * 4. Calls set_bounding_box() to provide the target surface with the + * tight bounding box of the page. + * + * 5. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_RENDER + * + * 6. Replays a subset of the recording-surface operations to the target surface + * + * 7. Calls set_paginated_mode() with an argument of %CAIRO_PAGINATED_MODE_FALLBACK + * + * 8. Replays the remaining operations to an image surface, sets an + * appropriate clip on the target, then paints the resulting image + * surface to the target. + * + * So, the target will see drawing operations during three separate + * stages, (ANALYZE, RENDER and FALLBACK). During the ANALYZE phase + * the target should not actually perform any rendering, (for example, + * if performing output to a file, no output should be generated + * during this stage). Instead the drawing functions simply need to + * return %CAIRO_STATUS_SUCCESS or %CAIRO_INT_STATUS_UNSUPPORTED to + * indicate whether rendering would be supported. And it should do + * this as quickly as possible. The FALLBACK phase allows the surface + * to distinguish fallback images from native rendering in case they + * need to be handled as a special case. + * + * Note: The paginated surface layer assumes that the target surface + * is "blank" by default at the beginning of each page, without any + * need for an explicit erase operation, (as opposed to an image + * surface, for example, which might have uninitialized content + * originally). As such, it optimizes away CLEAR operations that + * happen at the beginning of each page---the target surface will not + * even see these operations. + */ +cairo_private cairo_surface_t * +_cairo_paginated_surface_create (cairo_surface_t *target, + cairo_content_t content, + const cairo_paginated_surface_backend_t *backend); + +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); + +cairo_private cairo_status_t +_cairo_paginated_surface_set_size (cairo_surface_t *surface, + int width, + int height); + +#endif /* CAIRO_PAGINATED_H */ diff --git a/gfx/cairo/cairo/src/cairo-paginated-surface-private.h b/gfx/cairo/cairo/src/cairo-paginated-surface-private.h new file mode 100644 index 0000000000..ebf4b34249 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-paginated-surface-private.h @@ -0,0 +1,62 @@ +/* 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 + */ + +#ifndef CAIRO_PAGINATED_SURFACE_H +#define CAIRO_PAGINATED_SURFACE_H + +#include "cairo.h" + +#include "cairo-surface-private.h" + +typedef struct _cairo_paginated_surface { + cairo_surface_t base; + + /* The target surface to hold the final result. */ + cairo_surface_t *target; + + cairo_content_t content; + + /* Paginated-surface specific functions for the target */ + const cairo_paginated_surface_backend_t *backend; + + /* A cairo_recording_surface to record all operations. To be replayed + * against target, and also against image surface as necessary for + * fallbacks. */ + cairo_surface_t *recording_surface; + + int page_num; +} cairo_paginated_surface_t; + +#endif /* CAIRO_PAGINATED_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/cairo-paginated-surface.c b/gfx/cairo/cairo/src/cairo-paginated-surface.c new file mode 100644 index 0000000000..a3f7cd9b2d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-paginated-surface.c @@ -0,0 +1,803 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2007 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 Red Hat, Inc. + * + * Contributor(s): + * Carl Worth + * Keith Packard + * Adrian Johnson + */ + +/* The paginated surface layer exists to provide as much code sharing + * as possible for the various paginated surface backends in cairo + * (PostScript, PDF, etc.). See cairo-paginated-private.h for + * more details on how it works and how to use it. + */ + +#include "cairoint.h" + +#include "cairo-paginated-private.h" +#include "cairo-paginated-surface-private.h" +#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; + +static cairo_int_status_t +_cairo_paginated_surface_show_page (void *abstract_surface); + +static cairo_surface_t * +_cairo_paginated_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t rect; + rect.x = rect.y = 0.; + rect.width = width; + rect.height = height; + return cairo_recording_surface_create (content, &rect); +} + +static cairo_surface_t * +_create_recording_surface_for_target (cairo_surface_t *target, + cairo_content_t content) +{ + cairo_rectangle_int_t rect; + + if (_cairo_surface_get_extents (target, &rect)) { + cairo_rectangle_t recording_extents; + + recording_extents.x = rect.x; + recording_extents.y = rect.y; + recording_extents.width = rect.width; + recording_extents.height = rect.height; + + return cairo_recording_surface_create (content, &recording_extents); + } else { + return cairo_recording_surface_create (content, NULL); + } +} + +cairo_surface_t * +_cairo_paginated_surface_create (cairo_surface_t *target, + cairo_content_t content, + const cairo_paginated_surface_backend_t *backend) +{ + cairo_paginated_surface_t *surface; + cairo_status_t status; + + surface = _cairo_malloc (sizeof (cairo_paginated_surface_t)); + if (unlikely (surface == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + _cairo_surface_init (&surface->base, + &cairo_paginated_surface_backend, + NULL, /* device */ + 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. */ + surface->base.type = target->type; + + surface->target = cairo_surface_reference (target); + + surface->content = content; + surface->backend = backend; + + surface->recording_surface = _create_recording_surface_for_target (target, content); + status = surface->recording_surface->status; + if (unlikely (status)) + goto FAIL_CLEANUP_SURFACE; + + surface->page_num = 1; + surface->base.is_clear = TRUE; + + return &surface->base; + + FAIL_CLEANUP_SURFACE: + cairo_surface_destroy (target); + free (surface); + FAIL: + return _cairo_surface_create_in_error (status); +} + +cairo_bool_t +_cairo_surface_is_paginated (cairo_surface_t *surface) +{ + return surface->backend == &cairo_paginated_surface_backend; +} + +cairo_surface_t * +_cairo_paginated_surface_get_target (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->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) +{ + cairo_paginated_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (! surface->base.is_clear || surface->page_num == 1) { + /* Bypass some of the sanity checking in cairo-surface.c, as we + * know that the surface is finished... + */ + status = _cairo_paginated_surface_show_page (surface); + } + + /* XXX We want to propagate any errors from destroy(), but those are not + * returned via the api. So we need to explicitly finish the target, + * and check the status afterwards. However, we can only call finish() + * on the target, if we own it. + */ + if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->target->ref_count) == 1) + cairo_surface_finish (surface->target); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_surface_status (surface->target); + cairo_surface_destroy (surface->target); + + cairo_surface_finish (surface->recording_surface); + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_surface_status (surface->recording_surface); + cairo_surface_destroy (surface->recording_surface); + + return status; +} + +static cairo_surface_t * +_cairo_paginated_surface_create_image_surface (void *abstract_surface, + int width, + int height) +{ + cairo_paginated_surface_t *surface = abstract_surface; + cairo_surface_t *image; + cairo_font_options_t options; + + image = _cairo_image_surface_create_with_content (surface->content, + width, + height); + + cairo_surface_get_font_options (&surface->base, &options); + _cairo_surface_set_font_options (image, &options); + + 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, + void **image_extra) +{ + cairo_paginated_surface_t *surface = abstract_surface; + cairo_bool_t is_bounded; + cairo_surface_t *image; + cairo_status_t status; + cairo_rectangle_int_t extents; + + is_bounded = _cairo_surface_get_extents (surface->target, &extents); + if (! is_bounded) + return CAIRO_INT_STATUS_UNSUPPORTED; + + image = _cairo_paginated_surface_create_image_surface (surface, + extents.width, + extents.height); + + status = _cairo_recording_surface_replay (surface->recording_surface, image); + if (unlikely (status)) { + cairo_surface_destroy (image); + return status; + } + + *image_out = (cairo_image_surface_t*) image; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_paginated_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_int_status_t +_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) +{ + double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution; + double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution; + int x, y, width, height; + cairo_status_t status; + cairo_surface_t *image; + cairo_surface_pattern_t pattern; + cairo_clip_t *clip; + + x = rect->x; + y = rect->y; + width = rect->width; + height = rect->height; + image = _cairo_paginated_surface_create_image_surface (surface, + ceil (width * x_scale), + ceil (height * y_scale)); + 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); + + status = _cairo_recording_surface_replay (surface->recording_surface, image); + if (unlikely (status)) + goto CLEANUP_IMAGE; + + _cairo_pattern_init_for_surface (&pattern, image); + cairo_matrix_init (&pattern.base.matrix, + x_scale, 0, 0, y_scale, -x*x_scale, -y*y_scale); + /* the fallback should be rendered at native resolution, so disable + * filtering (if possible) to avoid introducing potential artifacts. */ + pattern.base.filter = CAIRO_FILTER_NEAREST; + + 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: + cairo_surface_destroy (image); + + return status; +} + +static cairo_int_status_t +_paint_page (cairo_paginated_surface_t *surface) +{ + cairo_surface_t *analysis; + cairo_int_status_t status; + cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback; + + if (unlikely (surface->target->status)) + return surface->target->status; + + analysis = _cairo_analysis_surface_create (surface->target); + if (unlikely (analysis->status)) + return _cairo_surface_set_error (surface->target, analysis->status); + + status = surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_ANALYZE); + 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; + + _cairo_analysis_surface_get_bounding_box (analysis, &bbox); + status = surface->backend->set_bounding_box (surface->target, &bbox); + if (unlikely (status)) + goto FAIL; + } + + if (surface->backend->set_fallback_images_required) { + cairo_bool_t has_fallbacks = _cairo_analysis_surface_has_unsupported (analysis); + + status = surface->backend->set_fallback_images_required (surface->target, + has_fallbacks); + if (unlikely (status)) + goto FAIL; + } + + /* Finer grained fallbacks are currently only supported for some + * surface types */ + if (surface->backend->supports_fine_grained_fallbacks != NULL && + surface->backend->supports_fine_grained_fallbacks (surface->target)) + { + has_supported = _cairo_analysis_surface_has_supported (analysis); + has_page_fallback = FALSE; + has_finegrained_fallback = _cairo_analysis_surface_has_unsupported (analysis); + } + else + { + if (_cairo_analysis_surface_has_unsupported (analysis)) { + has_supported = FALSE; + has_page_fallback = TRUE; + } else { + has_supported = TRUE; + has_page_fallback = FALSE; + } + has_finegrained_fallback = FALSE; + } + + if (has_supported) { + 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, + surface->target, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) + goto FAIL; + } + + if (has_page_fallback) { + cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; + + 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) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } + + status = _paint_fallback_image (surface, &extents); + if (unlikely (status)) + goto FAIL; + } + + if (has_finegrained_fallback) { + cairo_region_t *region; + int num_rects, i; + + status = surface->backend->set_paginated_mode (surface->target, + CAIRO_PAGINATED_MODE_FALLBACK); + if (unlikely (status)) + goto FAIL; + + region = _cairo_analysis_surface_get_unsupported (analysis); + + num_rects = cairo_region_num_rectangles (region); + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (region, i, &rect); + status = _paint_fallback_image (surface, &rect); + if (unlikely (status)) + goto FAIL; + } + } + + 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); + + return _cairo_surface_set_error (surface->target, status); +} + +static cairo_status_t +_start_page (cairo_paginated_surface_t *surface) +{ + if (surface->target->status) + return surface->target->status; + + if (! surface->backend->start_page) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_set_error (surface->target, + surface->backend->start_page (surface->target)); +} + +static cairo_int_status_t +_cairo_paginated_surface_copy_page (void *abstract_surface) +{ + cairo_status_t status; + cairo_paginated_surface_t *surface = abstract_surface; + + status = _start_page (surface); + if (unlikely (status)) + return status; + + status = _paint_page (surface); + if (unlikely (status)) + return status; + + surface->page_num++; + + /* XXX: It might make sense to add some support here for calling + * cairo_surface_copy_page on the target surface. It would be an + * optimization for the output, but the interaction with image + * fallbacks gets tricky. For now, we just let the target see a + * show_page and we implement the copying by simply not destroying + * the recording-surface. */ + + cairo_surface_show_page (surface->target); + return cairo_surface_status (surface->target); +} + +static cairo_int_status_t +_cairo_paginated_surface_show_page (void *abstract_surface) +{ + cairo_status_t status; + cairo_paginated_surface_t *surface = abstract_surface; + + status = _start_page (surface); + if (unlikely (status)) + return status; + + status = _paint_page (surface); + if (unlikely (status)) + return status; + + cairo_surface_show_page (surface->target); + status = surface->target->status; + if (unlikely (status)) + return status; + + status = surface->recording_surface->status; + if (unlikely (status)) + return status; + + if (! surface->base.finished) { + cairo_surface_destroy (surface->recording_surface); + + surface->recording_surface = _create_recording_surface_for_target (surface->target, + surface->content); + status = surface->recording_surface->status; + if (unlikely (status)) + return status; + + surface->page_num++; + surface->base.is_clear = TRUE; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_paginated_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, rectangle); +} + +static void +_cairo_paginated_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + cairo_surface_get_font_options (surface->target, options); +} + +static cairo_int_status_t +_cairo_paginated_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_paint (surface->recording_surface, op, source, clip); +} + +static cairo_int_status_t +_cairo_paginated_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_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_mask (surface->recording_surface, op, source, mask, clip); +} + +static cairo_int_status_t +_cairo_paginated_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_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_stroke (surface->recording_surface, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +_cairo_paginated_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_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_fill (surface->recording_surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); +} + +static cairo_bool_t +_cairo_paginated_surface_has_show_text_glyphs (void *abstract_surface) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return cairo_surface_has_show_text_glyphs (surface->target); +} + +static cairo_int_status_t +_cairo_paginated_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_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_show_text_glyphs (surface->recording_surface, op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + 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) +{ + cairo_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_tag (surface->recording_surface, + begin, tag_name, attributes); +} + +static cairo_surface_t * +_cairo_paginated_surface_snapshot (void *abstract_other) +{ + cairo_paginated_surface_t *other = abstract_other; + + 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_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, + _cairo_paginated_surface_snapshot, + + _cairo_paginated_surface_copy_page, + _cairo_paginated_surface_show_page, + + _cairo_paginated_surface_get_extents, + _cairo_paginated_surface_get_font_options, + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _cairo_paginated_surface_paint, + _cairo_paginated_surface_mask, + _cairo_paginated_surface_stroke, + _cairo_paginated_surface_fill, + NULL, /* fill_stroke */ + NULL, /* show_glyphs */ + _cairo_paginated_surface_has_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 new file mode 100644 index 0000000000..ac85f27bee --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-bounds.c @@ -0,0 +1,230 @@ +/* -*- 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 + * + * 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-box-inline.h" +#include "cairo-error-private.h" +#include "cairo-path-fixed-private.h" + +typedef struct _cairo_path_bounder { + cairo_point_t current_point; + cairo_bool_t has_extents; + cairo_box_t extents; +} cairo_path_bounder_t; + +static cairo_status_t +_cairo_path_bounder_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_path_bounder_t *bounder = closure; + + bounder->current_point = *point; + + 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; +} + +static cairo_status_t +_cairo_path_bounder_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_path_bounder_t *bounder = closure; + + bounder->current_point = *point; + _cairo_box_add_point (&bounder->extents, point); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_bounder_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_path_bounder_t *bounder = closure; + + _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) +{ + return CAIRO_STATUS_SUCCESS; +} + +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; + + 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); + + if (bounder.has_extents) + *extents = bounder.extents; + + 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 +_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_rectangle_int_t *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; + } +} + +/* Adjusts the fill extents (above) by the device-space pen. */ +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) +{ + if (path->has_extents) { + cairo_box_t box_extents; + double dx, dy; + + _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; + + if (dy < min) + dy = min; + } + + 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); + + _cairo_box_round_to_rectangle (&box_extents, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } +} + +cairo_status_t +_cairo_path_fixed_stroke_extents (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_rectangle_int_t *extents) +{ + cairo_polygon_t polygon; + cairo_status_t status; + cairo_stroke_style_t style; + + /* 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; + } + + _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; +} + +cairo_bool_t +_cairo_path_fixed_extents (const cairo_path_fixed_t *path, + cairo_box_t *box) +{ + *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 new file mode 100644 index 0000000000..4000c9c586 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-fill.c @@ -0,0 +1,341 @@ +/* -*- 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 + * + * 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-boxes-private.h" +#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 { + 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 cairo_status_t +_cairo_filler_line_to (void *closure, + const cairo_point_t *point) +{ + 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 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 +_cairo_filler_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_filler_t *filler = closure; + cairo_status_t status; + + /* 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_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_spline_add_point_func_t)_cairo_filler_line_to, filler, + &filler->current_point, p1, p2, p3)) + { + return _cairo_filler_line_to (closure, p3); + } + + return _cairo_spline_decompose (&spline, filler->tolerance); +} + +cairo_status_t +_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, + double tolerance, + cairo_polygon_t *polygon) +{ + cairo_filler_t filler; + cairo_status_t status; + + 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_filler_move_to, + _cairo_filler_line_to, + _cairo_filler_curve_to, + _cairo_filler_close, + &filler); + if (unlikely (status)) + return status; + + 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, + double tolerance, + cairo_traps_t *traps) +{ + cairo_polygon_t polygon; + cairo_status_t status; + + if (_cairo_path_fixed_fill_is_empty (path)) + return CAIRO_STATUS_SUCCESS; + + _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; + + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &polygon, fill_rule); + + CLEANUP: + _cairo_polygon_fini (&polygon); + return status; +} + +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, boxes->limits, boxes->num_limits); + boxes->num_limits = 0; + + /* tolerance will be ignored as the path is rectilinear */ + 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, + fill_rule, + boxes); + } + + _cairo_polygon_fini (&polygon); + + return status; +} + +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; + cairo_status_t status; + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &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)) { + if (box.p1.y == box.p2.y || box.p1.x == box.p2.x) + continue; + + 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_boxes_add (boxes, antialias, &box); + if (unlikely (status)) + return status; + } + + if (_cairo_path_fixed_iter_at_end (&iter)) + return _cairo_bentley_ottmann_tessellate_boxes (boxes, fill_rule, boxes); + + /* path is not rectangular, try extracting clipped rectilinear edges */ + _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 new file mode 100644 index 0000000000..cf7cd0836f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-fixed-private.h @@ -0,0 +1,206 @@ +/* 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_PATH_FIXED_PRIVATE_H +#define CAIRO_PATH_FIXED_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" +#include "cairo-list-private.h" + +#define WATCH_PATH 0 +#if WATCH_PATH +#include +#endif + +enum cairo_path_op { + CAIRO_PATH_OP_MOVE_TO = 0, + CAIRO_PATH_OP_LINE_TO = 1, + CAIRO_PATH_OP_CURVE_TO = 2, + CAIRO_PATH_OP_CLOSE_PATH = 3 +}; + +/* we want to make sure a single byte is used for the enum */ +typedef char cairo_path_op_t; + +/* make _cairo_path_fixed fit into ~512 bytes -- about 50 items */ +#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; + unsigned int size_ops; + unsigned int num_points; + unsigned int size_points; + + cairo_path_op_t *op; + cairo_point_t *points; +} cairo_path_buf_t; + +typedef struct _cairo_path_buf_fixed { + cairo_path_buf_t base; + + cairo_path_op_t op[CAIRO_PATH_BUF_SIZE]; + 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 needs_move_to : 1; + unsigned int has_extents : 1; + unsigned int has_curve_to : 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; + + cairo_path_buf_fixed_t buf; +}; + +cairo_private void +_cairo_path_fixed_translate (cairo_path_fixed_t *path, + cairo_fixed_t offx, + cairo_fixed_t offy); + +cairo_private cairo_status_t +_cairo_path_fixed_append (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other, + cairo_fixed_t tx, + cairo_fixed_t ty); + +cairo_private unsigned long +_cairo_path_fixed_hash (const cairo_path_fixed_t *path); + +cairo_private unsigned long +_cairo_path_fixed_size (const cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_equal (const cairo_path_fixed_t *a, + const cairo_path_fixed_t *b); + +typedef struct _cairo_path_fixed_iter { + const cairo_path_buf_t *first; + const cairo_path_buf_t *buf; + unsigned int n_op; + unsigned int n_point; +} cairo_path_fixed_iter_t; + +cairo_private void +_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, + const cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, + cairo_box_t *box); + +cairo_private cairo_bool_t +_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter); + +static inline cairo_bool_t +_cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path) +{ + return path->fill_is_empty; +} + +static inline cairo_bool_t +_cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path) +{ + if (! path->fill_is_rectilinear) + return 0; + + if (! path->has_current_point || path->needs_move_to) + return 1; + + /* check whether the implicit close preserves the rectilinear property */ + return path->current_point.x == path->last_move_point.x || + path->current_point.y == path->last_move_point.y; +} + +static inline cairo_bool_t +_cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path) +{ + 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 new file mode 100644 index 0000000000..d741823460 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-fixed.c @@ -0,0 +1,1589 @@ +/* -*- 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. + * + * 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-box-inline.h" +#include "cairo-error-private.h" +#include "cairo-list-inline.h" +#include "cairo-path-fixed-private.h" +#include "cairo-slope-private.h" + +static cairo_status_t +_cairo_path_fixed_add (cairo_path_fixed_t *path, + cairo_path_op_t op, + const cairo_point_t *points, + int num_points); + +static void +_cairo_path_fixed_add_buf (cairo_path_fixed_t *path, + cairo_path_buf_t *buf); + +static cairo_path_buf_t * +_cairo_path_buf_create (int size_ops, int size_points); + +static void +_cairo_path_buf_destroy (cairo_path_buf_t *buf); + +static void +_cairo_path_buf_add_op (cairo_path_buf_t *buf, + cairo_path_op_t op); + +static void +_cairo_path_buf_add_points (cairo_path_buf_t *buf, + const cairo_point_t *points, + int num_points); + +void +_cairo_path_fixed_init (cairo_path_fixed_t *path) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); + + cairo_list_init (&path->buf.base.link); + + path->buf.base.num_ops = 0; + path->buf.base.num_points = 0; + path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op); + path->buf.base.size_points = ARRAY_LENGTH (path->buf.points); + path->buf.base.op = path->buf.op; + path->buf.base.points = path->buf.points; + + path->current_point.x = 0; + path->current_point.y = 0; + path->last_move_point = path->current_point; + + 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 +_cairo_path_fixed_init_copy (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other) +{ + cairo_path_buf_t *buf, *other_buf; + unsigned int num_points, num_ops; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); + + cairo_list_init (&path->buf.base.link); + + path->buf.base.op = path->buf.op; + path->buf.base.points = path->buf.points; + path->buf.base.size_ops = ARRAY_LENGTH (path->buf.op); + path->buf.base.size_points = ARRAY_LENGTH (path->buf.points); + + path->current_point = other->current_point; + path->last_move_point = other->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->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; + + path->buf.base.num_ops = other->buf.base.num_ops; + path->buf.base.num_points = other->buf.base.num_points; + memcpy (path->buf.op, other->buf.base.op, + other->buf.base.num_ops * sizeof (other->buf.op[0])); + memcpy (path->buf.points, other->buf.points, + other->buf.base.num_points * sizeof (other->buf.points[0])); + + num_points = num_ops = 0; + for (other_buf = cairo_path_buf_next (cairo_path_head (other)); + other_buf != cairo_path_head (other); + other_buf = cairo_path_buf_next (other_buf)) + { + num_ops += other_buf->num_ops; + num_points += other_buf->num_points; + } + + if (num_ops) { + buf = _cairo_path_buf_create (num_ops, num_points); + if (unlikely (buf == NULL)) { + _cairo_path_fixed_fini (path); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (other_buf = cairo_path_buf_next (cairo_path_head (other)); + other_buf != cairo_path_head (other); + other_buf = cairo_path_buf_next (other_buf)) + { + memcpy (buf->op + buf->num_ops, other_buf->op, + other_buf->num_ops * sizeof (buf->op[0])); + buf->num_ops += other_buf->num_ops; + + memcpy (buf->points + buf->num_points, other_buf->points, + other_buf->num_points * sizeof (buf->points[0])); + buf->num_points += other_buf->num_points; + } + + _cairo_path_fixed_add_buf (path, buf); + } + + return CAIRO_STATUS_SUCCESS; +} + +unsigned long +_cairo_path_fixed_hash (const cairo_path_fixed_t *path) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + const cairo_path_buf_t *buf; + unsigned int count; + + 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])); + count += buf->num_points; + } cairo_path_foreach_buf_end (buf, path); + hash = _cairo_hash_bytes (hash, &count, sizeof (count)); + + return hash; +} + +unsigned long +_cairo_path_fixed_size (const cairo_path_fixed_t *path) +{ + const cairo_path_buf_t *buf; + int num_points, num_ops; + + num_ops = num_points = 0; + cairo_path_foreach_buf_start (buf, path) { + num_ops += buf->num_ops; + num_points += buf->num_points; + } cairo_path_foreach_buf_end (buf, path); + + return num_ops * sizeof (buf->op[0]) + + num_points * sizeof (buf->points[0]); +} + +cairo_bool_t +_cairo_path_fixed_equal (const cairo_path_fixed_t *a, + const cairo_path_fixed_t *b) +{ + const cairo_path_buf_t *buf_a, *buf_b; + const cairo_path_op_t *ops_a, *ops_b; + const cairo_point_t *points_a, *points_b; + int num_points_a, num_ops_a; + int num_points_b, num_ops_b; + + if (a == b) + return TRUE; + + /* use the flags to quickly differentiate based on contents */ + if (a->has_curve_to != b->has_curve_to) + { + return FALSE; + } + + if (a->extents.p1.x != b->extents.p1.x || + a->extents.p1.y != b->extents.p1.y || + a->extents.p2.x != b->extents.p2.x || + a->extents.p2.y != b->extents.p2.y) + { + return FALSE; + } + + num_ops_a = num_points_a = 0; + cairo_path_foreach_buf_start (buf_a, a) { + num_ops_a += buf_a->num_ops; + num_points_a += buf_a->num_points; + } cairo_path_foreach_buf_end (buf_a, a); + + num_ops_b = num_points_b = 0; + cairo_path_foreach_buf_start (buf_b, b) { + num_ops_b += buf_b->num_ops; + num_points_b += buf_b->num_points; + } cairo_path_foreach_buf_end (buf_b, b); + + if (num_ops_a == 0 && num_ops_b == 0) + return TRUE; + + if (num_ops_a != num_ops_b || num_points_a != num_points_b) + return FALSE; + + buf_a = cairo_path_head (a); + num_points_a = buf_a->num_points; + num_ops_a = buf_a->num_ops; + ops_a = buf_a->op; + points_a = buf_a->points; + + buf_b = cairo_path_head (b); + num_points_b = buf_b->num_points; + num_ops_b = buf_b->num_ops; + ops_b = buf_b->op; + points_b = buf_b->points; + + while (TRUE) { + int num_ops = MIN (num_ops_a, num_ops_b); + int num_points = MIN (num_points_a, num_points_b); + + if (memcmp (ops_a, ops_b, num_ops * sizeof (cairo_path_op_t))) + return FALSE; + if (memcmp (points_a, points_b, num_points * sizeof (cairo_point_t))) + return FALSE; + + num_ops_a -= num_ops; + ops_a += num_ops; + num_points_a -= num_points; + points_a += num_points; + if (num_ops_a == 0 || num_points_a == 0) { + if (num_ops_a || num_points_a) + return FALSE; + + buf_a = cairo_path_buf_next (buf_a); + if (buf_a == cairo_path_head (a)) + break; + + num_points_a = buf_a->num_points; + num_ops_a = buf_a->num_ops; + ops_a = buf_a->op; + points_a = buf_a->points; + } + + num_ops_b -= num_ops; + ops_b += num_ops; + num_points_b -= num_points; + points_b += num_points; + if (num_ops_b == 0 || num_points_b == 0) { + if (num_ops_b || num_points_b) + return FALSE; + + buf_b = cairo_path_buf_next (buf_b); + if (buf_b == cairo_path_head (b)) + break; + + num_points_b = buf_b->num_points; + num_ops_b = buf_b->num_ops; + ops_b = buf_b->op; + points_b = buf_b->points; + } + } + + return TRUE; +} + +cairo_path_fixed_t * +_cairo_path_fixed_create (void) +{ + cairo_path_fixed_t *path; + + path = _cairo_malloc (sizeof (cairo_path_fixed_t)); + if (!path) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + _cairo_path_fixed_init (path); + return path; +} + +void +_cairo_path_fixed_fini (cairo_path_fixed_t *path) +{ + cairo_path_buf_t *buf; + + buf = cairo_path_buf_next (cairo_path_head (path)); + while (buf != cairo_path_head (path)) { + cairo_path_buf_t *this = buf; + buf = cairo_path_buf_next (buf); + _cairo_path_buf_destroy (this); + } + + VG (VALGRIND_MAKE_MEM_UNDEFINED (path, sizeof (cairo_path_fixed_t))); +} + +void +_cairo_path_fixed_destroy (cairo_path_fixed_t *path) +{ + _cairo_path_fixed_fini (path); + free (path); +} + +static cairo_path_op_t +_cairo_path_fixed_last_op (cairo_path_fixed_t *path) +{ + cairo_path_buf_t *buf; + + buf = cairo_path_tail (path); + assert (buf->num_ops != 0); + + return buf->op[buf->num_ops - 1]; +} + +static inline const cairo_point_t * +_cairo_path_fixed_penultimate_point (cairo_path_fixed_t *path) +{ + cairo_path_buf_t *buf; + + 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 +_cairo_path_fixed_move_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) +{ + _cairo_path_fixed_new_sub_path (path); + + 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; +} + +cairo_status_t +_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy) +{ + if (unlikely (! path->has_current_point)) + return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); + + return _cairo_path_fixed_move_to (path, + path->current_point.x + dx, + path->current_point.y + dy); + +} + +cairo_status_t +_cairo_path_fixed_line_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) +{ + cairo_status_t status; + cairo_point_t point; + + point.x = x; + point.y = y; + + /* When there is not yet a current point, the line_to operation + * becomes a move_to instead. Note: We have to do this by + * explicitly calling into _cairo_path_fixed_move_to to ensure + * that the last_move_point state is updated properly. + */ + 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_fixed_last_op (path) != CAIRO_PATH_OP_MOVE_TO) { + if (x == path->current_point.x && y == path->current_point.y) + return CAIRO_STATUS_SUCCESS; + } + + /* 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_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); + } else { + cairo_slope_t prev, self; + + _cairo_slope_init (&prev, p, &path->current_point); + _cairo_slope_init (&self, &path->current_point, &point); + if (_cairo_slope_equal (&prev, &self) && + /* cannot trim anti-parallel segments whilst stroking */ + ! _cairo_slope_backwards (&prev, &self)) + { + _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. + */ + } + } + } + + 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; + + _cairo_box_add_point (&path->extents, &point); + + return _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1); +} + +cairo_status_t +_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy) +{ + if (unlikely (! path->has_current_point)) + return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); + + return _cairo_path_fixed_line_to (path, + path->current_point.x + dx, + path->current_point.y + dy); +} + +cairo_status_t +_cairo_path_fixed_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t x0, cairo_fixed_t y0, + cairo_fixed_t x1, cairo_fixed_t y1, + cairo_fixed_t x2, cairo_fixed_t y2) +{ + 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); + 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; + + _cairo_box_add_curve_to (&path->extents, &path->current_point, + &point[0], &point[1], &point[2]); + + path->current_point = point[2]; + path->has_curve_to = TRUE; + path->stroke_is_rectilinear = FALSE; + path->fill_is_rectilinear = FALSE; + path->fill_maybe_region = FALSE; + path->fill_is_empty = FALSE; + + return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3); +} + +cairo_status_t +_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t dx0, cairo_fixed_t dy0, + cairo_fixed_t dx1, cairo_fixed_t dy1, + cairo_fixed_t dx2, cairo_fixed_t dy2) +{ + if (unlikely (! path->has_current_point)) + return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT); + + return _cairo_path_fixed_curve_to (path, + path->current_point.x + dx0, + path->current_point.y + dy0, + + path->current_point.x + dx1, + path->current_point.y + dy1, + + path->current_point.x + dx2, + path->current_point.y + dy2); +} + +cairo_status_t +_cairo_path_fixed_close_path (cairo_path_fixed_t *path) +{ + cairo_status_t status; + + if (! path->has_current_point) + return CAIRO_STATUS_SUCCESS; + + /* + * 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; + + /* + * 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 +_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, + cairo_fixed_t *x, + cairo_fixed_t *y) +{ + if (! path->has_current_point) + return FALSE; + + *x = path->current_point.x; + *y = path->current_point.y; + + return TRUE; +} + +static cairo_status_t +_cairo_path_fixed_add (cairo_path_fixed_t *path, + cairo_path_op_t op, + const cairo_point_t *points, + int num_points) +{ + cairo_path_buf_t *buf = cairo_path_tail (path); + + if (buf->num_ops + 1 > buf->size_ops || + buf->num_points + num_points > buf->size_points) + { + buf = _cairo_path_buf_create (buf->num_ops * 2, buf->num_points * 2); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_path_fixed_add_buf (path, buf); + } + + if (WATCH_PATH) { + const char *op_str[] = { + "move-to", + "line-to", + "curve-to", + "close-path", + }; + char buf[1024]; + int len = 0; + int i; + + len += snprintf (buf + len, sizeof (buf), "["); + for (i = 0; i < num_points; i++) { + if (i != 0) + len += snprintf (buf + len, sizeof (buf), " "); + len += snprintf (buf + len, sizeof (buf), "(%f, %f)", + _cairo_fixed_to_double (points[i].x), + _cairo_fixed_to_double (points[i].y)); + } + len += snprintf (buf + len, sizeof (buf), "]"); + +#define STRINGIFYFLAG(x) (path->x ? #x " " : "") + fprintf (stderr, + "_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); + _cairo_path_buf_add_points (buf, points, num_points); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_path_fixed_add_buf (cairo_path_fixed_t *path, + cairo_path_buf_t *buf) +{ + cairo_list_add_tail (&buf->link, &cairo_path_head (path)->link); +} + +COMPILE_TIME_ASSERT (sizeof (cairo_path_op_t) == 1); +static cairo_path_buf_t * +_cairo_path_buf_create (int size_ops, int size_points) +{ + cairo_path_buf_t *buf; + + /* adjust size_ops to ensure that buf->points is naturally aligned */ + size_ops += sizeof (double) - ((sizeof (cairo_path_buf_t) + size_ops) % sizeof (double)); + buf = _cairo_malloc_ab_plus_c (size_points, sizeof (cairo_point_t), size_ops + sizeof (cairo_path_buf_t)); + if (buf) { + buf->num_ops = 0; + buf->num_points = 0; + buf->size_ops = size_ops; + buf->size_points = size_points; + + buf->op = (cairo_path_op_t *) (buf + 1); + buf->points = (cairo_point_t *) (buf->op + size_ops); + } + + return buf; +} + +static void +_cairo_path_buf_destroy (cairo_path_buf_t *buf) +{ + free (buf); +} + +static void +_cairo_path_buf_add_op (cairo_path_buf_t *buf, + cairo_path_op_t op) +{ + buf->op[buf->num_ops++] = op; +} + +static void +_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); + buf->num_points += num_points; +} + +cairo_status_t +_cairo_path_fixed_interpret (const cairo_path_fixed_t *path, + 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 cairo_path_buf_t *buf; + cairo_status_t status; + + cairo_path_foreach_buf_start (buf, path) { + const cairo_point_t *points = buf->points; + unsigned int i; + + 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; + case CAIRO_PATH_OP_CLOSE_PATH: + status = (*close_path) (closure); + break; + } + + if (unlikely (status)) + return status; + } + } 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; +} + +typedef struct _cairo_path_fixed_append_closure { + cairo_point_t offset; + cairo_path_fixed_t *path; +} cairo_path_fixed_append_closure_t; + +static cairo_status_t +_append_move_to (void *abstract_closure, + const cairo_point_t *point) +{ + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_move_to (closure->path, + point->x + closure->offset.x, + point->y + closure->offset.y); +} + +static cairo_status_t +_append_line_to (void *abstract_closure, + const cairo_point_t *point) +{ + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_line_to (closure->path, + point->x + closure->offset.x, + point->y + closure->offset.y); +} + +static cairo_status_t +_append_curve_to (void *abstract_closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_curve_to (closure->path, + p0->x + closure->offset.x, + p0->y + closure->offset.y, + p1->x + closure->offset.x, + p1->y + closure->offset.y, + p2->x + closure->offset.x, + p2->y + closure->offset.y); +} + +static cairo_status_t +_append_close_path (void *abstract_closure) +{ + cairo_path_fixed_append_closure_t *closure = abstract_closure; + + return _cairo_path_fixed_close_path (closure->path); +} + +cairo_status_t +_cairo_path_fixed_append (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other, + cairo_fixed_t tx, + cairo_fixed_t ty) +{ + cairo_path_fixed_append_closure_t closure; + + closure.path = path; + closure.offset.x = tx; + closure.offset.y = ty; + + return _cairo_path_fixed_interpret (other, + _append_move_to, + _append_line_to, + _append_curve_to, + _append_close_path, + &closure); +} + +static void +_cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, + cairo_fixed_t offx, + cairo_fixed_t offy, + cairo_fixed_t scalex, + cairo_fixed_t scaley) +{ + cairo_path_buf_t *buf; + unsigned int i; + + 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) + buf->points[i].x = _cairo_fixed_mul (buf->points[i].x, scalex); + buf->points[i].x += offx; + + 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 +_cairo_path_fixed_translate (cairo_path_fixed_t *path, + cairo_fixed_t offx, + cairo_fixed_t offy) +{ + cairo_path_buf_t *buf; + unsigned int i; + + if (offx == 0 && offy == 0) + return; + + 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; + + 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 + * @matrix: a #cairo_matrix_t + * + * Transform the fixed-point path according to the given matrix. + * There is a fast path for the case where @matrix has no rotation + * or shear. + **/ +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; + + if (matrix->yx == 0.0 && matrix->xy == 0.0) { + /* Fast path for the common case of scale+transform */ + _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; + } + + _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++) { + _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); + + if (path->has_curve_to) { + cairo_bool_t is_tight; + + _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; + } + + /* 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 */ +typedef struct cairo_path_flattener { + double tolerance; + cairo_point_t current_point; + 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; +} cpf_t; + +static cairo_status_t +_cpf_move_to (void *closure, + const cairo_point_t *point) +{ + cpf_t *cpf = closure; + + cpf->current_point = *point; + + return cpf->move_to (cpf->closure, point); +} + +static cairo_status_t +_cpf_line_to (void *closure, + const cairo_point_t *point) +{ + cpf_t *cpf = closure; + + cpf->current_point = *point; + + return cpf->line_to (cpf->closure, point); +} + +static cairo_status_t +_cpf_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + cpf_t *cpf = closure; + cairo_spline_t spline; + + cairo_point_t *p0 = &cpf->current_point; + + if (! _cairo_spline_init (&spline, + (cairo_spline_add_point_func_t)cpf->line_to, + cpf->closure, + p0, p1, p2, p3)) + { + return _cpf_line_to (closure, p3); + } + + cpf->current_point = *p3; + + return _cairo_spline_decompose (&spline, cpf->tolerance); +} + +static cairo_status_t +_cpf_close_path (void *closure) +{ + cpf_t *cpf = closure; + + return cpf->close_path (cpf->closure); +} + +cairo_status_t +_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, + 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) +{ + cpf_t flattener; + + if (! path->has_curve_to) { + return _cairo_path_fixed_interpret (path, + move_to, + line_to, + NULL, + close_path, + closure); + } + + flattener.tolerance = tolerance; + flattener.move_to = move_to; + flattener.line_to = line_to; + flattener.close_path = close_path; + flattener.closure = closure; + return _cairo_path_fixed_interpret (path, + _cpf_move_to, + _cpf_line_to, + _cpf_curve_to, + _cpf_close_path, + &flattener); +} + +static inline void +_canonical_box (cairo_box_t *box, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + if (p1->x <= p2->x) { + box->p1.x = p1->x; + box->p2.x = p2->x; + } else { + box->p1.x = p2->x; + box->p2.x = p1->x; + } + + if (p1->y <= p2->y) { + box->p1.y = p1->y; + box->p2.y = p2->y; + } else { + box->p1.y = p2->y; + box->p2.y = p1->y; + } +} + +static inline cairo_bool_t +_path_is_quad (const cairo_path_fixed_t *path) +{ + const cairo_path_buf_t *buf = cairo_path_head (path); + + /* Do we have the right number of ops? */ + if (buf->num_ops < 4 || buf->num_ops > 6) + 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) + { + return FALSE; + } + + /* we accept an implicit close for filled paths */ + if (buf->num_ops > 4) { + /* 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. */ + if (buf->op[4] == CAIRO_PATH_OP_LINE_TO) { + if (buf->points[4].x != buf->points[0].x || + buf->points[4].y != buf->points[0].y) + return FALSE; + } else if (buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH) { + return FALSE; + } + + if (buf->num_ops == 6) { + /* A trailing CLOSE_PATH or MOVE_TO is ok */ + if (buf->op[5] != CAIRO_PATH_OP_MOVE_TO && + buf->op[5] != CAIRO_PATH_OP_CLOSE_PATH) + return FALSE; + } + } + + 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 && + buf->points[2].y == buf->points[3].y && + buf->points[3].x == buf->points[0].x) + { + _canonical_box (box, &buf->points[0], &buf->points[2]); + return TRUE; + } + + if (buf->points[0].x == buf->points[1].x && + buf->points[1].y == buf->points[2].y && + buf->points[2].x == buf->points[3].x && + buf->points[3].y == buf->points[0].y) + { + _canonical_box (box, &buf->points[0], &buf->points[2]); + return TRUE; + } + + return FALSE; +} + +/* + * Check whether the given path contains a single rectangle + * that is logically equivalent to: + * + * 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); + * + */ +cairo_bool_t +_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, + cairo_box_t *box) +{ + const cairo_path_buf_t *buf; + + 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->num_ops > 4) + return TRUE; + + return FALSE; +} + +void +_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter, + const cairo_path_fixed_t *path) +{ + iter->first = iter->buf = cairo_path_head (path); + iter->n_op = 0; + iter->n_point = 0; +} + +static cairo_bool_t +_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter) +{ + if (++iter->n_op >= iter->buf->num_ops) { + iter->buf = cairo_path_buf_next (iter->buf); + if (iter->buf == iter->first) { + iter->buf = NULL; + return FALSE; + } + + iter->n_op = 0; + iter->n_point = 0; + } + + return TRUE; +} + +cairo_bool_t +_cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter, + cairo_box_t *box) +{ + cairo_point_t points[5]; + cairo_path_fixed_iter_t iter; + + if (_iter->buf == NULL) + return FALSE; + + iter = *_iter; + + if (iter.n_op == iter.buf->num_ops && ! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + /* Check whether the ops are those that would be used for a rectangle */ + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO) + return FALSE; + points[0] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + return FALSE; + points[1] = iter.buf->points[iter.n_point++]; + if (! _cairo_path_fixed_iter_next_op (&iter)) + return FALSE; + + /* 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; + + if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO) + return FALSE; + points[3] = iter.buf->points[iter.n_point++]; + + /* 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 (! _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; + _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; + } + + /* Ok, we may have a box, if the points line up */ + if (points[0].y == points[1].y && + points[1].x == points[2].x && + points[2].y == points[3].y && + points[3].x == points[0].x) + { + box->p1 = points[0]; + box->p2 = points[2]; + *_iter = iter; + return TRUE; + } + + if (points[0].x == points[1].x && + points[1].y == points[2].y && + points[2].x == points[3].x && + points[3].y == points[0].y) + { + box->p1 = points[1]; + box->p2 = points[3]; + *_iter = iter; + return TRUE; + } + + return FALSE; +} + +cairo_bool_t +_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter) +{ + if (iter->buf == NULL) + return TRUE; + + 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 new file mode 100644 index 0000000000..1787fb1a3b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-in-fill.c @@ -0,0 +1,290 @@ +/* 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 + */ + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" + +typedef struct cairo_in_fill { + double tolerance; + cairo_bool_t on_edge; + int winding; + + cairo_fixed_t x, y; + + cairo_bool_t has_current_point; + cairo_point_t current_point; + cairo_point_t first_point; +} cairo_in_fill_t; + +static void +_cairo_in_fill_init (cairo_in_fill_t *in_fill, + double tolerance, + double x, + double y) +{ + in_fill->on_edge = FALSE; + in_fill->winding = 0; + in_fill->tolerance = tolerance; + + in_fill->x = _cairo_fixed_from_double (x); + in_fill->y = _cairo_fixed_from_double (y); + + in_fill->has_current_point = FALSE; + in_fill->current_point.x = 0; + in_fill->current_point.y = 0; +} + +static void +_cairo_in_fill_fini (cairo_in_fill_t *in_fill) +{ +} + +static int +edge_compare_for_y_against_x (const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_fixed_t y, + cairo_fixed_t x) +{ + cairo_fixed_t adx, ady; + cairo_fixed_t dx, dy; + cairo_int64_t L, R; + + adx = p2->x - p1->x; + dx = x - p1->x; + + if (adx == 0) + return -dx; + if ((adx ^ dx) < 0) + return adx; + + dy = y - p1->y; + ady = p2->y - p1->y; + + L = _cairo_int32x32_64_mul (dy, adx); + R = _cairo_int32x32_64_mul (dx, ady); + + return _cairo_int64_cmp (L, R); +} + +static void +_cairo_in_fill_add_edge (cairo_in_fill_t *in_fill, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + int dir; + + if (in_fill->on_edge) + return; + + /* count the number of edge crossing to -∞ */ + + dir = 1; + if (p2->y < p1->y) { + const cairo_point_t *tmp; + + tmp = p1; + p1 = p2; + p2 = tmp; + + dir = -1; + } + + /* First check whether the query is on an edge */ + if ((p1->x == in_fill->x && p1->y == in_fill->y) || + (p2->x == in_fill->x && p2->y == in_fill->y) || + (! (p2->y < in_fill->y || p1->y > in_fill->y || + (p1->x > in_fill->x && p2->x > in_fill->x) || + (p1->x < in_fill->x && p2->x < in_fill->x)) && + edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) == 0)) + { + in_fill->on_edge = TRUE; + return; + } + + /* edge is entirely above or below, note the shortening rule */ + if (p2->y <= in_fill->y || p1->y > in_fill->y) + return; + + /* edge lies wholly to the right */ + if (p1->x >= in_fill->x && p2->x >= in_fill->x) + return; + + if ((p1->x <= in_fill->x && p2->x <= in_fill->x) || + edge_compare_for_y_against_x (p1, p2, in_fill->y, in_fill->x) < 0) + { + in_fill->winding += dir; + } +} + +static cairo_status_t +_cairo_in_fill_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_in_fill_t *in_fill = closure; + + /* implicit close path */ + if (in_fill->has_current_point) { + _cairo_in_fill_add_edge (in_fill, + &in_fill->current_point, + &in_fill->first_point); + } + + in_fill->first_point = *point; + in_fill->current_point = *point; + in_fill->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_in_fill_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_in_fill_t *in_fill = closure; + + if (in_fill->has_current_point) + _cairo_in_fill_add_edge (in_fill, &in_fill->current_point, point); + + in_fill->current_point = *point; + in_fill->has_current_point = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_in_fill_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_in_fill_t *in_fill = closure; + cairo_spline_t spline; + cairo_fixed_t top, bot, left; + + /* first reject based on bbox */ + bot = top = in_fill->current_point.y; + if (b->y < top) top = b->y; + if (b->y > bot) bot = b->y; + if (c->y < top) top = c->y; + if (c->y > bot) bot = c->y; + if (d->y < top) top = d->y; + if (d->y > bot) bot = d->y; + if (bot < in_fill->y || top > in_fill->y) { + in_fill->current_point = *d; + return CAIRO_STATUS_SUCCESS; + } + + left = in_fill->current_point.x; + if (b->x < left) left = b->x; + if (c->x < left) left = c->x; + if (d->x < left) left = d->x; + if (left > in_fill->x) { + in_fill->current_point = *d; + return CAIRO_STATUS_SUCCESS; + } + + /* XXX Investigate direct inspection of the inflections? */ + if (! _cairo_spline_init (&spline, + (cairo_spline_add_point_func_t)_cairo_in_fill_line_to, + in_fill, + &in_fill->current_point, b, c, d)) + { + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_spline_decompose (&spline, in_fill->tolerance); +} + +static cairo_status_t +_cairo_in_fill_close_path (void *closure) +{ + cairo_in_fill_t *in_fill = closure; + + if (in_fill->has_current_point) { + _cairo_in_fill_add_edge (in_fill, + &in_fill->current_point, + &in_fill->first_point); + + in_fill->has_current_point = FALSE; + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + double x, + double y) +{ + cairo_in_fill_t in_fill; + cairo_status_t status; + cairo_bool_t is_inside; + + 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_in_fill_move_to, + _cairo_in_fill_line_to, + _cairo_in_fill_curve_to, + _cairo_in_fill_close_path, + &in_fill); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_in_fill_close_path (&in_fill); + + if (in_fill.on_edge) { + is_inside = TRUE; + } else switch (fill_rule) { + case CAIRO_FILL_RULE_EVEN_ODD: + is_inside = in_fill.winding & 1; + break; + case CAIRO_FILL_RULE_WINDING: + is_inside = in_fill.winding != 0; + break; + default: + ASSERT_NOT_REACHED; + is_inside = FALSE; + break; + } + + _cairo_in_fill_fini (&in_fill); + + return is_inside; +} diff --git a/gfx/cairo/cairo/src/cairo-path-private.h b/gfx/cairo/cairo/src/cairo-path-private.h new file mode 100644 index 0000000000..7b54317e20 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-private.h @@ -0,0 +1,57 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * 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): + * Carl D. Worth + */ + +#ifndef CAIRO_PATH_PRIVATE_H +#define CAIRO_PATH_PRIVATE_H + +#include "cairoint.h" + +cairo_private cairo_path_t * +_cairo_path_create (cairo_path_fixed_t *path, + cairo_t *cr); + +cairo_private cairo_path_t * +_cairo_path_create_flat (cairo_path_fixed_t *path, + cairo_t *cr); + +cairo_private cairo_path_t * +_cairo_path_create_in_error (cairo_status_t status); + +cairo_private cairo_status_t +_cairo_path_append_to_context (const cairo_path_t *path, + cairo_t *cr); + +#endif /* CAIRO_PATH_DATA_PRIVATE_H */ 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 0000000000..fba170c638 --- /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 0000000000..3f7c498028 --- /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 0000000000..1363ffa86e --- /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 0000000000..3178765608 --- /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 new file mode 100644 index 0000000000..64cec8f272 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path-stroke.c @@ -0,0 +1,1485 @@ +/* -*- 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" +#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; + + void *closure; + cairo_status_t (*add_external_edge) (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2); + cairo_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]); + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints); + cairo_status_t (*add_convex_quad) (void *closure, + const cairo_point_t quad[4]); + + cairo_pen_t pen; + + cairo_point_t current_point; + 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 bounds; +} cairo_stroker_t; + +static void +_cairo_stroker_limit (cairo_stroker_t *stroker, + const cairo_path_fixed_t *path, + const cairo_box_t *boxes, + int num_boxes) +{ + double dx, dy; + cairo_fixed_t fdx, fdy; + + stroker->has_bounds = TRUE; + _cairo_boxes_get_extents (boxes, num_boxes, &stroker->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. + */ + + _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); + + stroker->bounds.p1.x -= fdx; + stroker->bounds.p2.x += fdx; + + stroker->bounds.p1.y -= fdy; + 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) +{ + _cairo_pen_fini (&stroker->pen); +} + +static void +_translate_point (cairo_point_t *point, const cairo_point_t *offset) +{ + point->x += offset->x; + point->y += offset->y; +} + +static int +_cairo_stroker_join_is_clockwise (const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + cairo_slope_t in_slope, out_slope; + + _cairo_slope_init (&in_slope, &in->point, &in->cw); + _cairo_slope_init (&out_slope, &out->point, &out->cw); + + return _cairo_slope_compare (&in_slope, &out_slope) < 0; +} + +/** + * _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) +{ + 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 cairo_status_t +_tessellate_fan (cairo_stroker_t *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) +{ + cairo_point_t stack_points[64], *points = stack_points; + cairo_pen_t *pen = &stroker->pen; + int start, stop, num_points = 0; + cairo_status_t status; + + if (stroker->has_bounds && + ! _cairo_box_contains_point (&stroker->bounds, midpt)) + goto BEVEL; + + 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); + + status = stroker->add_external_edge (stroker->closure, + &last, &p); + if (unlikely (status)) + return status; + last = p; + + if (start-- == 0) + start += pen->num_vertices; + } + 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, num_points); + } + + if (points != stack_points) + free (points); + + return status; + +BEVEL: + /* Ensure a leak free connection... */ + if (stroker->add_external_edge != NULL) { + if (clockwise) + return stroker->add_external_edge (stroker->closure, inpt, outpt); + else + return stroker->add_external_edge (stroker->closure, outpt, inpt); + } else { + stack_points[0] = *midpt; + stack_points[1] = *inpt; + stack_points[2] = *outpt; + return stroker->add_triangle (stroker->closure, stack_points); + } +} + +static cairo_status_t +_cairo_stroker_join (cairo_stroker_t *stroker, + const cairo_stroke_face_t *in, + const cairo_stroke_face_t *out) +{ + int clockwise = _cairo_stroker_join_is_clockwise (out, in); + const cairo_point_t *inpt, *outpt; + cairo_point_t points[4]; + cairo_status_t status; + + 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 CAIRO_STATUS_SUCCESS; + } + + if (clockwise) { + if (stroker->add_external_edge != NULL) { + status = stroker->add_external_edge (stroker->closure, + &out->cw, &in->point); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &in->point, &in->cw); + if (unlikely (status)) + return status; + } + + inpt = &in->ccw; + outpt = &out->ccw; + } else { + if (stroker->add_external_edge != NULL) { + status = stroker->add_external_edge (stroker->closure, + &in->ccw, &in->point); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &in->point, &out->ccw); + if (unlikely (status)) + return status; + } + + inpt = &in->cw; + outpt = &out->cw; + } + + switch (stroker->style.line_join) { + case CAIRO_LINE_JOIN_ROUND: + /* construct a fan around the common midpoint */ + return _tessellate_fan (stroker, + &in->dev_vector, + &out->dev_vector, + &in->point, inpt, outpt, + clockwise); + + 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 (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) != + _cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy)) + { + if (stroker->add_external_edge != NULL) { + points[0].x = _cairo_fixed_from_double (mx); + points[0].y = _cairo_fixed_from_double (my); + + if (clockwise) { + status = stroker->add_external_edge (stroker->closure, + inpt, &points[0]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &points[0], outpt); + if (unlikely (status)) + return status; + } else { + status = stroker->add_external_edge (stroker->closure, + outpt, &points[0]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &points[0], inpt); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; + } else { + points[0] = in->point; + points[1] = *inpt; + points[2].x = _cairo_fixed_from_double (mx); + points[2].y = _cairo_fixed_from_double (my); + points[3] = *outpt; + + return stroker->add_convex_quad (stroker->closure, points); + } + } + } + } + + /* fall through ... */ + + case CAIRO_LINE_JOIN_BEVEL: + if (stroker->add_external_edge != NULL) { + if (clockwise) { + return stroker->add_external_edge (stroker->closure, + inpt, outpt); + } else { + return stroker->add_external_edge (stroker->closure, + outpt, inpt); + } + } else { + points[0] = in->point; + points[1] = *inpt; + points[2] = *outpt; + + return stroker->add_triangle (stroker->closure, points); + } + } +} + +static cairo_status_t +_cairo_stroker_add_cap (cairo_stroker_t *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; + + return _tessellate_fan (stroker, + &f->dev_vector, + &slope, + &f->point, &f->cw, &f->ccw, + FALSE); + + } + + 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->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; + + if (stroker->add_external_edge != NULL) { + cairo_status_t status; + + status = stroker->add_external_edge (stroker->closure, + &quad[0], &quad[1]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &quad[1], &quad[2]); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &quad[2], &quad[3]); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } else { + return stroker->add_convex_quad (stroker->closure, quad); + } + } + + case CAIRO_LINE_CAP_BUTT: + default: + if (stroker->add_external_edge != NULL) { + return stroker->add_external_edge (stroker->closure, + &f->ccw, &f->cw); + } else { + return CAIRO_STATUS_SUCCESS; + } + } +} + +static cairo_status_t +_cairo_stroker_add_leading_cap (cairo_stroker_t *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; + + return _cairo_stroker_add_cap (stroker, &reversed); +} + +static cairo_status_t +_cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker, + const cairo_stroke_face_t *face) +{ + return _cairo_stroker_add_cap (stroker, face); +} + +static inline cairo_bool_t +_compute_normalized_device_slope (double *dx, double *dy, + const cairo_matrix_t *ctm_inverse, + double *mag_out) +{ + double dx0 = *dx, dy0 = *dy; + double mag; + + cairo_matrix_transform_distance (ctm_inverse, &dx0, &dy0); + + if (dx0 == 0.0 && dy0 == 0.0) { + if (mag_out) + *mag_out = 0.0; + return FALSE; + } + + 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; + } + + if (mag_out) + *mag_out = mag; + + return TRUE; +} + +static void +_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; + + /* + * 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_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); + + 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 cairo_status_t +_cairo_stroker_add_caps (cairo_stroker_t *stroker) +{ + cairo_status_t status; + + /* 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 */ + double dx = 1.0, dy = 0.0; + cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; + cairo_stroke_face_t face; + + _compute_normalized_device_slope (&dx, &dy, + stroker->ctm_inverse, NULL); + + /* arbitrarily choose first_point + * first_point and current_point should be the same */ + _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face); + + status = _cairo_stroker_add_leading_cap (stroker, &face); + if (unlikely (status)) + return status; + + status = _cairo_stroker_add_trailing_cap (stroker, &face); + if (unlikely (status)) + return status; + } + + if (stroker->has_first_face) { + status = _cairo_stroker_add_leading_cap (stroker, + &stroker->first_face); + if (unlikely (status)) + return status; + } + + if (stroker->has_current_face) { + status = _cairo_stroker_add_trailing_cap (stroker, + &stroker->current_face); + if (unlikely (status)) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, + const cairo_point_t *p1, + const cairo_point_t *p2, + cairo_slope_t *dev_slope, + double slope_dx, double slope_dy, + cairo_stroke_face_t *start, + cairo_stroke_face_t *end) +{ + _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start); + *end = *start; + + if (p1->x == p2->x && p1->y == p2->y) + return CAIRO_STATUS_SUCCESS; + + end->point = *p2; + end->ccw.x += p2->x - p1->x; + end->ccw.y += p2->y - p1->y; + end->cw.x += p2->x - p1->x; + end->cw.y += p2->y - p1->y; + + if (stroker->add_external_edge != NULL) { + cairo_status_t status; + + status = stroker->add_external_edge (stroker->closure, + &end->cw, &start->cw); + if (unlikely (status)) + return status; + + status = stroker->add_external_edge (stroker->closure, + &start->ccw, &end->ccw); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } else { + cairo_point_t quad[4]; + + quad[0] = start->cw; + quad[1] = end->cw; + quad[2] = end->ccw; + quad[3] = start->ccw; + + return stroker->add_convex_quad (stroker->closure, quad); + } +} + +static cairo_status_t +_cairo_stroker_move_to (void *closure, + const cairo_point_t *point) +{ + cairo_stroker_t *stroker = closure; + cairo_status_t status; + + /* reset the dash pattern for new sub paths */ + _cairo_stroker_dash_start (&stroker->dash); + + /* Cap the start and end of the previous sub path as needed */ + status = _cairo_stroker_add_caps (stroker); + if (unlikely (status)) + return status; + + stroker->first_point = *point; + stroker->current_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 +_cairo_stroker_line_to (void *closure, + const cairo_point_t *point) +{ + cairo_stroker_t *stroker = closure; + cairo_stroke_face_t start, end; + cairo_point_t *p1 = &stroker->current_point; + cairo_slope_t dev_slope; + double slope_dx, slope_dy; + cairo_status_t status; + + stroker->has_initial_sub_path = TRUE; + + if (p1->x == point->x && p1->y == point->y) + return CAIRO_STATUS_SUCCESS; + + _cairo_slope_init (&dev_slope, p1, point); + slope_dx = _cairo_fixed_to_double (point->x - p1->x); + slope_dy = _cairo_fixed_to_double (point->y - p1->y); + _compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL); + + status = _cairo_stroker_add_sub_edge (stroker, + p1, point, + &dev_slope, + slope_dx, slope_dy, + &start, &end); + if (unlikely (status)) + return status; + + if (stroker->has_current_face) { + /* Join with final face from previous segment */ + status = _cairo_stroker_join (stroker, + &stroker->current_face, + &start); + if (unlikely (status)) + return status; + } 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; + + stroker->current_point = *point; + + 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 + */ +static cairo_status_t +_cairo_stroker_line_to_dashed (void *closure, + const cairo_point_t *p2) +{ + cairo_stroker_t *stroker = closure; + double mag, remain, step_length = 0; + double slope_dx, slope_dy; + double dx2, dy2; + cairo_stroke_face_t sub_start, sub_end; + cairo_point_t *p1 = &stroker->current_point; + cairo_slope_t dev_slope; + cairo_line_t segment; + cairo_bool_t fully_in_bounds; + cairo_status_t status; + + stroker->has_initial_sub_path = stroker->dash.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->bounds, p1) || + ! _cairo_box_contains_point (&stroker->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 (! _compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, &mag)) + { + 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->bounds, &segment))) + { + status = _cairo_stroker_add_sub_edge (stroker, + &segment.p1, &segment.p2, + &dev_slope, + slope_dx, slope_dy, + &sub_start, &sub_end); + if (unlikely (status)) + return status; + + if (stroker->has_current_face) + { + /* Join with final face from previous segment */ + status = _cairo_stroker_join (stroker, + &stroker->current_face, + &sub_start); + if (unlikely (status)) + return status; + + 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 */ + status = _cairo_stroker_add_leading_cap (stroker, &sub_start); + if (unlikely (status)) + return status; + } + + if (remain) { + /* Cap dash end if not at end of segment */ + status = _cairo_stroker_add_trailing_cap (stroker, &sub_end); + if (unlikely (status)) + return status; + } else { + stroker->current_face = sub_end; + stroker->has_current_face = TRUE; + } + } else { + if (stroker->has_current_face) { + /* Cap final face from previous segment */ + status = _cairo_stroker_add_trailing_cap (stroker, + &stroker->current_face); + if (unlikely (status)) + return status; + + 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 (p2, &dev_slope, + slope_dx, slope_dy, + stroker, + &stroker->current_face); + + status = _cairo_stroker_add_leading_cap (stroker, + &stroker->current_face); + if (unlikely (status)) + return status; + + stroker->has_current_face = TRUE; + } + + stroker->current_point = *p2; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + cairo_stroker_t *stroker = closure; + cairo_spline_t spline; + cairo_line_join_t line_join_save; + cairo_stroke_face_t face; + double slope_dx, slope_dy; + 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_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, + spline_to, + stroker, + &stroker->current_point, b, c, 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 + single point, then we have nothing to do. */ + if (stroker->pen.num_vertices <= 1) + return CAIRO_STATUS_SUCCESS; + + /* Compute the initial face */ + if (! stroker->dash.dashed || stroker->dash.dash_on) { + slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); + slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); + if (_compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL)) + { + _compute_face (&stroker->current_point, + &spline.initial_slope, + slope_dx, slope_dy, + stroker, &face); + } + if (stroker->has_current_face) { + status = _cairo_stroker_join (stroker, + &stroker->current_face, &face); + if (unlikely (status)) + return status; + } else if (! stroker->has_first_face) { + stroker->first_face = face; + stroker->has_first_face = TRUE; + } + + stroker->current_face = face; + stroker->has_current_face = TRUE; + } + + /* Temporarily modify the stroker to use round joins to guarantee + * smooth stroked curves. */ + line_join_save = stroker->style.line_join; + stroker->style.line_join = CAIRO_LINE_JOIN_ROUND; + + status = _cairo_spline_decompose (&spline, stroker->tolerance); + if (unlikely (status)) + return status; + + /* And join the final face */ + if (! stroker->dash.dashed || stroker->dash.dash_on) { + slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); + slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); + if (_compute_normalized_device_slope (&slope_dx, &slope_dy, + stroker->ctm_inverse, NULL)) + { + _compute_face (&stroker->current_point, + &spline.final_slope, + slope_dx, slope_dy, + stroker, &face); + } + + status = _cairo_stroker_join (stroker, &stroker->current_face, &face); + if (unlikely (status)) + return status; + + stroker->current_face = face; + } + + stroker->style.line_join = line_join_save; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_stroker_close_path (void *closure) +{ + cairo_stroker_t *stroker = closure; + cairo_status_t status; + + if (stroker->dash.dashed) + status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); + else + status = _cairo_stroker_line_to (stroker, &stroker->first_point); + if (unlikely (status)) + return status; + + if (stroker->has_first_face && stroker->has_current_face) { + /* Join first and final faces of sub path */ + status = _cairo_stroker_join (stroker, + &stroker->current_face, + &stroker->first_face); + if (unlikely (status)) + return status; + } else { + /* Cap the start and end of the sub path as needed */ + status = _cairo_stroker_add_caps (stroker); + if (unlikely (status)) + return status; + } + + 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_shaper (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_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]), + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints), + cairo_status_t (*add_convex_quad) (void *closure, + const cairo_point_t quad[4]), + void *closure) +{ + cairo_stroker_t stroker; + cairo_status_t status; + + status = _cairo_stroker_init (&stroker, path, stroke_style, + ctm, ctm_inverse, tolerance, + NULL, 0); + if (unlikely (status)) + return status; + + stroker.add_triangle = add_triangle; + stroker.add_triangle_fan = add_triangle_fan; + stroker.add_convex_quad = add_convex_quad; + stroker.closure = closure; + + status = _cairo_path_fixed_interpret (path, + _cairo_stroker_move_to, + stroker.dash.dashed ? + _cairo_stroker_line_to_dashed : + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + + if (unlikely (status)) + goto BAIL; + + /* Cap the start and end of the final sub path as needed */ + status = _cairo_stroker_add_caps (&stroker); + +BAIL: + _cairo_stroker_fini (&stroker); + + return status; +} + +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_stroker_t stroker; + cairo_status_t status; + + 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; + + status = _cairo_path_fixed_interpret (path, + _cairo_stroker_move_to, + stroker.dash.dashed ? + _cairo_stroker_line_to_dashed : + _cairo_stroker_line_to, + _cairo_stroker_curve_to, + _cairo_stroker_close_path, + &stroker); + + if (unlikely (status)) + goto BAIL; + + /* Cap the start and end of the final sub path as needed */ + status = _cairo_stroker_add_caps (&stroker); + +BAIL: + _cairo_stroker_fini (&stroker); + + return status; +} + +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_int_status_t status; + cairo_polygon_t polygon; + + _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, + ctm_inverse, + tolerance, + &polygon); + if (unlikely (status)) + goto BAIL; + + status = _cairo_polygon_status (&polygon); + if (unlikely (status)) + goto BAIL; + + status = _cairo_bentley_ottmann_tessellate_polygon (traps, &polygon, + CAIRO_FILL_RULE_WINDING); + +BAIL: + _cairo_polygon_fini (&polygon); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-path.c b/gfx/cairo/cairo/src/cairo-path.c new file mode 100644 index 0000000000..566e86f5a8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-path.c @@ -0,0 +1,479 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc. + * 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): + * Carl D. Worth + */ + +#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" + +/** + * SECTION:cairo-paths + * @Title: Paths + * @Short_Description: Creating paths and manipulating path data + * + * 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; +} cpc_t; + +static cairo_status_t +_cpc_move_to (void *closure, + const cairo_point_t *point) +{ + cpc_t *cpc = closure; + + cpc->count += 2; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpc_line_to (void *closure, + const cairo_point_t *point) +{ + cpc_t *cpc = closure; + + cpc->count += 2; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpc_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + cpc_t *cpc = closure; + + cpc->count += 4; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpc_close_path (void *closure) +{ + cpc_t *cpc = closure; + + cpc->count += 1; + + return CAIRO_STATUS_SUCCESS; +} + +static int +_cairo_path_count (cairo_path_t *path, + cairo_path_fixed_t *path_fixed, + double tolerance, + cairo_bool_t flatten) +{ + cairo_status_t status; + cpc_t cpc; + + cpc.count = 0; + + if (flatten) { + status = _cairo_path_fixed_interpret_flat (path_fixed, + _cpc_move_to, + _cpc_line_to, + _cpc_close_path, + &cpc, + tolerance); + } else { + status = _cairo_path_fixed_interpret (path_fixed, + _cpc_move_to, + _cpc_line_to, + _cpc_curve_to, + _cpc_close_path, + &cpc); + } + + if (unlikely (status)) + return -1; + + return cpc.count; +} + +/* Closure for path interpretation. */ +typedef struct cairo_path_populate { + cairo_path_data_t *data; + cairo_t *cr; +} cpp_t; + +static cairo_status_t +_cpp_move_to (void *closure, + const cairo_point_t *point) +{ + cpp_t *cpp = closure; + cairo_path_data_t *data = cpp->data; + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + _cairo_backend_to_user (cpp->cr, &x, &y); + + data->header.type = CAIRO_PATH_MOVE_TO; + data->header.length = 2; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x; + data[1].point.y = y; + + cpp->data += data->header.length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpp_line_to (void *closure, + const cairo_point_t *point) +{ + cpp_t *cpp = closure; + cairo_path_data_t *data = cpp->data; + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + + _cairo_backend_to_user (cpp->cr, &x, &y); + + data->header.type = CAIRO_PATH_LINE_TO; + data->header.length = 2; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x; + data[1].point.y = y; + + cpp->data += data->header.length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpp_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + cpp_t *cpp = closure; + cairo_path_data_t *data = cpp->data; + double x1, y1; + double x2, y2; + double x3, y3; + + x1 = _cairo_fixed_to_double (p1->x); + y1 = _cairo_fixed_to_double (p1->y); + _cairo_backend_to_user (cpp->cr, &x1, &y1); + + x2 = _cairo_fixed_to_double (p2->x); + y2 = _cairo_fixed_to_double (p2->y); + _cairo_backend_to_user (cpp->cr, &x2, &y2); + + x3 = _cairo_fixed_to_double (p3->x); + y3 = _cairo_fixed_to_double (p3->y); + _cairo_backend_to_user (cpp->cr, &x3, &y3); + + data->header.type = CAIRO_PATH_CURVE_TO; + data->header.length = 4; + + /* We index from 1 to leave room for data->header */ + data[1].point.x = x1; + data[1].point.y = y1; + + data[2].point.x = x2; + data[2].point.y = y2; + + data[3].point.x = x3; + data[3].point.y = y3; + + cpp->data += data->header.length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cpp_close_path (void *closure) +{ + cpp_t *cpp = closure; + cairo_path_data_t *data = cpp->data; + + data->header.type = CAIRO_PATH_CLOSE_PATH; + data->header.length = 1; + + cpp->data += data->header.length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_populate (cairo_path_t *path, + cairo_path_fixed_t *path_fixed, + cairo_t *cr, + cairo_bool_t flatten) +{ + cairo_status_t status; + cpp_t cpp; + + cpp.data = path->data; + cpp.cr = cr; + + if (flatten) { + status = _cairo_path_fixed_interpret_flat (path_fixed, + _cpp_move_to, + _cpp_line_to, + _cpp_close_path, + &cpp, + cairo_get_tolerance (cr)); + } else { + status = _cairo_path_fixed_interpret (path_fixed, + _cpp_move_to, + _cpp_line_to, + _cpp_curve_to, + _cpp_close_path, + &cpp); + } + + if (unlikely (status)) + return status; + + /* Sanity check the count */ + assert (cpp.data - path->data == path->num_data); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_path_t * +_cairo_path_create_in_error (cairo_status_t status) +{ + cairo_path_t *path; + + /* special case NO_MEMORY so as to avoid allocations */ + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_path_t*) &_cairo_path_nil; + + 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 = 0; + path->data = NULL; + path->status = status; + + return path; +} + +static cairo_path_t * +_cairo_path_create_internal (cairo_path_fixed_t *path_fixed, + cairo_t *cr, + cairo_bool_t flatten) +{ + cairo_path_t *path; + + 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_get_tolerance (cr), + flatten); + if (path->num_data < 0) { + free (path); + return (cairo_path_t*) &_cairo_path_nil; + } + + if (path->num_data) { + path->data = _cairo_malloc_ab (path->num_data, + sizeof (cairo_path_data_t)); + if (unlikely (path->data == NULL)) { + free (path); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_path_t*) &_cairo_path_nil; + } + + path->status = _cairo_path_populate (path, path_fixed, cr, flatten); + } else { + path->data = NULL; + path->status = CAIRO_STATUS_SUCCESS; + } + + return path; +} + +/** + * cairo_path_destroy: + * @path: a path previously returned by either cairo_copy_path() or + * cairo_copy_path_flat(). + * + * Immediately releases all memory associated with @path. After a call + * to cairo_path_destroy() the @path pointer is no longer valid and + * should not be used further. + * + * Note: cairo_path_destroy() should only be called with a + * 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) +{ + if (path == NULL || path == &_cairo_path_nil) + return; + + 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 + * @cr: the current graphics context + * + * Creates a user-space #cairo_path_t copy of the given device-space + * @path. The @cr parameter provides the inverse CTM for the + * conversion. + * + * Return value: the new copy of the path. If there is insufficient + * memory a pointer to a special static nil #cairo_path_t will be + * returned instead with status==%CAIRO_STATUS_NO_MEMORY and + * data==%NULL. + **/ +cairo_path_t * +_cairo_path_create (cairo_path_fixed_t *path, + cairo_t *cr) +{ + 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 + * @cr: the current graphics context + * + * Creates a flattened, user-space #cairo_path_t copy of the given + * 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. + * + * Return value: the flattened copy of the path. If there is insufficient + * memory a pointer to a special static nil #cairo_path_t will be + * returned instead with status==%CAIRO_STATUS_NO_MEMORY and + * data==%NULL. + **/ +cairo_path_t * +_cairo_path_create_flat (cairo_path_fixed_t *path, + cairo_t *cr) +{ + return _cairo_path_create_internal (path, cr, TRUE); +} + +/** + * _cairo_path_append_to_context: + * @path: the path data to be appended + * @cr: a cairo context + * + * Append @path to the current path within @cr. + * + * Return value: %CAIRO_STATUS_INVALID_PATH_DATA if the data in @path + * is invalid, and %CAIRO_STATUS_SUCCESS otherwise. + **/ +cairo_status_t +_cairo_path_append_to_context (const cairo_path_t *path, + cairo_t *cr) +{ + const cairo_path_data_t *p, *end; + + end = &path->data[path->num_data]; + for (p = &path->data[0]; p < end; p += p->header.length) { + switch (p->header.type) { + case CAIRO_PATH_MOVE_TO: + if (unlikely (p->header.length < 2)) + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + + cairo_move_to (cr, p[1].point.x, p[1].point.y); + break; + + case CAIRO_PATH_LINE_TO: + if (unlikely (p->header.length < 2)) + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + + cairo_line_to (cr, p[1].point.x, p[1].point.y); + break; + + case CAIRO_PATH_CURVE_TO: + if (unlikely (p->header.length < 4)) + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + + cairo_curve_to (cr, + p[1].point.x, p[1].point.y, + p[2].point.x, p[2].point.y, + p[3].point.x, p[3].point.y); + break; + + case CAIRO_PATH_CLOSE_PATH: + if (unlikely (p->header.length < 1)) + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + + cairo_close_path (cr); + break; + + default: + return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA); + } + + 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 0000000000..97e8ea034e --- /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 0000000000..26d584e686 --- /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 new file mode 100644 index 0000000000..32811af595 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pattern.c @@ -0,0 +1,4791 @@ +/* -*- 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 David Reveman + * Copyright © 2005 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of 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. + * + * Authors: David Reveman + * Keith Packard + * Carl Worth + */ + +#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 + * @Title: cairo_pattern_t + * @Short_Description: Sources for drawing + * @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 + * brush too. + * + * A cairo pattern is created by using one of the many constructors, + * of the form + * cairo_pattern_create_type() + * or implicitly through + * cairo_set_source_type() + * functions. + **/ + +static freed_pool_t freed_pattern_pool[5]; + +static const cairo_solid_pattern_t _cairo_pattern_nil = { + { + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_NO_MEMORY, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_FILTER_DEFAULT, /* filter */ + 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_REFERENCE_COUNT_INVALID, /* ref_count */ + CAIRO_STATUS_NULL_POINTER, /* status */ + { 0, 0, 0, NULL }, /* user_data */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ + CAIRO_FILTER_DEFAULT, /* filter */ + 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_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 */ + 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_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 */ + 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_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 */ + 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 + * @status: a status value indicating an error + * + * Atomically sets pattern->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS. + * + * All assignments of an error status to pattern->status should happen + * through _cairo_pattern_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the nil + * objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + **/ +static cairo_status_t +_cairo_pattern_set_error (cairo_pattern_t *pattern, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&pattern->status, status); + + return _cairo_error (status); +} + +void +_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) +{ +#if HAVE_VALGRIND + switch (type) { + case CAIRO_PATTERN_TYPE_SOLID: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_MESH: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + break; + } +#endif + + pattern->type = type; + pattern->status = CAIRO_STATUS_SUCCESS; + + /* Set the reference count to zero for on-stack patterns. + * Callers needs to explicitly increment the count for heap allocations. */ + CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); + + _cairo_user_data_array_init (&pattern->user_data); + + 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 +_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, + const cairo_gradient_pattern_t *other) +{ + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR) + { + cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; + cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other; + + *dst = *src; + } + else + { + cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern; + cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other; + + *dst = *src; + } + + if (other->stops == other->stops_embedded) + pattern->stops = pattern->stops_embedded; + else if (other->stops) + { + pattern->stops = _cairo_malloc_ab (other->stops_size, + sizeof (cairo_gradient_stop_t)); + if (unlikely (pattern->stops == NULL)) { + pattern->stops_size = 0; + pattern->n_stops = 0; + return _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY); + } + + memcpy (pattern->stops, other->stops, + other->n_stops * sizeof (cairo_gradient_stop_t)); + } + + 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); + + switch (other->type) { + case CAIRO_PATTERN_TYPE_SOLID: { + cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern; + cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t))); + + *dst = *src; + } break; + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern; + cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t))); + + *dst = *src; + cairo_surface_reference (dst->surface); + } break; + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { + cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern; + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other; + + if (other->type == CAIRO_PATTERN_TYPE_LINEAR) { + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t))); + } else { + VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t))); + } + + status = _cairo_gradient_pattern_init_copy (dst, src); + if (unlikely (status)) + 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; +} + +void +_cairo_pattern_init_static_copy (cairo_pattern_t *pattern, + const cairo_pattern_t *other) +{ + int size; + + assert (other->status == CAIRO_STATUS_SUCCESS); + + switch (other->type) { + default: + ASSERT_NOT_REACHED; + case CAIRO_PATTERN_TYPE_SOLID: + size = sizeof (cairo_solid_pattern_t); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + size = sizeof (cairo_surface_pattern_t); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + size = sizeof (cairo_linear_pattern_t); + break; + 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 +_cairo_pattern_init_snapshot (cairo_pattern_t *pattern, + const cairo_pattern_t *other) +{ + cairo_status_t status; + + /* We don't bother doing any fancy copy-on-write implementation + * for the pattern's data. It's generally quite tiny. */ + status = _cairo_pattern_init_copy (pattern, other); + if (unlikely (status)) + return status; + + /* But we do let the surface snapshot stuff be as fancy as it + * would like to be. */ + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = + (cairo_surface_pattern_t *) pattern; + cairo_surface_t *surface = surface_pattern->surface; + + surface_pattern->surface = _cairo_surface_snapshot (surface); + + cairo_surface_destroy (surface); + + status = surface_pattern->surface->status; + } else if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) + status = _cairo_raster_source_pattern_snapshot (pattern); + + return status; +} + +void +_cairo_pattern_fini (cairo_pattern_t *pattern) +{ + _cairo_user_data_array_fini (&pattern->user_data); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + break; + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *surface_pattern = + (cairo_surface_pattern_t *) pattern; + + cairo_surface_destroy (surface_pattern->surface); + } break; + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: { + cairo_gradient_pattern_t *gradient = + (cairo_gradient_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_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_MESH: + VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + break; + } +#endif +} + +cairo_status_t +_cairo_pattern_create_copy (cairo_pattern_t **pattern_out, + const cairo_pattern_t *other) +{ + cairo_pattern_t *pattern; + cairo_status_t status; + + if (other->status) + return other->status; + + switch (other->type) { + case CAIRO_PATTERN_TYPE_SOLID: + pattern = _cairo_malloc (sizeof (cairo_solid_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + pattern = _cairo_malloc (sizeof (cairo_surface_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + pattern = _cairo_malloc (sizeof (cairo_linear_pattern_t)); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + 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; + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + } + if (unlikely (pattern == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pattern_init_copy (pattern, other); + if (unlikely (status)) { + free (pattern); + return status; + } + + CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1); + *pattern_out = pattern; + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, + const cairo_color_t *color) +{ + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); + pattern->color = *color; +} + +void +_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, + cairo_surface_t *surface) +{ + if (surface->status) { + /* Force to solid to simplify the pattern_fini process. */ + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); + _cairo_pattern_set_error (&pattern->base, surface->status); + return; + } + + _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE); + + pattern->surface = cairo_surface_reference (surface); +} + +static void +_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern, + cairo_pattern_type_t type) +{ + _cairo_pattern_init (&pattern->base, type); + + pattern->n_stops = 0; + pattern->stops_size = 0; + pattern->stops = NULL; +} + +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->pd1.x = x0; + pattern->pd1.y = y0; + pattern->pd2.x = x1; + pattern->pd2.y = y1; +} + +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->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 * +_cairo_pattern_create_solid (const cairo_color_t *color) +{ + cairo_solid_pattern_t *pattern; + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]); + if (unlikely (pattern == NULL)) { + /* None cached, need to create a new pattern. */ + 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; + } + } + + _cairo_pattern_init_solid (pattern, color); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); + + return &pattern->base; +} + +cairo_pattern_t * +_cairo_pattern_create_in_error (cairo_status_t status) +{ + cairo_pattern_t *pattern; + + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_pattern_t *)&_cairo_pattern_nil.base; + + CAIRO_MUTEX_INITIALIZE (); + + pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); + if (pattern->status == CAIRO_STATUS_SUCCESS) + status = _cairo_pattern_set_error (pattern, status); + + return pattern; +} + +/** + * cairo_pattern_create_rgb: + * @red: red component of the color + * @green: green component of the color + * @blue: blue component of the color + * + * Creates a new #cairo_pattern_t corresponding to an opaque color. The + * color components are floating point numbers in the range 0 to 1. + * If the values passed in are outside that range, they will be + * clamped. + * + * 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.0 + **/ +cairo_pattern_t * +cairo_pattern_create_rgb (double red, double green, double blue) +{ + return cairo_pattern_create_rgba (red, green, blue, 1.0); +} +slim_hidden_def (cairo_pattern_create_rgb); + +/** + * cairo_pattern_create_rgba: + * @red: red component of the color + * @green: green component of the color + * @blue: blue component of the color + * @alpha: alpha component of the color + * + * Creates a new #cairo_pattern_t corresponding to a translucent color. + * The color components are floating point numbers in the range 0 to + * 1. If the values passed in are outside that range, they will be + * clamped. + * + * 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.0 + **/ +cairo_pattern_t * +cairo_pattern_create_rgba (double red, double green, double blue, + double alpha) +{ + 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); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); + + _cairo_color_init_rgba (&color, red, green, blue, alpha); + + CAIRO_MUTEX_INITIALIZE (); + + return _cairo_pattern_create_solid (&color); +} +slim_hidden_def (cairo_pattern_create_rgba); + +/** + * cairo_pattern_create_for_surface: + * @surface: the surface + * + * Create a new #cairo_pattern_t for the given surface. + * + * 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.0 + **/ +cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface) +{ + cairo_surface_pattern_t *pattern; + + if (surface == NULL) { + _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); + return (cairo_pattern_t*) &_cairo_pattern_nil_null_pointer; + } + + if (surface->status) + return _cairo_pattern_create_in_error (surface->status); + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]); + if (unlikely (pattern == NULL)) { + 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; + } + } + + CAIRO_MUTEX_INITIALIZE (); + + _cairo_pattern_init_for_surface (pattern, surface); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); + + return &pattern->base; +} +slim_hidden_def (cairo_pattern_create_for_surface); + +/** + * cairo_pattern_create_linear: + * @x0: x coordinate of the start point + * @y0: y coordinate of the start point + * @x1: x coordinate of the end point + * @y1: y coordinate of the end point + * + * Create a new linear gradient #cairo_pattern_t along the line defined + * by (x0, y0) and (x1, y1). Before using the gradient pattern, a + * number of color stops should be defined using + * cairo_pattern_add_color_stop_rgb() or + * cairo_pattern_add_color_stop_rgba(). + * + * Note: The coordinates here are 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.0 + **/ +cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, double x1, double y1) +{ + cairo_linear_pattern_t *pattern; + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]); + if (unlikely (pattern == NULL)) { + 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; + } + } + + CAIRO_MUTEX_INITIALIZE (); + + _cairo_pattern_init_linear (pattern, x0, y0, x1, y1); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); + + return &pattern->base.base; +} + +/** + * cairo_pattern_create_radial: + * @cx0: x coordinate for the center of the start circle + * @cy0: y coordinate for the center of the start circle + * @radius0: radius of the start circle + * @cx1: x coordinate for the center of the end circle + * @cy1: y coordinate for the center of the end circle + * @radius1: radius of the end circle + * + * Creates a new radial gradient #cairo_pattern_t between the two + * circles defined by (cx0, cy0, radius0) and (cx1, cy1, radius1). Before using the + * gradient pattern, a number of color stops should be defined using + * cairo_pattern_add_color_stop_rgb() or + * cairo_pattern_add_color_stop_rgba(). + * + * Note: The coordinates here are 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.0 + **/ +cairo_pattern_t * +cairo_pattern_create_radial (double cx0, double cy0, double radius0, + double cx1, double cy1, double radius1) +{ + cairo_radial_pattern_t *pattern; + + pattern = + _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]); + if (unlikely (pattern == NULL)) { + 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; + } + } + + CAIRO_MUTEX_INITIALIZE (); + + _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1); + CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); + + 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 + * + * Increases the reference count on @pattern by one. This prevents + * @pattern from being destroyed until a matching call to + * cairo_pattern_destroy() is made. + * + * 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) +{ + if (pattern == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) + return pattern; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); + + _cairo_reference_count_inc (&pattern->ref_count); + + return pattern; +} +slim_hidden_def (cairo_pattern_reference); + +/** + * cairo_pattern_get_type: + * @pattern: a #cairo_pattern_t + * + * Get the pattern's type. See #cairo_pattern_type_t for available + * types. + * + * Return value: The type of @pattern. + * + * Since: 1.2 + **/ +cairo_pattern_type_t +cairo_pattern_get_type (cairo_pattern_t *pattern) +{ + return pattern->type; +} + +/** + * cairo_pattern_status: + * @pattern: a #cairo_pattern_t + * + * Checks whether an error has previously occurred for this + * pattern. + * + * 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) +{ + return pattern->status; +} + +/** + * cairo_pattern_destroy: + * @pattern: a #cairo_pattern_t + * + * 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) +{ + cairo_pattern_type_t type; + + if (pattern == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&pattern->ref_count)) + return; + + type = pattern->type; + _cairo_pattern_fini (pattern); + + /* maintain a small cache of freed patterns */ + if (type < ARRAY_LENGTH (freed_pattern_pool)) + _freed_pool_put (&freed_pattern_pool[type], pattern); + else + free (pattern); +} +slim_hidden_def (cairo_pattern_destroy); + +/** + * cairo_pattern_get_reference_count: + * @pattern: a #cairo_pattern_t + * + * Returns the current reference count of @pattern. + * + * Return value: the current reference count of @pattern. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_pattern_get_reference_count (cairo_pattern_t *pattern) +{ + if (pattern == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&pattern->ref_count); +} + +/** + * cairo_pattern_get_user_data: + * @pattern: a #cairo_pattern_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @pattern using the + * specified key. If no user data has been attached with the given + * key this function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + * + * Since: 1.4 + **/ +void * +cairo_pattern_get_user_data (cairo_pattern_t *pattern, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&pattern->user_data, + key); +} + +/** + * cairo_pattern_set_user_data: + * @pattern: a #cairo_pattern_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the #cairo_pattern_t + * @destroy: a #cairo_destroy_func_t which will be called when the + * #cairo_t is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @pattern. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_set_user_data (cairo_pattern_t *pattern, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) + return pattern->status; + + return _cairo_user_data_array_set_data (&pattern->user_data, + 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) +{ + cairo_gradient_stop_t *new_stops; + int old_size = pattern->stops_size; + int embedded_size = ARRAY_LENGTH (pattern->stops_embedded); + int new_size = 2 * MAX (old_size, 4); + + /* we have a local buffer at pattern->stops_embedded. try to fulfill the request + * from there. */ + if (old_size < embedded_size) { + pattern->stops = pattern->stops_embedded; + pattern->stops_size = embedded_size; + return CAIRO_STATUS_SUCCESS; + } + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + assert (pattern->n_stops <= pattern->stops_size); + + if (pattern->stops == pattern->stops_embedded) { + new_stops = _cairo_malloc_ab (new_size, sizeof (cairo_gradient_stop_t)); + if (new_stops) + memcpy (new_stops, pattern->stops, old_size * sizeof (cairo_gradient_stop_t)); + } else { + new_stops = _cairo_realloc_ab (pattern->stops, + new_size, + sizeof (cairo_gradient_stop_t)); + } + + if (unlikely (new_stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pattern->stops = new_stops; + pattern->stops_size = new_size; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_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, + double blue, + double alpha) +{ + cairo_gradient_stop_t *stops; + unsigned int i; + + if (pattern->n_stops >= pattern->stops_size) { + cairo_status_t status = _cairo_pattern_gradient_grow (pattern); + if (unlikely (status)) { + status = _cairo_pattern_set_error (&pattern->base, status); + return; + } + } + + stops = pattern->stops; + + for (i = 0; i < pattern->n_stops; i++) + { + if (offset < stops[i].offset) + { + memmove (&stops[i + 1], &stops[i], + sizeof (cairo_gradient_stop_t) * (pattern->n_stops - i)); + + break; + } + } + + stops[i].offset = offset; + + stops[i].color.red = red; + stops[i].color.green = green; + stops[i].color.blue = blue; + stops[i].color.alpha = alpha; + + stops[i].color.red_short = _cairo_color_double_to_short (red); + stops[i].color.green_short = _cairo_color_double_to_short (green); + stops[i].color.blue_short = _cairo_color_double_to_short (blue); + stops[i].color.alpha_short = _cairo_color_double_to_short (alpha); + + pattern->n_stops++; +} + +/** + * cairo_pattern_add_color_stop_rgb: + * @pattern: a #cairo_pattern_t + * @offset: an offset in the range [0.0 .. 1.0] + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * + * Adds an opaque color stop to a gradient pattern. The offset + * specifies the location along the gradient's control vector. For + * example, a linear gradient's control vector is from (x0,y0) to + * (x1,y1) while a radial gradient's control vector is from any point + * on the start circle to the corresponding point on the end circle. + * + * The color is specified in the same way as in cairo_set_source_rgb(). + * + * If two (or more) stops are specified with identical offset values, + * they will be sorted according to the order in which the stops are + * added, (stops added earlier will compare less than stops added + * later). This can be useful for reliably making sharp color + * transitions instead of the typical blend. + * + * + * 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, + double offset, + double red, + double green, + double blue) +{ + cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, 1.0); +} + +/** + * cairo_pattern_add_color_stop_rgba: + * @pattern: a #cairo_pattern_t + * @offset: an offset in the range [0.0 .. 1.0] + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * @alpha: alpha component of color + * + * Adds a translucent color stop to a gradient pattern. The offset + * specifies the location along the gradient's control vector. For + * example, a linear gradient's control vector is from (x0,y0) to + * (x1,y1) while a radial gradient's control vector is from any point + * on the start circle to the corresponding point on the end circle. + * + * The color is specified in the same way as in cairo_set_source_rgba(). + * + * If two (or more) stops are specified with identical offset values, + * they will be sorted according to the order in which the stops are + * added, (stops added earlier will compare less than stops added + * later). This can be useful for reliably making sharp color + * transitions instead of the typical blend. + * + * 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, + double red, + double green, + double blue, + double alpha) +{ + 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); + alpha = _cairo_restrict_value (alpha, 0.0, 1.0); + + _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: + * @pattern: a #cairo_pattern_t + * @matrix: a #cairo_matrix_t + * + * Sets the pattern's transformation matrix to @matrix. This matrix is + * a transformation from user space to pattern space. + * + * When a pattern is first created it always has the identity matrix + * for its transformation matrix, which means that pattern space is + * initially identical to user space. + * + * Important: Please note that the direction of this transformation + * matrix is from user space to pattern space. This means that if you + * imagine the flow from a pattern to user space (and on to device + * space), then coordinates in that flow will be transformed by the + * inverse of the pattern matrix. + * + * For example, if you want to make a pattern appear twice as large as + * it does by default the correct code to use is: + * + * + * cairo_matrix_init_scale (&matrix, 0.5, 0.5); + * cairo_pattern_set_matrix (pattern, &matrix); + * + * + * Meanwhile, using values of 2.0 rather than 0.5 in the code above + * would cause the pattern to appear at half of its default size. + * + * 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, + const cairo_matrix_t *matrix) +{ + cairo_matrix_t inverse; + cairo_status_t status; + + if (pattern->status) + return; + + if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0) + return; + + pattern->matrix = *matrix; + _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_MATRIX); + + inverse = *matrix; + status = cairo_matrix_invert (&inverse); + if (unlikely (status)) + status = _cairo_pattern_set_error (pattern, status); +} +slim_hidden_def (cairo_pattern_set_matrix); + +/** + * cairo_pattern_get_matrix: + * @pattern: a #cairo_pattern_t + * @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) +{ + *matrix = pattern->matrix; +} + +/** + * cairo_pattern_set_filter: + * @pattern: a #cairo_pattern_t + * @filter: a #cairo_filter_t describing the filter to use for resizing + * the pattern + * + * Sets the filter to be used for resizing when using this pattern. + * See #cairo_filter_t for details on each filter. + * + * * Note that you might want to control filtering even when you do not + * have an explicit #cairo_pattern_t object, (for example when using + * cairo_set_source_surface()). In these cases, it is convenient to + * use cairo_get_source() to get access to the pattern that cairo + * creates implicitly. For example: + * + * + * 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) +{ + if (pattern->status) + return; + + pattern->filter = filter; + _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_FILTER); +} + +/** + * cairo_pattern_get_filter: + * @pattern: a #cairo_pattern_t + * + * Gets the current filter for a pattern. See #cairo_filter_t + * 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) +{ + return pattern->filter; +} + +/** + * cairo_pattern_set_extend: + * @pattern: a #cairo_pattern_t + * @extend: a #cairo_extend_t describing how the area outside of the + * pattern will be drawn + * + * Sets the mode to be used for drawing outside the area of a pattern. + * See #cairo_extend_t for details on the semantics of each extend + * strategy. + * + * 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) +{ + if (pattern->status) + return; + + pattern->extend = extend; + _cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND); +} + +/** + * cairo_pattern_get_extend: + * @pattern: a #cairo_pattern_t + * + * Gets the current extend mode for a pattern. See #cairo_extend_t + * for details on the semantics of each extend strategy. + * + * 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) +{ + return pattern->extend; +} +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) +{ + if (pattern->status) + return; + + cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); +} + +static cairo_bool_t +_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear) +{ + 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) +{ + /* 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 +_gradient_is_clear (const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents) +{ + unsigned int i; + + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + + if (gradient->n_stops == 0 || + (gradient->base.extend == CAIRO_EXTEND_NONE && + gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) + return TRUE; + + if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) { + /* degenerate radial gradients are clear */ + 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; + } + + for (i = 0; i < gradient->n_stops; i++) + if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color)) + return FALSE; + + 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_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 + * argument is initialized to the color the pattern represents. + * This functions doesn't handle completely transparent gradients, + * thus it should be called only after _cairo_pattern_is_clear has + * returned FALSE. + * + * Return value: %TRUE if the pattern is a solid color. + **/ +cairo_bool_t +_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, + const cairo_rectangle_int_t *extents, + cairo_color_t *color) +{ + unsigned int i; + + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + + /* 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) { + double t[2]; + + /* We already know that the pattern is not clear, thus if some + * part of it is clear, the whole is not solid. + */ + + if (extents == NULL) + return FALSE; + + _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, + &gradient->stops[i].color)) + return FALSE; + + _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 TRUE; +} + +/** + * _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 + * the pattern's alpha value when converted to a byte is 255, so if a + * backend actually supported deep alpha channels this function might + * not do the right thing. + * + * Return value: %TRUE if the pattern is an opaque, solid color. + **/ +cairo_bool_t +_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern) +{ + cairo_solid_pattern_t *solid; + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return FALSE; + + solid = (cairo_solid_pattern_t *) pattern; + + return CAIRO_COLOR_IS_OPAQUE (&solid->color); +} + +static cairo_bool_t +_surface_is_opaque (const cairo_surface_pattern_t *pattern, + 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 (! _cairo_surface_get_extents (pattern->surface, &extents)) + return TRUE; + + if (sample == NULL) + return FALSE; + + return _cairo_rectangle_contains_rectangle (&extents, sample); +} + +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 +_surface_is_clear (const cairo_surface_pattern_t *pattern) +{ + cairo_rectangle_int_t extents; + + if (_cairo_surface_get_extents (pattern->surface, &extents) && + (extents.width == 0 || extents.height == 0)) + return TRUE; + + return pattern->surface->is_clear && + 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 *sample) +{ + unsigned int i; + + assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || + gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); + + if (gradient->n_stops == 0 || + (gradient->base.extend == CAIRO_EXTEND_NONE && + gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) + return FALSE; + + if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { + if (gradient->base.extend == CAIRO_EXTEND_NONE) { + double t[2]; + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; + + /* EXTEND_NONE degenerate radial gradients are clear */ + if (_linear_pattern_is_degenerate (linear)) + return FALSE; + + if (sample == NULL) + return FALSE; + + _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)) + return FALSE; + + return TRUE; +} + +/** + * _cairo_pattern_is_opaque: + * + * Convenience function to determine whether a pattern is an opaque + * pattern (of any type). The same caveats that apply to + * _cairo_pattern_is_opaque_solid apply here as well. + * + * Return value: %TRUE if the pattern is a opaque. + **/ +cairo_bool_t +_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern, + const cairo_rectangle_int_t *sample) +{ + const cairo_pattern_union_t *pattern; + + if (abstract_pattern->has_component_alpha) + return FALSE; + + pattern = (cairo_pattern_union_t *) abstract_pattern; + switch (pattern->base.type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_pattern_is_opaque_solid (abstract_pattern); + case CAIRO_PATTERN_TYPE_SURFACE: + return _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, sample); + case CAIRO_PATTERN_TYPE_MESH: + return FALSE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +cairo_bool_t +_cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern) +{ + const cairo_pattern_union_t *pattern; + + if (abstract_pattern->has_component_alpha) + return FALSE; + + pattern = (cairo_pattern_union_t *) abstract_pattern; + 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 + * Returns: the optimized #cairo_filter_t to use with @pattern. + * + * Possibly optimize the filter to a simpler value depending on transformation + **/ +cairo_filter_t +_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern) +{ + 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)) { + return CAIRO_FILTER_NEAREST; + } else { + /* 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. + */ + 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_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + default: + break; + } + + return pattern->filter; +} + +/** + * _cairo_hypot: + * Returns: value similar to hypot(@x,@y) + * + * 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. + **/ +static inline double +_cairo_hypot(double x, double y) +{ + return hypot(x, y); +} + +/** + * _cairo_pattern_sampled_area: + * + * 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_sampled_area (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_rectangle_int_t *sample) +{ + double x1, x2, y1, y2; + double padx, pady; + + /* Assume filters are interpolating, which means identity + cannot change the image */ + if (_cairo_matrix_is_identity (&pattern->matrix)) { + *sample = *extents; + return; + } + + /* 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); + + /* 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; + } + + /* 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; + + y1 = floor (y1 - pady); + if (y1 < CAIRO_RECT_INT_MIN) y1 = CAIRO_RECT_INT_MIN; + sample->y = y1; + + 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; +} + +/** + * _cairo_pattern_get_extents: + * + * Return the "target-space" extents of @pattern in @extents. + * + * 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_bool_t is_vector) +{ + double x1, y1, x2, y2; + 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: + goto UNBOUNDED; + + case CAIRO_PATTERN_TYPE_SURFACE: + { + cairo_rectangle_int_t surface_extents; + const cairo_surface_pattern_t *surface_pattern = + (const cairo_surface_pattern_t *) pattern; + cairo_surface_t *surface = surface_pattern->surface; + + if (! _cairo_surface_get_extents (surface, &surface_extents)) + goto UNBOUNDED; + + if (surface_extents.width == 0 || surface_extents.height == 0) + goto EMPTY; + + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + 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; + + case CAIRO_PATTERN_TYPE_RADIAL: + { + const cairo_radial_pattern_t *radial = + (const cairo_radial_pattern_t *) pattern; + double cx1, cy1; + double cx2, cy2; + double r1, r2; + + 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; + } + + /* TODO: in some cases (focus outside/on the circle) it is + * half-bounded. */ + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + cx1 = radial->cd1.center.x; + cy1 = radial->cd1.center.y; + r1 = radial->cd1.radius; + + cx2 = radial->cd2.center.x; + cy2 = radial->cd2.center.y; + r2 = radial->cd2.radius; + + 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; + + case CAIRO_PATTERN_TYPE_LINEAR: + { + const cairo_linear_pattern_t *linear = + (const cairo_linear_pattern_t *) pattern; + + if (pattern->extend != CAIRO_EXTEND_NONE) + goto UNBOUNDED; + + 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->pd1.x == linear->pd2.x) { + x1 = -HUGE_VAL; + x2 = HUGE_VAL; + 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; + + default: + ASSERT_NOT_REACHED; + } + + if (_cairo_matrix_is_translation (&pattern->matrix)) { + x1 -= pattern->matrix.x0; x2 -= pattern->matrix.x0; + 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); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_matrix_transform_bounding_box (&imatrix, + &x1, &y1, &x2, &y2, + NULL); + } + + if (!round_x) { + x1 -= 0.5; + x2 += 0.5; + } + if (x1 < CAIRO_RECT_INT_MIN) + ix1 = CAIRO_RECT_INT_MIN; + else + ix1 = _cairo_lround (x1); + if (x2 > 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; + + return; + + UNBOUNDED: + /* unbounded patterns -> 'infinite' extents */ + _cairo_unbounded_rectangle_init (extents); + return; + + EMPTY: + extents->x = extents->y = 0; + extents->width = extents->height = 0; + 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_solid_pattern_t *solid) +{ + hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color)); + + return hash; +} + +static unsigned long +_cairo_gradient_color_stops_hash (unsigned long hash, + const cairo_gradient_pattern_t *gradient) +{ + unsigned int n; + + hash = _cairo_hash_bytes (hash, + &gradient->n_stops, + sizeof (gradient->n_stops)); + + for (n = 0; n < gradient->n_stops; n++) { + hash = _cairo_hash_bytes (hash, + &gradient->stops[n].offset, + sizeof (double)); + hash = _cairo_hash_bytes (hash, + &gradient->stops[n].color, + sizeof (cairo_color_stop_t)); + } + + return hash; +} + +unsigned long +_cairo_linear_pattern_hash (unsigned long hash, + const cairo_linear_pattern_t *linear) +{ + 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); +} + +unsigned long +_cairo_radial_pattern_hash (unsigned long hash, + const cairo_radial_pattern_t *radial) +{ + 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_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh) +{ + 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) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + + if (pattern->status) + return 0; + + hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type)); + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { + hash = _cairo_hash_bytes (hash, + &pattern->matrix, sizeof (pattern->matrix)); + hash = _cairo_hash_bytes (hash, + &pattern->filter, sizeof (pattern->filter)); + hash = _cairo_hash_bytes (hash, + &pattern->extend, sizeof (pattern->extend)); + hash = _cairo_hash_bytes (hash, + &pattern->has_component_alpha, + sizeof (pattern->has_component_alpha)); + } + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + 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, (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 cairo_bool_t +_cairo_solid_pattern_equal (const cairo_solid_pattern_t *a, + const cairo_solid_pattern_t *b) +{ + return _cairo_color_equal (&a->color, &b->color); +} + +static cairo_bool_t +_cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, + const cairo_gradient_pattern_t *b) +{ + unsigned int n; + + if (a->n_stops != b->n_stops) + return FALSE; + + for (n = 0; n < a->n_stops; n++) { + if (a->stops[n].offset != b->stops[n].offset) + return FALSE; + if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color)) + return FALSE; + } + + return TRUE; +} + +cairo_bool_t +_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, + const cairo_linear_pattern_t *b) +{ + if (a->pd1.x != b->pd1.x) + return FALSE; + + if (a->pd1.y != b->pd1.y) + return FALSE; + + if (a->pd2.x != b->pd2.x) + return FALSE; + + if (a->pd2.y != b->pd2.y) + return FALSE; + + return _cairo_gradient_color_stops_equal (&a->base, &b->base); +} + +cairo_bool_t +_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, + const cairo_radial_pattern_t *b) +{ + if (a->cd1.center.x != b->cd1.center.x) + return FALSE; + + if (a->cd1.center.y != b->cd1.center.y) + return FALSE; + + if (a->cd1.radius != b->cd1.radius) + return FALSE; + + if (a->cd2.center.x != b->cd2.center.x) + return FALSE; + + if (a->cd2.center.y != b->cd2.center.y) + return FALSE; + + if (a->cd2.radius != b->cd2.radius) + return FALSE; + + return _cairo_gradient_color_stops_equal (&a->base, &b->base); +} + +static cairo_bool_t +_cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a, + const cairo_mesh_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) +{ + if (a->status || b->status) + return FALSE; + + if (a == b) + return TRUE; + + if (a->type != b->type) + return FALSE; + + if (a->has_component_alpha != b->has_component_alpha) + return FALSE; + + if (a->type != CAIRO_PATTERN_TYPE_SOLID) { + if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) + return FALSE; + + if (a->filter != b->filter) + return FALSE; + + if (a->extend != b->extend) + return FALSE; + } + + switch (a->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_solid_pattern_equal ((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 ((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; + } +} + +/** + * 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 + * @blue: return value for blue component of color, or %NULL + * @alpha: return value for alpha component of color, or %NULL + * + * Gets the solid color for a solid color pattern. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid + * color pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_rgba (cairo_pattern_t *pattern, + double *red, double *green, + double *blue, double *alpha) +{ + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; + double r0, g0, b0, a0; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + _cairo_color_get_rgba (&solid->color, &r0, &g0, &b0, &a0); + + if (red) + *red = r0; + if (green) + *green = g0; + if (blue) + *blue = b0; + if (alpha) + *alpha = a0; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * 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. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a surface + * pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_surface (cairo_pattern_t *pattern, + cairo_surface_t **surface) +{ + cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (surface) + *surface = spat->surface; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * 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 + * @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 and offset information at the given @index for a + * 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 + * not a gradient pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is + * returned. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, + int index, double *offset, + double *red, double *green, + double *blue, double *alpha) +{ + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && + pattern->type != CAIRO_PATTERN_TYPE_RADIAL) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (index < 0 || (unsigned int) index >= gradient->n_stops) + return _cairo_error (CAIRO_STATUS_INVALID_INDEX); + + if (offset) + *offset = gradient->stops[index].offset; + if (red) + *red = gradient->stops[index].color.red; + if (green) + *green = gradient->stops[index].color.green; + if (blue) + *blue = gradient->stops[index].color.blue; + if (alpha) + *alpha = gradient->stops[index].color.alpha; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_pattern_get_color_stop_count: + * @pattern: a #cairo_pattern_t + * @count: return value for the number of color stops, or %NULL + * + * Gets the number of color stops specified in the given gradient + * pattern. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a gradient + * pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, + int *count) +{ + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && + pattern->type != CAIRO_PATTERN_TYPE_RADIAL) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (count) + *count = gradient->n_stops; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * 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 + * @x1: return value for the x coordinate of the second point, or %NULL + * @y1: return value for the y coordinate of the second point, or %NULL + * + * Gets the gradient endpoints for a linear gradient. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a linear + * gradient pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_linear_points (cairo_pattern_t *pattern, + double *x0, double *y0, + double *x1, double *y1) +{ + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (x0) + *x0 = linear->pd1.x; + if (y0) + *y0 = linear->pd1.y; + if (x1) + *x1 = linear->pd2.x; + if (y1) + *y1 = linear->pd2.y; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * 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 + * @r0: return value for the radius of the first circle, or %NULL + * @x1: return value for the x coordinate of the center of the second circle, or %NULL + * @y1: return value for the y coordinate of the center of the second circle, or %NULL + * @r1: return value for the radius of the second circle, or %NULL + * + * Gets the gradient endpoint circles for a radial gradient, each + * specified as a center coordinate and a radius. + * + * Return value: %CAIRO_STATUS_SUCCESS, or + * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a radial + * gradient pattern. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, + double *x0, double *y0, double *r0, + double *x1, double *y1, double *r1) +{ + cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern; + + if (pattern->status) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL) + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + + if (x0) + *x0 = radial->cd1.center.x; + if (y0) + *y0 = radial->cd1.center.y; + if (r0) + *r0 = radial->cd1.radius; + if (x1) + *x1 = radial->cd2.center.x; + if (y1) + *y1 = radial->cd2.center.y; + if (r1) + *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) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++) + _freed_pool_reset (&freed_pattern_pool[i]); +} + +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 0000000000..81c5759263 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf-interchange.c @@ -0,0 +1,1881 @@ +/* -*- 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_int_status_t status; + + status = _cairo_pdf_surface_object_begin (surface, node->res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Type /StructElem\n" + " /S /%s\n" + " /P %d 0 R\n", + 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->object_stream.stream, " /K %d 0 R\n", child->res.id); + } else { + _cairo_output_stream_printf (surface->object_stream.stream, " /K [ "); + if (node->annot_res.id != 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /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->object_stream.stream, "%d 0 R ", child->res.id); + } + _cairo_output_stream_printf (surface->object_stream.stream, "]\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->object_stream.stream, " /Pg %d 0 R\n", page_res->id); + + if (num_mcid == 1 && node->annot_res.id == 0) { + _cairo_output_stream_printf (surface->object_stream.stream, " /K %d\n", mcid_elem->mcid); + } else { + _cairo_output_stream_printf (surface->object_stream.stream, " /K [ "); + if (node->annot_res.id != 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /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->object_stream.stream, "%d ", mcid_elem->mcid); + } else { + _cairo_output_stream_printf (surface->object_stream.stream, + "\n << /Type /MCR /Pg %d 0 R /MCID %d >> ", + page_res->id, + mcid_elem->mcid); + } + } + _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); + } + } + } + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + + _cairo_pdf_surface_object_end (surface); + + return _cairo_output_stream_get_status (surface->object_stream.stream); +} + +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; + + _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->object_stream.stream, + "[%d 0 R /XYZ %f %f 0]\n", + res.id, + x, + height - y); + } else { + _cairo_output_stream_printf (surface->object_stream.stream, + "[%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; + cairo_pdf_forward_link_t *link; + cairo_pdf_resource_t link_res; + + /* If the dest is known, emit an explicit dest */ + if (link_attrs->dest) { + cairo_pdf_named_dest_t key; + cairo_pdf_named_dest_t *named_dest; + + /* check if we already have this 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) { + 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; + + _cairo_output_stream_printf (surface->object_stream.stream, " /Dest "); + status = cairo_pdf_interchange_write_explicit_dest (surface, + named_dest->page, + TRUE, + x, y); + return status; + } + } + + /* If the page is known, emit an explicit dest */ + if (!link_attrs->dest) { + if (link_attrs->page < 1) + return _cairo_tag_error ("Link attribute: \"page=%d\" page must be >= 1", link_attrs->page); + + if (link_attrs->page <= (int)_cairo_array_num_elements (&surface->pages)) { + _cairo_output_stream_printf (surface->object_stream.stream, " /Dest "); + return cairo_pdf_interchange_write_explicit_dest (surface, + link_attrs->page, + link_attrs->has_pos, + link_attrs->pos.x, + link_attrs->pos.y); + } + } + + /* Link refers to a future or unknown page. Use an indirect object + * and write the link at the end of the document */ + + link = _cairo_malloc (sizeof (cairo_pdf_forward_link_t)); + if (unlikely (link == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + link_res = _cairo_pdf_surface_new_object (surface); + if (link_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->object_stream.stream, + " /Dest %d 0 R\n", + link_res.id); + + link->res = link_res; + link->dest = link_attrs->dest ? strdup (link_attrs->dest) : NULL; + link->page = link_attrs->page; + link->has_pos = link_attrs->has_pos; + link->pos = link_attrs->pos; + status = _cairo_array_append (&surface->forward_links, link); + + return status; +} + +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) { + status = _cairo_utf8_to_pdf_string (link_attrs->uri, &dest); + if (unlikely (status)) + return status; + + if (dest[0] != '(') { + free (dest); + return _cairo_tag_error ("Link attribute: \"url=%s\" URI may only contain ASCII characters", + link_attrs->uri); + } + + _cairo_output_stream_printf (surface->object_stream.stream, + " /A <<\n" + " /Type /Action\n" + " /S /URI\n" + " /URI %s\n" + " >>\n", + dest); + free (dest); + } else if (link_attrs->link_type == TAG_LINK_FILE) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /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->object_stream.stream, + " /D %s\n", + dest); + free (dest); + } else { + if (link_attrs->has_pos) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /D [%d %f %f 0]\n", + link_attrs->page, + link_attrs->pos.x, + link_attrs->pos.y); + } else { + _cairo_output_stream_printf (surface->object_stream.stream, + " /D [%d null null 0]\n", + link_attrs->page); + } + } + _cairo_output_stream_printf (surface->object_stream.stream, + " >>\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); + if (node->annot_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_array_append (&surface->page_annots, &node->annot_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_object_begin (surface, node->annot_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Type /Annot\n" + " /Subtype /Link\n" + " /StructParent %d\n", + sp); + + height = surface->height; + if (num_rects > 0) { + cairo_rectangle_int_t bbox_rect; + + _cairo_output_stream_printf (surface->object_stream.stream, + " /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->object_stream.stream, &rectf, height); + _cairo_output_stream_printf (surface->object_stream.stream, " "); + } + _cairo_output_stream_printf (surface->object_stream.stream, + "]\n" + " /Rect [ "); + write_rect_int_to_pdf_bbox (surface->object_stream.stream, &bbox_rect, height); + _cairo_output_stream_printf (surface->object_stream.stream, " ]\n"); + } else { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Rect [ "); + write_rect_int_to_pdf_bbox (surface->object_stream.stream, &node->extents.extents, height); + _cairo_output_stream_printf (surface->object_stream.stream, " ]\n"); + } + + status = cairo_pdf_interchange_write_link_action (surface, &annot->link_attrs); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + " /BS << /W 0 >>" + ">>\n"); + + _cairo_pdf_surface_object_end (surface); + status = _cairo_output_stream_get_status (surface->object_stream.stream); + } + + 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; + cairo_int_status_t status; + + if (cairo_list_is_empty (&ic->struct_root->children)) + return CAIRO_STATUS_SUCCESS; + + surface->struct_tree_root = _cairo_pdf_surface_new_object (surface); + if (surface->struct_tree_root.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + 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); + + status = _cairo_pdf_surface_object_begin (surface, surface->struct_tree_root); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Type /StructTreeRoot\n" + " /ParentTree %d 0 R\n", + 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->object_stream.stream, " /K [ %d 0 R ]\n", child->res.id); + } else { + _cairo_output_stream_printf (surface->object_stream.stream, " /K [ "); + + cairo_list_foreach_entry (child, cairo_pdf_struct_tree_node_t, + &ic->struct_root->children, link) + { + _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", child->res.id); + } + _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); + } + + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); + + 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); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pdf_surface_object_begin (surface, res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "[\n"); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&ic->mcid_to_tree, i, &node); + _cairo_output_stream_printf (surface->object_stream.stream, " %d 0 R\n", node->res.id); + } + _cairo_output_stream_printf (surface->object_stream.stream, + "]\n"); + _cairo_pdf_surface_object_end (surface); + + 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; + cairo_int_status_t status; + + num_elems = _cairo_array_num_elements (&ic->parent_tree); + if (num_elems > 0) { + ic->parent_tree_res = _cairo_pdf_surface_new_object (surface); + if (ic->parent_tree_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pdf_surface_object_begin (surface, ic->parent_tree_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Nums [\n"); + for (i = 0; i < num_elems; i++) { + res = _cairo_array_index (&ic->parent_tree, i); + if (res->id) { + _cairo_output_stream_printf (surface->object_stream.stream, + " %d %d 0 R\n", + i, + res->id); + } + } + _cairo_output_stream_printf (surface->object_stream.stream, + " ]\n" + ">>\n"); + _cairo_pdf_surface_object_end (surface); + } + + 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); + if (outline->res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + surface->outlines_dict_res = outline->res; + status = _cairo_pdf_surface_object_begin (surface, outline->res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Type /Outlines\n" + " /First %d 0 R\n" + " /Last %d 0 R\n" + " /Count %d\n" + ">>\n", + outline->first_child->res.id, + outline->last_child->res.id, + outline->count); + _cairo_pdf_surface_object_end (surface); + + 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; + + status = _cairo_pdf_surface_object_begin (surface, outline->res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Title %s\n" + " /Parent %d 0 R\n", + name, + outline->parent->res.id); + free (name); + + if (outline->prev) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Prev %d 0 R\n", + outline->prev->res.id); + } + + if (outline->next) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Next %d 0 R\n", + outline->next->res.id); + } + + if (outline->first_child) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /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->object_stream.stream, + " /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->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); + } + + 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_forward_links (cairo_pdf_surface_t *surface) +{ + int num_elems, i; + cairo_pdf_forward_link_t *link; + cairo_int_status_t status; + cairo_pdf_named_dest_t key; + cairo_pdf_named_dest_t *named_dest; + cairo_pdf_interchange_t *ic = &surface->interchange; + + num_elems = _cairo_array_num_elements (&surface->forward_links); + for (i = 0; i < num_elems; i++) { + link = _cairo_array_index (&surface->forward_links, i); + if (link->page > (int)_cairo_array_num_elements (&surface->pages)) + return _cairo_tag_error ("Link attribute: \"page=%d\" page exceeds page count (%d)", + link->page, _cairo_array_num_elements (&surface->pages)); + + + status = _cairo_pdf_surface_object_begin (surface, link->res); + if (unlikely (status)) + return status; + + if (link->dest) { + key.attrs.name = link->dest; + init_named_dest_key (&key); + named_dest = _cairo_hash_table_lookup (ic->named_dests, &key.base); + if (named_dest) { + 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); + } else { + return _cairo_tag_error ("Link to dest=\"%s\" not found", link->dest); + } + } else { + cairo_pdf_interchange_write_explicit_dest (surface, + link->page, + link->has_pos, + link->pos.x, + link->pos.y); + } + _cairo_pdf_surface_object_end (surface); + } + + return CAIRO_STATUS_SUCCESS; +} + +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); + if (surface->page_labels_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pdf_surface_object_begin (surface, surface->page_labels_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Nums [\n"); + 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->object_stream.stream, " %d << ", i); + + if (num) + _cairo_output_stream_printf (surface->object_stream.stream, "/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->object_stream.stream, "/P %s ", s); + free (s); + } + + _cairo_output_stream_printf (surface->object_stream.stream, ">>\n"); + } + free (prev_prefix); + prev_prefix = prefix; + prefix = NULL; + prev_num = num; + } + free (prefix); + free (prev_prefix); + _cairo_output_stream_printf (surface->object_stream.stream, + " ]\n" + ">>\n"); + _cairo_pdf_surface_object_end (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_collect_external_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; + + if (!dest->attrs.internal) + 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; + cairo_int_status_t status; + + 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_external_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); + if (ic->dests_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pdf_surface_object_begin (surface, ic->dests_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Names [\n"); + 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->object_stream.stream, + " (%s) [%d 0 R /XYZ %f %f 0]\n", + dest->attrs.name, + page_res.id, + x, + height - y); + } + _cairo_output_stream_printf (surface->object_stream.stream, + " ]\n" + ">>\n"); + _cairo_pdf_surface_object_end (surface); + + 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); + if (surface->names_dict_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pdf_surface_object_begin (surface, surface->names_dict_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Dests %d 0 R >>\n", + ic->dests_res.id); + _cairo_pdf_surface_object_end (surface); + } + + 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; + cairo_int_status_t status; + + surface->docinfo_res = _cairo_pdf_surface_new_object (surface); + if (surface->docinfo_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pdf_surface_object_begin (surface, surface->docinfo_res); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Producer (cairo %s (https://cairographics.org))\n", + cairo_version_string ()); + + if (ic->docinfo.title) + _cairo_output_stream_printf (surface->object_stream.stream, " /Title %s\n", ic->docinfo.title); + + if (ic->docinfo.author) + _cairo_output_stream_printf (surface->object_stream.stream, " /Author %s\n", ic->docinfo.author); + + if (ic->docinfo.subject) + _cairo_output_stream_printf (surface->object_stream.stream, " /Subject %s\n", ic->docinfo.subject); + + if (ic->docinfo.keywords) + _cairo_output_stream_printf (surface->object_stream.stream, " /Keywords %s\n", ic->docinfo.keywords); + + if (ic->docinfo.creator) + _cairo_output_stream_printf (surface->object_stream.stream, " /Creator %s\n", ic->docinfo.creator); + + if (ic->docinfo.create_date) + _cairo_output_stream_printf (surface->object_stream.stream, " /CreationDate %s\n", ic->docinfo.create_date); + + if (ic->docinfo.mod_date) + _cairo_output_stream_printf (surface->object_stream.stream, " /ModDate %s\n", ic->docinfo.mod_date); + + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); + + 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 || + tag_type == TAG_TREE_TYPE_LINK_ONLY) { + + 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_forward_links (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); +} + +void +_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); +} + +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 new file mode 100644 index 0000000000..ed8be7f96e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf-operators-private.h @@ -0,0 +1,184 @@ +/* -*- 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 + * Copyright © 2006 Red Hat, Inc + * Copyright © 2007 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 University of Southern + * California. + * + * Contributor(s): + * Kristian Høgsberg + * Carl Worth + * Adrian Johnson + */ + +#ifndef CAIRO_PDF_OPERATORS_H +#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 + * line so that an entire line can be emitted in as one string. If the + * glyphs in a line exceeds this size the only downside is the slight + * overhead of emitting two strings. + */ +#define PDF_GLYPH_BUFFER_SIZE 200 + +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; + double x_position; + double x_advance; +} cairo_pdf_glyph_t; + +typedef struct _cairo_pdf_operators { + cairo_output_stream_t *stream; + cairo_matrix_t cairo_to_pdf; + 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 */ + + /* PDF text state */ + cairo_bool_t is_new_text_object; /* text object started but matrix and font not yet selected */ + unsigned int font_id; + unsigned int subset_id; + cairo_matrix_t text_matrix; /* PDF text matrix (Tlm in the PDF reference) */ + cairo_matrix_t cairo_to_pdftext; /* translate cairo coords to PDF text space */ + cairo_matrix_t font_matrix_inverse; + 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]; + + /* PDF line style */ + cairo_bool_t has_line_style; + double line_width; + cairo_line_cap_t line_cap; + cairo_line_join_t line_join; + double miter_limit; + cairo_bool_t has_dashes; +} cairo_pdf_operators_t; + +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_bool_t ps); + +cairo_private cairo_status_t +_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators); + +cairo_private void +_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, + cairo_pdf_operators_use_font_subset_t use_font_subset, + void *closure); + +cairo_private void +_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream); + + +cairo_private void +_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, + cairo_matrix_t *cairo_to_pdf); + +cairo_private void +_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, + cairo_bool_t enable); + +cairo_private cairo_status_t +_cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators); + +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, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, + const cairo_stroke_style_t *style, + double scale); + +cairo_private cairo_int_status_t +_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, + 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, + 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, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + 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_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, + 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_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 new file mode 100644 index 0000000000..328e893d71 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf-operators.c @@ -0,0 +1,1595 @@ +/* -*- 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 + * Copyright © 2006 Red Hat, Inc + * Copyright © 2007, 2008 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * 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): + * Kristian Høgsberg + * Carl Worth + * Adrian Johnson + */ + +#include "cairoint.h" + +#if CAIRO_HAS_PDF_OPERATORS + +#include "cairo-error-private.h" +#include "cairo-pdf-operators-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-scaled-font-subsets-private.h" + +static cairo_status_t +_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators); + + +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_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; + pdf_operators->num_glyphs = 0; + pdf_operators->has_line_style = FALSE; + pdf_operators->use_actual_text = FALSE; +} + +cairo_status_t +_cairo_pdf_operators_fini (cairo_pdf_operators_t *pdf_operators) +{ + return _cairo_pdf_operators_flush (pdf_operators); +} + +void +_cairo_pdf_operators_set_font_subsets_callback (cairo_pdf_operators_t *pdf_operators, + cairo_pdf_operators_use_font_subset_t use_font_subset, + void *closure) +{ + pdf_operators->use_font_subset = use_font_subset; + pdf_operators->use_font_subset_closure = closure; +} + +/* Change the output stream to a different stream. + * _cairo_pdf_operators_flush() should always be called before calling + * this function. + */ +void +_cairo_pdf_operators_set_stream (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream) +{ + pdf_operators->stream = stream; + pdf_operators->has_line_style = FALSE; +} + +void +_cairo_pdf_operators_set_cairo_to_pdf_matrix (cairo_pdf_operators_t *pdf_operators, + cairo_matrix_t *cairo_to_pdf) +{ + pdf_operators->cairo_to_pdf = *cairo_to_pdf; + pdf_operators->has_line_style = FALSE; +} + +cairo_private void +_cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators, + cairo_bool_t enable) +{ + pdf_operators->use_actual_text = enable; +} + +/* Finish writing out any pending commands to the stream. This + * function must be called by the surface before emitting anything + * into the PDF stream. + * + * pdf_operators may leave the emitted PDF for some operations + * unfinished in case subsequent operations can be merged. This + * function will finish off any incomplete operation so the stream + * will be in a state where the surface may emit its own PDF + * operations (eg changing patterns). + * + */ +cairo_status_t +_cairo_pdf_operators_flush (cairo_pdf_operators_t *pdf_operators) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (pdf_operators->in_text_object) + status = _cairo_pdf_operators_end_text (pdf_operators); + + return status; +} + +/* Reset the known graphics state of the PDF consumer. ie no + * 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 + * operations. + * + * This should be called when starting a new stream or after emitting + * the 'Q' operator (where pdf-operators functions were called inside + * the q/Q pair). + */ +void +_cairo_pdf_operators_reset (cairo_pdf_operators_t *pdf_operators) +{ + pdf_operators->has_line_style = FALSE; +} + +/* A word wrap stream can be used as a filter to do word wrapping on + * top of an existing output stream. The word wrapping is quite + * simple, using isspace to determine characters that separate + * words. Any word that will cause the column count exceed the given + * max_column will have a '\n' character emitted before it. + * + * The stream is careful to maintain integrity for words that cross + * the boundary from one call to write to the next. + * + * Note: This stream does not guarantee that the output will never + * exceed max_column. In particular, if a single word is larger than + * max_column it will not be broken up. + */ + +typedef 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_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 +_word_wrap_stream_count_word_up_to (word_wrap_stream_t *stream, + const unsigned char *data, int length) +{ + const unsigned char *s = data; + int count = 0; + + while (length--) { + if (_cairo_isspace (*s) || *s == '<' || *s == '(') { + stream->state = WRAP_STATE_DELIMITER; + break; + } + + count++; + stream->column++; + s++; + } + + if (count) + _cairo_output_stream_write (stream->output, data, count); + + return count; +} + + +/* Emit hexstring bytes up to either the end of the ASCII hexstring or the number + * of columns remaining. + */ +static int +_word_wrap_stream_count_hexstring_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 (*s == '>') { + stream->state = WRAP_STATE_DELIMITER; + break; + } + + if (stream->column > stream->max_column) { + newline = TRUE; + break; + } + 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; +} + +/* 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 +_word_wrap_stream_write (cairo_output_stream_t *base, + const unsigned char *data, + unsigned int length) +{ + word_wrap_stream_t *stream = (word_wrap_stream_t *) base; + int count; + + while (length) { + 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++; + if (*data == '\n' || stream->column >= stream->max_column) { + _cairo_output_stream_printf (stream->output, "\n"); + 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; + } + 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); +} + +static cairo_status_t +_word_wrap_stream_close (cairo_output_stream_t *base) +{ + word_wrap_stream_t *stream = (word_wrap_stream_t *) base; + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_output_stream_t * +_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 = _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; + } + + _cairo_output_stream_init (&stream->base, + _word_wrap_stream_write, + NULL, + _word_wrap_stream_close); + stream->output = output; + stream->max_column = max_column; + stream->ps_output = ps; + stream->column = 0; + stream->state = WRAP_STATE_DELIMITER; + stream->in_escape = FALSE; + stream->escape_digits = 0; + + return &stream->base; +} + +typedef struct _pdf_path_info { + cairo_output_stream_t *output; + cairo_matrix_t *path_transform; + cairo_line_cap_t line_cap; + cairo_point_t last_move_to_point; + cairo_bool_t has_sub_path; +} pdf_path_info_t; + +static cairo_status_t +_cairo_pdf_path_move_to (void *closure, + const cairo_point_t *point) +{ + pdf_path_info_t *info = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + info->last_move_to_point = *point; + info->has_sub_path = FALSE; + cairo_matrix_transform_point (info->path_transform, &x, &y); + _cairo_output_stream_printf (info->output, + "%g %g m ", x, y); + + return _cairo_output_stream_get_status (info->output); +} + +static cairo_status_t +_cairo_pdf_path_line_to (void *closure, + const cairo_point_t *point) +{ + pdf_path_info_t *info = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (info->line_cap != CAIRO_LINE_CAP_ROUND && + ! info->has_sub_path && + point->x == info->last_move_to_point.x && + point->y == info->last_move_to_point.y) + { + return CAIRO_STATUS_SUCCESS; + } + + info->has_sub_path = TRUE; + cairo_matrix_transform_point (info->path_transform, &x, &y); + _cairo_output_stream_printf (info->output, + "%g %g l ", x, y); + + return _cairo_output_stream_get_status (info->output); +} + +static cairo_status_t +_cairo_pdf_path_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + pdf_path_info_t *info = closure; + double bx = _cairo_fixed_to_double (b->x); + double by = _cairo_fixed_to_double (b->y); + double cx = _cairo_fixed_to_double (c->x); + double cy = _cairo_fixed_to_double (c->y); + double dx = _cairo_fixed_to_double (d->x); + double dy = _cairo_fixed_to_double (d->y); + + info->has_sub_path = TRUE; + cairo_matrix_transform_point (info->path_transform, &bx, &by); + cairo_matrix_transform_point (info->path_transform, &cx, &cy); + cairo_matrix_transform_point (info->path_transform, &dx, &dy); + _cairo_output_stream_printf (info->output, + "%g %g %g %g %g %g c ", + bx, by, cx, cy, dx, dy); + return _cairo_output_stream_get_status (info->output); +} + +static cairo_status_t +_cairo_pdf_path_close_path (void *closure) +{ + pdf_path_info_t *info = closure; + + if (info->line_cap != CAIRO_LINE_CAP_ROUND && + ! info->has_sub_path) + { + return CAIRO_STATUS_SUCCESS; + } + + _cairo_output_stream_printf (info->output, + "h\n"); + + return _cairo_output_stream_get_status (info->output); +} + +static cairo_status_t +_cairo_pdf_path_rectangle (pdf_path_info_t *info, cairo_box_t *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); + + cairo_matrix_transform_point (info->path_transform, &x1, &y1); + cairo_matrix_transform_point (info->path_transform, &x2, &y2); + _cairo_output_stream_printf (info->output, + "%g %g %g %g re ", + x1, y1, x2 - x1, y2 - y1); + + return _cairo_output_stream_get_status (info->output); +} + +/* The line cap value is needed to workaround the fact that PostScript + * and PDF semantics for stroking degenerate sub-paths do not match + * cairo semantics. (PostScript draws something for any line cap + * value, while cairo draws something only for round caps). + * + * When using this function to emit a path to be filled, rather than + * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that + * the stroke workaround will not modify the path being emitted. + */ +static cairo_status_t +_cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, + const cairo_path_fixed_t*path, + cairo_matrix_t *path_transform, + cairo_line_cap_t line_cap) +{ + cairo_output_stream_t *word_wrap; + cairo_status_t status, status2; + pdf_path_info_t info; + cairo_box_t box; + + 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); + + info.output = word_wrap; + info.path_transform = path_transform; + info.line_cap = line_cap; + 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_pdf_path_move_to, + _cairo_pdf_path_line_to, + _cairo_pdf_path_curve_to, + _cairo_pdf_path_close_path, + &info); + } + + status2 = _cairo_output_stream_destroy (word_wrap); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +cairo_int_status_t +_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule) +{ + const char *pdf_operator; + cairo_status_t status; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + if (! path->has_current_point) { + /* construct an empty path */ + _cairo_output_stream_printf (pdf_operators->stream, "0 0 m "); + } else { + status = _cairo_pdf_operators_emit_path (pdf_operators, + path, + &pdf_operators->cairo_to_pdf, + CAIRO_LINE_CAP_ROUND); + if (unlikely (status)) + return status; + } + + switch (fill_rule) { + default: + ASSERT_NOT_REACHED; + case CAIRO_FILL_RULE_WINDING: + pdf_operator = "W"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + pdf_operator = "W*"; + break; + } + + _cairo_output_stream_printf (pdf_operators->stream, + "%s n\n", + pdf_operator); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static int +_cairo_pdf_line_cap (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: + return 0; + case CAIRO_LINE_CAP_ROUND: + return 1; + case CAIRO_LINE_CAP_SQUARE: + return 2; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static int +_cairo_pdf_line_join (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: + return 0; + case CAIRO_LINE_JOIN_ROUND: + return 1; + case CAIRO_LINE_JOIN_BEVEL: + return 2; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +cairo_int_status_t +_cairo_pdf_operators_emit_stroke_style (cairo_pdf_operators_t *pdf_operators, + const cairo_stroke_style_t *style, + double scale) +{ + double *dash = style->dash; + int num_dashes = style->num_dashes; + double dash_offset = style->dash_offset; + double line_width = style->line_width * scale; + + /* PostScript has "special needs" when it comes to zero-length + * dash segments with butt caps. It apparently (at least + * according to ghostscript) draws hairlines for this + * case. That's not what the cairo semantics want, so we first + * touch up the array to eliminate any 0.0 values that will + * result in "on" segments. + */ + if (num_dashes && style->line_cap == CAIRO_LINE_CAP_BUTT) { + int i; + + /* If there's an odd number of dash values they will each get + * interpreted as both on and off. So we first explicitly + * expand the array to remove the duplicate usage so that we + * can modify some of the values. + */ + if (num_dashes % 2) { + dash = _cairo_malloc_abc (num_dashes, 2, sizeof (double)); + if (unlikely (dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (dash, style->dash, num_dashes * sizeof (double)); + memcpy (dash + num_dashes, style->dash, num_dashes * sizeof (double)); + + num_dashes *= 2; + } + + for (i = 0; i < num_dashes; i += 2) { + if (dash[i] == 0.0) { + /* Do not modify the dashes in-place, as we may need to also + * replay this stroke to an image fallback. + */ + if (dash == style->dash) { + dash = _cairo_malloc_ab (num_dashes, sizeof (double)); + if (unlikely (dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + memcpy (dash, style->dash, num_dashes * sizeof (double)); + } + + /* If we're at the front of the list, we first rotate + * two elements from the end of the list to the front + * of the list before folding away the 0.0. Or, if + * there are only two dash elements, then there is + * nothing at all to draw. + */ + if (i == 0) { + double last_two[2]; + + if (num_dashes == 2) { + free (dash); + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + /* The cases of num_dashes == 0, 1, or 3 elements + * cannot exist, so the rotation of 2 elements + * will always be safe */ + memcpy (last_two, dash + num_dashes - 2, sizeof (last_two)); + memmove (dash + 2, dash, (num_dashes - 2) * sizeof (double)); + memcpy (dash, last_two, sizeof (last_two)); + dash_offset += dash[0] + dash[1]; + i = 2; + } + dash[i-1] += dash[i+1]; + num_dashes -= 2; + memmove (dash + i, dash + i + 2, (num_dashes - i) * sizeof (double)); + /* If we might have just rotated, it's possible that + * we rotated a 0.0 value to the front of the list. + * Set i to -2 so it will get incremented to 0. */ + if (i == 2) + i = -2; + } + } + } + + if (!pdf_operators->has_line_style || pdf_operators->line_width != line_width) { + _cairo_output_stream_printf (pdf_operators->stream, + "%f w\n", + line_width); + pdf_operators->line_width = line_width; + } + + if (!pdf_operators->has_line_style || pdf_operators->line_cap != style->line_cap) { + _cairo_output_stream_printf (pdf_operators->stream, + "%d J\n", + _cairo_pdf_line_cap (style->line_cap)); + pdf_operators->line_cap = style->line_cap; + } + + if (!pdf_operators->has_line_style || pdf_operators->line_join != style->line_join) { + _cairo_output_stream_printf (pdf_operators->stream, + "%d j\n", + _cairo_pdf_line_join (style->line_join)); + pdf_operators->line_join = style->line_join; + } + + if (num_dashes) { + int d; + + _cairo_output_stream_printf (pdf_operators->stream, "["); + for (d = 0; d < num_dashes; d++) + _cairo_output_stream_printf (pdf_operators->stream, " %f", dash[d] * scale); + _cairo_output_stream_printf (pdf_operators->stream, "] %f d\n", + dash_offset * scale); + pdf_operators->has_dashes = TRUE; + } else if (!pdf_operators->has_line_style || pdf_operators->has_dashes) { + _cairo_output_stream_printf (pdf_operators->stream, "[] 0.0 d\n"); + pdf_operators->has_dashes = FALSE; + } + if (dash != style->dash) + free (dash); + + if (!pdf_operators->has_line_style || pdf_operators->miter_limit != style->miter_limit) { + _cairo_output_stream_printf (pdf_operators->stream, + "%f M ", + style->miter_limit < 1.0 ? 1.0 : style->miter_limit); + pdf_operators->miter_limit = style->miter_limit; + } + pdf_operators->has_line_style = TRUE; + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +/* Scale the matrix so the largest absolute value of the non + * translation components is 1.0. Return the scale required to restore + * the matrix to the original values. + * + * eg the matrix [ 100 0 0 50 20 10 ] + * + * is rescaled to [ 1 0 0 0.5 0.2 0.1 ] + * and the scale returned is 100 + */ +static void +_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) +{ + double s; + + 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); + *scale = s; + s = 1.0/s; + cairo_matrix_scale (m, s, s); +} + +static cairo_int_status_t +_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, + 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_int_status_t status; + cairo_matrix_t m, path_transform; + cairo_bool_t has_ctm = TRUE; + double scale = 1.0; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + /* Optimize away the stroke ctm when it does not affect the + * stroke. There are other ctm cases that could be optimized + * however this is the most common. + */ + if (fabs(ctm->xx) == 1.0 && fabs(ctm->yy) == 1.0 && + fabs(ctm->xy) == 0.0 && fabs(ctm->yx) == 0.0) + { + has_ctm = FALSE; + } + + /* The PDF CTM is transformed to the user space CTM when stroking + * so the correct pen shape will be used. This also requires that + * the path be transformed to user space when emitted. The + * conversion of path coordinates to user space may cause rounding + * errors. For example the device space point (1.234, 3.142) when + * transformed to a user space CTM of [100 0 0 100 0 0] will be + * emitted as (0.012, 0.031). + * + * To avoid the rounding problem we scale the user space CTM + * matrix so that all the non translation components of the matrix + * are <= 1. The line width and and dashes are scaled by the + * inverse of the scale applied to the CTM. This maintains the + * shape of the stroke pen while keeping the user space CTM within + * the range that maximizes the precision of the emitted path. + */ + if (has_ctm) { + m = *ctm; + /* Zero out the translation since it does not affect the pen + * shape however it may cause unnecessary digits to be emitted. + */ + m.x0 = 0.0; + m.y0 = 0.0; + _cairo_matrix_factor_out_scale (&m, &scale); + path_transform = m; + status = cairo_matrix_invert (&path_transform); + if (unlikely (status)) + return status; + + cairo_matrix_multiply (&m, &m, &pdf_operators->cairo_to_pdf); + } + + status = _cairo_pdf_operators_emit_stroke_style (pdf_operators, style, scale); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + + if (has_ctm) { + _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; + } + + status = _cairo_pdf_operators_emit_path (pdf_operators, + path, + &path_transform, + style->line_cap); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (pdf_operators->stream, "%s", pdf_operator); + if (has_ctm) + _cairo_output_stream_printf (pdf_operators->stream, " Q"); + + _cairo_output_stream_printf (pdf_operators->stream, "\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +cairo_int_status_t +_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse) +{ + return _cairo_pdf_operators_emit_stroke (pdf_operators, + path, + style, + ctm, + ctm_inverse, + "S"); +} + +cairo_int_status_t +_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule) +{ + const char *pdf_operator; + cairo_status_t status; + + if (pdf_operators->in_text_object) { + status = _cairo_pdf_operators_end_text (pdf_operators); + if (unlikely (status)) + return status; + } + + status = _cairo_pdf_operators_emit_path (pdf_operators, + path, + &pdf_operators->cairo_to_pdf, + CAIRO_LINE_CAP_ROUND); + if (unlikely (status)) + return status; + + switch (fill_rule) { + default: + ASSERT_NOT_REACHED; + case CAIRO_FILL_RULE_WINDING: + pdf_operator = "f"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + pdf_operator = "f*"; + break; + } + + _cairo_output_stream_printf (pdf_operators->stream, + "%s\n", + pdf_operator); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +cairo_int_status_t +_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse) +{ + const char *operator; + + switch (fill_rule) { + default: + ASSERT_NOT_REACHED; + case CAIRO_FILL_RULE_WINDING: + operator = "B"; + break; + case CAIRO_FILL_RULE_EVEN_ODD: + operator = "B*"; + break; + } + + return _cairo_pdf_operators_emit_stroke (pdf_operators, + path, + style, + ctm, + ctm_inverse, + 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 + * that the glyphs are positioned at their natural glyph advances. */ +static cairo_status_t +_cairo_pdf_operators_emit_glyph_string (cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream) +{ + int i; + + _cairo_output_stream_printf (stream, "%s", pdf_operators->is_latin ? "(" : "<"); + for (i = 0; i < pdf_operators->num_glyphs; i++) { + _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, "%sTj\n", pdf_operators->is_latin ? ")" : ">"); + + return _cairo_output_stream_get_status (stream); +} + +/* Emit the string of glyphs using the 'TJ' operator. + * + * The TJ operator takes an array of strings of glyphs. Each string of + * glyphs is displayed using the glyph advances of each glyph to + * position the glyphs. A relative adjustment to the glyph advance may + * be specified by including the adjustment between two strings. The + * adjustment is in units of text space * -1000. + */ +static cairo_status_t +_cairo_pdf_operators_emit_glyph_string_with_positioning ( + cairo_pdf_operators_t *pdf_operators, + cairo_output_stream_t *stream) +{ + int i; + + _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) + { + double delta = pdf_operators->glyphs[i].x_position - pdf_operators->cur_x; + int rounded_delta; + + delta = -1000.0*delta; + /* As the delta is in 1/1000 of a unit of text space, + * rounding to an integer should still provide sufficient + * precision. We round the delta before adding to Tm_x so + * that we keep track of the accumulated rounding error in + * the PDF interpreter and compensate for it when + * calculating subsequent deltas. + */ + rounded_delta = _cairo_lround (delta); + if (abs(rounded_delta) < 3) + rounded_delta = 0; + if (rounded_delta != 0) { + 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 + * space before adding to the current text + * position. */ + delta = rounded_delta/-1000.0; + pdf_operators->cur_x += delta; + } + + _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, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">"); + + return _cairo_output_stream_get_status (stream); +} + +static cairo_status_t +_cairo_pdf_operators_flush_glyphs (cairo_pdf_operators_t *pdf_operators) +{ + cairo_output_stream_t *word_wrap_stream; + cairo_status_t status, status2; + int i; + double x; + + if (pdf_operators->num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + 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); + + /* Check if glyph advance used to position every glyph */ + x = pdf_operators->cur_x; + for (i = 0; i < pdf_operators->num_glyphs; i++) { + if (fabs(pdf_operators->glyphs[i].x_position - x) > GLYPH_POSITION_TOLERANCE) + break; + x += pdf_operators->glyphs[i].x_advance; + } + if (i == pdf_operators->num_glyphs) { + status = _cairo_pdf_operators_emit_glyph_string (pdf_operators, + word_wrap_stream); + } else { + status = _cairo_pdf_operators_emit_glyph_string_with_positioning ( + pdf_operators, word_wrap_stream); + } + + pdf_operators->num_glyphs = 0; + pdf_operators->glyph_buf_x_pos = pdf_operators->cur_x; + status2 = _cairo_output_stream_destroy (word_wrap_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +static cairo_status_t +_cairo_pdf_operators_add_glyph (cairo_pdf_operators_t *pdf_operators, + cairo_scaled_font_subsets_glyph_t *glyph, + double x_position) +{ + double x, y; + + x = glyph->x_advance; + y = glyph->y_advance; + if (glyph->is_scaled) + cairo_matrix_transform_distance (&pdf_operators->font_matrix_inverse, &x, &y); + + pdf_operators->glyphs[pdf_operators->num_glyphs].x_position = x_position; + pdf_operators->glyphs[pdf_operators->num_glyphs].glyph_index = glyph->subset_glyph_index; + pdf_operators->glyphs[pdf_operators->num_glyphs].x_advance = x; + pdf_operators->glyph_buf_x_pos += x; + pdf_operators->num_glyphs++; + if (pdf_operators->num_glyphs == PDF_GLYPH_BUFFER_SIZE) + return _cairo_pdf_operators_flush_glyphs (pdf_operators); + + return CAIRO_STATUS_SUCCESS; +} + +/* Use 'Tm' operator to set the PDF text matrix. */ +static cairo_status_t +_cairo_pdf_operators_set_text_matrix (cairo_pdf_operators_t *pdf_operators, + cairo_matrix_t *matrix) +{ + cairo_matrix_t inverse; + cairo_status_t status; + + /* We require the matrix to be invertable. */ + inverse = *matrix; + status = cairo_matrix_invert (&inverse); + if (unlikely (status)) + return status; + + pdf_operators->text_matrix = *matrix; + pdf_operators->cur_x = 0; + pdf_operators->cur_y = 0; + pdf_operators->glyph_buf_x_pos = 0; + _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); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, + &pdf_operators->cairo_to_pdf, + &pdf_operators->cairo_to_pdftext); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +#define TEXT_MATRIX_TOLERANCE 1e-6 + +/* Set the translation components of the PDF text matrix to x, y. The + * 'Td' operator is used to transform the text matrix. + */ +static cairo_status_t +_cairo_pdf_operators_set_text_position (cairo_pdf_operators_t *pdf_operators, + double x, + double y) +{ + cairo_matrix_t translate, inverse; + cairo_status_t status; + + /* The Td operator transforms the text_matrix with: + * + * text_matrix' = T x text_matrix + * + * where T is a translation matrix with the translation components + * set to the Td operands tx and ty. + */ + inverse = pdf_operators->text_matrix; + status = cairo_matrix_invert (&inverse); + assert (status == CAIRO_STATUS_SUCCESS); + pdf_operators->text_matrix.x0 = x; + pdf_operators->text_matrix.y0 = y; + cairo_matrix_multiply (&translate, &pdf_operators->text_matrix, &inverse); + if (fabs(translate.x0) < TEXT_MATRIX_TOLERANCE) + translate.x0 = 0.0; + if (fabs(translate.y0) < TEXT_MATRIX_TOLERANCE) + translate.y0 = 0.0; + _cairo_output_stream_printf (pdf_operators->stream, + "%f %f Td\n", + translate.x0, + translate.y0); + pdf_operators->cur_x = 0; + pdf_operators->cur_y = 0; + pdf_operators->glyph_buf_x_pos = 0; + + pdf_operators->cairo_to_pdftext = pdf_operators->text_matrix; + status = cairo_matrix_invert (&pdf_operators->cairo_to_pdftext); + assert (status == CAIRO_STATUS_SUCCESS); + cairo_matrix_multiply (&pdf_operators->cairo_to_pdftext, + &pdf_operators->cairo_to_pdf, + &pdf_operators->cairo_to_pdftext); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +/* Select the font using the 'Tf' operator. The font size is set to 1 + * as we use the 'Tm' operator to set the font scale. + */ +static cairo_status_t +_cairo_pdf_operators_set_font_subset (cairo_pdf_operators_t *pdf_operators, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + cairo_status_t status; + + _cairo_output_stream_printf (pdf_operators->stream, + "/f-%d-%d 1 Tf\n", + subset_glyph->font_id, + subset_glyph->subset_id); + if (pdf_operators->use_font_subset) { + status = pdf_operators->use_font_subset (subset_glyph->font_id, + subset_glyph->subset_id, + pdf_operators->use_font_subset_closure); + if (unlikely (status)) + return status; + } + 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; + else + pdf_operators->hex_width = 2; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_pdf_operators_begin_text (cairo_pdf_operators_t *pdf_operators) +{ + _cairo_output_stream_printf (pdf_operators->stream, "BT\n"); + + pdf_operators->in_text_object = TRUE; + pdf_operators->num_glyphs = 0; + pdf_operators->glyph_buf_x_pos = 0; + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static cairo_status_t +_cairo_pdf_operators_end_text (cairo_pdf_operators_t *pdf_operators) +{ + cairo_status_t status; + + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (pdf_operators->stream, "ET\n"); + + pdf_operators->in_text_object = FALSE; + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +/* Compare the scale components of two matrices. The translation + * components are ignored. */ +static cairo_bool_t +_cairo_matrix_scale_equal (cairo_matrix_t *a, cairo_matrix_t *b) +{ + return (a->xx == b->xx && + a->xy == b->xy && + a->yx == b->yx && + a->yy == b->yy); +} + +static cairo_status_t +_cairo_pdf_operators_begin_actualtext (cairo_pdf_operators_t *pdf_operators, + const char *utf8, + int utf8_len) +{ + uint16_t *utf16; + int utf16_len; + cairo_status_t status; + int i; + + _cairo_output_stream_printf (pdf_operators->stream, "/Span << /ActualText stream, + "%04x", (int) (utf16[i])); + } + free (utf16); + } + _cairo_output_stream_printf (pdf_operators->stream, "> >> BDC\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static cairo_status_t +_cairo_pdf_operators_end_actualtext (cairo_pdf_operators_t *pdf_operators) +{ + _cairo_output_stream_printf (pdf_operators->stream, "EMC\n"); + + return _cairo_output_stream_get_status (pdf_operators->stream); +} + +static cairo_status_t +_cairo_pdf_operators_emit_glyph (cairo_pdf_operators_t *pdf_operators, + cairo_glyph_t *glyph, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + double x, y; + cairo_status_t status; + + if (pdf_operators->is_new_text_object || + pdf_operators->font_id != subset_glyph->font_id || + pdf_operators->subset_id != subset_glyph->subset_id) + { + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_set_font_subset (pdf_operators, subset_glyph); + if (unlikely (status)) + return status; + + pdf_operators->is_new_text_object = FALSE; + } + + x = glyph->x; + y = glyph->y; + cairo_matrix_transform_point (&pdf_operators->cairo_to_pdftext, &x, &y); + + /* The TJ operator for displaying text strings can only set + * the horizontal position of the glyphs. If the y position + * (in text space) changes, use the Td operator to change the + * 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 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. + */ + if (fabs(x - pdf_operators->glyph_buf_x_pos) > 10 || + fabs(y - pdf_operators->cur_y) > GLYPH_POSITION_TOLERANCE) + { + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + x = glyph->x; + y = glyph->y; + cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); + status = _cairo_pdf_operators_set_text_position (pdf_operators, x, y); + if (unlikely (status)) + return status; + + x = 0.0; + y = 0.0; + } + + status = _cairo_pdf_operators_add_glyph (pdf_operators, + subset_glyph, + x); + return status; +} + +/* A utf8_len of -1 indicates no unicode text. A utf8_len = 0 is an + * empty string. + */ +static cairo_int_status_t +_cairo_pdf_operators_emit_cluster (cairo_pdf_operators_t *pdf_operators, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_cluster_flags_t cluster_flags, + cairo_scaled_font_t *scaled_font) +{ + cairo_scaled_font_subsets_glyph_t subset_glyph; + cairo_glyph_t *cur_glyph; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + int i; + + /* If the cluster maps 1 glyph to 1 or more unicode characters, we + * first try _map_glyph() with the unicode string to see if it can + * use toUnicode to map our glyph to the unicode. This will fail + * if the glyph is already mapped to a different unicode string. + * + * We also go through this path if no unicode mapping was + * supplied (utf8_len < 0). + * + * Mapping a glyph to a zero length unicode string requires the + * use of ActualText. + */ + if (num_glyphs == 1 && utf8_len != 0) { + status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, + scaled_font, + glyphs->index, + utf8, + utf8_len, + &subset_glyph); + if (unlikely (status)) + return status; + + if (subset_glyph.utf8_is_mapped || utf8_len < 0) { + status = _cairo_pdf_operators_emit_glyph (pdf_operators, + glyphs, + &subset_glyph); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } + } + + 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; + } + + if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD) + cur_glyph = glyphs + num_glyphs - 1; + else + 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++) { + status = _cairo_scaled_font_subsets_map_glyph (pdf_operators->font_subsets, + scaled_font, + cur_glyph->index, + NULL, -1, + &subset_glyph); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_emit_glyph (pdf_operators, + cur_glyph, + &subset_glyph); + if (unlikely (status)) + return status; + + if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) + cur_glyph--; + else + cur_glyph++; + } + + 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; +} + +cairo_int_status_t +_cairo_pdf_operators_show_text_glyphs (cairo_pdf_operators_t *pdf_operators, + 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_status_t status; + int i; + cairo_matrix_t text_matrix, invert_y_axis; + double x, y; + const char *cur_text; + cairo_glyph_t *cur_glyph; + + pdf_operators->font_matrix_inverse = scaled_font->font_matrix; + status = cairo_matrix_invert (&pdf_operators->font_matrix_inverse); + if (status == CAIRO_STATUS_INVALID_MATRIX) + return CAIRO_STATUS_SUCCESS; + assert (status == CAIRO_STATUS_SUCCESS); + + pdf_operators->is_new_text_object = FALSE; + if (pdf_operators->in_text_object == FALSE) { + status = _cairo_pdf_operators_begin_text (pdf_operators); + if (unlikely (status)) + return status; + + /* Force Tm and Tf to be emitted when starting a new text + * object.*/ + pdf_operators->is_new_text_object = TRUE; + } + + cairo_matrix_init_scale (&invert_y_axis, 1, -1); + text_matrix = scaled_font->scale; + + /* Invert y axis in device space */ + cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix); + + if (pdf_operators->is_new_text_object || + ! _cairo_matrix_scale_equal (&pdf_operators->text_matrix, &text_matrix)) + { + status = _cairo_pdf_operators_flush_glyphs (pdf_operators); + if (unlikely (status)) + return status; + + x = glyphs[0].x; + y = glyphs[0].y; + cairo_matrix_transform_point (&pdf_operators->cairo_to_pdf, &x, &y); + text_matrix.x0 = x; + text_matrix.y0 = y; + status = _cairo_pdf_operators_set_text_matrix (pdf_operators, &text_matrix); + if (status == CAIRO_STATUS_INVALID_MATRIX) + return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + } + + if (num_clusters > 0) { + cur_text = utf8; + if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) + cur_glyph = glyphs + num_glyphs; + else + cur_glyph = glyphs; + for (i = 0; i < num_clusters; i++) { + if ((cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) + cur_glyph -= clusters[i].num_glyphs; + status = _cairo_pdf_operators_emit_cluster (pdf_operators, + cur_text, + clusters[i].num_bytes, + cur_glyph, + clusters[i].num_glyphs, + cluster_flags, + scaled_font); + if (unlikely (status)) + return status; + + cur_text += clusters[i].num_bytes; + if (!(cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)) + cur_glyph += clusters[i].num_glyphs; + } + } else { + for (i = 0; i < num_glyphs; i++) { + status = _cairo_pdf_operators_emit_cluster (pdf_operators, + NULL, + -1, /* no unicode string available */ + &glyphs[i], + 1, + FALSE, + scaled_font); + if (unlikely (status)) + return status; + } + } + + 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 0000000000..0ca8cb7d51 --- /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 0000000000..b114b3104b --- /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 new file mode 100644 index 0000000000..b2d8575507 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf-surface-private.h @@ -0,0 +1,423 @@ +/* -*- 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 + * Copyright © 2006 Red Hat, Inc + * Copyright © 2007, 2008 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * 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): + * Kristian Høgsberg + * Carl Worth + * Adrian Johnson + */ + +#ifndef CAIRO_PDF_SURFACE_PRIVATE_H +#define CAIRO_PDF_SURFACE_PRIVATE_H + +#include "cairo-pdf.h" + +#include "cairo-surface-private.h" +#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; +} cairo_pdf_resource_t; + + +#define CAIRO_NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY + 1) + +typedef struct _cairo_pdf_group_resources { + cairo_bool_t operators[CAIRO_NUM_OPERATORS]; + 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; + +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; + 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; + +typedef struct _cairo_pdf_pattern { + double width; + double height; + cairo_rectangle_int_t extents; + cairo_pattern_t *pattern; + cairo_pdf_resource_t pattern_res; + cairo_pdf_resource_t gstate_res; + cairo_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 { + PDF_PAINT, + PDF_MASK, + PDF_FILL, + PDF_STROKE, + PDF_SHOW_GLYPHS +} cairo_pdf_operation_t; + +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; + cairo_pdf_resource_t source_res; + cairo_pattern_t *mask; + cairo_path_fixed_t path; + cairo_fill_rule_t fill_rule; + cairo_stroke_style_t style; + cairo_matrix_t ctm; + cairo_matrix_t ctm_inverse; + char *utf8; + int utf8_len; + cairo_glyph_t *glyphs; + int num_glyphs; + cairo_text_cluster_t *clusters; + int num_clusters; + cairo_bool_t cluster_flags; + 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; + +typedef struct _cairo_pdf_forward_link { + cairo_pdf_resource_t res; + char *dest; + int page; + cairo_bool_t has_pos; + cairo_point_double_t pos; +} cairo_pdf_forward_link_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 { + cairo_surface_t base; + + /* Prefer the name "output" here to avoid confusion over the + * structure within a PDF document known as a "stream". */ + cairo_output_stream_t *output; + + 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; + cairo_array_t rgb_linear_functions; + cairo_array_t alpha_linear_functions; + cairo_array_t page_patterns; + cairo_array_t page_surfaces; + cairo_array_t doc_surfaces; + cairo_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_streams; + + cairo_pdf_resource_t content; + cairo_pdf_resource_t content_resources; + cairo_pdf_group_resources_t resources; + cairo_bool_t has_fallback_images; + cairo_bool_t header_emitted; + + struct { + cairo_bool_t active; + cairo_pdf_resource_t self; + cairo_pdf_resource_t length; + long start_offset; + cairo_bool_t compressed; + cairo_output_stream_t *old_output; + } pdf_stream; + + struct { + cairo_bool_t active; + cairo_output_stream_t *stream; + 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; + + struct { + cairo_bool_t active; + cairo_output_stream_t *stream; + cairo_pdf_resource_t resource; + cairo_array_t objects; + } object_stream; + + cairo_surface_clipper_t clipper; + + cairo_pdf_operators_t pdf_operators; + cairo_paginated_mode_t paginated_mode; + cairo_bool_t select_pattern_gstate_saved; + + cairo_bool_t force_fallbacks; + + cairo_operator_t current_operator; + cairo_bool_t current_pattern_is_solid_color; + cairo_bool_t current_color_is_stroke; + double current_color_red; + double current_color_green; + 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_array_t forward_links; + 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 void +_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_surface_object_begin (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t resource); + +cairo_private void +_cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface); + +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 new file mode 100644 index 0000000000..9496941113 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf-surface.c @@ -0,0 +1,8964 @@ +/* -*- 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 + * Copyright © 2006 Red Hat, Inc + * Copyright © 2007, 2008 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * 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): + * Kristian Høgsberg + * Carl Worth + * Adrian Johnson + */ + +#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 + +/* + * Page Structure of the Generated PDF: + * + * Each page requiring fallbacks images contains a knockout group at + * the top level. The first operation of the knockout group paints a + * group containing all the supported drawing operations. Fallback + * images (if any) are painted in the knockout group. This ensures + * that fallback images do not composite with any content under the + * fallback images. + * + * Streams: + * + * This PDF surface has three types of streams: + * - PDF Stream + * - Content Stream + * - Group Stream + * - Object stream + * + * Calling _cairo_output_stream_printf (surface->output, ...) will + * write to the currently open stream. + * + * PDF Stream: + * A PDF Stream may be opened and closed with the following functions: + * _cairo_pdf_surface_open stream () + * _cairo_pdf_surface_close_stream () + * + * PDF Streams are written directly to the PDF file. They are used for + * fonts, images and patterns. + * + * Content Stream: + * The Content Stream is opened and closed with the following functions: + * _cairo_pdf_surface_open_content_stream () + * _cairo_pdf_surface_close_content_stream () + * + * The Content Stream contains the text and graphics operators. + * + * Group Stream: + * A Group Stream may be opened and closed with the following functions: + * _cairo_pdf_surface_open_group () + * _cairo_pdf_surface_close_group () + * + * A Group Stream is a Form XObject. It is used for short sequences + * of operators. As the content is very short the group is stored in + * memory until it is closed. This allows some optimization such as + * including the Resource dictionary and stream length inside the + * XObject instead of using an indirect object. + * + * Object Stream (PDF 1.5) + * An Object Stream may be opened and closed with the following functions: + * _cairo_pdf_surface_open_object_stream () + * _cairo_pdf_surface_close_object_stream () + * + * An Object Stream contains one or more objects compressed into a stream. + * Only non stream objects are permitted. When emitting objects intended for + * the Object Stream, enclose the emit object operation with + * _cairo_pdf_surface_object_begin()/_cairo_pdf_surface_object_end(). + */ + +/** + * SECTION:cairo-pdf + * @Title: PDF Surfaces + * @Short_Description: Rendering PDF documents + * @See_Also: #cairo_surface_t + * + * 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[] = +{ + CAIRO_PDF_VERSION_1_4, + CAIRO_PDF_VERSION_1_5 +}; + +#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions) + +static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] = +{ + "PDF 1.4", + "PDF 1.5" +}; + +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 +}; + +/* PDF cross-reference stream types */ +typedef enum { + PDF_OBJECT_FREE = 0, + PDF_OBJECT_UNCOMPRESSED = 1, + PDF_OBJECT_COMPRESSED = 2, +} cairo_pdf_object_type_t; + +typedef struct _cairo_pdf_object { + cairo_pdf_object_type_t type; + union { + long offset; /* type == PDF_OBJECT_UNCOMPRESSED */ + struct compressed_obj { /* type == PDF_OBJECT_COMPRESSED */ + cairo_pdf_resource_t xref_stream; + int index; + } compressed_obj; + } u; +} cairo_pdf_object_t; + +typedef struct _cairo_xref_stream_object { + cairo_pdf_resource_t resource; + long offset; +} cairo_xref_stream_object_t; + +typedef struct _cairo_pdf_font { + unsigned int font_id; + unsigned int subset_id; + cairo_pdf_resource_t subset_resource; +} cairo_pdf_font_t; + +typedef struct _cairo_pdf_rgb_linear_function { + cairo_pdf_resource_t resource; + double color1[3]; + double color2[3]; +} cairo_pdf_rgb_linear_function_t; + +typedef struct _cairo_pdf_alpha_linear_function { + cairo_pdf_resource_t resource; + double alpha1; + double alpha2; +} cairo_pdf_alpha_linear_function_t; + +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_int_status_t +_cairo_pdf_surface_add_font (unsigned int font_id, + unsigned int subset_id, + void *closure); + +static void +_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *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, + const char *fmt, + ...) CAIRO_PRINTF_FORMAT(4, 5); +static cairo_int_status_t +_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface); + +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 cairo_int_status_t +_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface); + +static cairo_int_status_t +_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t catalog); + +static long +_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface); + +static cairo_int_status_t +_cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t xref_res, + cairo_pdf_resource_t root_res, + cairo_pdf_resource_t info_res, + long *xref_offset); + +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_emit_font_subsets (cairo_pdf_surface_t *surface); + +static cairo_bool_t +_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b); + +static const cairo_surface_backend_t cairo_pdf_surface_backend; +static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend; + +cairo_pdf_resource_t +_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface) +{ + cairo_pdf_resource_t resource; + cairo_int_status_t status; + cairo_pdf_object_t object; + + /* Default to Uncompressed. If this object is used with + * _cairo_pdf_surface_object_begin() and Object Streams are + * enabled it will be changed to Compressed. */ + object.type = PDF_OBJECT_UNCOMPRESSED; + object.u.offset = _cairo_output_stream_get_position (surface->output); + + status = _cairo_array_append (&surface->objects, &object); + if (unlikely (status)) { + resource.id = 0; + return resource; + } + + resource = surface->next_available_resource; + surface->next_available_resource.id++; + + return resource; +} + +void +_cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t resource) +{ + cairo_pdf_object_t *object; + + object = _cairo_array_index (&surface->objects, resource.id - 1); + object->u.offset = _cairo_output_stream_get_position (surface->output); +} + +static void +_cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface, + double width, + double height) +{ + surface->width = width; + surface->height = height; + 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 +_path_covers_bbox (cairo_pdf_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + return _cairo_path_fixed_is_box (path, &box) && + box.p1.x <= 0 && + box.p1.y <= 0 && + box.p2.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y >= _cairo_fixed_from_double (surface->height); +} + +static cairo_status_t +_cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_pdf_surface_t *surface = cairo_container_of (clipper, + cairo_pdf_surface_t, + clipper); + cairo_int_status_t status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (path == NULL) { + _cairo_output_stream_printf (surface->output, "Q q\n"); + + surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return CAIRO_STATUS_SUCCESS; + } + + if (_path_covers_bbox (surface, path)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule); +} + +static cairo_surface_t * +_cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output, + double width, + double height) +{ + cairo_pdf_surface_t *surface; + cairo_status_t status, status_ignored; + + surface = _cairo_malloc (sizeof (cairo_pdf_surface_t)); + if (unlikely (surface == NULL)) { + /* destroy stream on behalf of caller */ + status = _cairo_output_stream_destroy (output); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&surface->base, + &cairo_pdf_surface_backend, + NULL, /* device */ + 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, 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)); + _cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t)); + _cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t)); + _cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t)); + _cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *)); + _cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t)); + + _cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t)); + _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); + goto BAIL0; + } + + _cairo_pdf_group_resources_init (&surface->resources); + + surface->font_subsets = _cairo_scaled_font_subsets_create_composite (); + if (! surface->font_subsets) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto 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) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL2; + } + + surface->struct_tree_root.id = 0; + surface->pdf_version = CAIRO_PDF_VERSION_1_5; + surface->compress_streams = TRUE; + surface->pdf_stream.active = FALSE; + surface->pdf_stream.old_output = NULL; + surface->group_stream.active = FALSE; + surface->group_stream.stream = NULL; + surface->group_stream.mem_stream = NULL; + surface->object_stream.active = FALSE; + surface->object_stream.stream = NULL; + _cairo_array_init (&surface->object_stream.objects, sizeof (cairo_xref_stream_object_t)); + + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + + surface->force_fallbacks = FALSE; + surface->select_pattern_gstate_saved = FALSE; + surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; + surface->header_emitted = FALSE; + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_pdf_surface_clipper_intersect_clip_path); + + _cairo_pdf_operators_init (&surface->pdf_operators, + surface->output, + &surface->cairo_to_pdf, + 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)); + _cairo_array_init (&surface->forward_links, sizeof (cairo_pdf_forward_link_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_streams = FALSE; + + surface->paginated_surface = _cairo_paginated_surface_create ( + &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, + &cairo_pdf_surface_paginated_backend); + + status = surface->paginated_surface->status; + if (status == CAIRO_STATUS_SUCCESS) { + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + return surface->paginated_surface; + } + +BAIL2: + _cairo_scaled_font_subsets_destroy (surface->font_subsets); +BAIL1: + _cairo_hash_table_destroy (surface->all_surfaces); +BAIL0: + _cairo_array_fini (&surface->objects); + free (surface); + + /* destroy stream on behalf of caller */ + status_ignored = _cairo_output_stream_destroy (output); + + return _cairo_surface_create_in_error (status); +} + +/** + * cairo_pdf_surface_create_for_stream: + * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL + * to indicate a no-op @write_func. With a no-op @write_func, + * the surface may be queried or used as a source without + * generating any temporary files. + * @closure: the closure argument for @write_func + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PDF surface of the specified size in points to be written + * incrementally to the stream represented by @write_func and @closure. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *output; + + output = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (output)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); + + return _cairo_pdf_surface_create_for_stream_internal (output, + width_in_points, + height_in_points); +} + +/** + * cairo_pdf_surface_create: + * @filename: a filename for the PDF output (must be writable), %NULL may be + * used to specify no output. This will generate a PDF surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PDF surface of the specified size in points to be written + * to @filename. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_pdf_surface_create (const char *filename, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *output; + + output = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (output)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output)); + + return _cairo_pdf_surface_create_for_stream_internal (output, + width_in_points, + height_in_points); +} + +static cairo_bool_t +_cairo_surface_is_pdf (cairo_surface_t *surface) +{ + return surface->backend == &cairo_pdf_surface_backend; +} + +/* If the abstract_surface is a paginated surface, and that paginated + * surface's target is a pdf_surface, then set pdf_surface to that + * target. Otherwise return FALSE. + */ +static cairo_bool_t +_extract_pdf_surface (cairo_surface_t *surface, + cairo_pdf_surface_t **pdf_surface) +{ + cairo_surface_t *target; + cairo_status_t status_ignored; + + if (surface->status) + return FALSE; + if (surface->finished) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_paginated (surface)) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + target = _cairo_paginated_surface_get_target (surface); + if (target->status) { + status_ignored = _cairo_surface_set_error (surface, + target->status); + return FALSE; + } + if (target->finished) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_pdf (target)) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + *pdf_surface = (cairo_pdf_surface_t *) target; + return TRUE; +} + +/** + * cairo_pdf_surface_restrict_to_version: + * @surface: a PDF #cairo_surface_t + * @version: PDF version + * + * Restricts the generated PDF file to @version. See cairo_pdf_get_versions() + * for a list of available version values that can be used here. + * + * This function should only be called before any drawing operations + * have been performed on the given surface. The simplest way to do + * this is to call this function immediately after creating the + * surface. + * + * Since: 1.10 + **/ +void +cairo_pdf_surface_restrict_to_version (cairo_surface_t *abstract_surface, + cairo_pdf_version_t version) +{ + cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */ + + if (! _extract_pdf_surface (abstract_surface, &surface)) + return; + + if (version < CAIRO_PDF_VERSION_LAST) + surface->pdf_version = version; + + _cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, + version >= CAIRO_PDF_VERSION_1_5); +} + +/** + * cairo_pdf_get_versions: + * @versions: supported version list + * @num_versions: list length + * + * Used to retrieve the list of supported versions. See + * cairo_pdf_surface_restrict_to_version(). + * + * Since: 1.10 + **/ +void +cairo_pdf_get_versions (cairo_pdf_version_t const **versions, + int *num_versions) +{ + if (versions != NULL) + *versions = _cairo_pdf_versions; + + if (num_versions != NULL) + *num_versions = CAIRO_PDF_VERSION_LAST; +} + +/** + * cairo_pdf_version_to_string: + * @version: a version id + * + * Get the string representation of the given @version id. This function + * will return %NULL if @version isn't valid. See cairo_pdf_get_versions() + * for a way to get the list of valid version ids. + * + * Return value: the string associated to given version. + * + * Since: 1.10 + **/ +const char * +cairo_pdf_version_to_string (cairo_pdf_version_t version) +{ + if (version >= CAIRO_PDF_VERSION_LAST) + return NULL; + + return _cairo_pdf_version_strings[version]; +} + +/** + * cairo_pdf_surface_set_size: + * @surface: a PDF #cairo_surface_t + * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) + * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) + * + * Changes the size of a PDF surface for the current (and + * subsequent) pages. + * + * This function should only be called before any drawing operations + * have been performed on the current page. The simplest way to do + * this is to call this function immediately after creating the + * surface or immediately after completing a page with either + * cairo_show_page() or cairo_copy_page(). + * + * Since: 1.2 + **/ +void +cairo_pdf_surface_set_size (cairo_surface_t *surface, + double width_in_points, + 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; + + _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 +_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface) +{ + int i, size; + cairo_pdf_pattern_t *pattern; + cairo_pdf_source_surface_t *src_surface; + cairo_pdf_smask_group_t *group; + + size = _cairo_array_num_elements (&surface->page_patterns); + for (i = 0; i < size; i++) { + pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i); + cairo_pattern_destroy (pattern->pattern); + } + _cairo_array_truncate (&surface->page_patterns, 0); + + size = _cairo_array_num_elements (&surface->page_surfaces); + for (i = 0; i < size; i++) { + src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i); + cairo_surface_destroy (src_surface->surface); + } + _cairo_array_truncate (&surface->page_surfaces, 0); + + size = _cairo_array_num_elements (&surface->smask_groups); + for (i = 0; i < size; i++) { + _cairo_array_copy_element (&surface->smask_groups, i, &group); + _cairo_pdf_smask_group_destroy (group); + } + _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 +_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res) +{ + int i; + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) + res->operators[i] = FALSE; + + _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)); +} + +static void +_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); +} + +static void +_cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res) +{ + int i; + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) + res->operators[i] = FALSE; + + _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); +} + +static void +_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface, + cairo_operator_t op) +{ + cairo_pdf_group_resources_t *res = &surface->resources; + + res->operators[op] = TRUE; +} + +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_int_status_t status; + cairo_pdf_group_resources_t *res = &surface->resources; + + num_alphas = _cairo_array_num_elements (&res->alphas); + for (i = 0; i < num_alphas; i++) { + _cairo_array_copy_element (&res->alphas, i, &other); + if (alpha == other) { + *index = i; + return CAIRO_STATUS_SUCCESS; + } + } + + status = _cairo_array_append (&res->alphas, &alpha); + if (unlikely (status)) + return status; + + *index = _cairo_array_num_elements (&res->alphas) - 1; + + return CAIRO_STATUS_SUCCESS; +} + +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_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_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_int_status_t +_cairo_pdf_surface_add_font (unsigned int font_id, + unsigned int subset_id, + void *closure) +{ + cairo_pdf_surface_t *surface = closure; + cairo_pdf_font_t font; + int num_fonts, i; + cairo_int_status_t status; + cairo_pdf_group_resources_t *res = &surface->resources; + + num_fonts = _cairo_array_num_elements (&res->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&res->fonts, i, &font); + if (font.font_id == font_id && + font.subset_id == subset_id) + return CAIRO_STATUS_SUCCESS; + } + + num_fonts = _cairo_array_num_elements (&surface->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&surface->fonts, i, &font); + if (font.font_id == font_id && + font.subset_id == subset_id) + return _cairo_array_append (&res->fonts, &font); + } + + font.font_id = font_id; + font.subset_id = subset_id; + font.subset_resource = _cairo_pdf_surface_new_object (surface); + if (font.subset_resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_array_append (&surface->fonts, &font); + if (unlikely (status)) + return status; + + return _cairo_array_append (&res->fonts, &font); +} + +static cairo_pdf_resource_t +_cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface, + unsigned int font_id, + unsigned int subset_id) +{ + cairo_pdf_font_t font; + int num_fonts, i; + + num_fonts = _cairo_array_num_elements (&surface->fonts); + for (i = 0; i < num_fonts; i++) { + _cairo_array_copy_element (&surface->fonts, i, &font); + if (font.font_id == font_id && font.subset_id == subset_id) + return font.subset_resource; + } + + font.subset_resource.id = 0; + return font.subset_resource; +} + +static const char * +_cairo_operator_to_pdf_blend_mode (cairo_operator_t op) +{ + switch (op) { + /* The extend blend mode operators */ + case CAIRO_OPERATOR_MULTIPLY: return "Multiply"; + case CAIRO_OPERATOR_SCREEN: return "Screen"; + case CAIRO_OPERATOR_OVERLAY: return "Overlay"; + case CAIRO_OPERATOR_DARKEN: return "Darken"; + case CAIRO_OPERATOR_LIGHTEN: return "Lighten"; + case CAIRO_OPERATOR_COLOR_DODGE: return "ColorDodge"; + case CAIRO_OPERATOR_COLOR_BURN: return "ColorBurn"; + case CAIRO_OPERATOR_HARD_LIGHT: return "HardLight"; + case CAIRO_OPERATOR_SOFT_LIGHT: return "SoftLight"; + case CAIRO_OPERATOR_DIFFERENCE: return "Difference"; + case CAIRO_OPERATOR_EXCLUSION: return "Exclusion"; + case CAIRO_OPERATOR_HSL_HUE: return "Hue"; + case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation"; + case CAIRO_OPERATOR_HSL_COLOR: return "Color"; + case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity"; + + default: + /* The original Porter-Duff set */ + 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: + case CAIRO_OPERATOR_SATURATE: + return "Normal"; + } +} + +static void +_cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface, + cairo_pdf_group_resources_t *res, + cairo_bool_t gs0) +{ + int num_alphas, num_smasks, num_resources, i; + double alpha; + cairo_pdf_resource_t *smask, *pattern, *shading, *xobject; + cairo_pdf_font_t *font; + + _cairo_output_stream_printf (surface->output, "<<\n"); + + num_alphas = _cairo_array_num_elements (&res->alphas); + num_smasks = _cairo_array_num_elements (&res->smasks); + if (num_alphas > 0 || num_smasks > 0) { + _cairo_output_stream_printf (surface->output, + " /ExtGState <<\n"); + + if (gs0) { + _cairo_output_stream_printf (surface->output, + " /gs0 << /BM /Normal /SMask /None /CA 1.0 /ca 1.0 >>\n"); + } + + for (i = 0; i < CAIRO_NUM_OPERATORS; i++) { + if (res->operators[i]) { + _cairo_output_stream_printf (surface->output, + " /b%d << /BM /%s >>\n", + i, _cairo_operator_to_pdf_blend_mode(i)); + } + } + + for (i = 0; i < num_alphas; i++) { + _cairo_array_copy_element (&res->alphas, i, &alpha); + _cairo_output_stream_printf (surface->output, + " /a%d << /CA %f /ca %f >>\n", + i, alpha, alpha); + } + + for (i = 0; i < num_smasks; i++) { + smask = _cairo_array_index (&res->smasks, i); + _cairo_output_stream_printf (surface->output, + " /s%d %d 0 R\n", + smask->id, smask->id); + } + + _cairo_output_stream_printf (surface->output, + " >>\n"); + } + + num_resources = _cairo_array_num_elements (&res->patterns); + if (num_resources > 0) { + _cairo_output_stream_printf (surface->output, + " /Pattern <<"); + for (i = 0; i < num_resources; i++) { + pattern = _cairo_array_index (&res->patterns, i); + _cairo_output_stream_printf (surface->output, + " /p%d %d 0 R", + pattern->id, pattern->id); + } + + _cairo_output_stream_printf (surface->output, + " >>\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, + " /XObject <<"); + + for (i = 0; i < num_resources; i++) { + xobject = _cairo_array_index (&res->xobjects, i); + _cairo_output_stream_printf (surface->output, + " /x%d %d 0 R", + xobject->id, xobject->id); + } + + _cairo_output_stream_printf (surface->output, + " >>\n"); + } + + num_resources = _cairo_array_num_elements (&res->fonts); + if (num_resources > 0) { + _cairo_output_stream_printf (surface->output," /Font <<\n"); + for (i = 0; i < num_resources; i++) { + font = _cairo_array_index (&res->fonts, i); + _cairo_output_stream_printf (surface->output, + " /f-%d-%d %d 0 R\n", + font->font_id, + font->subset_id, + font->subset_resource.id); + } + _cairo_output_stream_printf (surface->output, " >>\n"); + } + + _cairo_output_stream_printf (surface->output, + ">>\n"); +} + +static cairo_pdf_smask_group_t * +_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface, + const cairo_rectangle_int_t *extents) +{ + cairo_pdf_smask_group_t *group; + + group = calloc (1, sizeof (cairo_pdf_smask_group_t)); + if (unlikely (group == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + group->group_res = _cairo_pdf_surface_new_object (surface); + if (group->group_res.id == 0) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + free (group); + return NULL; + } + 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; +} + +static void +_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group) +{ + if (group->operation == PDF_FILL || group->operation == PDF_STROKE) + _cairo_path_fixed_fini (&group->path); + if (group->source) + cairo_pattern_destroy (group->source); + if (group->mask) + cairo_pattern_destroy (group->mask); + free (group->utf8); + free (group->glyphs); + free (group->clusters); + if (group->scaled_font) + cairo_scaled_font_destroy (group->scaled_font); + free (group); +} + +static cairo_int_status_t +_cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface, + cairo_pdf_smask_group_t *group) +{ + return _cairo_array_append (&surface->smask_groups, &group); +} + +static cairo_bool_t +_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; + + 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) +{ + 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 +_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) +{ + 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; + + 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; + + 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; + } + + return CAIRO_STATUS_SUCCESS; +} + +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_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: + 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; + } + + 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) { + if (pdf_source) + *pdf_source = surface_entry; + + 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; + } + + 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; + 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; + } + + 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; + } + + /* 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; + + 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_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_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_INT_STATUS_SUCCESS; + } + + status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern); + if (unlikely (status)) + return status; + + pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface); + if (pdf_pattern.pattern_res.id == 0) { + cairo_pattern_destroy (pdf_pattern.pattern); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pdf_pattern.gstate_res.id = 0; + + /* gradient patterns require an smask object to implement transparency */ + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || + pattern->type == CAIRO_PATTERN_TYPE_RADIAL || + pattern->type == CAIRO_PATTERN_TYPE_MESH) + { + 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); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + } + + pdf_pattern.width = surface->width; + pdf_pattern.height = surface->height; + if (extents != NULL) { + pdf_pattern.extents = *extents; + } else { + pdf_pattern.extents.x = 0; + pdf_pattern.extents.y = 0; + pdf_pattern.extents.width = surface->width; + pdf_pattern.extents.height = surface->height; + } + + *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)) { + cairo_pattern_destroy (pdf_pattern.pattern); + return status; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +/* 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, + const char *fmt, + ...) +{ + va_list ap; + cairo_pdf_resource_t self, length; + cairo_output_stream_t *output = NULL; + + if (resource) { + self = *resource; + _cairo_pdf_surface_update_object (surface, self); + } else { + self = _cairo_pdf_surface_new_object (surface); + if (self.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + length = _cairo_pdf_surface_new_object (surface); + if (length.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (compressed) { + output = _cairo_deflate_stream_create (surface->output); + if (_cairo_output_stream_get_status (output)) + return _cairo_output_stream_destroy (output); + } + + surface->pdf_stream.active = TRUE; + surface->pdf_stream.self = self; + surface->pdf_stream.length = length; + surface->pdf_stream.compressed = compressed; + surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Length %d 0 R\n", + surface->pdf_stream.self.id, + surface->pdf_stream.length.id); + if (compressed) + _cairo_output_stream_printf (surface->output, + " /Filter /FlateDecode\n"); + + if (fmt != NULL) { + va_start (ap, fmt); + _cairo_output_stream_vprintf (surface->output, fmt, ap); + va_end (ap); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "stream\n"); + + surface->pdf_stream.start_offset = _cairo_output_stream_get_position (surface->output); + + if (compressed) { + assert (surface->pdf_stream.old_output == NULL); + surface->pdf_stream.old_output = surface->output; + 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_int_status_t +_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) +{ + cairo_int_status_t status; + long length; + + if (! surface->pdf_stream.active) + return CAIRO_INT_STATUS_SUCCESS; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + + if (surface->pdf_stream.compressed) { + cairo_int_status_t status2; + + status2 = _cairo_output_stream_destroy (surface->output); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = status2; + + surface->output = surface->pdf_stream.old_output; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); + surface->pdf_stream.old_output = NULL; + } + + length = _cairo_output_stream_get_position (surface->output) - + surface->pdf_stream.start_offset; + _cairo_output_stream_printf (surface->output, + "\n" + "endstream\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, + surface->pdf_stream.length); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + " %ld\n" + "endobj\n", + surface->pdf_stream.length.id, + length); + + surface->pdf_stream.active = FALSE; + + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = _cairo_output_stream_get_status (surface->output); + + return status; +} + +static void +_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, + const cairo_box_double_t *bbox) +{ + _cairo_pdf_surface_update_object (surface, resource); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /XObject\n" + " /Length %d\n", + resource.id, + _cairo_memory_stream_length (mem_stream)); + + if (surface->compress_streams) { + _cairo_output_stream_printf (surface->output, + " /Filter /FlateDecode\n"); + } + + _cairo_output_stream_printf (surface->output, + " /Subtype /Form\n" + " /BBox [ %f %f %f %f ]\n" + " /Group <<\n" + " /Type /Group\n" + " /S /Transparency\n" + " /I true\n" + " /CS /DeviceRGB\n", + bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y); + + if (is_knockout_group) + _cairo_output_stream_printf (surface->output, + " /K true\n"); + + _cairo_output_stream_printf (surface->output, + " >>\n" + " /Resources\n"); + _cairo_pdf_surface_emit_group_resources (surface, resources, TRUE); + _cairo_output_stream_printf (surface->output, + ">>\n" + "stream\n"); + _cairo_memory_stream_copy (mem_stream, surface->output); + _cairo_output_stream_printf (surface->output, + "endstream\n" + "endobj\n"); +} + +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_int_status_t status; + + assert (surface->pdf_stream.active == FALSE); + assert (surface->group_stream.active == FALSE); + + surface->group_stream.active = TRUE; + + surface->group_stream.mem_stream = _cairo_memory_stream_create (); + + if (surface->compress_streams) { + surface->group_stream.stream = + _cairo_deflate_stream_create (surface->group_stream.mem_stream); + } else { + surface->group_stream.stream = surface->group_stream.mem_stream; + } + status = _cairo_output_stream_get_status (surface->group_stream.stream); + + surface->group_stream.old_output = surface->output; + surface->output = surface->group_stream.stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); + _cairo_pdf_group_resources_clear (&surface->resources); + + if (resource) { + surface->group_stream.resource = *resource; + } else { + surface->group_stream.resource = _cairo_pdf_surface_new_object (surface); + if (surface->group_stream.resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + surface->group_stream.is_knockout = FALSE; + surface->group_stream.bbox = *bbox; + + /* Reset gstate */ + _cairo_output_stream_printf (surface->output, "/gs0 gs\n"); + surface->current_pattern_is_solid_color = FALSE; + surface->current_operator = CAIRO_OPERATOR_OVER; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface, + const cairo_box_double_t *bbox) +{ + cairo_int_status_t status; + + status = _cairo_pdf_surface_open_group (surface, bbox, NULL); + if (unlikely (status)) + return status; + + surface->group_stream.is_knockout = TRUE; + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t *group) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS, status2; + + assert (surface->pdf_stream.active == FALSE); + assert (surface->group_stream.active == TRUE); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + if (surface->compress_streams) { + status = _cairo_output_stream_destroy (surface->group_stream.stream); + surface->group_stream.stream = NULL; + + _cairo_output_stream_printf (surface->group_stream.mem_stream, + "\n"); + } + surface->output = surface->group_stream.old_output; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); + surface->group_stream.active = FALSE; + _cairo_pdf_surface_write_memory_stream (surface, + surface->group_stream.mem_stream, + surface->group_stream.resource, + &surface->resources, + 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_INT_STATUS_SUCCESS) + status = status2; + + surface->group_stream.mem_stream = NULL; + surface->group_stream.stream = NULL; + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_open_object_stream (cairo_pdf_surface_t *surface) +{ + if (surface->pdf_version < CAIRO_PDF_VERSION_1_5) { + /* Object streams not supported. All objects will be written + * directly to the file. */ + assert (surface->pdf_stream.active == FALSE); + assert (surface->group_stream.active == FALSE); + surface->object_stream.stream = surface->output; + } else { + surface->object_stream.resource = _cairo_pdf_surface_new_object (surface); + if (surface->object_stream.resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_array_truncate (&surface->object_stream.objects, 0); + surface->object_stream.stream = _cairo_memory_stream_create (); + surface->object_stream.active = TRUE; + } + return _cairo_output_stream_get_status (surface->object_stream.stream); +} + +cairo_int_status_t +_cairo_pdf_surface_object_begin (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t resource) +{ + cairo_xref_stream_object_t xref_obj; + cairo_pdf_object_t *object; + cairo_int_status_t status; + + if (surface->object_stream.active) { + xref_obj.resource = resource; + xref_obj.offset = _cairo_output_stream_get_position (surface->object_stream.stream); + status = _cairo_array_append (&surface->object_stream.objects, &xref_obj); + if (unlikely (status)) + return status; + + object = _cairo_array_index (&surface->objects, resource.id - 1); + object->type = PDF_OBJECT_COMPRESSED; + object->u.compressed_obj.xref_stream = surface->object_stream.resource; + object->u.compressed_obj.index = _cairo_array_num_elements (&surface->object_stream.objects) - 1; + + } else { + _cairo_pdf_surface_update_object (surface, resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n", + resource.id); + } + return CAIRO_INT_STATUS_SUCCESS; +} + +void +_cairo_pdf_surface_object_end (cairo_pdf_surface_t *surface) +{ + if (!surface->object_stream.active) { + _cairo_output_stream_printf (surface->output, + "endobj\n"); + } +} + +static int _cairo_xref_stream_object_compare (const void *a, const void *b) +{ + const cairo_xref_stream_object_t *a_obj = a; + const cairo_xref_stream_object_t *b_obj = b; + + if (a_obj->offset < b_obj->offset) + return -1; + else if (a_obj->offset > b_obj->offset) + return 1; + else + return 0; +} + +static cairo_int_status_t +_cairo_pdf_surface_close_object_stream (cairo_pdf_surface_t *surface) +{ + int i, num_objects; + cairo_xref_stream_object_t *xref_obj; + long start_pos, length; + cairo_output_stream_t *index_stream; + cairo_output_stream_t *deflate_stream; + cairo_pdf_resource_t length_res; + cairo_int_status_t status; + cairo_pdf_object_t *object; + + if (!surface->object_stream.active) { + surface->object_stream.stream = NULL; + return CAIRO_INT_STATUS_SUCCESS; + } + + num_objects = _cairo_array_num_elements (&surface->object_stream.objects); + if (num_objects == 0) { + object = _cairo_array_index (&surface->objects, surface->object_stream.resource.id - 1); + object->type = PDF_OBJECT_FREE; + return CAIRO_INT_STATUS_SUCCESS; + } + + index_stream = _cairo_memory_stream_create (); + /* PDF requires the object id/offset pairs to be sorted by offset. */ + _cairo_array_sort (&surface->object_stream.objects, _cairo_xref_stream_object_compare); + for (i = 0; i < num_objects; i++) { + xref_obj = _cairo_array_index (&surface->object_stream.objects, i); + _cairo_output_stream_printf (index_stream, + "%d %ld\n", + xref_obj->resource.id, + xref_obj->offset); + } + + length_res = _cairo_pdf_surface_new_object (surface); + if (length_res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_pdf_surface_update_object (surface, surface->object_stream.resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /ObjStm\n" + " /Length %d 0 R\n" + " /N %d\n" + " /First %d\n", + surface->object_stream.resource.id, + length_res.id, + num_objects, + _cairo_memory_stream_length (index_stream)); + + if (surface->compress_streams) { + _cairo_output_stream_printf (surface->output, + " /Filter /FlateDecode\n"); + } + + _cairo_output_stream_printf (surface->output, + ">>\n" + "stream\n"); + + start_pos = _cairo_output_stream_get_position (surface->output); + if (surface->compress_streams) { + deflate_stream = _cairo_deflate_stream_create (surface->output); + _cairo_memory_stream_copy (index_stream, deflate_stream); + _cairo_memory_stream_copy (surface->object_stream.stream, deflate_stream); + status = _cairo_output_stream_destroy (deflate_stream); + if (unlikely (status)) + return status; + + length = _cairo_output_stream_get_position (surface->output) - start_pos; + } else { + _cairo_memory_stream_copy (index_stream, surface->output); + _cairo_memory_stream_copy (surface->object_stream.stream, surface->output); + length = _cairo_output_stream_get_position (surface->output) - start_pos; + } + + _cairo_output_stream_printf (surface->output, + "\n" + "endstream\n" + "endobj\n"); + + _cairo_pdf_surface_update_object (surface, + length_res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + " %ld\n" + "endobj\n", + length_res.id, + length); + + status = _cairo_output_stream_destroy (index_stream); + if (unlikely (status)) + return status; + + status = _cairo_output_stream_destroy (surface->object_stream.stream); + if (unlikely (status)) + return status; + + surface->object_stream.stream = NULL; + surface->object_stream.active = FALSE; + + return _cairo_output_stream_get_status (surface->output); +} + +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_int_status_t status; + + assert (surface->pdf_stream.active == FALSE); + assert (surface->group_stream.active == FALSE); + + surface->content_resources = _cairo_pdf_surface_new_object (surface); + if (surface->content_resources.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (is_form) { + assert (bbox != NULL); + + if (is_group) { + status = + _cairo_pdf_surface_open_stream (surface, + resource, + surface->compress_streams, + " /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_streams, + " /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_streams, + NULL); + _cairo_output_stream_printf (surface->output, + "1 0 0 -1 0 %f cm\n", + surface->height); + } + if (unlikely (status)) + return status; + + 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_int_status_t +_cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface, + cairo_bool_t is_form) +{ + cairo_int_status_t status; + + assert (surface->pdf_stream.active == TRUE); + assert (surface->group_stream.active == FALSE); + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, "Q\n"); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + _cairo_pdf_surface_update_object (surface, surface->content_resources); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n", + surface->content_resources.id); + _cairo_pdf_surface_emit_group_resources (surface, &surface->resources, is_form); + _cairo_output_stream_printf (surface->output, + "endobj\n"); + + return _cairo_output_stream_get_status (surface->output); +} + +static void +_cairo_pdf_source_surface_entry_pluck (void *entry, void *closure) +{ + cairo_pdf_source_surface_entry_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_pdf_surface_finish (void *abstract_surface) +{ + cairo_pdf_surface_t *surface = abstract_surface; + long offset; + cairo_pdf_resource_t catalog; + cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; + int size, i; + cairo_pdf_source_surface_t doc_surface; + cairo_pdf_jbig2_global_t *global; + char *label; + cairo_pdf_resource_t xref_res; + + /* Some of the data may be in an inconistent state if there is an error status. */ + if (surface->base.status != CAIRO_STATUS_SUCCESS) + goto CLEANUP; + + _cairo_pdf_surface_clear (surface); + + status = _cairo_pdf_surface_open_object_stream (surface); + if (unlikely (status)) + return status; + + /* Emit unbounded surfaces */ + _cairo_pdf_surface_write_patterns_and_smask_groups (surface, TRUE); + + status = surface->base.status; + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_pdf_surface_emit_font_subsets (surface); + + status = _cairo_pdf_surface_write_pages (surface); + if (unlikely (status)) + return status; + + status = _cairo_pdf_interchange_write_document_objects (surface); + if (unlikely (status)) + return status; + + catalog = _cairo_pdf_surface_new_object (surface); + if (catalog.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pdf_surface_write_catalog (surface, catalog); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_close_object_stream (surface); + if (unlikely (status)) + return status; + + if (surface->pdf_version >= CAIRO_PDF_VERSION_1_5) + { + xref_res = _cairo_pdf_surface_new_object (surface); + status = _cairo_pdf_surface_write_xref_stream (surface, + xref_res, + catalog, + surface->docinfo_res, + &offset); + } else { + offset = _cairo_pdf_surface_write_xref (surface); + _cairo_output_stream_printf (surface->output, + "trailer\n" + "<< /Size %d\n" + " /Root %d 0 R\n" + " /Info %d 0 R\n" + ">>\n", + surface->next_available_resource.id, + catalog.id, + surface->docinfo_res.id); + } + _cairo_output_stream_printf (surface->output, + "startxref\n" + "%ld\n" + "%%%%EOF\n", + offset); + + CLEANUP: + + /* pdf_operators has already been flushed when the last stream was + * closed so we should never be writing anything here - however, + * the stream may itself be in an error state. */ + status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + /* close any active streams still open due to fatal errors */ + status2 = _cairo_pdf_surface_close_stream (surface); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + if (surface->group_stream.stream != NULL) { + status2 = _cairo_output_stream_destroy (surface->group_stream.stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + if (surface->group_stream.mem_stream != NULL) { + status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + if (surface->pdf_stream.active) + surface->output = surface->pdf_stream.old_output; + if (surface->group_stream.active) + surface->output = surface->group_stream.old_output; + + /* and finish the pdf surface */ + status2 = _cairo_output_stream_destroy (surface->output); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_pdf_group_resources_fini (&surface->resources); + + _cairo_array_fini (&surface->objects); + _cairo_array_fini (&surface->pages); + _cairo_array_fini (&surface->rgb_linear_functions); + _cairo_array_fini (&surface->alpha_linear_functions); + _cairo_array_fini (&surface->page_patterns); + _cairo_array_fini (&surface->page_surfaces); + _cairo_array_fini (&surface->object_stream.objects); + + size = _cairo_array_num_elements (&surface->doc_surfaces); + for (i = 0; i < size; i++) { + _cairo_array_copy_element (&surface->doc_surfaces, i, &doc_surface); + cairo_surface_destroy (doc_surface.surface); + } + _cairo_array_fini (&surface->doc_surfaces); + _cairo_hash_table_foreach (surface->all_surfaces, + _cairo_pdf_source_surface_entry_pluck, + surface->all_surfaces); + _cairo_hash_table_destroy (surface->all_surfaces); + _cairo_array_fini (&surface->smask_groups); + _cairo_array_fini (&surface->fonts); + _cairo_array_fini (&surface->knockout_group); + _cairo_array_fini (&surface->page_annots); + _cairo_array_fini (&surface->forward_links); + + 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); + + _cairo_pdf_interchange_fini (surface); + + return status; +} + +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) { + const char *version; + + switch (surface->pdf_version) { + case CAIRO_PDF_VERSION_1_4: + version = "1.4"; + break; + default: + case CAIRO_PDF_VERSION_1_5: + version = "1.5"; + break; + } + + _cairo_output_stream_printf (surface->output, + "%%PDF-%s\n", version); + _cairo_output_stream_printf (surface->output, + "%%%c%c%c%c\n", 181, 237, 174, 251); + surface->header_emitted = TRUE; + } + + _cairo_pdf_group_resources_clear (&surface->resources); + 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; +} + +static cairo_int_status_t +_cairo_pdf_surface_has_fallback_images (void *abstract_surface, + cairo_bool_t has_fallbacks) +{ + cairo_int_status_t status; + cairo_pdf_surface_t *surface = abstract_surface; + cairo_box_double_t bbox; + + surface->has_fallback_images = 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; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + return TRUE; +} + +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_int_status_t +_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface, + cairo_image_surface_t *image, + cairo_bool_t stencil_mask, + cairo_bool_t interpolate, + cairo_pdf_resource_t *stream_res) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + char *alpha; + unsigned long alpha_size; + uint32_t *pixel32; + uint8_t *pixel8; + 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 ); + + 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 (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 { + alpha_size = image->height * image->width; + alpha = _cairo_malloc_ab (image->height, image->width); + } + + if (unlikely (alpha == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + i = 0; + for (y = 0; y < image->height; y++) { + 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; + } + } 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++; + } + } + + 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; + + _cairo_output_stream_write (surface->output, alpha, alpha_size); + status = _cairo_pdf_surface_close_stream (surface); + + CLEANUP_ALPHA: + free (alpha); + CLEANUP: + return status; +} + +/** + * _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_int_status_t status = CAIRO_STATUS_SUCCESS; + char *data; + unsigned long data_size; + uint32_t *pixel; + int i, x, y, bit; + cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */ + cairo_bool_t need_smask; + cairo_image_color_t color; + cairo_image_surface_t *image; + cairo_image_transparency_t transparency; + char smask_buf[30]; + + 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; + + 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; + } + + i = 0; + 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 + * appropriately, but my attempts to do that so far have + * failed. */ + if (image->format == CAIRO_FORMAT_ARGB32) { + uint8_t a; + a = (*pixel & 0xff000000) >> 24; + if (a == 0) { + r = g = b = 0; + } else { + 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) { + r = (*pixel & 0x00ff0000) >> 16; + g = (*pixel & 0x0000ff00) >> 8; + b = (*pixel & 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_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; + } + } + } + + if (need_smask) + snprintf(smask_buf, sizeof(smask_buf), " /SMask %d 0 R\n", smask.id); + else + 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, data, data_size); + status = _cairo_pdf_surface_close_stream (surface); + +CLEANUP_RGB: + free (data); +CLEANUP: + if (image != image_surf) + cairo_surface_destroy (&image->base); + + return status; +} + +static cairo_int_status_t +_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_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; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length); + if (status) + return status; + + 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; + + _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_jpeg_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 char *colorspace; + char smask_buf[30]; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (unlikely (source->status)) + return source->status; + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if ((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; + + 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; + + _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_ccitt_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; + 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]; + + 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; + + 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; + + /* 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; + + free (params); + + /* At this point we know emitting jbig2 will succeed. */ + if (test) + return CAIRO_STATUS_SUCCESS; + + 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"); + + if (ccitt_params.encoded_byte_align) + p += snprintf (p, end - p, " /EncodedByteAlign true"); + + if (!ccitt_params.end_of_block) + p += snprintf (p, end - p, " /EndOfBlock false"); + + if (ccitt_params.black_is_1) + p += snprintf (p, end - p, " /BlackIs1 true"); + + if (ccitt_params.damaged_rows_before_error > 0) { + p += snprintf (p, end - p, " /DamagedRowsBeforeError %d", + ccitt_params.damaged_rows_before_error); + } + + 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 { + 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_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; + + /* Reset gstate */ + _cairo_output_stream_printf (surface->output, "/gs0 gs\n"); + + 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, TRUE); + + _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_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); + double xstep, ystep; + 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; + + 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, + 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; + + 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: + { + /* In PS/PDF, (as far as I can tell), all patterns are + * repeating. So we support cairo's EXTEND_NONE semantics + * by setting the repeat step size to a size large enough + * to guarantee that no more than a single occurrence will + * be visible. + * + * First, map the surface extents into pattern space (since + * xstep and ystep are in pattern space). Then use an upper + * bound on the length of the diagonal of the pattern image + * and the surface as repeat size. This guarantees to never + * repeat visibly. + */ + double x1 = 0.0, y1 = 0.0; + double x2 = surface->surface_extents.width; + double y2 = surface->surface_extents.height; + _cairo_matrix_transform_bounding_box (&pattern->matrix, + &x1, &y1, &x2, &y2, + NULL); + + /* Rather than computing precise bounds of the union, just + * add the surface extents unconditionally. We only + * 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_extents.width + pattern_extents.height); + } + break; + case CAIRO_EXTEND_REPEAT: + xstep = pattern_extents.width; + ystep = pattern_extents.height; + break; + + case CAIRO_EXTEND_REFLECT: + 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. */ + default: + ASSERT_NOT_REACHED; + xstep = 0; + ystep = 0; + } + + /* At this point, (that is, within the surface backend interface), + * the pattern's matrix maps from cairo's device space to cairo's + * pattern space, (both with their origin at the upper-left, and + * cairo's pattern space of size width,height). + * + * Then, we must emit a PDF pattern object that maps from its own + * pattern space, (which has a size that we establish in the BBox + * dictionary entry), to the PDF page's *initial* space, (which + * does not benefit from the Y-axis flipping matrix that we emit + * on each page). So the PDF patterns matrix maps from a + * (width,height) pattern space to a device space with the origin + * in the lower-left corner. + * + * So to handle all of that, we start with an identity matrix for + * the PDF pattern to device matrix. We translate it up by the + * image height then flip it in the Y direction, (moving us from + * the PDF origin to cairo's origin). We then multiply in the + * inverse of the cairo pattern matrix, (since it maps from device + * to pattern, while we're setting up pattern to device). Finally, + * we translate back down by the image height and flip again to + * end up at the lower-left origin that PDF expects. + * + * Additionally, within the stream that paints the pattern itself, + * we are using a PDF image object that has a size of (1,1) so we + * have to scale it up by the image width and height to fill our + * pattern cell. + */ + cairo_p2d = pattern->matrix; + status = cairo_matrix_invert (&cairo_p2d); + /* 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 (&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 [ %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.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, + pdf_source->surface_res.id, + pdf_source->surface_res.id); + if (unlikely (status)) + return status; + + if (pdf_source->emit_image) { + snprintf(draw_surface, + sizeof (draw_surface), + "q %d 0 0 %d 0 0 cm /x%d Do Q", + 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_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", + draw_surface); + } + + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + return _cairo_output_stream_get_status (surface->output); +} + +typedef struct _cairo_pdf_color_stop { + double offset; + double color[4]; + cairo_pdf_resource_t resource; +} cairo_pdf_color_stop_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, + cairo_pdf_resource_t *function) +{ + int num_elems, i; + cairo_pdf_rgb_linear_function_t elem; + cairo_pdf_resource_t res; + cairo_int_status_t status; + + num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem); + if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0) + continue; + if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0) + continue; + *function = elem.resource; + return CAIRO_STATUS_SUCCESS; + } + + res = _cairo_pdf_surface_new_object (surface); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /FunctionType 2\n" + " /Domain [ 0 1 ]\n" + " /C0 [ %f %f %f ]\n" + " /C1 [ %f %f %f ]\n" + " /N 1\n" + ">>\n" + "endobj\n", + res.id, + stop1->color[0], + stop1->color[1], + stop1->color[2], + stop2->color[0], + stop2->color[1], + stop2->color[2]); + + elem.resource = res; + memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3); + memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3); + + status = _cairo_array_append (&surface->rgb_linear_functions, &elem); + *function = res; + + return status; +} + +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, + cairo_pdf_resource_t *function) +{ + int num_elems, i; + cairo_pdf_alpha_linear_function_t elem; + cairo_pdf_resource_t res; + cairo_int_status_t status; + + num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions); + for (i = 0; i < num_elems; i++) { + _cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem); + if (elem.alpha1 != stop1->color[3]) + continue; + if (elem.alpha2 != stop2->color[3]) + continue; + *function = elem.resource; + return CAIRO_STATUS_SUCCESS; + } + + res = _cairo_pdf_surface_new_object (surface); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /FunctionType 2\n" + " /Domain [ 0 1 ]\n" + " /C0 [ %f ]\n" + " /C1 [ %f ]\n" + " /N 1\n" + ">>\n" + "endobj\n", + res.id, + stop1->color[3], + stop2->color[3]); + + elem.resource = res; + elem.alpha1 = stop1->color[3]; + elem.alpha2 = stop2->color[3]; + + status = _cairo_array_append (&surface->alpha_linear_functions, &elem); + *function = res; + + return status; +} + +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, + cairo_bool_t is_alpha, + cairo_pdf_resource_t *function) +{ + cairo_pdf_resource_t res; + unsigned int i; + cairo_int_status_t status; + + /* emit linear gradients between pairs of subsequent stops... */ + for (i = 0; i < n_stops-1; i++) { + if (is_alpha) { + status = cairo_pdf_surface_emit_alpha_linear_function (surface, + &stops[i], + &stops[i+1], + &stops[i].resource); + if (unlikely (status)) + return status; + } else { + status = cairo_pdf_surface_emit_rgb_linear_function (surface, + &stops[i], + &stops[i+1], + &stops[i].resource); + if (unlikely (status)) + return status; + } + } + + /* ... and stitch them together */ + res = _cairo_pdf_surface_new_object (surface); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /FunctionType 3\n" + " /Domain [ %f %f ]\n", + res.id, + stops[0].offset, + stops[n_stops - 1].offset); + + _cairo_output_stream_printf (surface->output, + " /Functions [ "); + for (i = 0; i < n_stops-1; i++) + _cairo_output_stream_printf (surface->output, + "%d 0 R ", stops[i].resource.id); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + " /Bounds [ "); + for (i = 1; i < n_stops-1; i++) + _cairo_output_stream_printf (surface->output, + "%f ", stops[i].offset); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + " /Encode [ "); + for (i = 1; i < n_stops; i++) + _cairo_output_stream_printf (surface->output, + "0 1 "); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + *function = res; + + return _cairo_output_stream_get_status (surface->output); +} + + +static void +calc_gradient_color (cairo_pdf_color_stop_t *new_stop, + cairo_pdf_color_stop_t *stop1, + cairo_pdf_color_stop_t *stop2) +{ + int i; + double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); + + for (i = 0; i < 4; i++) + new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); +} + +#define COLOR_STOP_EPSILON 1e-6 + +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, + cairo_pdf_resource_t *alpha_function) +{ + cairo_pdf_color_stop_t *allstops, *stops; + unsigned int n_stops; + unsigned int i; + cairo_bool_t emit_alpha = FALSE; + cairo_int_status_t status; + + color_function->id = 0; + alpha_function->id = 0; + + allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t)); + if (unlikely (allstops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + stops = &allstops[1]; + n_stops = pattern->n_stops; + + for (i = 0; i < n_stops; i++) { + stops[i].color[0] = pattern->stops[i].color.red; + stops[i].color[1] = pattern->stops[i].color.green; + stops[i].color[2] = pattern->stops[i].color.blue; + stops[i].color[3] = pattern->stops[i].color.alpha; + if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3])) + emit_alpha = TRUE; + stops[i].offset = pattern->stops[i].offset; + } + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + 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_pdf_color_stop_t)); + else + calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); + stops = allstops; + n_stops++; + } + stops[0].offset = 0.0; + + if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { + memcpy (&stops[n_stops], + &stops[n_stops - 1], + sizeof (cairo_pdf_color_stop_t)); + } else { + calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); + } + n_stops++; + } + stops[n_stops-1].offset = 1.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_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], + &stops[n_stops - 1], + color_function); + if (unlikely (status)) + goto BAIL; + + if (emit_alpha) { + status = cairo_pdf_surface_emit_alpha_linear_function (surface, + &stops[0], + &stops[n_stops - 1], + alpha_function); + if (unlikely (status)) + goto BAIL; + } + } else { + /* multiple stops: stitch. XXX possible optimization: regularly spaced + * stops do not require stitching. XXX */ + status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, + n_stops, + stops, + FALSE, + color_function); + if (unlikely (status)) + goto BAIL; + + if (emit_alpha) { + status = _cairo_pdf_surface_emit_stitched_colorgradient (surface, + n_stops, + stops, + TRUE, + alpha_function); + if (unlikely (status)) + goto BAIL; + } + } + +BAIL: + free (allstops); + return status; +} + +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, + int begin, + int end) +{ + cairo_pdf_resource_t res; + int i; + + res = _cairo_pdf_surface_new_object (surface); + if (res.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /FunctionType 3\n" + " /Domain [ %d %d ]\n", + res.id, + begin, + end); + + _cairo_output_stream_printf (surface->output, + " /Functions [ "); + for (i = begin; i < end; i++) + _cairo_output_stream_printf (surface->output, + "%d 0 R ", function->id); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + " /Bounds [ "); + for (i = begin + 1; i < end; i++) + _cairo_output_stream_printf (surface->output, + "%d ", i); + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + " /Encode [ "); + for (i = begin; i < end; i++) { + if ((i % 2) && pattern->base.extend == CAIRO_EXTEND_REFLECT) { + _cairo_output_stream_printf (surface->output, + "1 0 "); + } else { + _cairo_output_stream_printf (surface->output, + "0 1 "); + } + } + _cairo_output_stream_printf (surface->output, + "]\n"); + + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + *function = res; + + return _cairo_output_stream_get_status (surface->output); +} + +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_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_streams, + " /Type /XObject\n" + " /Subtype /Form\n" + " /FormType 1\n" + " /BBox [ %f %f %f %f ]\n" + " /Resources\n" + " << /ExtGState\n" + " << /a0 << /ca 1 /CA 1 >>" + " >>\n" + "%s" + " >>\n" + " /Group\n" + " << /Type /Group\n" + " /S /Transparency\n" + " /I true\n" + " /CS /DeviceGray\n" + " >>\n", + x1,y1,x2,y2, + buf); + if (unlikely (status)) + return status; + + 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)) + return status; + + smask_resource = _cairo_pdf_surface_new_object (surface); + if (smask_resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Mask\n" + " /S /Luminosity\n" + " /G %d 0 R\n" + ">>\n" + "endobj\n", + smask_resource.id, + surface->pdf_stream.self.id); + + /* Create GState which uses the transparency group as an SMask. */ + _cairo_pdf_surface_update_object (surface, gstate_resource); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /ExtGState\n" + " /SMask %d 0 R\n" + " /ca 1\n" + " /CA 1\n" + " /AIS false\n" + ">>\n" + "endobj\n", + gstate_resource.id, + smask_resource.id); + + return _cairo_output_stream_get_status (surface->output); +} + +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_output_stream_printf (surface->output, + "%d 0 obj\n", + pattern_resource.id); + + 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 { + _cairo_output_stream_printf (surface->output, + " /Extend [ false false ]\n"); + } + + _cairo_output_stream_printf (surface->output, + " /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; + + assert (pdf_pattern->gstate_res.id != 0); + + /* Create pattern for SMask. */ + mask_resource = _cairo_pdf_surface_new_object (surface); + if (mask_resource.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _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)) + return status; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface, + cairo_pdf_pattern_t *pdf_pattern) +{ + cairo_matrix_t pat_to_pdf; + 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; + + 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); + + 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); + + status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern); + if (unlikely (status)) + return status; + + 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" + "<< /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 [ ", + pdf_pattern->pattern_res.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); + + if (pdf_pattern->gstate_res.id != 0) { + cairo_pdf_resource_t mask_resource; + + /* Create pattern for SMask. */ + res = _cairo_pdf_surface_new_object (surface); + if (unlikely (res.id == 0)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern); + if (unlikely (status)) + return status; + + _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); + + 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)) + return status; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern) +{ + cairo_int_status_t status; + + switch (pdf_pattern->pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + ASSERT_NOT_REACHED; + status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + 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: + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern); + break; + + case CAIRO_PATTERN_TYPE_MESH: + status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern); + break; + + default: + ASSERT_NOT_REACHED; + status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); + break; + } + + return status; +} + +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_matrix_t cairo_p2d, pdf_p2d; + cairo_int_status_t status; + int alpha_id; + double x_offset; + double y_offset; + cairo_pdf_source_surface_entry_t *pdf_source; + + 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->matrix; + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + 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, 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_print_matrix (surface->output, &pdf_p2d); + _cairo_output_stream_printf (surface->output, " cm\n"); + } + + status = _cairo_pdf_surface_add_alpha (surface, alpha, &alpha_id); + if (unlikely (status)) + return status; + + 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, pdf_source->surface_res); +} + +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_int_status_t status; + + if (op == surface->current_operator) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/b%d gs\n", op); + surface->current_operator = op; + _cairo_pdf_surface_add_operator (surface, op); + + return CAIRO_STATUS_SUCCESS; +} + +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_int_status_t status; + int alpha; + const cairo_color_t *solid_color = NULL; + + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern; + + solid_color = &solid->color; + } + + if (solid_color != NULL) { + if (surface->current_pattern_is_solid_color == FALSE || + surface->current_color_red != solid_color->red || + surface->current_color_green != solid_color->green || + surface->current_color_blue != solid_color->blue || + surface->current_color_is_stroke != is_stroke) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "%f %f %f ", + solid_color->red, + solid_color->green, + solid_color->blue); + + if (is_stroke) + _cairo_output_stream_printf (surface->output, "RG "); + else + _cairo_output_stream_printf (surface->output, "rg "); + + surface->current_color_red = solid_color->red; + surface->current_color_green = solid_color->green; + surface->current_color_blue = solid_color->blue; + surface->current_color_is_stroke = is_stroke; + } + + if (surface->current_pattern_is_solid_color == FALSE || + surface->current_color_alpha != solid_color->alpha) + { + status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/a%d gs\n", + alpha); + surface->current_color_alpha = solid_color->alpha; + } + + surface->current_pattern_is_solid_color = TRUE; + } else { + status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_add_pattern (surface, pattern_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + /* fill-stroke calls select_pattern twice. Don't save if the + * gstate is already saved. */ + if (!surface->select_pattern_gstate_saved) + _cairo_output_stream_printf (surface->output, "q "); + + if (is_stroke) { + _cairo_output_stream_printf (surface->output, + "/Pattern CS /p%d SCN ", + pattern_res.id); + } else { + _cairo_output_stream_printf (surface->output, + "/Pattern cs /p%d scn ", + pattern_res.id); + } + _cairo_output_stream_printf (surface->output, + "/a%d gs\n", + alpha); + surface->select_pattern_gstate_saved = TRUE; + surface->current_pattern_is_solid_color = FALSE; + } + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface) +{ + cairo_int_status_t status; + + if (surface->select_pattern_gstate_saved) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _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; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_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, FALSE); + if (unlikely (status)) + return status; + + _cairo_surface_clipper_reset (&surface->clipper); + + status = _cairo_pdf_surface_write_page (surface); + if (unlikely (status)) + return status; + + _cairo_pdf_surface_clear (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_pdf_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_pdf_surface_t *surface = abstract_surface; + + if (surface->surface_bounded) + *rectangle = surface->surface_extents; + + return surface->surface_bounded; +} + +static void +_cairo_pdf_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + 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_pdf_surface_write_pages (cairo_pdf_surface_t *surface) +{ + cairo_pdf_resource_t page; + int num_pages, i; + cairo_int_status_t status; + + status = _cairo_pdf_surface_object_begin (surface, surface->pages_resource); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Type /Pages\n" + " /Kids [ "); + + num_pages = _cairo_array_num_elements (&surface->pages); + for (i = 0; i < num_pages; i++) { + _cairo_array_copy_element (&surface->pages, i, &page); + _cairo_output_stream_printf (surface->object_stream.stream, "%d 0 R ", page.id); + } + + _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); + _cairo_output_stream_printf (surface->object_stream.stream, " /Count %d\n", num_pages); + + + /* TODO: Figure out which other defaults to be inherited by /Page + * objects. */ + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); + + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_int_status_t +_cairo_utf8_to_pdf_string (const char *utf8, char **str_out) +{ + int i; + int len; + unsigned char *p; + cairo_bool_t ascii; + char *str; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + ascii = TRUE; + p = (unsigned char *)utf8; + len = 0; + while (*p) { + if (*p < 32 || *p > 126) { + ascii = FALSE; + break; + } + if (*p == '(' || *p == ')' || *p == '\\') + len += 2; + else + len++; + p++; + } + + if (ascii) { + str = _cairo_malloc (len + 3); + if (str == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + str[0] = '('; + p = (unsigned char *)utf8; + i = 1; + while (*p) { + if (*p == '(' || *p == ')' || *p == '\\') + str[i++] = '\\'; + str[i++] = *p; + p++; + } + str[i++] = ')'; + str[i++] = 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_int_status_t status; + int i; + + if (utf8 && *utf8) { + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); + 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, "<"); + if (utf16 == NULL || utf16_len == 0) { + /* According to the "ToUnicode Mapping File Tutorial" + * http://www.adobe.com/devnet/acrobat/pdfs/5411.ToUnicode.pdf + * + * Glyphs that do not map to a Unicode code point must be + * mapped to 0xfffd "REPLACEMENT CHARACTER". + */ + _cairo_output_stream_printf (surface->output, + "fffd"); + } else { + for (i = 0; i < utf16_len; i++) + _cairo_output_stream_printf (surface->output, + "%04x", (int) (utf16[i])); + } + _cairo_output_stream_printf (surface->output, ">"); + + free (utf16); + + return CAIRO_STATUS_SUCCESS; +} + +/* Bob Jenkins hash + * + * Public domain code from: + * http://burtleburtle.net/bob/hash/doobs.html + */ + +#define HASH_MIX(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +static uint32_t +_hash_data (const unsigned char *data, int length, uint32_t initval) +{ + uint32_t a, b, c, len; + + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + while (len >= 12) { + a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24)); + b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24)); + c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24)); + HASH_MIX (a,b,c); + data += 12; + len -= 12; + } + + c += length; + switch(len) { + case 11: c+= ((uint32_t) data[10] << 24); /* 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); + + return c; +} + +static void +_create_font_subset_tag (cairo_scaled_font_subset_t *font_subset, + const char *font_name, + char *tag) +{ + uint32_t hash; + int i; + + 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); + + for (i = 0; i < 6; i++) { + tag[i] = 'A' + (hash % 26); + hash /= 26; + } + tag[i] = 0; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface, + cairo_scaled_font_subset_t *font_subset, + cairo_pdf_resource_t *stream) +{ + unsigned int i, num_bfchar; + cairo_int_status_t status; + + stream->id = 0; + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + surface->compress_streams, + NULL); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "<< /Registry (Adobe)\n" + " /Ordering (UCS)\n" + " /Supplement 0\n" + ">> def\n" + "/CMapName /Adobe-Identity-UCS def\n" + "/CMapType 2 def\n" + "1 begincodespacerange\n"); + + if (font_subset->is_composite && !font_subset->is_latin) { + _cairo_output_stream_printf (surface->output, + "<0000> \n"); + } else { + _cairo_output_stream_printf (surface->output, + "<00> \n"); + } + + _cairo_output_stream_printf (surface->output, + "endcodespacerange\n"); + + if (font_subset->is_scaled) { + /* Type 3 fonts include glyph 0 in the subset */ + num_bfchar = font_subset->num_glyphs; + + /* The CMap specification has a limit of 100 characters per beginbfchar operator */ + _cairo_output_stream_printf (surface->output, + "%d beginbfchar\n", + num_bfchar > 100 ? 100 : num_bfchar); + + for (i = 0; i < num_bfchar; i++) { + if (i != 0 && i % 100 == 0) { + _cairo_output_stream_printf (surface->output, + "endbfchar\n" + "%d beginbfchar\n", + num_bfchar - i > 100 ? 100 : num_bfchar - i); + } + _cairo_output_stream_printf (surface->output, "<%02x> ", i); + status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, + font_subset->utf8[i]); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n"); + } + } else { + /* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */ + num_bfchar = font_subset->num_glyphs - 1; + + /* The CMap specification has a limit of 100 characters per beginbfchar operator */ + _cairo_output_stream_printf (surface->output, + "%d beginbfchar\n", + num_bfchar > 100 ? 100 : num_bfchar); + + for (i = 0; i < num_bfchar; i++) { + if (i != 0 && i % 100 == 0) { + _cairo_output_stream_printf (surface->output, + "endbfchar\n" + "%d beginbfchar\n", + num_bfchar - i > 100 ? 100 : num_bfchar - i); + } + 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); + + status = _cairo_pdf_surface_emit_unicode_for_glyph (surface, + font_subset->utf8[i + 1]); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n"); + } + } + + _cairo_output_stream_printf (surface->output, + "endbfchar\n"); + + _cairo_output_stream_printf (surface->output, + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n"); + + *stream = surface->pdf_stream.self; + return _cairo_pdf_surface_close_stream (surface); +} + +#define PDF_UNITS_PER_EM 1000 + +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) +{ + 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, last_glyph; + cairo_int_status_t status; + char tag[10]; + + _create_font_subset_tag (font_subset, subset->ps_name, tag); + + subset_resource = _cairo_pdf_surface_get_font_resource (surface, + font_subset->font_id, + font_subset->subset_id); + if (subset_resource.id == 0) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + TRUE, + font_subset->is_latin ? + " /Subtype /Type1C\n" : + " /Subtype /CIDFontType0C\n"); + if (unlikely (status)) + return status; + + stream = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, + subset->data, subset->data_length); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_to_unicode_stream (surface, + font_subset, + &to_unicode_stream); + if (_cairo_int_status_is_error (status)) + return status; + + descriptor = _cairo_pdf_surface_new_object (surface); + if (descriptor.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /FontDescriptor\n" + " /FontName /%s+%s\n", + descriptor.id, + tag, + subset->ps_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" + " /FontBBox [ %ld %ld %ld %ld ]\n" + " /ItalicAngle 0\n" + " /Ascent %ld\n" + " /Descent %ld\n" + " /CapHeight %ld\n" + " /StemV 80\n" + " /StemH 80\n" + " /FontFile3 %u 0 R\n" + ">>\n" + "endobj\n", + (long)(subset->x_min*PDF_UNITS_PER_EM), + (long)(subset->y_min*PDF_UNITS_PER_EM), + (long)(subset->x_max*PDF_UNITS_PER_EM), + (long)(subset->y_max*PDF_UNITS_PER_EM), + (long)(subset->ascent*PDF_UNITS_PER_EM), + (long)(subset->descent*PDF_UNITS_PER_EM), + (long)(subset->y_max*PDF_UNITS_PER_EM), + stream.id); + + 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 /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); + + 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, + " %f", + (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) + 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, + " %f", + (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; + status = _cairo_array_append (&surface->fonts, &font); + + return status; +} + +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_int_status_t status; + cairo_cff_subset_t subset; + char name[64]; + + snprintf (name, sizeof name, "CairoFont-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_cff_subset_init (&subset, name, font_subset); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); + + _cairo_cff_subset_fini (&subset); + + return status; +} + +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_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); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset); + + _cairo_cff_fallback_fini (&subset); + + return status; +} + +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_int_status_t status; + unsigned long length; + unsigned int i, last_glyph; + char tag[10]; + + _create_font_subset_tag (font_subset, subset->base_font, tag); + + subset_resource = _cairo_pdf_surface_get_font_resource (surface, + font_subset->font_id, + font_subset->subset_id); + if (subset_resource.id == 0) + return CAIRO_STATUS_SUCCESS; + + length = subset->header_length + subset->data_length + subset->trailer_length; + status = _cairo_pdf_surface_open_stream (surface, + NULL, + TRUE, + " /Length1 %lu\n" + " /Length2 %lu\n" + " /Length3 %lu\n", + subset->header_length, + subset->data_length, + subset->trailer_length); + if (unlikely (status)) + return status; + + stream = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, subset->data, length); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_to_unicode_stream (surface, + font_subset, + &to_unicode_stream); + 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); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /FontDescriptor\n" + " /FontName /%s+%s\n" + " /Flags 4\n" + " /FontBBox [ %ld %ld %ld %ld ]\n" + " /ItalicAngle 0\n" + " /Ascent %ld\n" + " /Descent %ld\n" + " /CapHeight %ld\n" + " /StemV 80\n" + " /StemH 80\n" + " /FontFile %u 0 R\n" + ">>\n" + "endobj\n", + descriptor.id, + tag, + subset->base_font, + (long)(subset->x_min*PDF_UNITS_PER_EM), + (long)(subset->y_min*PDF_UNITS_PER_EM), + (long)(subset->x_max*PDF_UNITS_PER_EM), + (long)(subset->y_max*PDF_UNITS_PER_EM), + (long)(subset->ascent*PDF_UNITS_PER_EM), + (long)(subset->descent*PDF_UNITS_PER_EM), + (long)(subset->y_max*PDF_UNITS_PER_EM), + stream.id); + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type1\n" + " /BaseFont /%s+%s\n" + " /FirstChar %d\n" + " /LastChar %d\n" + " /FontDescriptor %d 0 R\n", + subset_resource.id, + tag, + subset->base_font, + font_subset->is_latin ? 32 : 0, + last_glyph, + descriptor.id); + + 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, + " %f", + (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, + " %f", + (subset->widths[i]*PDF_UNITS_PER_EM)); + } + + _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"); + + font.font_id = font_subset->font_id; + font.subset_id = font_subset->subset_id; + font.subset_resource = subset_resource; + return _cairo_array_append (&surface->fonts, &font); +} + +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_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); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); + + _cairo_type1_subset_fini (&subset); + return status; +} + +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_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); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset); + + _cairo_type1_fallback_fini (&subset); + return status; +} + +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_int_status_t status; + cairo_pdf_font_t font; + cairo_truetype_subset_t subset; + unsigned int i, last_glyph; + char tag[10]; + + subset_resource = _cairo_pdf_surface_get_font_resource (surface, + font_subset->font_id, + font_subset->subset_id); + if (subset_resource.id == 0) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_truetype_subset_init_pdf (&subset, font_subset); + if (unlikely (status)) + return status; + + _create_font_subset_tag (font_subset, subset.ps_name, tag); + + status = _cairo_pdf_surface_open_stream (surface, + NULL, + TRUE, + " /Length1 %lu\n", + subset.data_length); + if (unlikely (status)) { + _cairo_truetype_subset_fini (&subset); + return status; + } + + stream = surface->pdf_stream.self; + _cairo_output_stream_write (surface->output, + subset.data, subset.data_length); + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) { + _cairo_truetype_subset_fini (&subset); + return status; + } + + status = _cairo_pdf_surface_emit_to_unicode_stream (surface, + font_subset, + &to_unicode_stream); + if (_cairo_int_status_is_error (status)) { + _cairo_truetype_subset_fini (&subset); + return status; + } + + descriptor = _cairo_pdf_surface_new_object (surface); + if (descriptor.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 /FontDescriptor\n" + " /FontName /%s+%s\n", + descriptor.id, + tag, + subset.ps_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 %d\n" + " /FontBBox [ %ld %ld %ld %ld ]\n" + " /ItalicAngle 0\n" + " /Ascent %ld\n" + " /Descent %ld\n" + " /CapHeight %ld\n" + " /StemV 80\n" + " /StemH 80\n" + " /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), + (long)(subset.y_max*PDF_UNITS_PER_EM), + (long)(subset.ascent*PDF_UNITS_PER_EM), + (long)(subset.descent*PDF_UNITS_PER_EM), + (long)(subset.y_max*PDF_UNITS_PER_EM), + stream.id); + + 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, + " %f", + (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, + " %f", + (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; + status = _cairo_array_append (&surface->fonts, &font); + + _cairo_truetype_subset_fini (&subset); + + return status; +} + +static cairo_int_status_t +_cairo_pdf_emit_imagemask (cairo_image_surface_t *image, + cairo_output_stream_t *stream) +{ + uint8_t *byte, output_byte; + int row, col, num_cols; + + /* The only image type supported by Type 3 fonts are 1-bit image + * masks */ + assert (image->format == CAIRO_FORMAT_A1); + + _cairo_output_stream_printf (stream, + "BI\n" + "/IM true\n" + "/W %d\n" + "/H %d\n" + "/BPC 1\n" + "/D [1 0]\n", + image->width, + image->height); + + _cairo_output_stream_printf (stream, + "ID "); + + num_cols = (image->width + 7) / 8; + for (row = 0; row < image->height; row++) { + byte = image->data + row * image->stride; + for (col = 0; col < num_cols; col++) { + output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); + _cairo_output_stream_write (stream, &output_byte, 1); + byte++; + } + } + + _cairo_output_stream_printf (stream, + "\nEI\n"); + + return _cairo_output_stream_get_status (stream); +} + +static cairo_int_status_t +_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_pdf_surface_t *surface = closure; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + cairo_int_status_t status2; + unsigned int i; + cairo_surface_t *type3_surface; + cairo_output_stream_t *null_stream; + + null_stream = _cairo_null_stream_create (); + type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, + null_stream, + _cairo_pdf_emit_imagemask, + surface->font_subsets, + FALSE); + if (unlikely (type3_surface->status)) { + status2 = _cairo_output_stream_destroy (null_stream); + return type3_surface->status; + } + + _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, + _cairo_pdf_surface_add_font, + surface); + + for (i = 0; i < font_subset->num_glyphs; i++) { + status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, + font_subset->glyphs[i]); + if (unlikely (status)) + break; + } + + cairo_surface_destroy (type3_surface); + status2 = _cairo_output_stream_destroy (null_stream); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = status2; + + return status; +} + +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_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; + unsigned int i; + cairo_box_t font_bbox = {{0,0},{0,0}}; + cairo_box_t bbox = {{0,0},{0,0}}; + cairo_surface_t *type3_surface; + + if (font_subset->num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + subset_resource = _cairo_pdf_surface_get_font_resource (surface, + font_subset->font_id, + font_subset->subset_id); + if (subset_resource.id == 0) + return CAIRO_STATUS_SUCCESS; + + glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t)); + if (unlikely (glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double)); + if (unlikely (widths == NULL)) { + free (glyphs); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_pdf_group_resources_clear (&surface->resources); + type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, + NULL, + _cairo_pdf_emit_imagemask, + surface->font_subsets, + FALSE); + if (unlikely (type3_surface->status)) { + free (glyphs); + free (widths); + return type3_surface->status; + } + + _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, + _cairo_pdf_surface_add_font, + surface); + + for (i = 0; i < font_subset->num_glyphs; i++) { + status = _cairo_pdf_surface_open_stream (surface, + NULL, + surface->compress_streams, + NULL); + if (unlikely (status)) + break; + + glyphs[i] = surface->pdf_stream.self; + status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, + surface->output, + font_subset->glyphs[i], + &bbox, + &widths[i]); + if (unlikely (status)) + break; + + status = _cairo_pdf_surface_close_stream (surface); + if (unlikely (status)) + break; + + if (i == 0) { + font_bbox.p1.x = bbox.p1.x; + font_bbox.p1.y = bbox.p1.y; + font_bbox.p2.x = bbox.p2.x; + font_bbox.p2.y = bbox.p2.y; + } else { + if (bbox.p1.x < font_bbox.p1.x) + font_bbox.p1.x = bbox.p1.x; + if (bbox.p1.y < font_bbox.p1.y) + font_bbox.p1.y = bbox.p1.y; + if (bbox.p2.x > font_bbox.p2.x) + font_bbox.p2.x = bbox.p2.x; + if (bbox.p2.y > font_bbox.p2.y) + font_bbox.p2.y = bbox.p2.y; + } + } + cairo_surface_destroy (type3_surface); + if (unlikely (status)) { + free (glyphs); + free (widths); + return status; + } + + encoding = _cairo_pdf_surface_new_object (surface); + if (encoding.id == 0) { + free (glyphs); + free (widths); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Encoding\n" + " /Differences [0", encoding.id); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " /%d", i); + _cairo_output_stream_printf (surface->output, + "]\n" + ">>\n" + "endobj\n"); + + char_procs = _cairo_pdf_surface_new_object (surface); + if (char_procs.id == 0) { + free (glyphs); + free (widths); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<<\n", char_procs.id); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, + " /%d %d 0 R\n", + i, glyphs[i].id); + _cairo_output_stream_printf (surface->output, + ">>\n" + "endobj\n"); + + free (glyphs); + + status = _cairo_pdf_surface_emit_to_unicode_stream (surface, + font_subset, + &to_unicode_stream); + if (_cairo_int_status_is_error (status)) { + free (widths); + return status; + } + + _cairo_pdf_surface_update_object (surface, subset_resource); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Font\n" + " /Subtype /Type3\n" + " /FontBBox [%f %f %f %f]\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.p1.y), + _cairo_fixed_to_double (font_bbox.p2.x), + _cairo_fixed_to_double (font_bbox.p2.y), + encoding.id, + char_procs.id, + font_subset->num_glyphs - 1); + + _cairo_output_stream_printf (surface->output, + " /Widths ["); + for (i = 0; i < font_subset->num_glyphs; i++) + _cairo_output_stream_printf (surface->output, " %f", widths[i]); + _cairo_output_stream_printf (surface->output, + "]\n"); + free (widths); + + _cairo_output_stream_printf (surface->output, + " /Resources\n"); + _cairo_pdf_surface_emit_group_resources (surface, &surface->resources, FALSE); + + 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; + return _cairo_array_append (&surface->fonts, &font); +} + +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_int_status_t 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_type1_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; + + status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_SUCCESS; +} + +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_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_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface) +{ + cairo_int_status_t status; + + status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, + _cairo_pdf_surface_analyze_user_font_subset, + surface); + if (unlikely (status)) + goto BAIL; + + status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, + _cairo_pdf_surface_emit_unscaled_font_subset, + surface); + if (unlikely (status)) + goto BAIL; + + status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, + _cairo_pdf_surface_emit_scaled_font_subset, + surface); + if (unlikely (status)) + goto BAIL; + + status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, + _cairo_pdf_surface_emit_scaled_font_subset, + surface); + +BAIL: + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + surface->font_subsets = NULL; + + return status; +} + +static cairo_int_status_t +_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t catalog) +{ + cairo_status_t status; + + status = _cairo_pdf_surface_object_begin (surface, catalog); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /Type /Catalog\n" + " /Pages %d 0 R\n", + surface->pages_resource.id); + + if (surface->struct_tree_root.id != 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /StructTreeRoot %d 0 R\n", + surface->struct_tree_root.id); + if (surface->tagged) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /MarkInfo << /Marked true >>\n"); + } + } + + if (surface->outlines_dict_res.id != 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Outlines %d 0 R\n", + surface->outlines_dict_res.id); + } + + if (surface->page_labels_res.id != 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /PageLabels %d 0 R\n", + surface->page_labels_res.id); + } + + if (surface->names_dict_res.id != 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Names %d 0 R\n", + surface->names_dict_res.id); + } + + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); + + return status; +} + +static long +_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface) +{ + cairo_pdf_object_t *object; + int num_objects, i; + long offset; + char buffer[11]; + + num_objects = _cairo_array_num_elements (&surface->objects); + + offset = _cairo_output_stream_get_position (surface->output); + _cairo_output_stream_printf (surface->output, + "xref\n" + "%d %d\n", + 0, num_objects + 1); + + _cairo_output_stream_printf (surface->output, + "0000000000 65535 f \n"); + for (i = 0; i < num_objects; i++) { + object = _cairo_array_index (&surface->objects, i); + snprintf (buffer, sizeof buffer, "%010ld", object->u.offset); + _cairo_output_stream_printf (surface->output, + "%s 00000 n \n", buffer); + } + + return offset; +} + +static void +_cairo_write_xref_stream_entry (cairo_output_stream_t *stream, + int id, + int type, + int field2_size, + long field2, + int field3, + cairo_bool_t write_as_comments) +{ + char buf[20]; + int i; + + if (write_as_comments) { + _cairo_output_stream_printf (stream, "%% %5d %2d %10ld %d\n", id, type, field2, field3); + } else { + /* Each field is big endian */ + buf[0] = type; /* field 1 */ + for (i = field2_size - 1; i >= 0; i--) { + buf[i + 1] = field2 & 0xff; + field2 >>= 8; + } + buf[field2_size + 1] = field3 >> 8; + buf[field2_size + 2] = field3 & 0xff; + _cairo_output_stream_write (stream, buf, field2_size + 3); + } +} + +static void +_cairo_write_xref_stream_entrys (cairo_pdf_surface_t *surface, + cairo_output_stream_t *stream, + int field2_size, + cairo_bool_t write_as_comments) +{ + cairo_pdf_object_t *object; + int num_objects, i; + + /* PDF requires this to be first entry */ + _cairo_write_xref_stream_entry (stream, + 0, + PDF_OBJECT_FREE, + field2_size, + 0, /* next free object number */ + 0xffff, /* next generation number */ + write_as_comments); + + num_objects = _cairo_array_num_elements (&surface->objects); + for (i = 0; i < num_objects; i++) { + object = _cairo_array_index (&surface->objects, i); + if (object->type == PDF_OBJECT_UNCOMPRESSED) { + _cairo_write_xref_stream_entry (stream, + i + 1, + object->type, + field2_size, + object->u.offset, + 0, /* generation number */ + write_as_comments); + } else if (object->type == PDF_OBJECT_COMPRESSED) { + _cairo_write_xref_stream_entry (stream, + i + 1, + object->type, + field2_size, + object->u.compressed_obj.xref_stream.id, + object->u.compressed_obj.index, + write_as_comments); + } else { + _cairo_write_xref_stream_entry (stream, + i + 1, + PDF_OBJECT_FREE, + field2_size, + 0, + 0xffff, + write_as_comments); + } + } +} + +static cairo_int_status_t +_cairo_pdf_surface_write_xref_stream (cairo_pdf_surface_t *surface, + cairo_pdf_resource_t xref_res, + cairo_pdf_resource_t root_res, + cairo_pdf_resource_t info_res, + long *xref_offset) +{ + cairo_output_stream_t *mem_stream; + cairo_output_stream_t *xref_stream; + long offset; + int offset_bytes; + cairo_status_t status; + + *xref_offset = _cairo_output_stream_get_position (surface->output); + + /* Find the minimum number of bytes required to represent offsets in the generated file (up to this point). */ + offset_bytes = 0; + offset = *xref_offset; + while (offset > 0) { + offset >>= 8; + offset_bytes++; + } + + mem_stream = _cairo_memory_stream_create (); + xref_stream = _cairo_deflate_stream_create (mem_stream); + _cairo_write_xref_stream_entrys (surface, xref_stream, offset_bytes, FALSE); + + status = _cairo_output_stream_destroy (xref_stream); + if (unlikely (status)) + return status; + + _cairo_pdf_surface_update_object (surface, xref_res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /XRef\n" + " /Length %d\n" + " /Filter /FlateDecode\n" + " /Size %d\n" + " /W [1 %d 2]\n" + " /Root %d 0 R\n" + " /Info %d 0 R\n" + ">>\n", + xref_res.id, + _cairo_memory_stream_length (mem_stream), + surface->next_available_resource.id, + offset_bytes, + root_res.id, + info_res.id); + + if (!surface->compress_streams) { + /* Adobe Reader requires xref streams to be flate encoded (PDF + * Reference 1.7, implementation note 20). This means + * compression must always be enabled on this stream. To + * facilitate debugging when compress_stream is disabled, emit + * a human readable format of the xref stream as PDF comments. + */ + _cairo_output_stream_printf (surface->output, + "%% id type offset/obj gen/index\n"); + _cairo_write_xref_stream_entrys (surface, surface->output, offset_bytes, TRUE); + } + + _cairo_output_stream_printf (surface->output, + "stream\n"); + _cairo_memory_stream_copy (mem_stream, surface->output); + status = _cairo_output_stream_destroy (mem_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "\n" + "endstream\n" + "endobj\n"); + + return _cairo_output_stream_get_status (surface->output); +} + +static cairo_int_status_t +_cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface, + cairo_pdf_smask_group_t *group) +{ + cairo_pdf_resource_t mask_group; + cairo_pdf_resource_t smask; + cairo_pdf_smask_group_t *smask_group; + cairo_pdf_resource_t pattern_res, gstate_res; + cairo_int_status_t status; + cairo_box_double_t bbox; + + /* Create mask group */ + _get_bbox_from_extents (&group->extents, &bbox); + status = _cairo_pdf_surface_open_group (surface, &bbox, NULL); + if (unlikely (status)) + return status; + + 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; + + _cairo_output_stream_printf (surface->output, "Q\n"); + } else { + 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; + + 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); + + 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); + if (unlikely (status)) + return status; + + /* Create source group */ + status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res); + if (unlikely (status)) + return status; + + 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; + + _cairo_output_stream_printf (surface->output, "Q\n"); + } else { + 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; + + 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); + + 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); + if (unlikely (status)) + return status; + + /* Create an smask based on the alpha component of mask_group */ + smask = _cairo_pdf_surface_new_object (surface); + if (smask.id == 0) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /Mask\n" + " /S /Alpha\n" + " /G %d 0 R\n" + ">>\n" + "endobj\n", + smask.id, + mask_group.id); + + /* Create a GState that uses the smask */ + _cairo_pdf_surface_update_object (surface, group->group_res); + _cairo_output_stream_printf (surface->output, + "%d 0 obj\n" + "<< /Type /ExtGState\n" + " /SMask %d 0 R\n" + " /ca 1\n" + " /CA 1\n" + " /AIS false\n" + ">>\n" + "endobj\n", + group->group_res.id, + smask.id); + + return _cairo_output_stream_get_status (surface->output); +} + +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_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) { + status = _cairo_pdf_surface_write_mask_group (surface, group); + goto RESTORE_SIZE; + } + + _get_bbox_from_extents (&group->extents, &bbox); + status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_select_pattern (surface, + group->source, + group->source_res, + group->operation == PDF_STROKE); + if (unlikely (status)) + return status; + + switch (group->operation) { + case PDF_PAINT: + _cairo_output_stream_printf (surface->output, + "0 0 %f %f re f\n", + surface->width, surface->height); + break; + case PDF_MASK: + ASSERT_NOT_REACHED; + break; + case PDF_FILL: + status = _cairo_pdf_operators_fill (&surface->pdf_operators, + &group->path, + group->fill_rule); + break; + case PDF_STROKE: + status = _cairo_pdf_operators_stroke (&surface->pdf_operators, + &group->path, + &group->style, + &group->ctm, + &group->ctm_inverse); + break; + case PDF_SHOW_GLYPHS: + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + group->utf8, group->utf8_len, + group->glyphs, group->num_glyphs, + group->clusters, group->num_clusters, + group->cluster_flags, + group->scaled_font); + break; + } + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (status)) + return status; + + 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_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; + 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 + * may also be appended to surface->patterns. + * + * Writing recording surface patterns will cause additional patterns + * and groups to be appended. + */ + 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)) || + (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); + status = _cairo_pdf_surface_write_smask_group (surface, group); + if (unlikely (status)) + return status; + } + + for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) { + _cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern); + status = _cairo_pdf_surface_emit_pattern (surface, &pattern); + if (unlikely (status)) + return status; + } + + 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, 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_int_status_t +_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface) +{ + 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_surface_open_object_stream (surface); + if (unlikely (status)) + return status; + + 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) { + 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; + + len = _cairo_array_num_elements (&surface->knockout_group); + for (i = 0; i < len; i++) { + _cairo_array_copy_element (&surface->knockout_group, i, &res); + _cairo_output_stream_printf (surface->output, + "/x%d Do\n", + res.id); + status = _cairo_pdf_surface_add_xobject (surface, res); + if (unlikely (status)) + return status; + } + _cairo_output_stream_printf (surface->output, + "/x%d Do\n", + surface->content.id); + status = _cairo_pdf_surface_add_xobject (surface, surface->content); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_close_group (surface, &knockout); + if (unlikely (status)) + return status; + + _cairo_pdf_group_resources_clear (&surface->resources); + status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->output, + "/x%d Do\n", + knockout.id); + status = _cairo_pdf_surface_add_xobject (surface, knockout); + if (unlikely (status)) + return status; + + status = _cairo_pdf_surface_close_content_stream (surface, FALSE); + if (unlikely (status)) + return status; + } + + 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); + + status = _cairo_pdf_surface_object_begin (surface, *page); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->object_stream.stream, + "<< /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", + page_num, + surface->pages_resource.id, + surface->width, + surface->height, + surface->content.id, + surface->content_resources.id); + + if (surface->page_parent_tree >= 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /StructParents %d\n", + surface->page_parent_tree); + } + + num_annots = _cairo_array_num_elements (&surface->page_annots); + if (num_annots > 0) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Annots [ "); + for (i = 0; i < num_annots; i++) { + _cairo_array_copy_element (&surface->page_annots, i, &res); + _cairo_output_stream_printf (surface->object_stream.stream, + "%d 0 R ", + res.id); + } + _cairo_output_stream_printf (surface->object_stream.stream, "]\n"); + } + + if (thumbnail_res.id) { + _cairo_output_stream_printf (surface->object_stream.stream, + " /Thumb %d 0 R\n", + thumbnail_res.id); + } + + _cairo_output_stream_printf (surface->object_stream.stream, + ">>\n"); + _cairo_pdf_surface_object_end (surface); + + status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface, FALSE); + if (unlikely (status)) + return status; + + return _cairo_pdf_surface_close_object_stream (surface); +} + +static cairo_int_status_t +_cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + 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); + 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) + status = CAIRO_STATUS_SUCCESS; + else + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_bool_t +_surface_pattern_supported (cairo_surface_pattern_t *pattern) +{ + cairo_extend_t extend; + + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return TRUE; + + if (pattern->surface->backend->acquire_source_image == NULL) + return FALSE; + + /* Does an ALPHA-only source surface even make sense? Maybe, but I + * don't think it's worth the extra code to support it. */ + +/* XXX: Need to write this function here... + if (pattern->surface->content == CAIRO_CONTENT_ALPHA) + return FALSE; +*/ + + extend = cairo_pattern_get_extend (&pattern->base); + switch (extend) { + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_REPEAT: + case CAIRO_EXTEND_REFLECT: + /* There's no point returning FALSE for EXTEND_PAD, as the image + * surface does not currently implement it either */ + case CAIRO_EXTEND_PAD: + return TRUE; + } + + ASSERT_NOT_REACHED; + return FALSE; +} + +static cairo_bool_t +_pattern_supported (const cairo_pattern_t *pattern) +{ + 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; + + case CAIRO_PATTERN_TYPE_SURFACE: + return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern); + + default: + ASSERT_NOT_REACHED; + return FALSE; + } +} + +static cairo_bool_t +_pdf_operator_supported (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_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: + return TRUE; + + default: + case CAIRO_OPERATOR_CLEAR: + case CAIRO_OPERATOR_SOURCE: + 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: + case CAIRO_OPERATOR_SATURATE: + return FALSE; + } +} + +static cairo_int_status_t +_cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + if (surface->force_fallbacks && + surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (! _pattern_supported (pattern)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (_pdf_operator_supported (op)) { + 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) { + 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.x) > rec_extents.x + rec_extents.width || + _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + } + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + } + + return CAIRO_STATUS_SUCCESS; + } + + + /* The SOURCE operator is supported if the pattern is opaque or if + * there is nothing painted underneath. */ + if (op == CAIRO_OPERATOR_SOURCE) { + 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 (_cairo_pattern_is_opaque (pattern, extents)) { + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } else { + /* FIXME: The analysis surface does not yet have + * the capability to analyze a non opaque recording + * surface and mark it supported if there is + * nothing underneath. For now recording surfaces of + * type CONTENT_COLOR_ALPHA painted with + * OPERATOR_SOURCE will result in a fallback + * image. */ + + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } else { + return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface, + surface_pattern); + } + } + + if (_cairo_pattern_is_opaque (pattern, extents)) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_bool_t +_cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface) +{ + cairo_box_double_t bbox; + cairo_int_status_t status; + + status = _cairo_pdf_surface_close_content_stream (surface, FALSE); + if (unlikely (status)) + return status; + + status = _cairo_array_append (&surface->knockout_group, &surface->content); + if (unlikely (status)) + return status; + + _cairo_pdf_group_resources_clear (&surface->resources); + 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_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_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; + + /* Check that source and mask are images */ + + 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; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + ((cairo_surface_pattern_t *) source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + { + 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; + } + + 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; + } + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _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; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _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)) + 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, op, + &extents.bounded, + &pattern_res, &gstate_res); + if (unlikely (status)) + goto cleanup; + + if (gstate_res.id != 0) { + 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); + 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); + goto cleanup; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_add_xobject (surface, group->group_res); + if (unlikely (status)) + goto cleanup; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, source, + pattern_res, FALSE); + if (unlikely (status)) + goto cleanup; + + _cairo_output_stream_printf (surface->output, + "%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)) + 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_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_pdf_smask_group_t *group; + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + cairo_rectangle_int_t r; + cairo_box_t box; + double alpha; + + status = _cairo_composite_rectangles_init_for_mask (&extents, + &surface->base, + op, source, mask, 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) { + cairo_int_status_t source_status, mask_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) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + 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)) + goto cleanup; + } + + assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); + assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded)); + + /* get the accurate extents */ + status = _cairo_pattern_get_ink_extents (source, &r); + if (unlikely (status)) + goto cleanup; + + /* 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); + goto cleanup; + } + status = _cairo_pattern_create_copy (&group->mask, mask); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + goto cleanup; + } + group->source_res = _cairo_pdf_surface_new_object (surface); + if (group->source_res.id == 0) { + _cairo_pdf_smask_group_destroy (group); + 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); + goto cleanup; + } + + status = _cairo_pdf_surface_add_smask (surface, group->group_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_add_xobject (surface, group->source_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (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, + 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_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_stroke (&extents, + &surface->base, + op, source, + path, style, 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, style, + ctm, ctm_inverse, + 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_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_pdf_surface_set_clip (surface, &extents); + if (unlikely (status)) + goto cleanup; + + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, + &extents.bounded, + &pattern_res, &gstate_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + goto cleanup; + + if (gstate_res.id != 0) { + 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); + 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); + goto cleanup; + } + + group->style = *style; + group->ctm = *ctm; + group->ctm_inverse = *ctm_inverse; + status = _cairo_pdf_surface_add_smask_group (surface, group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + goto cleanup; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_add_xobject (surface, group->group_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + goto cleanup; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_stroke (&surface->pdf_operators, + path, + style, + ctm, + ctm_inverse); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (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, + const cairo_path_fixed_t*path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_pdf_surface_t *surface = abstract_surface; + cairo_int_status_t status; + cairo_pdf_smask_group_t *group; + cairo_pdf_resource_t pattern_res, gstate_res; + cairo_composite_rectangles_t extents; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &surface->base, + op, source, path, + clip); + 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, + &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)) + goto cleanup; + + 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)) + 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; + + if (_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_operators_clip (&surface->pdf_operators, + path, + fill_rule); + if (unlikely (status)) + goto cleanup; + + 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"); + 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, op, + &extents.bounded, + &pattern_res, &gstate_res); + if (unlikely (status)) + goto cleanup; + + if (gstate_res.id != 0) { + 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); + 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); + 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); + goto cleanup; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_add_xobject (surface, group->group_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + goto cleanup; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_fill (&surface->pdf_operators, + path, + fill_rule); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (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_stroke (void *abstract_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_pdf_surface_t *surface = abstract_surface; + cairo_int_status_t status; + cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; + 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 + * for us. During render we may still encounter unsupported + * combinations of fill/stroke patterns. However we can return + * unsupported anytime to let the _fill and _stroke functions take + * over. + */ + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* PDF rendering of fill-stroke is not the same as cairo when + * either the fill or stroke is not opaque. + */ + if ( !_cairo_pattern_is_opaque (fill_source, NULL) || + !_cairo_pattern_is_opaque (stroke_source, NULL)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (fill_op != stroke_op) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* 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)) + goto cleanup; + + /* 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)) + goto cleanup; + + fill_pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source, + fill_op, + &extents.bounded, + &fill_pattern_res, + &gstate_res); + if (unlikely (status)) + goto cleanup; + + assert (gstate_res.id == 0); + + stroke_pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, + stroke_source, + stroke_op, + &extents.bounded, + &stroke_pattern_res, + &gstate_res); + if (unlikely (status)) + goto cleanup; + + assert (gstate_res.id == 0); + + /* As PDF has separate graphics state for fill and stroke we can + * select both at the same time */ + status = _cairo_pdf_surface_select_pattern (surface, fill_source, + fill_pattern_res, FALSE); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_select_pattern (surface, stroke_source, + stroke_pattern_res, TRUE); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators, + path, + fill_rule, + stroke_style, + stroke_ctm, + stroke_ctm_inverse); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (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 +_cairo_pdf_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static cairo_int_status_t +_cairo_pdf_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_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_int_status_t status; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &surface->base, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + &overlap); + 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; + } + + assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded)); + + status = _cairo_pdf_surface_set_clip (surface, &extents); + if (unlikely (status)) + goto cleanup; + + pattern_res.id = 0; + gstate_res.id = 0; + status = _cairo_pdf_surface_add_pdf_pattern (surface, source, op, + &extents.bounded, + &pattern_res, &gstate_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_select_operator (surface, op); + if (unlikely (status)) + goto cleanup; + + if (gstate_res.id != 0) { + 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); + goto cleanup; + } + group->source_res = pattern_res; + + if (utf8_len) { + group->utf8 = _cairo_malloc (utf8_len); + if (unlikely (group->utf8 == NULL)) { + _cairo_pdf_smask_group_destroy (group); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } + memcpy (group->utf8, utf8, utf8_len); + } + group->utf8_len = utf8_len; + + if (num_glyphs) { + group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unlikely (group->glyphs == NULL)) { + _cairo_pdf_smask_group_destroy (group); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } + memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + } + group->num_glyphs = num_glyphs; + + if (num_clusters) { + group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t)); + if (unlikely (group->clusters == NULL)) { + _cairo_pdf_smask_group_destroy (group); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto cleanup; + } + memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters); + } + group->num_clusters = num_clusters; + + group->scaled_font = cairo_scaled_font_reference (scaled_font); + status = _cairo_pdf_surface_add_smask_group (surface, group); + if (unlikely (status)) { + _cairo_pdf_smask_group_destroy (group); + goto cleanup; + } + + status = _cairo_pdf_surface_add_smask (surface, gstate_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_add_xobject (surface, group->group_res); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + goto cleanup; + + _cairo_output_stream_printf (surface->output, + "q /s%d gs /x%d Do Q\n", + gstate_res.id, + group->group_res.id); + } else { + status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE); + if (unlikely (status)) + goto cleanup; + + /* Each call to show_glyphs() with a transclucent pattern must + * be in a separate text object otherwise overlapping text + * from separate calls to show_glyphs will not composite with + * each other. */ + if (! _cairo_pattern_is_opaque (source, &extents.bounded)) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + goto cleanup; + } + + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_surface_unselect_pattern (surface); + if (unlikely (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 cairo_int_status_t +_cairo_pdf_surface_tag (void *abstract_surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes) +{ + 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, + _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, /* snapshot */ + + NULL, /* _cairo_pdf_surface_copy_page */ + _cairo_pdf_surface_show_page, + + _cairo_pdf_surface_get_extents, + _cairo_pdf_surface_get_font_options, + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + /* Here are the drawing functions */ + _cairo_pdf_surface_paint, + _cairo_pdf_surface_mask, + _cairo_pdf_surface_stroke, + _cairo_pdf_surface_fill, + _cairo_pdf_surface_fill_stroke, + 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 +cairo_pdf_surface_paginated_backend = { + _cairo_pdf_surface_start_page, + _cairo_pdf_surface_set_paginated_mode, + 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 new file mode 100644 index 0000000000..5be0b3f1be --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pdf.h @@ -0,0 +1,161 @@ +/* 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 + */ + +#ifndef CAIRO_PDF_H +#define CAIRO_PDF_H + +#include "cairo.h" + +#if CAIRO_HAS_PDF_SURFACE + +CAIRO_BEGIN_DECLS + +/** + * cairo_pdf_version_t: + * @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 + **/ +typedef enum _cairo_pdf_version { + CAIRO_PDF_VERSION_1_4, + CAIRO_PDF_VERSION_1_5 +} cairo_pdf_version_t; + +cairo_public cairo_surface_t * +cairo_pdf_surface_create (const char *filename, + double width_in_points, + double height_in_points); + +cairo_public cairo_surface_t * +cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points); + +cairo_public void +cairo_pdf_surface_restrict_to_version (cairo_surface_t *surface, + cairo_pdf_version_t version); + +cairo_public void +cairo_pdf_get_versions (cairo_pdf_version_t const **versions, + int *num_versions); + +cairo_public const char * +cairo_pdf_version_to_string (cairo_pdf_version_t version); + +cairo_public void +cairo_pdf_surface_set_size (cairo_surface_t *surface, + double width_in_points, + double height_in_points); + +/** + * 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 */ +# error Cairo was not compiled with support for the pdf backend +#endif /* CAIRO_HAS_PDF_SURFACE */ + +#endif /* CAIRO_PDF_H */ diff --git a/gfx/cairo/cairo/src/cairo-pen.c b/gfx/cairo/cairo/src/cairo-pen.c new file mode 100644 index 0000000000..9bf960423d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-pen.c @@ -0,0 +1,475 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2008 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * 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-error-private.h" +#include "cairo-slope-private.h" + +static void +_cairo_pen_compute_slopes (cairo_pen_t *pen); + +cairo_status_t +_cairo_pen_init (cairo_pen_t *pen, + double radius, + double tolerance, + const cairo_matrix_t *ctm) +{ + int i; + int reflect; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); + + pen->radius = radius; + pen->tolerance = tolerance; + + reflect = _cairo_matrix_compute_determinant (ctm) < 0.; + + pen->num_vertices = _cairo_pen_vertices_needed (tolerance, + radius, + ctm); + + if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { + pen->vertices = _cairo_malloc_ab (pen->num_vertices, + sizeof (cairo_pen_vertex_t)); + if (unlikely (pen->vertices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else { + pen->vertices = pen->vertices_embedded; + } + + /* + * Compute pen coordinates. To generate the right ellipse, compute points around + * a circle in user space and transform them to device space. To get a consistent + * orientation in device space, flip the pen if the transformation matrix + * is reflecting + */ + for (i=0; i < pen->num_vertices; i++) { + 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); + } + + _cairo_pen_compute_slopes (pen); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_pen_fini (cairo_pen_t *pen) +{ + if (pen->vertices != pen->vertices_embedded) + free (pen->vertices); + + + VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); +} + +cairo_status_t +_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (pen, sizeof (cairo_pen_t))); + + *pen = *other; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + pen->vertices = pen->vertices_embedded; + if (pen->num_vertices) { + if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { + pen->vertices = _cairo_malloc_ab (pen->num_vertices, + sizeof (cairo_pen_vertex_t)); + if (unlikely (pen->vertices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + memcpy (pen->vertices, other->vertices, + pen->num_vertices * sizeof (cairo_pen_vertex_t)); + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) +{ + cairo_status_t status; + int num_vertices; + int i; + + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + num_vertices = pen->num_vertices + num_points; + if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) || + pen->vertices != pen->vertices_embedded) + { + cairo_pen_vertex_t *vertices; + + if (pen->vertices == pen->vertices_embedded) { + vertices = _cairo_malloc_ab (num_vertices, + sizeof (cairo_pen_vertex_t)); + if (unlikely (vertices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (vertices, pen->vertices, + pen->num_vertices * sizeof (cairo_pen_vertex_t)); + } else { + vertices = _cairo_realloc_ab (pen->vertices, + num_vertices, + sizeof (cairo_pen_vertex_t)); + if (unlikely (vertices == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pen->vertices = vertices; + } + + pen->num_vertices = num_vertices; + + /* initialize new vertices */ + for (i=0; i < num_points; i++) + pen->vertices[pen->num_vertices-num_points+i].point = point[i]; + + status = _cairo_hull_compute (pen->vertices, &pen->num_vertices); + if (unlikely (status)) + return status; + + _cairo_pen_compute_slopes (pen); + + return CAIRO_STATUS_SUCCESS; +} + +/* +The circular pen in user space is transformed into an ellipse in +device space. + +We construct the pen by computing points along the circumference +using equally spaced angles. + +We show that this approximation to the ellipse has maximum error at the +major axis of the ellipse. + +Set + + M = major axis length + m = minor axis length + +Align 'M' along the X axis and 'm' along the Y axis and draw +an ellipse parameterized by angle 't': + + x = M cos t y = m sin t + +Perturb t by ± d and compute two new points (x+,y+), (x-,y-). +The distance from the average of these two points to (x,y) represents +the maximum error in approximating the ellipse with a polygon formed +from vertices 2∆ radians apart. + + x+ = M cos (t+∆) y+ = m sin (t+∆) + x- = M cos (t-∆) y- = m sin (t-∆) + +Now compute the approximation error, E: + + Ex = (x - (x+ + x-) / 2) + Ex = (M cos(t) - (Mcos(t+∆) + Mcos(t-∆))/2) + = M (cos(t) - (cos(t)cos(∆) + sin(t)sin(∆) + + cos(t)cos(∆) - sin(t)sin(∆))/2) + = M(cos(t) - cos(t)cos(∆)) + = M cos(t) (1 - cos(∆)) + + Ey = y - (y+ - y-) / 2 + = m sin (t) - (m sin(t+∆) + m sin(t-∆)) / 2 + = m (sin(t) - (sin(t)cos(∆) + cos(t)sin(∆) + + sin(t)cos(∆) - cos(t)sin(∆))/2) + = m (sin(t) - sin(t)cos(∆)) + = m sin(t) (1 - cos(∆)) + + E² = Ex² + Ey² + = (M cos(t) (1 - cos (∆)))² + (m sin(t) (1-cos(∆)))² + = (1 - cos(∆))² (M² cos²(t) + m² sin²(t)) + = (1 - cos(∆))² ((m² + M² - m²) cos² (t) + m² sin²(t)) + = (1 - cos(∆))² (M² - m²) cos² (t) + (1 - cos(∆))² m² + +Find the extremum by differentiation wrt t and setting that to zero + +∂(E²)/∂(t) = (1-cos(∆))² (M² - m²) (-2 cos(t) sin(t)) + + 0 = 2 cos (t) sin (t) + 0 = sin (2t) + t = nπ + +Which is to say that the maximum and minimum errors occur on the +axes of the ellipse at 0 and π radians: + + E²(0) = (1-cos(∆))² (M² - m²) + (1-cos(∆))² m² + = (1-cos(∆))² M² + E²(π) = (1-cos(∆))² m² + +maximum error = M (1-cos(∆)) +minimum error = m (1-cos(∆)) + +We must make maximum error ≤ tolerance, so compute the ∆ needed: + + tolerance = M (1-cos(∆)) + tolerance / M = 1 - cos (∆) + cos(∆) = 1 - tolerance/M + ∆ = acos (1 - tolerance / M); + +Remembering that ∆ is half of our angle between vertices, +the number of vertices is then + + vertices = ceil(2π/2∆). + = ceil(π/∆). + +Note that this also equation works for M == m (a circle) as it +doesn't matter where on the circle the error is computed. +*/ + +int +_cairo_pen_vertices_needed (double tolerance, + double radius, + const cairo_matrix_t *matrix) +{ + /* + * the pen is a circle that gets transformed to an ellipse by matrix. + * 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; + + if (tolerance >= 4*major_axis) { /* XXX relaxed from 2*major for inkscape */ + num_vertices = 1; + } else if (tolerance >= major_axis) { + num_vertices = 4; + } else { + num_vertices = ceil (2*M_PI / acos (1 - tolerance / major_axis)); + + /* number of vertices must be even */ + if (num_vertices % 2) + num_vertices++; + + /* And we must always have at least 4 vertices. */ + if (num_vertices < 4) + num_vertices = 4; + } + + return num_vertices; +} + +static void +_cairo_pen_compute_slopes (cairo_pen_t *pen) +{ + int i, i_prev; + cairo_pen_vertex_t *prev, *v, *next; + + for (i=0, i_prev = pen->num_vertices - 1; + i < pen->num_vertices; + i_prev = i++) { + prev = &pen->vertices[i_prev]; + v = &pen->vertices[i]; + next = &pen->vertices[(i + 1) % pen->num_vertices]; + + _cairo_slope_init (&v->slope_cw, &prev->point, &v->point); + _cairo_slope_init (&v->slope_ccw, &v->point, &next->point); + } +} +/* + * Find active pen vertex for clockwise edge of stroke at the given slope. + * + * The strictness of the inequalities here is delicate. The issue is + * that the slope_ccw member of one pen vertex will be equivalent to + * the slope_cw member of the next pen vertex in a counterclockwise + * order. However, for this function, we care strongly about which + * vertex is returned. + * + * [I think the "care strongly" above has to do with ensuring that the + * pen's "extra points" from the spline's initial and final slopes are + * properly found when beginning the spline stroking.] + */ +int +_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope) +{ + int i; + + for (i=0; i < pen->num_vertices; i++) { + if ((_cairo_slope_compare (slope, &pen->vertices[i].slope_ccw) < 0) && + (_cairo_slope_compare (slope, &pen->vertices[i].slope_cw) >= 0)) + break; + } + + /* If the desired slope cannot be found between any of the pen + * vertices, then we must have a degenerate pen, (such as a pen + * that's been transformed to a line). In that case, we consider + * the first pen vertex as the appropriate clockwise vertex. + */ + if (i == pen->num_vertices) + i = 0; + + return i; +} + +/* Find active pen vertex for counterclockwise edge of stroke at the given slope. + * + * Note: See the comments for _cairo_pen_find_active_cw_vertex_index + * for some details about the strictness of the inequalities here. + */ +int +_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope) +{ + cairo_slope_t slope_reverse; + int i; + + slope_reverse = *slope; + slope_reverse.dx = -slope_reverse.dx; + slope_reverse.dy = -slope_reverse.dy; + + for (i=pen->num_vertices-1; i >= 0; i--) { + if ((_cairo_slope_compare (&pen->vertices[i].slope_ccw, &slope_reverse) >= 0) && + (_cairo_slope_compare (&pen->vertices[i].slope_cw, &slope_reverse) < 0)) + break; + } + + /* If the desired slope cannot be found between any of the pen + * vertices, then we must have a degenerate pen, (such as a pen + * that's been transformed to a line). In that case, we consider + * the last pen vertex as the appropriate counterclockwise vertex. + */ + if (i < 0) + i = pen->num_vertices - 1; + + 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 0000000000..d705025c8e --- /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-platform.h b/gfx/cairo/cairo/src/cairo-platform.h new file mode 100644 index 0000000000..bfec29f674 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-platform.h @@ -0,0 +1,69 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 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 + * 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): + * Stuart Parmenter + */ + +#ifndef CAIRO_PLATFORM_H +#define CAIRO_PLATFORM_H + +#include "prcpucfg.h" + +/* we're replacing any definition from cairoint.h etc */ +#undef cairo_public + +#ifdef HAVE_VISIBILITY_HIDDEN_ATTRIBUTE +#define CVISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define CVISIBILITY_HIDDEN __hidden +#else +#define CVISIBILITY_HIDDEN +#endif + +/* In libxul builds we don't ever want to export cairo symbols */ +#define cairo_public extern CVISIBILITY_HIDDEN + +#define CCALLBACK +#define CCALLBACK_DECL +#define CSTATIC_CALLBACK(__x) static __x + +#ifdef MOZILLA_VERSION +#include "cairo-rename.h" +#endif + +#if defined(IS_BIG_ENDIAN) +#define WORDS_BIGENDIAN +#define FLOAT_WORDS_BIGENDIAN +#endif + +#endif /* CAIRO_PLATFORM_H */ diff --git a/gfx/cairo/cairo/src/cairo-png.c b/gfx/cairo/cairo/src/cairo-png.c new file mode 100644 index 0000000000..f576047b19 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-png.c @@ -0,0 +1,973 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2003 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 + * Kristian Høgsberg + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-output-stream-private.h" + +#include +#include +#include + +/** + * SECTION:cairo-png + * @Title: PNG Support + * @Short_Description: Reading and writing PNG images + * @See_Also: #cairo_surface_t + * + * 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: + * + * 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; + void *closure; + cairo_output_stream_t *png_data; +}; + + +/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ +static void +unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + uint8_t alpha; + + memcpy (&pixel, b, sizeof (uint32_t)); + alpha = (pixel & 0xff000000) >> 24; + if (alpha == 0) { + b[0] = b[1] = b[2] = b[3] = 0; + } else { + b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; + b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; + b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; + b[3] = alpha; + } + } +} + +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) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *b = &data[i]; + uint32_t pixel; + + memcpy (&pixel, b, sizeof (uint32_t)); + + b[0] = (pixel & 0xff0000) >> 16; + b[1] = (pixel & 0x00ff00) >> 8; + b[2] = (pixel & 0x0000ff) >> 0; + b[3] = 0; + } +} + +/* Use a couple of simple error callbacks that do not print anything to + * stderr and rely on the user to check for errors via the #cairo_status_t + * return. + */ +static void +png_simple_error_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_PNG_ERROR); + +#ifdef PNG_SETJMP_SUPPORTED + longjmp (png_jmpbuf (png), 1); +#endif + + /* if we get here, then we have to choice but to abort ... */ +} + +static void +png_simple_warning_callback (png_structp png, + png_const_charp error_msg) +{ + /* 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. + */ +} + + +/* Starting with libpng-1.2.30, we must explicitly specify an output_flush_fn. + * Otherwise, we will segfault if we are writing to a stream. */ +static void +png_simple_output_flush_fn (png_structp png_ptr) +{ +} + +static cairo_status_t +write_png (cairo_surface_t *surface, + png_rw_ptr write_func, + void *closure) +{ + int i; + cairo_int_status_t status; + cairo_image_surface_t *image; + cairo_image_surface_t * volatile clone; + void *image_extra; + png_struct *png; + png_info *info; + png_byte **volatile rows = NULL; + png_color_16 white; + int png_color_type; + int bpc; + unsigned char *volatile u16_copy = NULL; + + status = _cairo_surface_acquire_source_image (surface, + &image, + &image_extra); + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + else if (unlikely (status)) + return status; + + /* PNG complains about "Image width or height is zero in IHDR" */ + if (image->width == 0 || image->height == 0) { + status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); + goto BAIL1; + } + + /* 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; + + rows = _cairo_malloc_ab (clone->height, sizeof (png_byte*)); + if (unlikely (rows == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL2; + } + + 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, + png_simple_warning_callback); + if (unlikely (png == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL3; + } + + info = png_create_info_struct (png); + if (unlikely (info == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL4; + } + +#ifdef PNG_SETJMP_SUPPORTED + if (setjmp (png_jmpbuf (png))) + goto BAIL4; +#endif + + png_set_write_fn (png, closure, write_func, png_simple_output_flush_fn); + + switch (clone->format) { + case CAIRO_FORMAT_ARGB32: + 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: + bpc = 8; + png_color_type = PNG_COLOR_TYPE_RGB; + break; + case CAIRO_FORMAT_A8: + bpc = 8; + png_color_type = PNG_COLOR_TYPE_GRAY; + break; + case CAIRO_FORMAT_A1: + 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: + status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT); + goto BAIL4; + } + + png_set_IHDR (png, info, + clone->width, + clone->height, bpc, + png_color_type, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + white.gray = (1 << bpc) - 1; + white.red = white.blue = white.green = white.gray; + png_set_bKGD (png, info, &white); + + if (0) { /* XXX extract meta-data from surface (i.e. creation date) */ + png_time pt; + + png_convert_from_time_t (&pt, time (NULL)); + png_set_tIME (png, info, &pt); + } + + /* We have to call png_write_info() before setting up the write + * transformation, since it stores data internally in 'png' + * that is needed for the write transformation functions to work. + */ + png_write_info (png, info); + + if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { + if (clone->format != CAIRO_FORMAT_RGBA128F) + png_set_write_user_transform_fn (png, unpremultiply_data); + } else if (png_color_type == PNG_COLOR_TYPE_RGB) { + 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); + } + + png_write_image (png, rows); + png_write_end (png, info); + +BAIL4: + png_destroy_write_struct (&png, &info); +BAIL3: + free (rows); +BAIL2: + cairo_surface_destroy (&clone->base); + free (u16_copy); +BAIL1: + _cairo_surface_release_source_image (surface, image, image_extra); + + return status; +} + +static void +stdio_write_func (png_structp png, png_bytep data, png_size_t size) +{ + FILE *fp; + + fp = png_get_io_ptr (png); + while (size) { + size_t ret = fwrite (data, 1, size, fp); + size -= ret; + data += ret; + if (size && ferror (fp)) { + cairo_status_t *error = png_get_error_ptr (png); + if (*error == CAIRO_STATUS_SUCCESS) + *error = _cairo_error (CAIRO_STATUS_WRITE_ERROR); + png_error (png, NULL); + } + } +} + +/** + * cairo_surface_write_to_png: + * @surface: a #cairo_surface_t with pixel contents + * @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. + * + * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written + * successfully. Otherwise, %CAIRO_STATUS_NO_MEMORY if memory could not + * 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, 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, + const char *filename) +{ + FILE *fp; + cairo_status_t status; + + if (surface->status) + return surface->status; + + if (surface->finished) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + status = _cairo_fopen (filename, "wb", &fp); + + if (status != CAIRO_STATUS_SUCCESS) + return _cairo_error (status); + + if (fp == NULL) { + switch (errno) { + case ENOMEM: + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + default: + return _cairo_error (CAIRO_STATUS_WRITE_ERROR); + } + } + + status = write_png (surface, stdio_write_func, fp); + + if (fclose (fp) && status == CAIRO_STATUS_SUCCESS) + status = _cairo_error (CAIRO_STATUS_WRITE_ERROR); + + return status; +} + +struct png_write_closure_t { + cairo_write_func_t write_func; + void *closure; +}; + +static void +stream_write_func (png_structp png, png_bytep data, png_size_t size) +{ + cairo_status_t status; + struct png_write_closure_t *png_closure; + + png_closure = png_get_io_ptr (png); + status = png_closure->write_func (png_closure->closure, data, size); + if (unlikely (status)) { + cairo_status_t *error = png_get_error_ptr (png); + if (*error == CAIRO_STATUS_SUCCESS) + *error = status; + png_error (png, NULL); + } +} + +/** + * cairo_surface_write_to_png_stream: + * @surface: a #cairo_surface_t with pixel contents + * @write_func: a #cairo_write_func_t + * @closure: closure data for the write function + * + * Writes the image surface to the write function. + * + * Return value: %CAIRO_STATUS_SUCCESS if the PNG file was written + * 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, 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, + cairo_write_func_t write_func, + void *closure) +{ + struct png_write_closure_t png_closure; + + if (surface->status) + return surface->status; + + if (surface->finished) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + png_closure.write_func = write_func; + png_closure.closure = closure; + + return write_png (surface, stream_write_func, &png_closure); +} +slim_hidden_def (cairo_surface_write_to_png_stream); + +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = (alpha * color) + 0x80; + return ((temp + (temp >> 8)) >> 8); +} + +/* Premultiplies data and converts RGBA bytes => native endian */ +static void +premultiply_data (png_structp png, + png_row_infop row_info, + png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *base = &data[i]; + uint8_t alpha = base[3]; + uint32_t p; + + if (alpha == 0) { + p = 0; + } else { + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + + if (alpha != 0xff) { + red = multiply_alpha (alpha, red); + green = multiply_alpha (alpha, green); + blue = multiply_alpha (alpha, blue); + } + p = ((uint32_t)alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + } + memcpy (base, &p, sizeof (uint32_t)); + } +} + +/* Converts RGBx bytes to native endian xRGB */ +static void +convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) { + uint8_t *base = &data[i]; + uint8_t red = base[0]; + uint8_t green = base[1]; + uint8_t blue = base[2]; + uint32_t pixel; + + pixel = (0xffu << 24) | (red << 16) | (green << 8) | (blue << 0); + memcpy (base, &pixel, sizeof (uint32_t)); + } +} + +static cairo_status_t +stdio_read_func (void *closure, unsigned char *data, unsigned int size) +{ + FILE *file = closure; + + while (size) { + size_t ret; + + ret = fread (data, 1, size, file); + size -= ret; + data += ret; + + if (size && (feof (file) || ferror (file))) + return _cairo_error (CAIRO_STATUS_READ_ERROR); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +stream_read_func (png_structp png, png_bytep data, png_size_t size) +{ + cairo_status_t status; + struct png_read_closure_t *png_closure; + + png_closure = png_get_io_ptr (png); + status = png_closure->read_func (png_closure->closure, data, size); + if (unlikely (status)) { + cairo_status_t *error = png_get_error_ptr (png); + if (*error == CAIRO_STATUS_SUCCESS) + *error = status; + png_error (png, NULL); + } + + _cairo_output_stream_write (png_closure->png_data, data, size); +} + +static cairo_surface_t * +read_png (struct png_read_closure_t *png_closure) +{ + cairo_surface_t * volatile surface; + png_struct *png = NULL; + png_info *info; + 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; + cairo_format_t format; + cairo_status_t status; + unsigned char *mime_data; + unsigned long mime_data_length; + + png_closure->png_data = _cairo_memory_stream_create (); + + /* XXX: Perhaps we'll want some other error handlers? */ + png = png_create_read_struct (PNG_LIBPNG_VER_STRING, + &status, + png_simple_error_callback, + png_simple_warning_callback); + if (unlikely (png == NULL)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto BAIL; + } + + info = png_create_info_struct (png); + if (unlikely (info == NULL)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto BAIL; + } + + png_set_read_fn (png, png_closure, stream_read_func); + + status = CAIRO_STATUS_SUCCESS; +#ifdef PNG_SETJMP_SUPPORTED + if (setjmp (png_jmpbuf (png))) { + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } +#endif + + png_read_info (png, info); + + png_get_IHDR (png, info, + &png_width, &png_height, &depth, + &color_type, &interlace, NULL, NULL); + if (unlikely (status)) { /* catch any early warnings */ + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + + /* convert palette/gray image to rgb */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png); + + /* expand gray bit depth if needed */ + if (color_type == PNG_COLOR_TYPE_GRAY) { +#if PNG_LIBPNG_VER >= 10209 + png_set_expand_gray_1_2_4_to_8 (png); +#else + png_set_gray_1_2_4_to_8 (png); +#endif + } + + /* transform transparency to alpha */ + if (png_get_valid (png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png); + + if (depth < 8) + png_set_packing (png); + + /* convert grayscale to RGB */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb (png); + } + + if (interlace != PNG_INTERLACE_NONE) + png_set_interlace_handling (png); + + png_set_filler (png, 0xff, PNG_FILLER_AFTER); + + /* recheck header after setting EXPAND options */ + png_read_update_info (png, info); + png_get_IHDR (png, info, + &png_width, &png_height, &depth, + &color_type, &interlace, NULL, NULL); + if ((depth != 8 && depth != 16) || + ! (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA)) + { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_READ_ERROR)); + goto BAIL; + } + + switch (color_type) { + default: + ASSERT_NOT_REACHED; + /* fall-through just in case ;-) */ + + case PNG_COLOR_TYPE_RGB_ALPHA: + 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: + if (depth == 8) { + format = CAIRO_FORMAT_RGB24; + png_set_read_user_transform_fn (png, convert_bytes_to_data); + } else { + format = CAIRO_FORMAT_RGB96F; + } + break; + } + + stride = cairo_format_stride_for_width (format, png_width); + if (stride < 0) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_STRIDE)); + goto BAIL; + } + + data = _cairo_malloc_ab (png_height, stride); + if (unlikely (data == NULL)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto BAIL; + } + + row_pointers = _cairo_malloc_ab (png_height, sizeof (char *)); + if (unlikely (row_pointers == NULL)) { + surface = _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + goto BAIL; + } + + for (i = 0; i < png_height; i++) + row_pointers[i] = &data[i * (ptrdiff_t)stride]; + + png_read_image (png, row_pointers); + png_read_end (png, info); + + if (unlikely (status)) { /* catch any late warnings - probably hit an error already */ + surface = _cairo_surface_create_in_error (status); + 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); + if (surface->status) + goto BAIL; + + _cairo_image_surface_assume_ownership_of_data ((cairo_image_surface_t*)surface); + data = NULL; + + _cairo_debug_check_image_surface_is_defined (surface); + + status = _cairo_memory_stream_destroy (png_closure->png_data, + &mime_data, + &mime_data_length); + png_closure->png_data = NULL; + if (unlikely (status)) { + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + + status = cairo_surface_set_mime_data (surface, + CAIRO_MIME_TYPE_PNG, + mime_data, + mime_data_length, + free, + mime_data); + if (unlikely (status)) { + free (mime_data); + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + goto BAIL; + } + + BAIL: + free (row_pointers); + free (data); + if (png != NULL) + png_destroy_read_struct (&png, &info, NULL); + if (png_closure->png_data != NULL) { + cairo_status_t status_ignored; + + status_ignored = _cairo_output_stream_destroy (png_closure->png_data); + } + + return surface; +} + +/** + * cairo_image_surface_create_from_png: + * @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. + * + * Return value: a new #cairo_surface_t initialized with the contents + * of the PNG file, or a "nil" surface if any error occurred. A nil + * surface can be checked for with cairo_surface_status(surface) which + * may return one of the following values: + * + * %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); + + if (png_closure.closure == NULL) { + switch (errno) { + case ENOMEM: + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + break; + case ENOENT: + status = _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND); + break; + default: + status = _cairo_error (CAIRO_STATUS_READ_ERROR); + break; + } + return _cairo_surface_create_in_error (status); + } + + png_closure.read_func = stdio_read_func; + + surface = read_png (&png_closure); + + fclose (png_closure.closure); + + return surface; +} + +/** + * cairo_image_surface_create_from_png_stream: + * @read_func: function called to read the data of the file + * @closure: data to pass to @read_func. + * + * Creates a new image surface from PNG data read incrementally + * via the @read_func function. + * + * Return value: a new #cairo_surface_t initialized with the contents + * of the PNG file or a "nil" surface if the data read is not a valid PNG image + * or memory could not be allocated for the operation. A nil + * surface can be checked for with cairo_surface_status(surface) which + * may return one of the following values: + * + * %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, + void *closure) +{ + struct png_read_closure_t png_closure; + + png_closure.read_func = read_func; + png_closure.closure = closure; + + return read_png (&png_closure); +} 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 0000000000..2277e14e5c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-polygon-intersect.c @@ -0,0 +1,1474 @@ +/* + * 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; + while (right) { + 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; + }; + if (! right) + return; + + 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 0000000000..da6c9ab7c7 --- /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 new file mode 100644 index 0000000000..0e0b813285 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-polygon.c @@ -0,0 +1,608 @@ +/* -*- 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 + * + * 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-boxes-private.h" +#include "cairo-contour-private.h" +#include "cairo-error-private.h" + +#define DEBUG_POLYGON 0 + +#if DEBUG_POLYGON && !NDEBUG +static void +assert_last_edge_is_valid(cairo_polygon_t *polygon, + const cairo_box_t *limit) +{ + cairo_edge_t *edge; + cairo_fixed_t x; + + edge = &polygon->edges[polygon->num_edges-1]; + + assert (edge->bottom > edge->top); + assert (edge->top >= limit->p1.y); + assert (edge->bottom <= limit->p2.y); + + 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); + + 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) +{ + int n; + + polygon->limits = limits; + polygon->num_limits = num_limits; + + if (polygon->num_limits) { + polygon->limit = limits[0]; + for (n = 1; n < num_limits; n++) { + if (limits[n].p1.x < polygon->limit.p1.x) + polygon->limit.p1.x = limits[n].p1.x; + + if (limits[n].p1.y < polygon->limit.p1.y) + polygon->limit.p1.y = limits[n].p1.y; + + if (limits[n].p2.x > polygon->limit.p2.x) + polygon->limit.p2.x = limits[n].p2.x; + + if (limits[n].p2.y > polygon->limit.p2.y) + polygon->limit.p2.y = limits[n].p2.y; + } + } +} + +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_UNDEFINED (polygon, sizeof (cairo_polygon_t))); +} + +/* make room for at least one more edge */ +static cairo_bool_t +_cairo_polygon_grow (cairo_polygon_t *polygon) +{ + cairo_edge_t *new_edges; + int old_size = polygon->edges_size; + int new_size = 4 * old_size; + + if (CAIRO_INJECT_FAULT ()) { + polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + if (polygon->edges == polygon->edges_embedded) { + new_edges = _cairo_malloc_ab (new_size, sizeof (cairo_edge_t)); + if (new_edges != NULL) + memcpy (new_edges, polygon->edges, old_size * sizeof (cairo_edge_t)); + } else { + new_edges = _cairo_realloc_ab (polygon->edges, + new_size, sizeof (cairo_edge_t)); + } + + if (unlikely (new_edges == NULL)) { + polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + polygon->edges = new_edges; + polygon->edges_size = new_size; + + return TRUE; +} + +static void +_add_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + int top, int bottom, + int dir) +{ + cairo_edge_t *edge; + + assert (top < bottom); + + if (unlikely (polygon->num_edges == polygon->edges_size)) { + if (! _cairo_polygon_grow (polygon)) + return; + } + + edge = &polygon->edges[polygon->num_edges++]; + edge->line.p1 = *p1; + edge->line.p2 = *p2; + edge->top = top; + edge->bottom = bottom; + edge->dir = dir; + + if (top < polygon->extents.p1.y) + polygon->extents.p1.y = top; + if (bottom > polygon->extents.p2.y) + polygon->extents.p2.y = bottom; + + if (p1->x < polygon->extents.p1.x || p1->x > polygon->extents.p2.x) { + cairo_fixed_t x = p1->x; + if (top != p1->y) + x = _cairo_edge_compute_intersection_x_for_y (p1, p2, top); + if (x < polygon->extents.p1.x) + polygon->extents.p1.x = x; + if (x > polygon->extents.p2.x) + polygon->extents.p2.x = x; + } + + if (p2->x < polygon->extents.p1.x || p2->x > polygon->extents.p2.x) { + cairo_fixed_t x = p2->x; + if (bottom != p2->y) + x = _cairo_edge_compute_intersection_x_for_y (p1, p2, bottom); + if (x < polygon->extents.p1.x) + polygon->extents.p1.x = x; + if (x > polygon->extents.p2.x) + polygon->extents.p2.x = x; + } +} + +static void +_add_clipped_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + const int top, const int bottom, + const int dir) +{ + 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; + + bot_left.x = limits->p1.x; + bot_left.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); + 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) */ + + _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, &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; + + /* + * 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. + */ + + 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++; + } + + 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; + } + + 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--; + } + + 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 (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++; + } + + 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; + } + + 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--; + } + + 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 (top_y != bot_y) { + _add_edge (polygon, p1, p2, top_y, bot_y, dir); + assert_last_edge_is_valid (polygon, limits); + } + } + } +} + +static void +_cairo_polygon_add_edge (cairo_polygon_t *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2, + int dir) +{ + /* drop horizontal edges */ + if (p1->y == p2->y) + return; + + if (p1->y > p2->y) { + const cairo_point_t *t; + t = p1, p1 = p2, p2 = t; + dir = -dir; + } + + if (polygon->num_limits) { + if (p2->y <= polygon->limit.p1.y) + return; + + if (p1->y >= polygon->limit.p2.y) + return; + + _add_clipped_edge (polygon, p1, p2, p1->y, p2->y, dir); + } else + _add_edge (polygon, p1, p2, p1->y, p2->y, dir); +} + +cairo_status_t +_cairo_polygon_add_external_edge (void *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + _cairo_polygon_add_edge (polygon, p1, p2, 1); + return _cairo_polygon_status (polygon); +} + +cairo_status_t +_cairo_polygon_add_line (cairo_polygon_t *polygon, + const cairo_line_t *line, + int top, int bottom, + int dir) +{ + /* drop horizontal edges */ + if (line->p1.y == line->p2.y) + return CAIRO_STATUS_SUCCESS; + + if (bottom <= top) + return CAIRO_STATUS_SUCCESS; + + if (polygon->num_limits) { + if (line->p2.y <= polygon->limit.p1.y) + return CAIRO_STATUS_SUCCESS; + + if (line->p1.y >= polygon->limit.p2.y) + return CAIRO_STATUS_SUCCESS; + + _add_clipped_edge (polygon, &line->p1, &line->p2, top, bottom, dir); + } else + _add_edge (polygon, &line->p1, &line->p2, top, bottom, dir); + + return polygon->status; +} + +cairo_status_t +_cairo_polygon_add_contour (cairo_polygon_t *polygon, + const cairo_contour_t *contour) +{ + const struct _cairo_contour_chain *chain; + const cairo_point_t *prev = NULL; + int i; + + if (contour->chain.num_points <= 1) + return CAIRO_INT_STATUS_SUCCESS; + + 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]; + } + } + + return polygon->status; +} + +void +_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy) +{ + int n; + + 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; + } +} diff --git a/gfx/cairo/cairo/src/cairo-private.h b/gfx/cairo/cairo/src/cairo-private.h new file mode 100644 index 0000000000..9f4f55b7ce --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-private.h @@ -0,0 +1,64 @@ +/* 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_PRIVATE_H +#define CAIRO_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-reference-count-private.h" + +CAIRO_BEGIN_DECLS + +struct _cairo { + cairo_reference_count_t ref_count; + cairo_status_t status; + cairo_user_data_array_t user_data; + + 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 new file mode 100644 index 0000000000..f184031902 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-ps-surface-private.h @@ -0,0 +1,125 @@ +/* -*- 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 © 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 + * Kristian Høgsberg + * Keith Packard + */ + +#ifndef CAIRO_PS_SURFACE_PRIVATE_H +#define CAIRO_PS_SURFACE_PRIVATE_H + +#include "cairo-ps.h" + +#include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-pdf-operators-private.h" + +#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; + + /* Here final_stream corresponds to the stream/file passed to + * cairo_ps_surface_create surface is built. Meanwhile stream is a + * temporary stream in which the file output is built, (so that + * the header can be built and inserted into the target stream + * before the contents of the temporary stream are copied). */ + cairo_output_stream_t *final_stream; + + FILE *tmpfile; + cairo_output_stream_t *stream; + + cairo_bool_t eps; + cairo_bool_t contains_eps; + cairo_content_t content; + double width; + double height; + 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; + 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; + + int num_pages; + + cairo_paginated_mode_t paginated_mode; + + cairo_bool_t force_fallbacks; + cairo_bool_t has_creation_date; + time_t creation_date; + + cairo_scaled_font_subsets_t *font_subsets; + + cairo_list_t document_media; + cairo_array_t dsc_header_comments; + 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; + cairo_ps_level_t ps_level_used; + + cairo_surface_clipper_t clipper; + + 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 new file mode 100644 index 0000000000..4adccad6b7 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-ps-surface.c @@ -0,0 +1,5434 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2003 University of Southern California + * Copyright © 2005 Red Hat, Inc + * Copyright © 2007,2008 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * 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 + * Keith Packard + * Adrian Johnson + */ + + +/* + * Design of the PS output: + * + * The PS output is harmonised with the PDF operations using PS procedures + * to emulate the PDF operators. + * + * This has a number of advantages: + * 1. A large chunk of code is shared between the PDF and PS backends. + * See cairo-pdf-operators. + * 2. Using gs to do PS -> PDF and PDF -> PS will always work well. + */ + +#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 +#include +#include +#include + +/* 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) \ + fprintf (stderr, "%s::%d -- %s\n", __FUNCTION__, __LINE__, (s)) +#else +#define DEBUG_FALLBACK(s) +#endif + +#ifndef HAVE_CTIME_R +#define ctime_r(T, BUF) ctime (T) +#endif + +/** + * SECTION:cairo-ps + * @Title: PostScript Surfaces + * @Short_Description: Rendering PostScript documents + * @See_Also: #cairo_surface_t + * + * 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_form_emit (void *entry, void *closure); + +static const cairo_ps_level_t _cairo_ps_levels[] = +{ + CAIRO_PS_LEVEL_2, + CAIRO_PS_LEVEL_3 +}; + +#define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels) + +static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] = +{ + "PS Level 2", + "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; + int height; +} cairo_page_standard_media_t; + +static const cairo_page_standard_media_t _cairo_page_standard_media[] = +{ + { "A0", 2384, 3371 }, + { "A1", 1685, 2384 }, + { "A2", 1190, 1684 }, + { "A3", 842, 1190 }, + { "A4", 595, 842 }, + { "A5", 420, 595 }, + { "B4", 729, 1032 }, + { "B5", 516, 729 }, + { "Letter", 612, 792 }, + { "Tabloid", 792, 1224 }, + { "Ledger", 1224, 792 }, + { "Legal", 612, 1008 }, + { "Statement", 396, 612 }, + { "Executive", 540, 720 }, + { "Folio", 612, 936 }, + { "Quarto", 610, 780 }, + { "10x14", 720, 1008 }, +}; + +typedef struct _cairo_page_media { + char *name; + int width; + int height; + 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) +{ + char ctime_buf[26]; + time_t now; + char **comments; + int i, num_comments; + int level; + const char *eps_header = ""; + cairo_bool_t has_bbox; + + if (surface->has_creation_date) + now = surface->creation_date; + else + now = time (NULL); + + if (surface->ps_level_used == CAIRO_PS_LEVEL_2) + level = 2; + else + level = 3; + + if (surface->eps) + eps_header = " EPSF-3.0"; + + _cairo_output_stream_printf (surface->final_stream, + "%%!PS-Adobe-3.0%s\n" + "%%%%Creator: cairo %s (https://cairographics.org)\n" + "%%%%CreationDate: %s" + "%%%%Pages: %d\n", + eps_header, + cairo_version_string (), + ctime_r (&now, ctime_buf), + surface->num_pages); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%DocumentData: Clean7Bit\n" + "%%%%LanguageLevel: %d\n", + level); + + if (!cairo_list_is_empty (&surface->document_media)) { + cairo_page_media_t *page; + cairo_bool_t first = TRUE; + + cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { + if (first) { + _cairo_output_stream_printf (surface->final_stream, + "%%%%DocumentMedia: "); + first = FALSE; + } else { + _cairo_output_stream_printf (surface->final_stream, + "%%%%+ "); + } + _cairo_output_stream_printf (surface->final_stream, + "%s %d %d 0 () ()\n", + page->name, + page->width, + page->height); + } + } + + 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"); + + _cairo_output_stream_printf (surface->final_stream, + "%%%%BeginProlog\n"); + + if (surface->eps) { + _cairo_output_stream_printf (surface->final_stream, + "50 dict begin\n"); + } else { + _cairo_output_stream_printf (surface->final_stream, + "/languagelevel where\n" + "{ pop languagelevel } { 1 } ifelse\n" + "%d lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto\n" + " (This print job requires a PostScript Language Level %d printer.) show\n" + " showpage quit } if\n", + level, + level); + } + + _cairo_output_stream_printf (surface->final_stream, + "/q { gsave } bind def\n" + "/Q { grestore } bind def\n" + "/cm { 6 array astore concat } bind def\n" + "/w { setlinewidth } bind def\n" + "/J { setlinecap } bind def\n" + "/j { setlinejoin } bind def\n" + "/M { setmiterlimit } bind def\n" + "/d { setdash } bind def\n" + "/m { moveto } bind def\n" + "/l { lineto } bind def\n" + "/c { curveto } bind def\n" + "/h { closepath } bind def\n" + "/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto\n" + " 0 exch rlineto 0 rlineto closepath } bind def\n" + "/S { stroke } bind def\n" + "/f { fill } bind def\n" + "/f* { eofill } bind def\n" + "/n { newpath } bind def\n" + "/W { clip } bind def\n" + "/W* { eoclip } bind def\n" + "/BT { } bind def\n" + "/ET { } bind def\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" + "/Tj { show currentpoint cairo_store_point } bind def\n" + "/TJ {\n" + " {\n" + " dup\n" + " type /stringtype eq\n" + " { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse\n" + " } forall\n" + " currentpoint cairo_store_point\n" + "} bind def\n" + "/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore\n" + " cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def\n" + "/Tf { pop /cairo_font exch def /cairo_font_matrix where\n" + " { pop cairo_selectfont } if } bind def\n" + "/Td { matrix translate cairo_font_matrix matrix concatmatrix dup\n" + " /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point\n" + " /cairo_font where { pop cairo_selectfont } if } bind def\n" + "/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def\n" + " 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" + "/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) { + comments = _cairo_array_index (&surface->dsc_setup_comments, 0); + for (i = 0; i < num_comments; i++) { + _cairo_output_stream_printf (surface->final_stream, + "%s\n", comments[i]); + free (comments[i]); + comments[i] = NULL; + } + } +} + +static cairo_status_t +_cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) + + +{ + cairo_type1_subset_t subset; + cairo_status_t status; + int length; + char name[64]; + + snprintf (name, sizeof name, "f-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_type1_subset_init (&subset, name, font_subset, TRUE); + if (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_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; +} + + +static cairo_status_t +_cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) +{ + cairo_type1_subset_t subset; + cairo_status_t status; + int length; + char name[64]; + + snprintf (name, sizeof name, "f-%d-%d", + font_subset->font_id, font_subset->subset_id); + status = _cairo_type1_fallback_init_hex (&subset, name, font_subset); + if (unlikely (status)) + return status; + +#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); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) + + +{ + cairo_truetype_subset_t subset; + cairo_status_t status; + unsigned int i, begin, end; + + status = _cairo_truetype_subset_init_ps (&subset, font_subset); + 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_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" + "/FontName /%s def\n" + "/PaintType 0 def\n" + "/FontMatrix [ 1 0 0 1 0 0 ] def\n" + "/FontBBox [ 0 0 0 0 ] def\n" + "/Encoding 256 array def\n" + "0 1 255 { Encoding exch /.notdef put } for\n", + subset.ps_name); + + /* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */ + + 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); + } + } + } + + _cairo_output_stream_printf (surface->final_stream, + "/CharStrings %d dict dup begin\n" + "/.notdef 0 def\n", + font_subset->num_glyphs); + + for (i = 1; i < font_subset->num_glyphs; i++) { + if (font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (surface->final_stream, + "/%s %d def\n", + font_subset->glyph_names[i], i); + } else { + _cairo_output_stream_printf (surface->final_stream, + "/g%d %d def\n", i, i); + } + } + + _cairo_output_stream_printf (surface->final_stream, + "end readonly def\n"); + + _cairo_output_stream_printf (surface->final_stream, + "/sfnts [\n"); + begin = 0; + end = 0; + for (i = 0; i < subset.num_string_offsets; i++) { + end = subset.string_offsets[i]; + _cairo_output_stream_printf (surface->final_stream,"<"); + _cairo_output_stream_write_hex_string (surface->final_stream, + subset.data + begin, end - begin); + _cairo_output_stream_printf (surface->final_stream,"00>\n"); + begin = end; + } + if (subset.data_length > end) { + _cairo_output_stream_printf (surface->final_stream,"<"); + _cairo_output_stream_write_hex_string (surface->final_stream, + subset.data + end, subset.data_length - end); + _cairo_output_stream_printf (surface->final_stream,"00>\n"); + } + + _cairo_output_stream_printf (surface->final_stream, + "] def\n" + "/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_int_status_t +_cairo_ps_emit_imagemask (cairo_image_surface_t *image, + cairo_output_stream_t *stream) +{ + uint8_t *row, *byte; + int rows, cols; + + /* The only image type supported by Type 3 fonts are 1-bit image + * masks */ + assert (image->format == CAIRO_FORMAT_A1); + + _cairo_output_stream_printf (stream, + "<<\n" + " /ImageType 1\n" + " /Width %d\n" + " /Height %d\n" + " /ImageMatrix [%d 0 0 %d 0 %d]\n" + " /Decode [1 0]\n" + " /BitsPerComponent 1\n", + image->width, + image->height, + image->width, + -image->height, + image->height); + + _cairo_output_stream_printf (stream, + " /DataSource {<\n "); + for (row = image->data, rows = image->height; rows; row += image->stride, rows--) { + for (byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) { + uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); + _cairo_output_stream_printf (stream, "%02x ", output_byte); + } + _cairo_output_stream_printf (stream, "\n "); + } + _cairo_output_stream_printf (stream, ">}\n>>\n"); + + _cairo_output_stream_printf (stream, + "imagemask\n"); + + return _cairo_output_stream_get_status (stream); +} + +static cairo_int_status_t +_cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset, + void *closure) +{ + cairo_ps_surface_t *surface = closure; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + unsigned int i; + cairo_surface_t *type3_surface; + + type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font, + NULL, + _cairo_ps_emit_imagemask, + surface->font_subsets, + TRUE); + + for (i = 0; i < font_subset->num_glyphs; i++) { + status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface, + font_subset->glyphs[i]); + if (unlikely (status)) + break; + + } + cairo_surface_finish (type3_surface); + cairo_surface_destroy (type3_surface); + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface, + cairo_scaled_font_subset_t *font_subset) + + +{ + cairo_status_t status; + unsigned int i; + cairo_box_t font_bbox = {{0,0},{0,0}}; + cairo_box_t bbox = {{0,0},{0,0}}; + cairo_surface_t *type3_surface; + double width; + + if (font_subset->num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + +#if DEBUG_PS + _cairo_output_stream_printf (surface->final_stream, + "%% _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" + "/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, + TRUE); + status = type3_surface->status; + if (unlikely (status)) + return status; + + for (i = 0; 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); + } + } + + _cairo_output_stream_printf (surface->final_stream, + "/Glyphs [\n"); + + for (i = 0; i < font_subset->num_glyphs; i++) { + _cairo_output_stream_printf (surface->final_stream, + " { %% %d\n", i); + status = _cairo_type3_glyph_surface_emit_glyph (type3_surface, + surface->final_stream, + font_subset->glyphs[i], + &bbox, + &width); + if (unlikely (status)) + break; + + _cairo_output_stream_printf (surface->final_stream, + " }\n"); + if (i == 0) { + font_bbox.p1.x = bbox.p1.x; + font_bbox.p1.y = bbox.p1.y; + font_bbox.p2.x = bbox.p2.x; + font_bbox.p2.y = bbox.p2.y; + } else { + if (bbox.p1.x < font_bbox.p1.x) + font_bbox.p1.x = bbox.p1.x; + if (bbox.p1.y < font_bbox.p1.y) + font_bbox.p1.y = bbox.p1.y; + if (bbox.p2.x > font_bbox.p2.x) + font_bbox.p2.x = bbox.p2.x; + if (bbox.p2.y > font_bbox.p2.y) + font_bbox.p2.y = bbox.p2.y; + } + } + cairo_surface_finish (type3_surface); + cairo_surface_destroy (type3_surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->final_stream, + "] def\n" + "/FontBBox [%f %f %f %f] def\n" + "/BuildChar {\n" + " exch /Glyphs get\n" + " exch get\n" + " 10 dict begin exec end\n" + "} bind def\n" + "currentdict\n" + "end\n" + "/f-%d-%d exch definefont pop\n", + _cairo_fixed_to_double (font_bbox.p1.x), + - _cairo_fixed_to_double (font_bbox.p2.y), + _cairo_fixed_to_double (font_bbox.p2.x), + - _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_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_int_status_t status; + + status = _cairo_scaled_font_subset_create_glyph_names (font_subset); + if (_cairo_int_status_is_error (status)) + return status; + + status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_ps_surface_emit_type1_font_fallback (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + ASSERT_NOT_REACHED; + return CAIRO_STATUS_SUCCESS; +} + +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_int_status_t status; + + status = _cairo_scaled_font_subset_create_glyph_names (font_subset); + if (_cairo_int_status_is_error (status)) + return status; + + status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface) +{ + cairo_status_t status; + +#if DEBUG_PS + _cairo_output_stream_printf (surface->final_stream, + "%% _cairo_ps_surface_emit_font_subsets\n"); +#endif + + status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, + _cairo_ps_surface_analyze_user_font_subset, + surface); + if (unlikely (status)) + return status; + + status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets, + _cairo_ps_surface_emit_unscaled_font_subset, + surface); + if (unlikely (status)) + return status; + + status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets, + _cairo_ps_surface_emit_scaled_font_subset, + surface); + if (unlikely (status)) + return status; + + return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets, + _cairo_ps_surface_emit_scaled_font_subset, + surface); +} + + +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) +{ + char buf[4096]; + int n; + + if (ferror (surface->tmpfile) != 0) + return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); + + rewind (surface->tmpfile); + while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0) + _cairo_output_stream_write (surface->final_stream, buf, n); + + if (ferror (surface->tmpfile) != 0) + return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface) +{ + _cairo_output_stream_printf (surface->final_stream, + "%%%%Trailer\n"); + + if (surface->eps) { + _cairo_output_stream_printf (surface->final_stream, + "end\n"); + } + + _cairo_output_stream_printf (surface->final_stream, + "%%%%EOF\n"); +} + +static cairo_bool_t +_path_covers_bbox (cairo_ps_surface_t *surface, + cairo_path_fixed_t *path) +{ + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &box)) { + cairo_rectangle_int_t rect; + + _cairo_box_round_to_rectangle (&box, &rect); + + /* skip trivial whole-page clips */ + 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; + } + } + } + + return FALSE; +} + +static cairo_status_t +_cairo_ps_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_ps_surface_t *surface = cairo_container_of (clipper, + cairo_ps_surface_t, + clipper); + cairo_output_stream_t *stream = surface->stream; + cairo_status_t status; + + assert (surface->paginated_mode != CAIRO_PAGINATED_MODE_ANALYZE); + +#if DEBUG_PS + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_intersect_clip_path\n"); +#endif + + if (path == NULL) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (stream, "Q q\n"); + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return CAIRO_STATUS_SUCCESS; + } + + if (_path_covers_bbox (surface, path)) + return CAIRO_STATUS_SUCCESS; + + return _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); +} + +/* PLRM specifies a tolerance of 5 points when matching page sizes */ +static cairo_bool_t +_ps_page_dimension_equal (int a, int b) +{ + return (abs (a - b) < 5); +} + +static const char * +_cairo_ps_surface_get_page_media (cairo_ps_surface_t *surface) +{ + int width, height, i; + char buf[50]; + cairo_page_media_t *page; + const char *page_name; + + width = _cairo_lround (surface->width); + height = _cairo_lround (surface->height); + + /* search previously used page sizes */ + cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) { + if (_ps_page_dimension_equal (width, page->width) && + _ps_page_dimension_equal (height, page->height)) + return page->name; + } + + /* search list of standard page sizes */ + page_name = NULL; + for (i = 0; i < ARRAY_LENGTH (_cairo_page_standard_media); i++) { + if (_ps_page_dimension_equal (width, _cairo_page_standard_media[i].width) && + _ps_page_dimension_equal (height, _cairo_page_standard_media[i].height)) + { + page_name = _cairo_page_standard_media[i].name; + width = _cairo_page_standard_media[i].width; + height = _cairo_page_standard_media[i].height; + break; + } + } + + page = _cairo_malloc (sizeof (cairo_page_media_t)); + if (unlikely (page == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + if (page_name) { + page->name = strdup (page_name); + } else { + snprintf (buf, sizeof (buf), "%dx%dmm", + (int) _cairo_lround (surface->width * 25.4/72), + (int) _cairo_lround (surface->height * 25.4/72)); + page->name = strdup (buf); + } + + if (unlikely (page->name == NULL)) { + free (page); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + page->width = width; + page->height = height; + cairo_list_add_tail (&page->link, &surface->document_media); + + return page->name; +} + +static cairo_surface_t * +_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width, + double height) +{ + cairo_status_t status, status_ignored; + cairo_ps_surface_t *surface; + + surface = _cairo_malloc (sizeof (cairo_ps_surface_t)); + if (unlikely (surface == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP; + } + + _cairo_surface_init (&surface->base, + &cairo_ps_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA, + TRUE); /* is_vector */ + + surface->final_stream = stream; + + surface->tmpfile = tmpfile (); + if (surface->tmpfile == NULL) { + switch (errno) { + case ENOMEM: + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + break; + default: + status = _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR); + break; + } + goto CLEANUP_SURFACE; + } + + surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile); + status = _cairo_output_stream_get_status (surface->stream); + if (unlikely (status)) + goto CLEANUP_OUTPUT_STREAM; + + surface->font_subsets = _cairo_scaled_font_subsets_create_simple (); + if (unlikely (surface->font_subsets == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + 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, 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->current_pattern_is_solid_color = FALSE; + 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); + + _cairo_pdf_operators_init (&surface->pdf_operators, + surface->stream, + &surface->cairo_to_ps, + 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; + + surface->paginated_surface = _cairo_paginated_surface_create ( + &surface->base, + CAIRO_CONTENT_COLOR_ALPHA, + &cairo_ps_surface_paginated_backend); + status = surface->paginated_surface->status; + if (status == CAIRO_STATUS_SUCCESS) { + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + 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); + fclose (surface->tmpfile); + CLEANUP_SURFACE: + free (surface); + CLEANUP: + /* destroy stream on behalf of caller */ + status_ignored = _cairo_output_stream_destroy (stream); + + return _cairo_surface_create_in_error (status); +} + +/** + * cairo_ps_surface_create: + * @filename: a filename for the PS output (must be writable), %NULL may be + * used to specify no output. This will generate a PS surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PostScript surface of the specified size in points to be + * written to @filename. See cairo_ps_surface_create_for_stream() for + * a more flexible mechanism for handling the PostScript output than + * simply writing it to a named file. + * + * Note that the size of individual pages of the PostScript output can + * vary. See cairo_ps_surface_set_size(). + * + * 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.2 + **/ +cairo_surface_t * +cairo_ps_surface_create (const char *filename, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + +/** + * cairo_ps_surface_create_for_stream: + * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL + * to indicate a no-op @write_func. With a no-op @write_func, + * the surface may be queried or used as a source without + * generating any temporary files. + * @closure: the closure argument for @write_func + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a PostScript surface of the specified size in points to be + * written incrementally to the stream represented by @write_func and + * @closure. See cairo_ps_surface_create() for a more convenient way + * to simply direct the PostScript output to a named file. + * + * Note that the size of individual pages of the PostScript + * output can vary. See cairo_ps_surface_set_size(). + * + * 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.2 + **/ +cairo_surface_t * +cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_ps_surface_create_for_stream_internal (stream, + width_in_points, + height_in_points); +} + +static cairo_bool_t +_cairo_surface_is_ps (cairo_surface_t *surface) +{ + return surface->backend == &cairo_ps_surface_backend; +} + +/* If the abstract_surface is a paginated surface, and that paginated + * surface's target is a ps_surface, then set ps_surface to that + * target. Otherwise return FALSE. + */ +static cairo_bool_t +_extract_ps_surface (cairo_surface_t *surface, + cairo_bool_t set_error_on_failure, + cairo_ps_surface_t **ps_surface) +{ + cairo_surface_t *target; + + if (surface->status) + return FALSE; + if (surface->finished) { + if (set_error_on_failure) + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_paginated (surface)) { + if (set_error_on_failure) + _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) + _cairo_surface_set_error (surface, target->status); + return FALSE; + } + if (target->finished) { + if (set_error_on_failure) + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_ps (target)) { + if (set_error_on_failure) + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + *ps_surface = (cairo_ps_surface_t *) target; + return TRUE; +} + +/** + * cairo_ps_surface_restrict_to_level: + * @surface: a PostScript #cairo_surface_t + * @level: PostScript level + * + * Restricts the generated PostSript file to @level. See + * cairo_ps_get_levels() for a list of available level values that + * can be used here. + * + * This function should only be called before any drawing operations + * have been performed on the given surface. The simplest way to do + * this is to call this function immediately after creating the + * surface. + * + * Since: 1.6 + **/ +void +cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, + cairo_ps_level_t level) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + if (level < CAIRO_PS_LEVEL_LAST) + ps_surface->ps_level = level; +} + +/** + * cairo_ps_get_levels: + * @levels: supported level list + * @num_levels: list length + * + * Used to retrieve the list of supported levels. See + * cairo_ps_surface_restrict_to_level(). + * + * Since: 1.6 + **/ +void +cairo_ps_get_levels (cairo_ps_level_t const **levels, + int *num_levels) +{ + if (levels != NULL) + *levels = _cairo_ps_levels; + + if (num_levels != NULL) + *num_levels = CAIRO_PS_LEVEL_LAST; +} + +/** + * cairo_ps_level_to_string: + * @level: a level id + * + * Get the string representation of the given @level id. This function + * will return %NULL if @level id isn't valid. See cairo_ps_get_levels() + * for a way to get the list of valid level ids. + * + * Return value: the string associated to given level. + * + * Since: 1.6 + **/ +const char * +cairo_ps_level_to_string (cairo_ps_level_t level) +{ + if (level >= CAIRO_PS_LEVEL_LAST) + return NULL; + + return _cairo_ps_level_strings[level]; +} + +/** + * cairo_ps_surface_set_eps: + * @surface: a PostScript #cairo_surface_t + * @eps: %TRUE to output EPS format PostScript + * + * If @eps is %TRUE, the PostScript surface will output Encapsulated + * PostScript. + * + * This function should only be called before any drawing operations + * have been performed on the current page. The simplest way to do + * this is to call this function immediately after creating the + * surface. An Encapsulated PostScript file should never contain more + * than one page. + * + * Since: 1.6 + **/ +void +cairo_ps_surface_set_eps (cairo_surface_t *surface, + cairo_bool_t eps) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + ps_surface->eps = eps; +} + +/** + * cairo_ps_surface_get_eps: + * @surface: a PostScript #cairo_surface_t + * + * Check whether the PostScript surface will output Encapsulated PostScript. + * + * Return value: %TRUE if the surface will output Encapsulated PostScript. + * + * Since: 1.6 + **/ +cairo_public cairo_bool_t +cairo_ps_surface_get_eps (cairo_surface_t *surface) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, FALSE, &ps_surface)) + return FALSE; + + return ps_surface->eps; +} + +/** + * cairo_ps_surface_set_size: + * @surface: a PostScript #cairo_surface_t + * @width_in_points: new surface width, in points (1 point == 1/72.0 inch) + * @height_in_points: new surface height, in points (1 point == 1/72.0 inch) + * + * Changes the size of a PostScript surface for the current (and + * subsequent) pages. + * + * This function should only be called before any drawing operations + * have been performed on the current page. The simplest way to do + * this is to call this function immediately after creating the + * surface or immediately after completing a page with either + * cairo_show_page() or cairo_copy_page(). + * + * Since: 1.2 + **/ +void +cairo_ps_surface_set_size (cairo_surface_t *surface, + double width_in_points, + 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, 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); +} + +/** + * cairo_ps_surface_dsc_comment: + * @surface: a PostScript #cairo_surface_t + * @comment: a comment string to be emitted into the PostScript output + * + * Emit a comment into the PostScript output for the given 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 + * 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 + * 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 + * beyond these two conditions, this function will not enforce + * conformance of the comment with any particular specification. + * + * The comment string should not have a trailing newline. + * + * The DSC specifies different sections in which particular comments + * can appear. This function provides for comments to be emitted + * within three sections: the header, the Setup section, and the + * PageSetup section. Comments appearing in the first two sections + * apply to the entire document while comments in the BeginPageSetup + * section apply only to a single page. + * + * 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_dsc_begin_setup(). + * + * For comments to appear in the Setup section, this function should + * 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_dsc_begin_page_setup(). + * + * 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. + * + * Setup section: \%\%BeginSetup, \%\%EndSetup + * + * PageSetup section: \%\%BeginPageSetup, \%\%PageBoundingBox, \%\%EndPageSetup. + * + * 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_ps_surface_dsc_comment (surface, "%%Title: My excellent document"); + * cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover") + * ... + * cairo_ps_surface_dsc_begin_setup (surface); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White"); + * ... + * cairo_ps_surface_dsc_begin_page_setup (surface); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A3"); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *InputSlot LargeCapacity"); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaType Glossy"); + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor Blue"); + * ... draw to first page here .. + * cairo_show_page (cr); + * ... + * cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A5"); + * ... + * + * + * Since: 1.2 + **/ +void +cairo_ps_surface_dsc_comment (cairo_surface_t *surface, + const char *comment) +{ + cairo_ps_surface_t *ps_surface = NULL; + cairo_status_t status; + char *comment_copy; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + /* A couple of sanity checks on the comment value. */ + if (comment == NULL) { + status = _cairo_surface_set_error (surface, CAIRO_STATUS_NULL_POINTER); + return; + } + + if (comment[0] != '%' || strlen (comment) > 255) { + status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_DSC_COMMENT); + return; + } + + /* Then, copy the comment and store it in the appropriate array. */ + comment_copy = strdup (comment); + if (unlikely (comment_copy == NULL)) { + status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY); + return; + } + + status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy); + if (unlikely (status)) { + free (comment_copy); + status = _cairo_surface_set_error (surface, status); + return; + } +} + +/** + * cairo_ps_surface_dsc_begin_setup: + * @surface: a PostScript #cairo_surface_t + * + * This function indicates that subsequent calls to + * cairo_ps_surface_dsc_comment() should direct comments to the Setup + * section of the PostScript output. + * + * This function should be called at most once per surface, and must + * be called before any call to cairo_ps_surface_dsc_begin_page_setup() + * and before any drawing is performed to the surface. + * + * See cairo_ps_surface_dsc_comment() for more details. + * + * Since: 1.2 + **/ +void +cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments) + ps_surface->dsc_comment_target = &ps_surface->dsc_setup_comments; +} + +/** + * cairo_ps_surface_dsc_begin_page_setup: + * @surface: a PostScript #cairo_surface_t + * + * This function indicates that subsequent calls to + * cairo_ps_surface_dsc_comment() should direct comments to the + * PageSetup section of the PostScript output. + * + * This function call is only needed for the first page of a + * surface. It should be called after any call to + * cairo_ps_surface_dsc_begin_setup() and before any drawing is + * performed to the surface. + * + * See cairo_ps_surface_dsc_comment() for more details. + * + * Since: 1.2 + **/ +void +cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface) +{ + cairo_ps_surface_t *ps_surface = NULL; + + if (! _extract_ps_surface (surface, TRUE, &ps_surface)) + return; + + if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments || + ps_surface->dsc_comment_target == &ps_surface->dsc_setup_comments) + { + ps_surface->dsc_comment_target = &ps_surface->dsc_page_setup_comments; + } +} + +static cairo_status_t +_cairo_ps_surface_finish (void *abstract_surface) +{ + cairo_status_t status, status2; + cairo_ps_surface_t *surface = abstract_surface; + int i, num_comments; + char **comments; + + status = surface->base.status; + if (unlikely (status)) + goto CLEANUP; + + _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; + + _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); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + fclose (surface->tmpfile); + + status2 = _cairo_output_stream_destroy (surface->final_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + while (! cairo_list_is_empty (&surface->document_media)) { + cairo_page_media_t *page; + + page = cairo_list_first_entry (&surface->document_media, + cairo_page_media_t, + link); + cairo_list_del (&page->link); + free (page->name); + free (page); + } + + 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++) + free (comments[i]); + _cairo_array_fini (&surface->dsc_header_comments); + + num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments); + comments = _cairo_array_index (&surface->dsc_setup_comments, 0); + for (i = 0; i < num_comments; i++) + free (comments[i]); + _cairo_array_fini (&surface->dsc_setup_comments); + + 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++) + 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; +} + +static cairo_int_status_t +_cairo_ps_surface_start_page (void *abstract_surface) +{ + cairo_ps_surface_t *surface = abstract_surface; + + /* Increment before print so page numbers start at 1. */ + surface->num_pages++; + + 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; + + 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, + "Q Q\n" + "showpage\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +color_is_gray (double red, double green, double blue) +{ + const double epsilon = 0.00001; + + return (fabs (red - green) < epsilon && + fabs (red - blue) < epsilon); +} + +/** + * _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_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_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; + + if (image->base.status) + return image->base.status; + + transparency = _cairo_image_analyze_transparency (image); + switch (transparency) { + case CAIRO_IMAGE_IS_OPAQUE: + status = CAIRO_STATUS_SUCCESS; + break; + + case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: + if (surface->ps_level == CAIRO_PS_LEVEL_2) { + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + } else { + surface->ps_level_used = CAIRO_PS_LEVEL_3; + status = CAIRO_STATUS_SUCCESS; + } + break; + + case CAIRO_IMAGE_HAS_ALPHA: + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + break; + + case CAIRO_IMAGE_UNKNOWN: + ASSERT_NOT_REACHED; + } + + _cairo_surface_release_source_image (source_surface, image, image_extra); + _cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source_surface); + + return status; +} + +static cairo_bool_t +surface_pattern_supported (const cairo_surface_pattern_t *pattern) +{ + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) + return TRUE; + + if (pattern->surface->backend->acquire_source_image == NULL) + return FALSE; + + /* Does an ALPHA-only source surface even make sense? Maybe, but I + * don't think it's worth the extra code to support it. */ + +/* XXX: Need to write this function here... + content = pattern->surface->content; + if (content == CAIRO_CONTENT_ALPHA) + return FALSE; +*/ + + return TRUE; +} + +static cairo_bool_t +_gradient_pattern_supported (cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern) +{ + double min_alpha, max_alpha; + + if (surface->ps_level == CAIRO_PS_LEVEL_2) + return FALSE; + + /* Alpha gradients are only supported (by flattening the alpha) + * if there is no variation in the alpha across the gradient. */ + _cairo_pattern_alpha_range (pattern, &min_alpha, &max_alpha); + if (min_alpha != max_alpha) + return FALSE; + + surface->ps_level_used = CAIRO_PS_LEVEL_3; + + return TRUE; +} + +static cairo_bool_t +pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return TRUE; + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + return _gradient_pattern_supported (surface, pattern); + + 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; +} + +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) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + 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) { + 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) { + 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 + * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis + * surface. If the analysis surface determines that there is + * anything drawn under this operation, a fallback image will be + * used. Otherwise the operation will be replayed during the + * render stage and we blend the transparency into the white + * background to convert the pattern to opaque. + */ + 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); + + /* 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; +} + +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, mask, extents) != CAIRO_INT_STATUS_UNSUPPORTED; +} + +/* The "standard" implementation limit for PostScript string sizes is + * 65535 characters (see PostScript Language Reference, Appendix + * B). + */ +#define STRING_ARRAY_MAX_STRING_SIZE 65535 +#define STRING_ARRAY_MAX_COLUMN 72 + +typedef struct _string_array_stream { + cairo_output_stream_t base; + 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 +_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; + + if (length == 0) + return CAIRO_STATUS_SUCCESS; + + while (length--) { + 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++; + _cairo_output_stream_write (stream->output, &c, 1); + stream->column++; + + /* 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->tuple_count == 0 && + stream->string_size > STRING_ARRAY_MAX_STRING_SIZE - 4) + { + _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->column = 1; + } + } + + return _cairo_output_stream_get_status (stream->output); +} + +static cairo_status_t +_base85_string_wrap_stream_close (cairo_output_stream_t *base) +{ + string_array_stream_t *stream = (string_array_stream_t *) base; + + if (!stream->use_strings || stream->string_size != 0) + _cairo_output_stream_printf (stream->output, "~>"); + + return _cairo_output_stream_get_status (stream->output); +} + +/* 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. 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_strings_stream_create (cairo_output_stream_t *output) +{ + string_array_stream_t *stream; + + 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, + _base85_string_wrap_stream_write, + NULL, + _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_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_wrap_stream_create (cairo_output_stream_t *output) +{ + string_array_stream_t *stream; + + 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, + _base85_string_wrap_stream_write, + NULL, + _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; +} + + +/* PS Output - this section handles output of the parts of the recording + * surface we can render natively in PS. */ + +static cairo_status_t +_cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t *surface, + cairo_image_surface_t *image, + cairo_image_surface_t **opaque_image) +{ + cairo_surface_t *opaque; + cairo_surface_pattern_t pattern; + cairo_status_t status; + + opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + image->width, + image->height); + if (unlikely (opaque->status)) + return opaque->status; + + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { + status = _cairo_surface_paint (opaque, + CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, + NULL); + if (unlikely (status)) { + cairo_surface_destroy (opaque); + return status; + } + } + + _cairo_pattern_init_for_surface (&pattern, &image->base); + 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)) { + cairo_surface_destroy (opaque); + return status; + } + + *opaque_image = (cairo_image_surface_t *) opaque; + return CAIRO_STATUS_SUCCESS; +} + +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, *deflate_stream; + unsigned char *data_compressed; + unsigned long data_compressed_size; + cairo_status_t status, status2; + + if (use_strings) + string_array_stream = _base85_strings_stream_create (surface->stream); + else + string_array_stream = _base85_wrap_stream_create (surface->stream); + + status = _cairo_output_stream_get_status (string_array_stream); + if (unlikely (status)) + return _cairo_output_stream_destroy (string_array_stream); + + base85_stream = _cairo_base85_stream_create (string_array_stream); + status = _cairo_output_stream_get_status (base85_stream); + if (unlikely (status)) { + status2 = _cairo_output_stream_destroy (string_array_stream); + return _cairo_output_stream_destroy (base85_stream); + } + + 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); + status2 = _cairo_output_stream_destroy (string_array_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + return status; +} + +static const char * +get_interpolate (cairo_filter_t filter) +{ + const char *interpolate; + + switch (filter) { + default: + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + interpolate = "true"; + break; + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + case CAIRO_FILTER_GAUSSIAN: + interpolate = "false"; + break; + } + + return interpolate; +} + +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 (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) + { + 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; + color = CAIRO_IMAGE_IS_MONOCHROME; + transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA; + } else { + 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); + } + + /* 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; + } + 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; + } + + i = 0; + for (y = 0; y < ps_image->height; y++) { + if (params->stencil_mask || use_mask) { + /* mask row */ + 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; + } + } 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++; + } + + 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 { + compress = CAIRO_PS_COMPRESS_DEFLATE; + compress_filter = "FlateDecode"; + surface->ps_level_used = CAIRO_PS_LEVEL_3; + } + + 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, + data, + data_size, + compress, + TRUE); + if (unlikely (status)) + goto bail2; + + _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 (use_mask) { + _cairo_output_stream_printf (surface->stream, + "%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->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 cairo_ascii85_file /%s filter\n", + compress_filter); + } + + _cairo_output_stream_printf (surface->stream, + " /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", + ps_image->width, + -ps_image->height, + ps_image->height, + ps_image->width, + ps_image->height, + interpolate, + ps_image->width, + -ps_image->height, + ps_image->height); + } else { + const char *decode; + + if (!params->stencil_mask) { + _cairo_output_stream_printf (surface->stream, + "%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 cairo_ascii85_file /%s filter\n", + compress_filter); + } + + _cairo_output_stream_printf (surface->stream, + " /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->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, + data_size, + compress, + FALSE); + _cairo_output_stream_printf (surface->stream, "\n"); + } else { + status = CAIRO_STATUS_SUCCESS; + } + +bail2: + free (data); + +bail1: + 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_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; + + 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 (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + 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; + } + + /* 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, + "/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; + + _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"); + } + + _cairo_output_stream_printf (surface->stream, + "%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->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_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) +{ + 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; + + if (unlikely (params->src_surface->status)) + return params->src_surface->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; + + free (ccitt_params_string); + + if (ccitt_params.columns <= 0 || ccitt_params.rows <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* 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; + } + + 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; + + /* 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; + + /* 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; + } + + 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_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_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_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_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->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, 0); + _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", + recording_extents->x, + recording_extents->y, + recording_extents->width, + recording_extents->height); + } + + status = _cairo_recording_surface_replay_region (recording_surface, + subsurface ? recording_extents : NULL, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + if (unlikely (status)) + return status; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + 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->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); + + _cairo_array_truncate (&surface->recording_surf_stack, recording_surf_stack_size); + + return status; +} + +static void +_cairo_ps_surface_flatten_transparency (cairo_ps_surface_t *surface, + const cairo_color_t *color, + double *red, + double *green, + double *blue) +{ + *red = color->red; + *green = color->green; + *blue = color->blue; + + if (! CAIRO_COLOR_IS_OPAQUE (color)) { + *red *= color->alpha; + *green *= color->alpha; + *blue *= color->alpha; + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { + double one_minus_alpha = 1. - color->alpha; + *red += one_minus_alpha; + *green += one_minus_alpha; + *blue += one_minus_alpha; + } + } +} + +static void +_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface, + cairo_solid_pattern_t *pattern) +{ + double red, green, blue; + + _cairo_ps_surface_flatten_transparency (surface, &pattern->color, &red, &green, &blue); + + if (color_is_gray (red, green, blue)) + _cairo_output_stream_printf (surface->stream, + "%f g\n", + red); + else + _cairo_output_stream_printf (surface->stream, + "%f %f %f rg\n", + red, green, blue); +} + +/* + * 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_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; + + if (params->op != CAIRO_OPERATOR_OVER || params->stencil_mask) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (params->src_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) + return CAIRO_INT_STATUS_UNSUPPORTED; + + 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; + + if (test) + return CAIRO_STATUS_SUCCESS; + + 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; + } + + 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_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; + + status = _cairo_ps_surface_use_form (surface, + params, + test, + &ps_form); + if (test || status) + return status; + + /* _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, + params->src_surface, + params->src_op_extents, + FALSE); + } + } else { + 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_form_emit (void *entry, void *closure) +{ + 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; + + 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"); + } + + _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 +_path_fixed_init_rectangle (cairo_path_fixed_t *path, + cairo_rectangle_int_t *rect) +{ + cairo_status_t status; + + _cairo_path_fixed_init (path); + + status = _cairo_path_fixed_move_to (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 (path, + _cairo_fixed_from_int (rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (0), + _cairo_fixed_from_int (rect->height)); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (-rect->width), + _cairo_fixed_from_int (0)); + assert (status == CAIRO_STATUS_SUCCESS); + + status = _cairo_path_fixed_close_path (path); + assert (status == CAIRO_STATUS_SUCCESS); +} + +static cairo_status_t +_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; + cairo_matrix_t cairo_p2d, ps_p2d; + cairo_path_fixed_t path; + 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_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)) + goto release_source; + + cairo_p2d = pattern->matrix; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) { + 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 ", + -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 %d %d %d %d rectfill\n", + surface->content == CAIRO_CONTENT_COLOR ? 0 : 1, + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); + } + } + + status = cairo_matrix_invert (&cairo_p2d); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + ps_p2d = surface->cairo_to_ps; + cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); + cairo_matrix_translate (&ps_p2d, x_offset, y_offset); + + 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); + } + + 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_pattern_t *pattern, + cairo_rectangle_int_t *extents, + cairo_operator_t op) +{ + cairo_status_t status; + double xstep, ystep; + cairo_rectangle_int_t pattern_extents; + cairo_bool_t bounded; + cairo_matrix_t cairo_p2d, ps_p2d; + 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->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_source_surface_from_pattern (surface, + pattern, + extents, + &pattern_extents, + &bounded, + &src_op_extents, + &source_surface, + &x_offset, &y_offset); + if (unlikely (status)) + return status; + + 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: + { + /* In PS/PDF, (as far as I can tell), all patterns are + * repeating. So we support cairo's EXTEND_NONE semantics + * by setting the repeat step size to a size large enough + * to guarantee that no more than a single occurrence will + * be visible. + * + * First, map the surface extents into pattern space (since + * xstep and ystep are in pattern space). Then use an upper + * bound on the length of the diagonal of the pattern image + * and the surface as repeat size. This guarantees to never + * repeat visibly. + */ + double x1 = 0.0, y1 = 0.0; + double x2 = surface->surface_extents.width; + double y2 = surface->surface_extents.height; + _cairo_matrix_transform_bounding_box (&pattern->matrix, + &x1, &y1, &x2, &y2, + NULL); + + /* Rather than computing precise bounds of the union, just + * add the surface extents unconditionally. We only + * 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_extents.width + pattern_extents.height); + break; + } + case CAIRO_EXTEND_REPEAT: + xstep = pattern_extents.width; + ystep = pattern_extents.height; + break; + case CAIRO_EXTEND_REFLECT: + 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. */ + default: + ASSERT_NOT_REACHED; + xstep = 0; + ystep = 0; + } + + _cairo_output_stream_printf (surface->stream, + "/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); + } + + if (op == CAIRO_OPERATOR_SOURCE) { + _cairo_output_stream_printf (surface->stream, + "%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, CAIRO_EMIT_SURFACE_EMIT, ¶ms); + if (unlikely (status)) + goto release_source; + + _cairo_output_stream_printf (surface->stream, + " Q } bind def\n"); + + _cairo_output_stream_printf (surface->stream, + "<< /PatternType 1\n" + " /PaintType 1\n" + " /TilingType 1\n"); + _cairo_output_stream_printf (surface->stream, + " /XStep %f /YStep %f\n", + xstep, ystep); + + if (extend == CAIRO_EXTEND_REFLECT) { + cairo_matrix_t mat; + + _cairo_output_stream_printf (surface->stream, + " /BBox [%d %d %d %d]\n" + " /PaintProc {\n" + " 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, + " /BBox [0 0 %f %f]\n", + xstep, ystep); + } else { + _cairo_output_stream_printf (surface->stream, + " /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 { pop CairoPattern }\n"); + } + + _cairo_output_stream_printf (surface->stream, + ">>\n"); + + 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_multiply (&ps_p2d, &cairo_p2d, &ps_p2d); + 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, + " ]\n" + "makepattern setpattern\n"); + + 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 { + double offset; + double color[4]; +} cairo_ps_color_stop_t; + +static void +_cairo_ps_surface_emit_linear_colorgradient (cairo_ps_surface_t *surface, + cairo_ps_color_stop_t *stop1, + cairo_ps_color_stop_t *stop2) +{ + _cairo_output_stream_printf (surface->stream, + " << /FunctionType 2\n" + " /Domain [ 0 1 ]\n" + " /C0 [ %f %f %f ]\n" + " /C1 [ %f %f %f ]\n" + " /N 1\n" + " >>\n", + stop1->color[0], + stop1->color[1], + stop1->color[2], + stop2->color[0], + stop2->color[1], + stop2->color[2]); +} + +static void +_cairo_ps_surface_emit_stitched_colorgradient (cairo_ps_surface_t *surface, + unsigned int n_stops, + cairo_ps_color_stop_t stops[]) +{ + unsigned int i; + + _cairo_output_stream_printf (surface->stream, + "<< /FunctionType 3\n" + " /Domain [ 0 1 ]\n" + " /Functions [\n"); + for (i = 0; i < n_stops - 1; i++) + _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[i], &stops[i+1]); + + _cairo_output_stream_printf (surface->stream, " ]\n"); + + _cairo_output_stream_printf (surface->stream, " /Bounds [ "); + for (i = 1; i < n_stops-1; i++) + _cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset); + _cairo_output_stream_printf (surface->stream, "]\n"); + + _cairo_output_stream_printf (surface->stream, " /Encode [ 1 1 %d { pop 0 1 } for ]\n", + n_stops - 1); + + _cairo_output_stream_printf (surface->stream, ">>\n"); +} + +static void +calc_gradient_color (cairo_ps_color_stop_t *new_stop, + cairo_ps_color_stop_t *stop1, + cairo_ps_color_stop_t *stop2) +{ + int i; + double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset); + + for (i = 0; i < 4; i++) + new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]); +} + +#define COLOR_STOP_EPSILON 1e-6 + +static cairo_status_t +_cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface, + cairo_gradient_pattern_t *pattern) +{ + cairo_ps_color_stop_t *allstops, *stops; + unsigned int i, n_stops; + + allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t)); + if (unlikely (allstops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + stops = &allstops[1]; + n_stops = pattern->n_stops; + + for (i = 0; i < n_stops; i++) { + cairo_gradient_stop_t *stop = &pattern->stops[i]; + + stops[i].color[0] = stop->color.red; + stops[i].color[1] = stop->color.green; + stops[i].color[2] = stop->color.blue; + stops[i].color[3] = stop->color.alpha; + stops[i].offset = pattern->stops[i].offset; + } + + if (pattern->base.extend == CAIRO_EXTEND_REPEAT || + 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)); + else + calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]); + stops = allstops; + n_stops++; + } + stops[0].offset = 0.0; + + if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) { + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { + memcpy (&stops[n_stops], + &stops[n_stops - 1], + sizeof (cairo_ps_color_stop_t)); + } else { + calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]); + } + n_stops++; + } + stops[n_stops-1].offset = 1.0; + } + + for (i = 0; i < n_stops; i++) { + double red, green, blue; + cairo_color_t color; + + _cairo_color_init_rgba (&color, + stops[i].color[0], + stops[i].color[1], + stops[i].color[2], + stops[i].color[3]); + _cairo_ps_surface_flatten_transparency (surface, &color, + &red, &green, &blue); + stops[i].color[0] = red; + stops[i].color[1] = green; + stops[i].color[2] = blue; + } + + _cairo_output_stream_printf (surface->stream, + "/CairoFunction\n"); + 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_output_stream_printf (surface->stream, + "def\n"); + + free (allstops); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t *surface, + cairo_gradient_pattern_t *pattern, + int begin, + int end) +{ + _cairo_output_stream_printf (surface->stream, + "/CairoFunction\n" + "<< /FunctionType 3\n" + " /Domain [ %d %d ]\n" + " /Functions [ %d {CairoFunction} repeat ]\n" + " /Bounds [ %d 1 %d {} for ]\n", + begin, + end, + end - begin, + begin + 1, + end - 1); + + if (pattern->base.extend == CAIRO_EXTEND_REFLECT) { + _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { 2 mod 0 eq {0 1} {1 0} ifelse } for ]\n", + begin, + end - 1); + } else { + _cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { pop 0 1 } for ]\n", + begin, + end - 1); + } + + _cairo_output_stream_printf (surface->stream, ">> def\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_ps_surface_emit_gradient (cairo_ps_surface_t *surface, + cairo_gradient_pattern_t *pattern, + cairo_bool_t is_ps_pattern) +{ + cairo_matrix_t pat_to_ps; + cairo_circle_double_t start, end; + double domain[2]; + cairo_status_t status; + + assert (pattern->n_stops != 0); + + status = _cairo_ps_surface_emit_pattern_stops (surface, pattern); + if (unlikely (status)) + return status; + + 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, + 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; + } + + if (is_ps_pattern) { + _cairo_output_stream_printf (surface->stream, + "<< /PatternType 2\n" + " /Shading\n"); + } + + 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 { + _cairo_output_stream_printf (surface->stream, + " /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"); + + 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_mesh_pattern (cairo_ps_surface_t *surface, + cairo_mesh_pattern_t *pattern, + cairo_bool_t is_ps_pattern) +{ + cairo_matrix_t pat_to_ps; + cairo_status_t status; + cairo_pdf_shading_t shading; + int i; + + if (_cairo_array_num_elements (&pattern->patches) == 0) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + 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); + + status = _cairo_pdf_shading_init_color (&shading, pattern); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, + "currentfile\n" + "/ASCII85Decode filter /FlateDecode filter /ReusableStreamDecode filter\n"); + + 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, + "<< /PatternType 2\n" + " /Shading\n"); + } + + _cairo_output_stream_printf (surface->stream, + " << /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, + "]\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, + "currentdict /CairoData undef\n"); + + _cairo_pdf_shading_fini (&shading); + + return status; +} + +static cairo_status_t +_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_rectangle_int_t *extents, + cairo_operator_t op) +{ + cairo_status_t status; + + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + + if (surface->current_pattern_is_solid_color == FALSE || + ! _cairo_color_equal (&surface->current_color, &solid->color)) + { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); + + surface->current_pattern_is_solid_color = TRUE; + surface->current_color = solid->color; + } + + return CAIRO_STATUS_SUCCESS; + } + + surface->current_pattern_is_solid_color = FALSE; + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + return status; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + + _cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern); + break; + + case CAIRO_PATTERN_TYPE_SURFACE: + case CAIRO_PATTERN_TYPE_RASTER_SOURCE: + status = _cairo_ps_surface_emit_surface_pattern (surface, + (cairo_pattern_t *)pattern, + extents, + op); + if (unlikely (status)) + return status; + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + 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_MESH: + status = _cairo_ps_surface_emit_mesh_pattern (surface, + (cairo_mesh_pattern_t *) pattern, + TRUE); + if (unlikely (status)) + return status; + break; + } + + 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; + + if (surface->surface_bounded) + *rectangle = surface->surface_extents; + + return surface->surface_bounded; +} + +static void +_cairo_ps_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + 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, + 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_paint (&extents, + &surface->base, + op, source, clip); + if (unlikely (status)) + return status; + + 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, NULL, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (stream, + "%% _cairo_ps_surface_paint\n"); +#endif + + status = _cairo_ps_surface_set_clip (surface, &extents); + if (unlikely (status)) + goto cleanup_composite; + + if (_can_paint_pattern (source)) { + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + goto cleanup_composite; + + _cairo_output_stream_printf (stream, "q\n"); + status = _cairo_ps_surface_paint_pattern (surface, + source, + &extents.bounded, op, FALSE); + if (unlikely (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)) + goto cleanup_composite; + + _cairo_output_stream_printf (stream, "%d %d %d %d rectfill\n", + surface->surface_extents.x, + surface->surface_extents.y, + surface->surface_extents.width, + surface->surface_extents.height); + } + +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, + 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_ps_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &surface->base, + op, source, + path, style, 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, + ctm, 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; + } + + 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, NULL, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_stroke\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; + + 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, + const cairo_path_fixed_t*path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_composite_rectangles_t extents; + cairo_int_status_t status; + + status = _cairo_composite_rectangles_init_for_fill (&extents, + &surface->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; + } + + 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, NULL, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_fill\n"); +#endif + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (unlikely (status)) + goto cleanup_composite; + + status = _cairo_ps_surface_set_clip (surface, &extents); + if (unlikely (status)) + goto cleanup_composite; + + 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)) + goto cleanup_composite; + + status = _cairo_ps_surface_paint_pattern (surface, + source, + &extents.bounded, op, FALSE); + if (unlikely (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)) + 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_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; + + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &surface->base, + op, source, + scaled_font, + glyphs, num_glyphs, + clip, + &overlap); + if (unlikely (status)) + return status; + + 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, NULL, &extents.bounded)); + +#if DEBUG_PS + _cairo_output_stream_printf (surface->stream, + "%% _cairo_ps_surface_show_glyphs\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; + + 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 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) +{ + cairo_ps_surface_t *surface = abstract_surface; + cairo_status_t status; + + surface->paginated_mode = paginated_mode; + + 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); + + 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 *analysis_bbox) +{ + cairo_ps_surface_t *surface = abstract_surface; + int i, num_comments; + char **comments; + 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 */ + + _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 { + 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; + } + + _cairo_output_stream_printf (surface->stream, + "%%%%Page: %d %d\n", + surface->num_pages, + surface->num_pages); + + _cairo_output_stream_printf (surface->stream, + "%%%%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++) { + _cairo_output_stream_printf (surface->stream, + "%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; + } + _cairo_array_truncate (&surface->dsc_page_setup_comments, 0); + + if (!has_page_media && !surface->eps) { + page_media = _cairo_ps_surface_get_page_media (surface); + if (unlikely (page_media == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_output_stream_printf (surface->stream, + "%%%%PageMedia: %s\n", + page_media); + } + + 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\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)); + + surface->current_pattern_is_solid_color = FALSE; + _cairo_pdf_operators_reset (&surface->pdf_operators); + + return _cairo_output_stream_get_status (surface->stream); +} + +static cairo_bool_t +_cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + return TRUE; +} + +static const cairo_surface_backend_t cairo_ps_surface_backend = { + CAIRO_SURFACE_TYPE_PS, + _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, /* snapshot */ + + NULL, /* cairo_ps_surface_copy_page */ + _cairo_ps_surface_show_page, + + _cairo_ps_surface_get_extents, + _cairo_ps_surface_get_font_options, + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + /* Here are the drawing functions */ + + _cairo_ps_surface_paint, /* paint */ + _cairo_ps_surface_mask, + _cairo_ps_surface_stroke, + _cairo_ps_surface_fill, + 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 = { + _cairo_ps_surface_start_page, + _cairo_ps_surface_set_paginated_mode, + _cairo_ps_surface_set_bounding_box, + NULL, /* _cairo_ps_surface_has_fallback_images, */ + _cairo_ps_surface_supports_fine_grained_fallbacks, +}; diff --git a/gfx/cairo/cairo/src/cairo-ps.h b/gfx/cairo/cairo/src/cairo-ps.h new file mode 100644 index 0000000000..33d0e0d941 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-ps.h @@ -0,0 +1,116 @@ +/* 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 + */ + +#ifndef CAIRO_PS_H +#define CAIRO_PS_H + +#include "cairo.h" + +#if CAIRO_HAS_PS_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +/* PS-surface functions */ + +/** + * cairo_ps_level_t: + * @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 +} cairo_ps_level_t; + +cairo_public cairo_surface_t * +cairo_ps_surface_create (const char *filename, + double width_in_points, + double height_in_points); + +cairo_public cairo_surface_t * +cairo_ps_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points); + +cairo_public void +cairo_ps_surface_restrict_to_level (cairo_surface_t *surface, + cairo_ps_level_t level); + +cairo_public void +cairo_ps_get_levels (cairo_ps_level_t const **levels, + int *num_levels); + +cairo_public const char * +cairo_ps_level_to_string (cairo_ps_level_t level); + +cairo_public void +cairo_ps_surface_set_eps (cairo_surface_t *surface, + cairo_bool_t eps); + +cairo_public cairo_bool_t +cairo_ps_surface_get_eps (cairo_surface_t *surface); + +cairo_public void +cairo_ps_surface_set_size (cairo_surface_t *surface, + double width_in_points, + double height_in_points); + +cairo_public void +cairo_ps_surface_dsc_comment (cairo_surface_t *surface, + const char *comment); + +cairo_public void +cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface); + +cairo_public void +cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_PS_SURFACE */ +# error Cairo was not compiled with support for the ps backend +#endif /* CAIRO_HAS_PS_SURFACE */ + +#endif /* CAIRO_PS_H */ diff --git a/gfx/cairo/cairo/src/cairo-qt-surface.cpp b/gfx/cairo/cairo/src/cairo-qt-surface.cpp new file mode 100644 index 0000000000..d276f059e1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-qt-surface.cpp @@ -0,0 +1,1741 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * 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 + * 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): + * Vladimir Vukicevic + */ + +/* Get INT16_MIN etc. as per C99 */ +#define __STDC_LIMIT_MACROS + +#include "cairoint.h" + +#include "cairo-clip-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 + +#include +#include +#include +#include +#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 */ +#define ENABLE_FAST_FILL 0 +#define ENABLE_FAST_CLIP 0 + +#if 0 +#define D(x) x +static const char * +_opstr (cairo_operator_t op) +{ + const char *ops[] = { + "CLEAR", + "SOURCE", + "OVER", + "IN", + "OUT", + "ATOP", + "DEST", + "DEST_OVER", + "DEST_IN", + "DEST_OUT", + "DEST_ATOP", + "XOR", + "ADD", + "SATURATE" + }; + + if (op < CAIRO_OPERATOR_CLEAR || op > CAIRO_OPERATOR_SATURATE) + return "(\?\?\?)"; + + return ops[op]; +} +#else +#define D(x) do { } while(0) +#endif + +#ifndef CAIRO_INT_STATUS_SUCCESS +#define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS) +#endif + +/* Qt::PenStyle optimization based on the assumption that dots are 1*w and dashes are 3*w. */ +#define DOT_LENGTH 1.0 +#define DASH_LENGTH 3.0 + +struct cairo_qt_surface_t { + cairo_surface_t base; + + cairo_bool_t supports_porter_duff; + + QPainter *p; + + /* The pixmap/image constructors will store their objects here */ + QPixmap *pixmap; + QImage *image; + + QRect window; + + cairo_surface_clipper_t clipper; + + cairo_surface_t *image_equiv; +}; + +/* Will be true if we ever try to create a QPixmap and end + * up with one without an alpha channel. + */ +static cairo_bool_t _qpixmaps_have_no_alpha = FALSE; + +/* + * Helper methods + */ + +static QPainter::CompositionMode +_qpainter_compositionmode_from_cairo_op (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + return QPainter::CompositionMode_Clear; + + case CAIRO_OPERATOR_SOURCE: + return QPainter::CompositionMode_Source; + case CAIRO_OPERATOR_OVER: + return QPainter::CompositionMode_SourceOver; + case CAIRO_OPERATOR_IN: + return QPainter::CompositionMode_SourceIn; + case CAIRO_OPERATOR_OUT: + return QPainter::CompositionMode_SourceOut; + case CAIRO_OPERATOR_ATOP: + return QPainter::CompositionMode_SourceAtop; + + case CAIRO_OPERATOR_DEST: + return QPainter::CompositionMode_Destination; + case CAIRO_OPERATOR_DEST_OVER: + return QPainter::CompositionMode_DestinationOver; + case CAIRO_OPERATOR_DEST_IN: + return QPainter::CompositionMode_DestinationIn; + case CAIRO_OPERATOR_DEST_OUT: + return QPainter::CompositionMode_DestinationOut; + case CAIRO_OPERATOR_DEST_ATOP: + return QPainter::CompositionMode_DestinationAtop; + + case CAIRO_OPERATOR_XOR: + return QPainter::CompositionMode_Xor; + + default: + 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; + } +} + +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: + 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: + return TRUE; + + default: + ASSERT_NOT_REACHED; + 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: + return FALSE; + + } + } else { + return op == CAIRO_OPERATOR_OVER; + } +} + +static cairo_format_t +_cairo_format_from_qimage_format (QImage::Format fmt) +{ + switch (fmt) { + case QImage::Format_ARGB32_Premultiplied: + return CAIRO_FORMAT_ARGB32; + case QImage::Format_RGB32: + return CAIRO_FORMAT_RGB24; + case QImage::Format_Indexed8: // XXX not quite + return CAIRO_FORMAT_A8; +#ifdef WORDS_BIGENDIAN + case QImage::Format_Mono: +#else + case QImage::Format_MonoLSB: +#endif + return CAIRO_FORMAT_A1; + + 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: + ASSERT_NOT_REACHED; + return (cairo_format_t) -1; + } +} + +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: +#ifdef WORDS_BIGENDIAN + return QImage::Format_Mono; // XXX think we need to choose between this and LSB +#else + return QImage::Format_MonoLSB; +#endif + case CAIRO_FORMAT_RGB30: + return QImage::Format_Mono; + } + + return QImage::Format_Mono; +} + +static inline QMatrix +_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 */ +typedef struct _qpainter_path_transform { + QPainterPath path; + const cairo_matrix_t *ctm_inverse; +} qpainter_path_data; + +/* cairo path -> execute in context */ +static cairo_status_t +_cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point) +{ + qpainter_path_data *pdata = static_cast (closure); + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (pdata->ctm_inverse) + cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); + + pdata->path.moveTo(x, y); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point) +{ + qpainter_path_data *pdata = static_cast (closure); + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (pdata->ctm_inverse) + cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y); + + pdata->path.lineTo(x, y); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2) +{ + qpainter_path_data *pdata = static_cast (closure); + double x0 = _cairo_fixed_to_double (p0->x); + double y0 = _cairo_fixed_to_double (p0->y); + double x1 = _cairo_fixed_to_double (p1->x); + double y1 = _cairo_fixed_to_double (p1->y); + double x2 = _cairo_fixed_to_double (p2->x); + double y2 = _cairo_fixed_to_double (p2->y); + + if (pdata->ctm_inverse) { + cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0); + cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1); + cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2); + } + + pdata->path.cubicTo (x0, y0, x1, y1, x2, y2); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_qpainterpath_close_path (void *closure) +{ + qpainter_path_data *pdata = static_cast (closure); + + pdata->path.closeSubpath(); + + return CAIRO_STATUS_SUCCESS; +} + +static QPainterPath +path_to_qt (const cairo_path_fixed_t *path, + const cairo_matrix_t *ctm_inverse = NULL) +{ + qpainter_path_data data; + cairo_status_t status; + + if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse)) + ctm_inverse = NULL; + data.ctm_inverse = ctm_inverse; + + status = _cairo_path_fixed_interpret (path, + _cairo_path_to_qpainterpath_move_to, + _cairo_path_to_qpainterpath_line_to, + _cairo_path_to_qpainterpath_curve_to, + _cairo_path_to_qpainterpath_close_path, + &data); + assert (status == CAIRO_STATUS_SUCCESS); + + return data.path; +} + +static inline QPainterPath +path_to_qt (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_matrix_t *ctm_inverse = NULL) +{ + QPainterPath qpath = path_to_qt (path, ctm_inverse); + + qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ? + Qt::WindingFill : + Qt::OddEvenFill); + + return qpath; +} + +/* + * Surface backend methods + */ +static cairo_surface_t * +_cairo_qt_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + bool use_pixmap; + + D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content)); + + use_pixmap = qs->image == NULL; + if (use_pixmap) { + switch (content) { + case CAIRO_CONTENT_ALPHA: + use_pixmap = FALSE; + break; + case CAIRO_CONTENT_COLOR: + break; + case CAIRO_CONTENT_COLOR_ALPHA: + use_pixmap = ! _qpixmaps_have_no_alpha; + break; + } + } + + if (use_pixmap) { + cairo_surface_t *result = + cairo_qt_surface_create_with_qpixmap (content, width, height); + + /* XXX result->content is always content. ??? */ + if (result->content == content) { + D(fprintf(stderr, "qpixmap content: %d\n", content)); + return result; + } + + _qpixmaps_have_no_alpha = TRUE; + cairo_surface_destroy (result); + } + + D(fprintf (stderr, "qimage\n")); + return cairo_qt_surface_create_with_qimage + (_cairo_format_from_content (content), width, height); +} + +static cairo_status_t +_cairo_qt_surface_finish (void *abstract_surface) +{ + cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + + D(fprintf(stderr, "q[%p] finish\n", abstract_surface)); + + /* Only delete p if we created it */ + if (qs->image || qs->pixmap) + delete qs->p; + else + qs->p->restore (); + + if (qs->image_equiv) + cairo_surface_destroy (qs->image_equiv); + + _cairo_surface_clipper_reset (&qs->clipper); + + if (qs->image) + delete qs->image; + + if (qs->pixmap) + delete qs->pixmap; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_qimg_destroy (void *closure) +{ + QImage *qimg = (QImage *) closure; + delete qimg; +} + +static cairo_status_t +_cairo_qt_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + + D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface)); + + *image_extra = NULL; + + if (qs->image_equiv) { + *image_out = (cairo_image_surface_t*) + cairo_surface_reference (qs->image_equiv); + + return CAIRO_STATUS_SUCCESS; + } + + if (qs->pixmap) { + QImage *qimg = new QImage(qs->pixmap->toImage()); + cairo_surface_t *image; + cairo_status_t status; + + image = cairo_image_surface_create_for_data (qimg->bits(), + _cairo_format_from_qimage_format (qimg->format()), + qimg->width(), qimg->height(), + qimg->bytesPerLine()); + + status = _cairo_user_data_array_set_data (&image->user_data, + (const cairo_user_data_key_t *)&_qimg_destroy, + qimg, + _qimg_destroy); + if (status) { + cairo_surface_destroy (image); + return status; + } + + *image_out = (cairo_image_surface_t *) image; + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} + +static void +_cairo_qt_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + //cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + + D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface)); + + cairo_surface_destroy (&image->base); +} + +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)); + + if (qs->image_equiv) + return _cairo_image_surface_map_to_image (qs->image_equiv, + extents); + + QPoint offset; + + if (qs->pixmap) { + qimg = new QImage(qs->pixmap->toImage()); + } else { + // Try to figure out what kind of QPaintDevice we have, and + // how we can grab an image from it + QPaintDevice *pd = qs->p->device(); + if (!pd) + return (cairo_image_surface_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + + QPaintDevice *rpd = QPainter::redirected(pd, &offset); + if (rpd) + pd = rpd; + + if (pd->devType() == QInternal::Image) { + 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()); + } + } + + return (cairo_image_surface_t *) map_qimage_to_image (qimg, extents); +} + +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)); + + 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 ((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 qimage->qimg; + } + + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_qt_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + + extents->x = qs->window.x(); + extents->y = qs->window.y(); + extents->width = qs->window.width(); + extents->height = qs->window.height(); + + return TRUE; +} + +static cairo_status_t +_cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_qt_surface_t *qs = cairo_container_of (clipper, + cairo_qt_surface_t, + clipper); + + if (path == NULL) { + if (qs->pixmap || qs->image) { + // we own p + qs->p->setClipping (false); + } else { + qs->p->restore (); + qs->p->save (); + } + } else { + // XXX Antialiasing is ignored + qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs, + const cairo_region_t *clip_region) +{ + _cairo_surface_clipper_reset (&qs->clipper); + + if (clip_region == NULL) { + // How the clip path is reset depends on whether we own p or not + if (qs->pixmap || qs->image) { + // we own p + qs->p->setClipping (false); + } else { + qs->p->restore (); + qs->p->save (); + } + } else { + QRegion qr; + int num_rects = cairo_region_num_rectangles (clip_region); + for (int i = 0; i < num_rects; ++i) { + cairo_rectangle_int_t rect; + + cairo_region_get_rectangle (clip_region, i, &rect); + + QRect r(rect.x, rect.y, rect.width, rect.height); + qr = qr.unite(r); + } + + qs->p->setClipRegion (qr, Qt::IntersectClip); + } +} + +static cairo_int_status_t +_cairo_qt_surface_set_clip (cairo_qt_surface_t *qs, + const cairo_clip_t *clip) +{ + cairo_int_status_t status; + + D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)")); + + if (clip == NULL) { + _cairo_surface_clipper_reset (&qs->clipper); + // How the clip path is reset depends on whether we own p or not + if (qs->pixmap || qs->image) { + // we own p + qs->p->setClipping (false); + } else { + qs->p->restore (); + qs->p->save (); + } + + return CAIRO_INT_STATUS_SUCCESS; + } + +#if ENABLE_FAST_CLIP + // Qt will implicitly enable clipping, and will use ReplaceClip + // instead of IntersectClip if clipping was disabled before + + // Note: Qt is really bad at dealing with clip paths. It doesn't + // seem to usefully recognize rectangular paths, instead going down + // extremely slow paths whenever a clip path is set. So, + // we do a bunch of work here to try to get rectangles or regions + // down to Qt for clipping. + + cairo_region_t *clip_region = NULL; + + status = _cairo_clip_get_region (clip, &clip_region); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + // We weren't able to extract a region from the traps. + // Just hand the path down to QPainter. + status = (cairo_int_status_t) + _cairo_surface_clipper_set_clip (&qs->clipper, clip); + } else if (status == CAIRO_INT_STATUS_SUCCESS) { + _cairo_qt_surface_set_clip_region (qs, clip_region); + status = CAIRO_INT_STATUS_SUCCESS; + } +#else + status = (cairo_int_status_t) + _cairo_surface_clipper_set_clip (&qs->clipper, clip); +#endif + + return status; +} + +/* + * Brush conversion + */ + +struct PatternToBrushConverter { + PatternToBrushConverter (const cairo_pattern_t *pattern) + __attribute__ ((noinline)) : + mAcquiredImageParent(0), + mAcquiredImage(0), + mAcquiredImageExtra(0) + { + 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); + + mBrush = QBrush(color); + } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern; + cairo_surface_t *surface = spattern->surface; + + if (surface->type == CAIRO_SURFACE_TYPE_QT) { + cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; + + if (qs->image) { + mBrush = QBrush(*qs->image); + } else if (qs->pixmap) { + mBrush = QBrush(*qs->pixmap); + } else { + // do something smart + mBrush = QBrush(0xff0000ff); + } + } else { + cairo_image_surface_t *isurf = NULL; + + if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) { + isurf = (cairo_image_surface_t*) surface; + } else { + void *image_extra; + + if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) { + mAcquiredImageParent = surface; + mAcquiredImage = isurf; + mAcquiredImageExtra = image_extra; + } else { + isurf = NULL; + } + } + + if (isurf) { + mBrush = QBrush (QImage ((const uchar *) isurf->data, + isurf->width, + isurf->height, + isurf->stride, + _qimage_format_from_cairo_format (isurf->format))); + } else { + mBrush = QBrush(0x0000ffff); + } + } + } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || + pattern->type == CAIRO_PATTERN_TYPE_RADIAL) + { + QGradient *grad; + cairo_bool_t reverse_stops = FALSE; + cairo_bool_t emulate_reflect = FALSE; + double offset = 0.0; + + cairo_extend_t extend = pattern->extend; + + cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern; + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern; + 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_circle_double_t *c0, *c1; + double x0, y0, r0, x1, y1, r1; + + if (rpat->cd1.radius < rpat->cd2.radius) { + c0 = &rpat->cd1; + c1 = &rpat->cd2; + reverse_stops = FALSE; + } else { + c0 = &rpat->cd2; + c1 = &rpat->cd1; + reverse_stops = TRUE; + } + + x0 = c0->center.x; + y0 = c0->center.y; + r0 = c0->radius; + x1 = c1->center.x; + y1 = c1->center.y; + r1 = c1->radius; + + if (r0 == r1) { + grad = new QRadialGradient (x1, y1, r1, x1, y1); + } else { + double fx = (r1 * x0 - r0 * x1) / (r1 - r0); + double fy = (r1 * y0 - r0 * y1) / (r1 - r0); + + /* QPainter doesn't support the inner circle and use instead a gradient focal. + * That means we need to emulate the cairo behaviour by processing the + * cairo gradient stops. + * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, + * it's just a matter of stop position translation and calculation of + * the corresponding SVG radial gradient focal. + * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new + * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT + * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop + * list that maps to the original cairo stop list. + */ + if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) { + double r_org = r1; + double r, x, y; + + if (extend == CAIRO_EXTEND_REFLECT) { + r1 = 2 * r1 - r0; + emulate_reflect = TRUE; + } + + offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; + r = r1 - r0; + + /* New position of outer circle. */ + x = r * (x1 - fx) / r_org + fx; + y = r * (y1 - fy) / r_org + fy; + + x1 = x; + y1 = y; + r1 = r; + r0 = 0.0; + } else { + offset = r0 / r1; + } + + grad = new QRadialGradient (x1, y1, r1, fx, fy); + + if (extend == CAIRO_EXTEND_NONE && r0 != 0.0) + grad->setColorAt (r0 / r1, Qt::transparent); + } + } + + switch (extend) { + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_PAD: + grad->setSpread(QGradient::PadSpread); + + grad->setColorAt (0.0, Qt::transparent); + grad->setColorAt (1.0, Qt::transparent); + break; + + case CAIRO_EXTEND_REFLECT: + grad->setSpread(QGradient::ReflectSpread); + break; + + case CAIRO_EXTEND_REPEAT: + grad->setSpread(QGradient::RepeatSpread); + break; + } + + for (unsigned int i = 0; i < gpat->n_stops; i++) { + int index = i; + if (reverse_stops) + index = gpat->n_stops - i - 1; + + double offset = gpat->stops[i].offset; + QColor color; + color.setRgbF (gpat->stops[i].color.red, + gpat->stops[i].color.green, + gpat->stops[i].color.blue, + gpat->stops[i].color.alpha); + + if (emulate_reflect) { + offset = offset / 2.0; + grad->setColorAt (1.0 - offset, color); + } + + grad->setColorAt (offset, color); + } + + mBrush = QBrush(*grad); + + delete grad; + } + + if (mBrush.style() != Qt::NoBrush && + pattern->type != CAIRO_PATTERN_TYPE_SOLID && + ! _cairo_matrix_is_identity (&pattern->matrix)) + { + cairo_matrix_t pm = pattern->matrix; + cairo_status_t status = cairo_matrix_invert (&pm); + assert (status == CAIRO_STATUS_SUCCESS); + mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm)); + } + } + + ~PatternToBrushConverter () __attribute__ ((noinline)){ + if (mAcquiredImageParent) + _cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra); + } + + operator QBrush& () { + return mBrush; + } + + QBrush mBrush; + + private: + cairo_surface_t *mAcquiredImageParent; + cairo_image_surface_t *mAcquiredImage; + void *mAcquiredImageExtra; +}; + +struct PatternToPenConverter { + PatternToPenConverter (const cairo_pattern_t *source, + const cairo_stroke_style_t *style) : + mBrushConverter(source) + { + Qt::PenJoinStyle join = Qt::MiterJoin; + Qt::PenCapStyle cap = Qt::SquareCap; + + switch (style->line_cap) { + case CAIRO_LINE_CAP_BUTT: + cap = Qt::FlatCap; + break; + case CAIRO_LINE_CAP_ROUND: + cap = Qt::RoundCap; + break; + case CAIRO_LINE_CAP_SQUARE: + cap = Qt::SquareCap; + break; + } + + switch (style->line_join) { + case CAIRO_LINE_JOIN_MITER: + join = Qt::MiterJoin; + break; + case CAIRO_LINE_JOIN_ROUND: + join = Qt::RoundJoin; + break; + case CAIRO_LINE_JOIN_BEVEL: + join = Qt::BevelJoin; + break; + } + + mPen = QPen(mBrushConverter, style->line_width, Qt::SolidLine, cap, join); + mPen.setMiterLimit (style->miter_limit); + + if (style->dash && style->num_dashes) { + Qt::PenStyle pstyle = Qt::NoPen; + + if (style->num_dashes == 2) { + if ((style->dash[0] == style->line_width && + style->dash[1] == style->line_width && style->line_width <= 2.0) || + (style->dash[0] == 0.0 && + style->dash[1] == style->line_width * 2 && cap == Qt::RoundCap)) + { + pstyle = Qt::DotLine; + } else if (style->dash[0] == style->line_width * DASH_LENGTH && + style->dash[1] == style->line_width * DASH_LENGTH && + cap == Qt::FlatCap) + { + pstyle = Qt::DashLine; + } + } + + if (pstyle != Qt::NoPen) { + mPen.setStyle(pstyle); + return; + } + + unsigned int odd_dash = style->num_dashes % 2; + + QVector dashes (odd_dash ? style->num_dashes * 2 : style->num_dashes); + for (unsigned int i = 0; i < odd_dash+1; i++) { + for (unsigned int j = 0; j < style->num_dashes; j++) { + // In Qt, the dash lengths are given in units of line width, whereas + // in cairo, they are in user-space units. We'll always apply the CTM, + // so all we have to do here is divide cairo's dash lengths by the line + // width. + dashes.append (style->dash[j] / style->line_width); + } + } + + mPen.setDashPattern(dashes); + mPen.setDashOffset(style->dash_offset / style->line_width); + } + } + + ~PatternToPenConverter() { } + + operator QPen& () { + return mPen; + } + + QPen mPen; + PatternToBrushConverter mBrushConverter; +}; + +/* + * Core drawing operations + */ + +static bool +_cairo_qt_fast_fill (cairo_qt_surface_t *qs, + const cairo_pattern_t *source, + 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) +{ +#if ENABLE_FAST_FILL + QImage *qsSrc_image = NULL; + QPixmap *qsSrc_pixmap = NULL; + std::auto_ptr qsSrc_image_d; + + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source; + if (spattern->surface->type == CAIRO_SURFACE_TYPE_QT) { + cairo_qt_surface_t *p = (cairo_qt_surface_t*) spattern->surface; + + qsSrc_image = p->image; + qsSrc_pixmap = p->pixmap; + } else if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) { + cairo_image_surface_t *p = (cairo_image_surface_t*) spattern->surface; + qsSrc_image = new QImage((const uchar*) p->data, + p->width, + p->height, + p->stride, + _qimage_format_from_cairo_format(p->format)); + qsSrc_image_d.reset(qsSrc_image); + } + } + + if (!qsSrc_image && !qsSrc_pixmap) + return false; + + // We can only drawTiledPixmap; there's no drawTiledImage + if (! qsSrc_pixmap && + (source->extend == CAIRO_EXTEND_REPEAT || + source->extend == CAIRO_EXTEND_REFLECT)) + { + return false; + } + + QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix); + + // We can draw this faster by clipping and calling drawImage/drawPixmap. + // Use our own clipping function so that we can get the + // region handling to end up with the fastest possible clip. + // + // XXX Antialiasing will fail pretty hard here, since we can't clip with AA + // with QPainter. + qs->p->save(); + + if (path) { + cairo_int_status_t status; + + cairo_clip_t clip, old_clip = qs->clipper.clip; + + qs->clipper.clip = _cairo_clip_copy (&clip); + status = (cairo_int_status_t) _cairo_clip_clip (&clip, + path, + fill_rule, + tolerance, + antialias); + if (unlikely (status)) { + qs->p->restore(); + return false; + } + + status = _cairo_qt_surface_set_clip (qs, &clip); + if (unlikely (status)) { + qs->p->restore(); + return false; + } + + _cairo_clip_reset (&clip); + qs->clipper.clip = old_clip; + } + + qs->p->setWorldMatrix (sourceMatrix.inverted(), true); + + switch (source->extend) { + case CAIRO_EXTEND_REPEAT: + // XXX handle reflect by tiling 4 times first + case CAIRO_EXTEND_REFLECT: { + assert (qsSrc_pixmap); + + // Render the tiling to cover the entire destination window (because + // it'll be clipped). Transform the window rect by the inverse + // of the current world transform so that the device coordinates + // end up as the right thing. + QRectF dest = qs->p->worldTransform().inverted().mapRect(QRectF(qs->window)); + QPointF origin = sourceMatrix.map(QPointF(0.0, 0.0)); + + qs->p->drawTiledPixmap (dest, *qsSrc_pixmap, origin); + } + break; + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_PAD: // XXX not exactly right, but good enough + default: + if (qsSrc_image) + qs->p->drawImage (0, 0, *qsSrc_image); + else if (qsSrc_pixmap) + qs->p->drawPixmap (0, 0, *qsSrc_pixmap); + break; + } + + qs->p->restore(); + + return true; +#else + return false; +#endif +} + +static cairo_int_status_t +_cairo_qt_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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 (! _op_is_supported (qs, op)) + return _cairo_surface_fallback_paint (abstract_surface, op, source, clip); + + status = _cairo_qt_surface_set_clip (qs, clip); + if (unlikely (status)) + return status; + + if (qs->supports_porter_duff) + qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); + + if (! _cairo_qt_fast_fill (qs, source)) { + PatternToBrushConverter brush (source); + qs->p->fillRect (qs->window, brush); + } + + if (qs->supports_porter_duff) + qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_qt_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_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + + D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op))); + + if (! _op_is_supported (qs, op)) + 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)) + return status; + + if (qs->supports_porter_duff) + qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); + + // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is + // enabled + //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true); + qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST); + + if (! _cairo_qt_fast_fill (qs, source, + path, fill_rule, tolerance, antialias)) + { + PatternToBrushConverter brush(source); + qs->p->fillPath (path_to_qt (path, fill_rule), brush); + } + + if (qs->supports_porter_duff) + qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_qt_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_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + + D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op))); + + if (! _op_is_supported (qs, op)) + 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) + qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op)); + + qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true); + // XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is + // enabled + //qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true); + qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST); + + PatternToPenConverter pen(source, style); + + qs->p->setPen(pen); + qs->p->drawPath(path_to_qt (path, ctm_inverse)); + qs->p->setPen(Qt::black); + + qs->p->setWorldMatrix (savedMatrix, false); + + if (qs->supports_porter_duff) + qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver); + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_qt_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) +{ +#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 +_cairo_qt_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_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 && mask->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask; + cairo_int_status_t result; + + qs->p->setOpacity (solid_mask->color.alpha); + + result = _cairo_qt_surface_paint (abstract_surface, op, source, clip); + + qs->p->setOpacity (1.0); + + return result; + } + + // otherwise skip for now + return _cairo_surface_fallback_mask (abstract_surface, op, source, mask, clip); +} + +static cairo_status_t +_cairo_qt_surface_mark_dirty (void *abstract_surface, + int x, int y, + int width, int height) +{ + cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface; + + if (qs->p && !(qs->image || qs->pixmap)) + qs->p->save (); + + return CAIRO_STATUS_SUCCESS; +} + +/* + * Backend struct + */ + +static const cairo_surface_backend_t cairo_qt_surface_backend = { + CAIRO_SURFACE_TYPE_QT, + _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, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_qt_surface_get_extents, + NULL, /* get_font_options */ + + NULL, /* flush */ + _cairo_qt_surface_mark_dirty, + + _cairo_qt_surface_paint, + _cairo_qt_surface_mask, + _cairo_qt_surface_stroke, + _cairo_qt_surface_fill, + NULL, /* fill_stroke */ + _cairo_qt_surface_show_glyphs +}; + +cairo_surface_t * +cairo_qt_surface_create (QPainter *painter) +{ + cairo_qt_surface_t *qs; + + 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)); + + memset (qs, 0, sizeof(cairo_qt_surface_t)); + + _cairo_surface_init (&qs->base, + &cairo_qt_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA, + FALSE); /* is_vector */ + + _cairo_surface_clipper_init (&qs->clipper, + _cairo_qt_surface_clipper_intersect_clip_path); + + qs->p = painter; + if (qs->p->paintEngine()) + qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); + else + qs->supports_porter_duff = FALSE; + + // Save so that we can always get back to the original state + qs->p->save(); + + qs->window = painter->window(); + + D(fprintf(stderr, "qpainter_surface_create: window: [%d %d %d %d] pd:%d\n", + qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), + qs->supports_porter_duff)); + + return &qs->base; +} + +cairo_surface_t * +cairo_qt_surface_create_with_qimage (cairo_format_t format, + int width, + int height) +{ + cairo_qt_surface_t *qs; + + 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)); + + memset (qs, 0, sizeof(cairo_qt_surface_t)); + + _cairo_surface_init (&qs->base, + &cairo_qt_surface_backend, + NULL, /* device */ + _cairo_content_from_format (format), + FALSE); /* is_vector */ + + _cairo_surface_clipper_init (&qs->clipper, + _cairo_qt_surface_clipper_intersect_clip_path); + + + QImage *image = new QImage (width, height, + _qimage_format_from_cairo_format (format)); + + qs->image = image; + + if (!image->isNull()) { + qs->p = new QPainter(image); + qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); + } + + qs->image_equiv = cairo_image_surface_create_for_data (image->bits(), + format, + width, height, + image->bytesPerLine()); + + qs->window = QRect(0, 0, width, height); + + D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n", + qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), + qs->supports_porter_duff)); + + return &qs->base; +} + +cairo_surface_t * +cairo_qt_surface_create_with_qpixmap (cairo_content_t content, + int width, + int height) +{ + cairo_qt_surface_t *qs; + + if ((content & CAIRO_CONTENT_COLOR) == 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + 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)); + + memset (qs, 0, sizeof(cairo_qt_surface_t)); + + QPixmap *pixmap = new QPixmap (width, height); + if (pixmap == NULL) { + free (qs); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + // By default, a QPixmap is opaque; however, if it's filled + // with a color with a transparency component, it is converted + // to a format that preserves transparency. + if (content == CAIRO_CONTENT_COLOR_ALPHA) + pixmap->fill(Qt::transparent); + + _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); + + qs->pixmap = pixmap; + + if (!pixmap->isNull()) { + qs->p = new QPainter(pixmap); + qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff); + } + + qs->window = QRect(0, 0, width, height); + + D(fprintf(stderr, "qpainter_surface_create: qpixmap: [%d %d %d %d] pd:%d\n", + qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(), + qs->supports_porter_duff)); + + 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; + + /* 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; +} + +QImage * +cairo_qt_surface_get_qimage (cairo_surface_t *surface) +{ + cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; + + /* 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; +} + +cairo_surface_t * +cairo_qt_surface_get_image (cairo_surface_t *surface) +{ + cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface; + + /* 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; +} + +/* + * TODO: + * + * - Figure out why QBrush isn't working with non-repeated images + * + * - 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 EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that) + * + * - stroke-image failure + * + * - Implement mask() with non-solid masks (probably will need to use a temporary and use IN) + * + * - Implement gradient sources + * + * - Make create_similar smarter -- create QPixmaps in more circumstances + * (e.g. if the pixmap can have alpha) + * + * - Implement show_glyphs() in terms of Qt + * + */ diff --git a/gfx/cairo/cairo/src/cairo-qt.h b/gfx/cairo/cairo/src/cairo-qt.h new file mode 100644 index 0000000000..c20bbb18d0 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-qt.h @@ -0,0 +1,85 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * 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 + * 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): + * Vladimir Vukicevic + */ + +#ifndef CAIRO_QT_H +#define CAIRO_QT_H + +#include "cairo.h" + +#if CAIRO_HAS_QT_SURFACE + +#include +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_qt_surface_create (QPainter *painter); + +cairo_public cairo_surface_t * +cairo_qt_surface_create_with_qimage (cairo_format_t format, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_qt_surface_create_with_qpixmap (cairo_content_t content, + int width, + int height); + +cairo_public QPainter * +cairo_qt_surface_get_qpainter (cairo_surface_t *surface); + +/* XXX needs hooking to generic surface layer, my vote is for +cairo_public cairo_surface_t * +cairo_surface_map_image (cairo_surface_t *surface); +cairo_public void +cairo_surface_unmap_image (cairo_surface_t *surface, cairo_surface_t *image); +*/ +cairo_public cairo_surface_t * +cairo_qt_surface_get_image (cairo_surface_t *surface); + +cairo_public QImage * +cairo_qt_surface_get_qimage (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_QT_SURFACE */ + +# error Cairo was not compiled with support for the Qt backend + +#endif /* CAIRO_HAS_QT_SURFACE */ + +#endif /* CAIRO_QT_H */ diff --git a/gfx/cairo/cairo/src/cairo-quartz-font.c b/gfx/cairo/cairo/src/cairo-quartz-font.c new file mode 100644 index 0000000000..740ca108e7 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-quartz-font.c @@ -0,0 +1,922 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * 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 + * 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 Foundation. + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#include "cairoint.h" + +#include + +#include "cairo-image-surface-private.h" +#include "cairo-quartz.h" +#include "cairo-quartz-private.h" + +#include "cairo-error-private.h" + +/** + * SECTION:cairo-quartz-fonts + * @Title: Quartz (CGFont) Fonts + * @Short_Description: Font support via CGFont on OS X + * @See_Also: #cairo_font_face_t + * + * 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; +static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL; + +/* These aren't public before 10.5, and some have different names in 10.4 */ +static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL; +static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL; +static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL; +static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL; + +/* Not public, but present */ +static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL; +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; +static double (*CTFontGetAdvancesForGlyphsPtr) (CTFontRef, CTFontOrientation, const CGGlyph*, CGSize *, CFIndex) = NULL; +static CGRect (*CTFontGetBoundingRectsForGlyphsPtr) (CTFontRef, CTFontOrientation, const CGGlyph*, CGRect *, CFIndex) = NULL; + +/* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */ +typedef struct { + int ascent; + int descent; + int leading; +} quartz_CGFontMetrics; +static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL; +static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL; +static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL; +static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL; + +#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID +/* Not public anymore in 64-bits nor in 10.7 */ +static ATSFontRef (*FMGetATSFontRefFromFontPtr) (ATSUFontID iFont) = NULL; +#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */ + +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) + CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes"); + + CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars"); + if (!CGFontGetGlyphsForUnicharsPtr) + CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes"); + + CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox"); + + /* We just need one of these two */ + CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName"); + CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName"); + + /* These have the same name in 10.4 and 10.5 */ + CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm"); + CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances"); + + /* + * Some Tiger systems have a partial version of CoreText, which + * has incompatible signatures: CTFontCreateWithGraphicsFont + * accepts a double for the size argument even on i386 and all + * functions omit the CTFontOrientation arguments. Since the 10.4 + * CoreText library does not provide the CTFontCreatePathForGlyph + * symbol, use it to determine whether to use CoreText at all. + */ + CTFontCreatePathForGlyphPtr = dlsym(RTLD_DEFAULT, "CTFontCreatePathForGlyph"); + if (CTFontCreatePathForGlyphPtr) { + CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont"); + CTFontGetAdvancesForGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontGetAdvancesForGlyphs"); + CTFontGetBoundingRectsForGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontGetBoundingRectsForGlyphs"); + } else { + CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath"); + } + + CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics"); + CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent"); + CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent"); + CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading"); + + CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); + CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); + +#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID + FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont"); +#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */ + + if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) && + CGFontGetGlyphBBoxesPtr && + CGFontGetGlyphsForUnicharsPtr && + CGFontGetUnitsPerEmPtr && + CGFontGetGlyphAdvancesPtr && + ((CTFontCreateWithGraphicsFontPtr && CTFontCreatePathForGlyphPtr) || CGFontGetGlyphPathPtr) && + (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr))) + _cairo_quartz_font_symbols_present = TRUE; + + _cairo_quartz_font_symbol_lookup_done = TRUE; +} + +typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t; +typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t; + +struct _cairo_quartz_scaled_font { + cairo_scaled_font_t base; +}; + +struct _cairo_quartz_font_face { + cairo_font_face_t base; + + CGFontRef cgFont; + CTFontRef ctFont; +}; + +/* + * font face backend + */ + +static cairo_status_t +_cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + const char *family; + char *full_name; + CFStringRef cgFontName = NULL; + CGFontRef cgFont = NULL; + int loop; + + quartz_font_ensure_symbols(); + if (! _cairo_quartz_font_symbols_present) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + family = toy_face->family; + full_name = _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"; + else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans")) + family = "Helvetica"; + else if (!strcmp(family, "cursive")) + family = "Apple Chancery"; + else if (!strcmp(family, "fantasy")) + family = "Papyrus"; + else if (!strcmp(family, "monospace") || !strcmp(family, "mono")) + family = "Courier"; + + /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first, + * then drop the bold, then drop the slant, then drop both.. finally + * just use "Helvetica". And if Helvetica doesn't exist, give up. + */ + for (loop = 0; loop < 5; loop++) { + if (loop == 4) + family = "Helvetica"; + + strcpy (full_name, family); + + if (loop < 3 && (loop & 1) == 0) { + if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD) + strcat (full_name, " Bold"); + } + + if (loop < 3 && (loop & 2) == 0) { + if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC) + strcat (full_name, " Italic"); + else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE) + strcat (full_name, " Oblique"); + } + + if (CGFontCreateWithFontNamePtr) { + cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII); + cgFont = CGFontCreateWithFontNamePtr (cgFontName); + CFRelease (cgFontName); + } else { + cgFont = CGFontCreateWithNamePtr (full_name); + } + + if (cgFont) + break; + } + + if (!cgFont) { + /* Give up */ + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont); + CGFontRelease (cgFont); + + return CAIRO_STATUS_SUCCESS; +} + +static 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; + +static cairo_status_t +_cairo_quartz_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_out) +{ + cairo_quartz_font_face_t *font_face = abstract_face; + cairo_quartz_scaled_font_t *font = NULL; + cairo_status_t status; + cairo_font_extents_t fs_metrics; + double ems; + CGRect bbox; + + quartz_font_ensure_symbols(); + if (!_cairo_quartz_font_symbols_present) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font = _cairo_malloc (sizeof(cairo_quartz_scaled_font_t)); + if (font == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memset (font, 0, sizeof(cairo_quartz_scaled_font_t)); + + status = _cairo_scaled_font_init (&font->base, + &font_face->base, font_matrix, ctm, options, + &_cairo_quartz_scaled_font_backend); + if (status) + goto FINISH; + + ems = CGFontGetUnitsPerEmPtr (font_face->cgFont); + + /* initialize metrics */ + if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) { + fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems); + fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems); + fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + + (CGFontGetLeadingPtr (font_face->cgFont) / ems); + + bbox = CGFontGetFontBBoxPtr (font_face->cgFont); + fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; + fs_metrics.max_y_advance = 0.0; + } else { + CGGlyph wGlyph; + UniChar u; + + quartz_CGFontMetrics *m; + m = CGFontGetHMetricsPtr (font_face->cgFont); + + /* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */ + if (!m) { + status = _cairo_error(CAIRO_STATUS_NULL_POINTER); + goto FINISH; + } + + fs_metrics.ascent = (m->ascent / ems); + fs_metrics.descent = - (m->descent / ems); + fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems); + + /* We kind of have to guess here; W's big, right? */ + u = (UniChar) 'W'; + CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1); + if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) { + fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems; + fs_metrics.max_y_advance = 0.0; + } else { + fs_metrics.max_x_advance = 0.0; + fs_metrics.max_y_advance = 0.0; + } + } + + status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics); + +FINISH: + if (status != CAIRO_STATUS_SUCCESS) { + free (font); + } else { + *font_out = (cairo_scaled_font_t*) font; + } + + return status; +} + +const cairo_font_face_backend_t _cairo_quartz_font_face_backend = { + CAIRO_FONT_TYPE_QUARTZ, + _cairo_quartz_font_face_create_for_toy, + _cairo_quartz_font_face_destroy, + _cairo_quartz_font_face_scaled_font_create +}; + +/** + * 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 + * #CGFontRef. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * + * 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_quartz_font_face_create_for_cgfont (CGFontRef font) +{ + cairo_quartz_font_face_t *font_face; + + quartz_font_ensure_symbols(); + + 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); + return (cairo_font_face_t *)&_cairo_font_face_nil; + } + + font_face->cgFont = CGFontRetain (font); + + if (CTFontCreateWithGraphicsFontPtr) + font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL); + else + font_face->ctFont = NULL; + + _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend); + + return &font_face->base; +} + +/* + * scaled font backend + */ + +static cairo_quartz_font_face_t * +_cairo_quartz_scaled_to_face (void *abstract_font) +{ + cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font; + cairo_font_face_t *font_face = sfont->base.font_face; + assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ); + return (cairo_quartz_font_face_t*) font_face; +} + +static void +_cairo_quartz_scaled_font_fini(void *abstract_font) +{ +} + +static inline CGGlyph +_cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) { + unsigned long index = _cairo_scaled_glyph_index (scaled_glyph); + return index <= CGGLYPH_MAX ? index : CGGLYPH_INVALID; +} + +static cairo_int_status_t +_cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); + cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0}; + CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); + int advance; + CGRect bbox; + double xmin, ymin, xmax, ymax; + + if (unlikely (glyph == CGGLYPH_INVALID)) + goto FAIL; + + if (font_face->ctFont) { + CGSize advanceSize; + CTFontGetBoundingRectsForGlyphsPtr (font_face->ctFont, + kCTFontOrientationDefault, + &glyph, &bbox, 1); + + CTFontGetAdvancesForGlyphsPtr (font_face->ctFont, + kCTFontOrientationDefault, + &glyph, &advanceSize, 1); + + extents.x_advance = advanceSize.width; + extents.y_advance = advanceSize.height; + } else if (CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) && + CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) { + double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); + + /* broken fonts like Al Bayan return incorrect bounds for some null + * characters,see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */ + if (unlikely (bbox.origin.x == -32767 && + bbox.origin.y == -32767 && + bbox.size.width == 65534 && + bbox.size.height == 65534)) { + bbox.origin.x = bbox.origin.y = 0; + bbox.size.width = bbox.size.height = 0; + } + + bbox = CGRectMake (bbox.origin.x / emscale, + bbox.origin.y / emscale, + bbox.size.width / emscale, + bbox.size.height / emscale); + + extents.x_advance = advance / emscale; + extents.y_advance = 0.0; + } else { + goto FAIL; + }; + + /* Should we want to always integer-align glyph extents, we can do so in this way */ +#if 0 + { + CGAffineTransform textMatrix; + textMatrix = CGAffineTransformMake (font->base.scale.xx, + -font->base.scale.yx, + -font->base.scale.xy, + font->base.scale.yy, + 0.0f, 0.0f); + + bbox = CGRectApplyAffineTransform (bbox, textMatrix); + bbox = CGRectIntegral (bbox); + bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix)); + } +#endif + + xmin = CGRectGetMinX(bbox); + ymin = CGRectGetMinY(bbox); + xmax = CGRectGetMaxX(bbox); + ymax = CGRectGetMaxY(bbox); + + extents.x_bearing = xmin; + extents.y_bearing = - ymax; + extents.width = xmax - xmin; + extents.height = ymax - ymin; + +#if 0 + fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph, + extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance); +#endif + + FAIL: + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &font->base, + &extents); + + return status; +} + +static void +_cairo_quartz_path_apply_func (void *info, const CGPathElement *el) +{ + cairo_path_fixed_t *path = (cairo_path_fixed_t *) info; + cairo_status_t status; + + switch (el->type) { + case kCGPathElementMoveToPoint: + status = _cairo_path_fixed_move_to (path, + _cairo_fixed_from_double(el->points[0].x), + _cairo_fixed_from_double(el->points[0].y)); + assert(!status); + break; + case kCGPathElementAddLineToPoint: + status = _cairo_path_fixed_line_to (path, + _cairo_fixed_from_double(el->points[0].x), + _cairo_fixed_from_double(el->points[0].y)); + assert(!status); + break; + case kCGPathElementAddQuadCurveToPoint: { + cairo_fixed_t fx, fy; + double x, y; + if (!_cairo_path_fixed_get_current_point (path, &fx, &fy)) + fx = fy = 0; + x = _cairo_fixed_to_double (fx); + y = _cairo_fixed_to_double (fy); + + status = _cairo_path_fixed_curve_to (path, + _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0), + _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0), + _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0), + _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0), + _cairo_fixed_from_double(el->points[1].x), + _cairo_fixed_from_double(el->points[1].y)); + } + assert(!status); + break; + case kCGPathElementAddCurveToPoint: + status = _cairo_path_fixed_curve_to (path, + _cairo_fixed_from_double(el->points[0].x), + _cairo_fixed_from_double(el->points[0].y), + _cairo_fixed_from_double(el->points[1].x), + _cairo_fixed_from_double(el->points[1].y), + _cairo_fixed_from_double(el->points[2].x), + _cairo_fixed_from_double(el->points[2].y)); + assert(!status); + break; + case kCGPathElementCloseSubpath: + status = _cairo_path_fixed_close_path (path); + assert(!status); + break; + } +} + +static cairo_int_status_t +_cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); + CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); + CGAffineTransform textMatrix; + CGPathRef glyphPath; + cairo_path_fixed_t *path; + + if (unlikely (glyph == CGGLYPH_INVALID)) { + _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create()); + return CAIRO_STATUS_SUCCESS; + } + + /* scale(1,-1) * font->base.scale */ + textMatrix = CGAffineTransformMake (font->base.scale.xx, + font->base.scale.yx, + -font->base.scale.xy, + -font->base.scale.yy, + 0, 0); + + if (font_face->ctFont) { + glyphPath = CTFontCreatePathForGlyphPtr (font_face->ctFont, glyph, &textMatrix); + } else { + glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); + } + + if (!glyphPath) + return CAIRO_INT_STATUS_UNSUPPORTED; + + path = _cairo_path_fixed_create (); + if (!path) { + CGPathRelease (glyphPath); + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + } + + CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func); + + CGPathRelease (glyphPath); + + _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); + + cairo_image_surface_t *surface = NULL; + + CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); + + int advance; + CGRect bbox; + double width, height; + double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont); + + CGContextRef cgContext = NULL; + CGAffineTransform textMatrix; + CGRect glyphRect, glyphRectInt; + CGPoint glyphOrigin; + + //fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface); + + /* Create blank 2x2 image if we don't have this character. + * Maybe we should draw a better missing-glyph slug or something, + * but this is ok for now. + */ + 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) + return status; + + _cairo_scaled_glyph_set_surface (scaled_glyph, + &font->base, + surface); + return CAIRO_STATUS_SUCCESS; + } + + if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) || + !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox)) + { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* scale(1,-1) * font->base.scale * scale(1,-1) */ + textMatrix = CGAffineTransformMake (font->base.scale.xx, + -font->base.scale.yx, + -font->base.scale.xy, + font->base.scale.yy, + 0, -0); + glyphRect = CGRectMake (bbox.origin.x / emscale, + bbox.origin.y / emscale, + bbox.size.width / emscale, + bbox.size.height / emscale); + + glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix); + + /* Round the rectangle outwards, so that we don't have to deal + * with non-integer-pixel origins or dimensions. + */ + glyphRectInt = CGRectIntegral (glyphRect); + +#if 0 + fprintf (stderr, "glyphRect[o]: %f %f %f %f\n", + glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); + fprintf (stderr, "glyphRectInt: %f %f %f %f\n", + glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height); +#endif + + glyphOrigin = glyphRectInt.origin; + + //textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm)); + + width = glyphRectInt.size.width; + height = glyphRectInt.size.height; + + //fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height); + + surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + if (surface->base.status) + return surface->base.status; + + if (surface->width != 0 && surface->height != 0) { + cgContext = CGBitmapContextCreate (surface->data, + surface->width, + surface->height, + 8, + surface->stride, + NULL, + kCGImageAlphaOnly); + + if (cgContext == NULL) { + cairo_surface_destroy (&surface->base); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + CGContextSetFont (cgContext, font_face->cgFont); + CGContextSetFontSize (cgContext, 1.0); + CGContextSetTextMatrix (cgContext, textMatrix); + + switch (font->base.options.antialias) { + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: + CGContextSetShouldAntialias (cgContext, TRUE); + CGContextSetShouldSmoothFonts (cgContext, TRUE); + if (CGContextSetAllowsFontSmoothingPtr && + !CGContextGetAllowsFontSmoothingPtr (cgContext)) + CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE); + break; + case CAIRO_ANTIALIAS_NONE: + CGContextSetShouldAntialias (cgContext, FALSE); + break; + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_FAST: + CGContextSetShouldAntialias (cgContext, TRUE); + CGContextSetShouldSmoothFonts (cgContext, FALSE); + break; + case CAIRO_ANTIALIAS_DEFAULT: + default: + /* Don't do anything */ + break; + } + + CGContextSetAlpha (cgContext, 1.0); + CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1); + + CGContextRelease (cgContext); + } + + cairo_surface_set_device_offset (&surface->base, + - glyphOrigin.x, + height + glyphOrigin.y); + + _cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface); + + return status; +} + +static cairo_int_status_t +_cairo_quartz_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS)) + status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph); + + if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH)) + status = _cairo_quartz_init_glyph_path (font, scaled_glyph); + + if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE)) + status = _cairo_quartz_init_glyph_surface (font, scaled_glyph); + + return status; +} + +static unsigned long +_cairo_quartz_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + 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); + CGGlyph glyph[2]; + UniChar utf16[2]; + + int len = _cairo_ucs4_to_utf16 (ucs4, utf16); + CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, utf16, glyph, len); + + 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 = { + CAIRO_FONT_TYPE_QUARTZ, + _cairo_quartz_scaled_font_fini, + _cairo_quartz_scaled_glyph_init, + NULL, /* text_to_glyphs */ + _cairo_quartz_ucs4_to_index, + _cairo_quartz_load_truetype_table, + NULL, /* map_glyphs_to_unicode */ +}; + +/* + * private methods that the quartz surface uses + */ + +CGFontRef +_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font) +{ + cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(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; +} + +/* + * compat with old ATSUI backend + */ +#ifdef CAIRO_HAS_QUARTZ_ATSUFONTID + +/** + * 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 + * #ATSUFontID. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * + * 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_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id) +{ + quartz_font_ensure_symbols(); + + if (FMGetATSFontRefFromFontPtr != NULL) { + ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id); + CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont); + cairo_font_face_t *ff; + + ff = cairo_quartz_font_face_create_for_cgfont (cgFont); + + 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 */ +cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id); + +cairo_font_face_t * +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 /* CAIRO_HAS_QUARTZ_ATSUFONTID */ diff --git a/gfx/cairo/cairo/src/cairo-quartz-image-surface.c b/gfx/cairo/cairo/src/cairo-quartz-image-surface.c new file mode 100644 index 0000000000..30d92d6bed --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-quartz-image-surface.c @@ -0,0 +1,391 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * 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 + * 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 Foundation. + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#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))) +#define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE))) +#define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT))) + +static void +DataProviderReleaseCallback (void *image_info, const void *data, size_t size) +{ + free (image_info); +} + +static cairo_surface_t * +_cairo_quartz_image_surface_create_similar (void *asurface, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t *isurf = + _cairo_image_surface_create_with_content (content, width, height); + cairo_surface_t *result = cairo_quartz_image_surface_create (isurf); + cairo_surface_destroy (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; +} + +static cairo_status_t +_cairo_quartz_image_surface_finish (void *asurface) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + + CGImageRelease (surface->image); + cairo_surface_destroy ( (cairo_surface_t*) surface->imageSurface); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_quartz_image_surface_acquire_source_image (void *asurface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + + *image_out = surface->imageSurface; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +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); +} + +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 +_cairo_quartz_image_surface_get_extents (void *asurface, + cairo_rectangle_int_t *extents) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + + extents->x = 0; + extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + return TRUE; +} + +/* we assume some drawing happened to the image buffer; make sure it's + * represented in the CGImage on flush() + */ + +static cairo_status_t +_cairo_quartz_image_surface_flush (void *asurface, + unsigned flags) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface; + CGImageRef oldImage = surface->image; + CGImageRef newImage = NULL; + void *image_data; + const unsigned int size = surface->imageSurface->height * surface->imageSurface->stride; + if (flags) + return CAIRO_STATUS_SUCCESS; + + /* XXX only flush if the image has been modified. */ + + image_data = _cairo_malloc_ab ( surface->imageSurface->height, + surface->imageSurface->stride); + if (unlikely (!image_data)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (image_data, surface->imageSurface->data, + surface->imageSurface->height * surface->imageSurface->stride); + newImage = CairoQuartzCreateCGImage (surface->imageSurface->format, + surface->imageSurface->width, + surface->imageSurface->height, + surface->imageSurface->stride, + image_data, + TRUE, + NULL, + DataProviderReleaseCallback, + image_data); + + surface->image = newImage; + CGImageRelease (oldImage); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_quartz_image_surface_paint (void *abstract_surface, + cairo_operator_t op, + 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_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 */ + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_quartz_image_surface_get_extents, + NULL, /* get_font_options */ + + _cairo_quartz_image_surface_flush, + NULL, /* mark_dirty_rectangle */ + + _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: + * @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 + * when used as a source when rendering to a #cairo_quartz_surface. If + * the data in the image surface is ever updated, cairo_surface_flush() + * must be called on the #cairo_quartz_image_surface to ensure that the + * CGImageRef refers to the updated data. + * + * Return value: the newly created surface. + * + * Since: 1.6 + **/ +cairo_surface_t * +cairo_quartz_image_surface_create (cairo_surface_t *surface) +{ + cairo_quartz_image_surface_t *qisurf; + + CGImageRef image; + + cairo_image_surface_t *image_surface; + int width, height, stride; + cairo_format_t format; + void *image_data; + + if (surface->status) + return surface; + + if (! _cairo_surface_is_image (surface)) + return SURFACE_ERROR_TYPE_MISMATCH; + + image_surface = (cairo_image_surface_t*) surface; + width = image_surface->width; + height = image_surface->height; + stride = image_surface->stride; + format = image_surface->format; + + if (!_cairo_quartz_verify_surface_size(width, height)) + return SURFACE_ERROR_INVALID_SIZE; + + if (width == 0 || height == 0) + return SURFACE_ERROR_INVALID_SIZE; + + if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24) + return SURFACE_ERROR_INVALID_FORMAT; + + 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)); + + image_data = _cairo_malloc_ab (height, stride); + if (unlikely (!image_data)) { + free(qisurf); + return SURFACE_ERROR_NO_MEMORY; + } + + memcpy (image_data, image_surface->data, height * stride); + image = CairoQuartzCreateCGImage (format, + width, height, + stride, + image_data, + TRUE, + NULL, + DataProviderReleaseCallback, + image_data); + + if (!image) { + free (qisurf); + return SURFACE_ERROR_NO_MEMORY; + } + + _cairo_surface_init (&qisurf->base, + &cairo_quartz_image_surface_backend, + NULL, /* device */ + _cairo_content_from_format (format), + FALSE); /* is_vector */ + + qisurf->width = width; + qisurf->height = height; + + qisurf->image = image; + qisurf->imageSurface = (cairo_image_surface_t*) cairo_surface_reference(surface); + + return &qisurf->base; +} + + +cairo_surface_t * +cairo_quartz_image_surface_get_image (cairo_surface_t *asurface) +{ + cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface; + + /* 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 new file mode 100644 index 0000000000..9e8409c116 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-quartz-image.h @@ -0,0 +1,57 @@ +/* cairo - a vector graphics library with display and print output + * + * 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 + * 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 Foundation. + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#ifndef CAIRO_QUARTZ_IMAGE_H +#define CAIRO_QUARTZ_IMAGE_H + +#include "cairo.h" + +#if CAIRO_HAS_QUARTZ_IMAGE_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_quartz_image_surface_create (cairo_surface_t *image_surface); + +cairo_public cairo_surface_t * +cairo_quartz_image_surface_get_image (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */ +# error Cairo was not compiled with support for the quartz-image backend +#endif /* CAIRO_HAS_QUARTZ_IMAGE_SURFACE */ + +#endif /* CAIRO_QUARTZ_IMAGE_H */ diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h new file mode 100644 index 0000000000..d789191900 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h @@ -0,0 +1,125 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Calum Robinson + * Copyright (C) 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 + * 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 Calum Robinson + * + * Contributor(s): + * Calum Robinson + * Vladimir Vukicevic + */ + +#ifndef CAIRO_QUARTZ_PRIVATE_H +#define CAIRO_QUARTZ_PRIVATE_H + +#include "cairoint.h" + +#if CAIRO_HAS_QUARTZ_SURFACE +#include "cairo-quartz.h" +#include "cairo-surface-clipper-private.h" + +#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, + DO_LAYER +} cairo_quartz_action_t; + +/* define CTFontRef for pre-10.5 SDKs */ +typedef const struct __CTFont *CTFontRef; + +typedef struct cairo_quartz_surface { + cairo_surface_t base; + + CGContextRef cgContext; + CGAffineTransform cgContextBaseCTM; + + void *imageData; + cairo_surface_t *imageSurfaceEquiv; + + cairo_surface_clipper_t clipper; + + /** + * If non-null, this is the CGLayer for the surface. + */ + CGLayerRef cgLayer; + + cairo_rectangle_int_t extents; + cairo_rectangle_int_t virtual_extents; + + cairo_bool_t ownsData; +} cairo_quartz_surface_t; + +typedef struct cairo_quartz_image_surface { + cairo_surface_t base; + + int width, height; + + CGImageRef image; + cairo_image_surface_t *imageSurface; +} cairo_quartz_image_surface_t; + +cairo_private cairo_bool_t +_cairo_quartz_verify_surface_size(int width, int height); + +cairo_private cairo_bool_t +_cairo_surface_is_quartz (const cairo_surface_t *surface); + +cairo_private CGImageRef +CairoQuartzCreateCGImage (cairo_format_t format, + unsigned int width, + unsigned int height, + unsigned int stride, + void *data, + cairo_bool_t interpolate, + CGColorSpaceRef colorSpaceOverride, + CGDataProviderReleaseDataCallback releaseCallback, + void *releaseInfo); + +cairo_private CGFontRef +_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont); + +cairo_private 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 + +#endif /* CAIRO_HAS_QUARTZ_SURFACE */ + +#endif /* CAIRO_QUARTZ_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c new file mode 100644 index 0000000000..b97967fb21 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -0,0 +1,3007 @@ +/* -*- 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, 2007 Mozilla Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 Foundation. + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#define _GNU_SOURCE /* required for RTLD_DEFAULT */ +#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-recording-surface-private.h" +#include "cairo-tag-attributes-private.h" + +#include + +#ifndef RTLD_DEFAULT +#define RTLD_DEFAULT ((void *) 0) +#endif + +#include + +#undef QUARTZ_DEBUG + +#ifdef QUARTZ_DEBUG +#define ND(_x) fprintf _x +#else +#define ND(_x) do {} while(0) +#endif + +#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0) + +/** + * SECTION:cairo-quartz + * @Title: Quartz Surfaces + * @Short_Description: Rendering to Quartz surfaces + * @See_Also: #cairo_surface_t + * + * 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. + * + * 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. + */ +enum PrivateCGCompositeMode { + kPrivateCGCompositeClear = 0, + kPrivateCGCompositeCopy = 1, + kPrivateCGCompositeSourceOver = 2, + kPrivateCGCompositeSourceIn = 3, + kPrivateCGCompositeSourceOut = 4, + kPrivateCGCompositeSourceAtop = 5, + kPrivateCGCompositeDestinationOver = 6, + kPrivateCGCompositeDestinationIn = 7, + kPrivateCGCompositeDestinationOut = 8, + kPrivateCGCompositeDestinationAtop = 9, + kPrivateCGCompositeXOR = 10, + kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s))) + kPrivateCGCompositePlusLighter = 12, // (min (1, s + d)) +}; +typedef enum PrivateCGCompositeMode PrivateCGCompositeMode; +CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode); +#endif + +/* Some of these are present in earlier versions of the OS than where + * they are public; other are not public at all + */ +/* public since 10.5 */ +static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = 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; + +/* CTFontDrawGlyphs is not available until 10.7 */ +static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL; + +static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; + +/* + * Utility functions + */ + +#ifdef QUARTZ_DEBUG +static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest); +static void quartz_image_to_png (CGImageRef, char *dest); +#endif + +static cairo_quartz_surface_t * +_cairo_quartz_surface_create_internal (CGContextRef cgContext, + cairo_content_t content, + unsigned int width, + unsigned int height); + +/* Load all extra symbols */ +static void quartz_ensure_symbols (void) +{ + if (likely (_cairo_quartz_symbol_lookup_done)) + return; + + 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"); + + CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs"); + + _cairo_quartz_symbol_lookup_done = TRUE; +} + +CGImageRef +CairoQuartzCreateCGImage (cairo_format_t format, + unsigned int width, + unsigned int height, + unsigned int stride, + void *data, + cairo_bool_t interpolate, + CGColorSpaceRef colorSpaceOverride, + CGDataProviderReleaseDataCallback releaseCallback, + void *releaseInfo) +{ + CGImageRef image = NULL; + CGDataProviderRef dataProvider = NULL; + CGColorSpaceRef colorSpace = colorSpaceOverride; + CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host; + int bitsPerComponent, bitsPerPixel; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + if (colorSpace == NULL) + colorSpace = CGColorSpaceCreateDeviceRGB (); + bitinfo |= kCGImageAlphaPremultipliedFirst; + bitsPerComponent = 8; + bitsPerPixel = 32; + break; + + case CAIRO_FORMAT_RGB24: + if (colorSpace == NULL) + colorSpace = CGColorSpaceCreateDeviceRGB (); + bitinfo |= kCGImageAlphaNoneSkipFirst; + bitsPerComponent = 8; + bitsPerPixel = 32; + break; + + case CAIRO_FORMAT_A8: + bitsPerComponent = 8; + bitsPerPixel = 8; + break; + + case CAIRO_FORMAT_A1: +#ifdef WORDS_BIGENDIAN + bitsPerComponent = 1; + bitsPerPixel = 1; + break; +#endif + + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_INVALID: + default: + return NULL; + } + + dataProvider = CGDataProviderCreateWithData (releaseInfo, + data, + height * stride, + releaseCallback); + + if (unlikely (!dataProvider)) { + // manually release + if (releaseCallback) + releaseCallback (releaseInfo, data, height * stride); + goto FINISH; + } + + if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) { + cairo_quartz_float_t decode[] = {1.0, 0.0}; + image = CGImageMaskCreate (width, height, + bitsPerComponent, + bitsPerPixel, + stride, + dataProvider, + decode, + interpolate); + } else + image = CGImageCreate (width, height, + bitsPerComponent, + bitsPerPixel, + stride, + colorSpace, + bitinfo, + dataProvider, + NULL, + interpolate, + kCGRenderingIntentDefault); + +FINISH: + + CGDataProviderRelease (dataProvider); + + if (colorSpace != colorSpaceOverride) + CGColorSpaceRelease (colorSpace); + + return image; +} + +static inline cairo_bool_t +_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) +{ + if (unlikely (cgc == NULL)) + return FALSE; + + if (likely (CGContextGetTypePtr)) { + /* 4 is the type value of a bitmap context */ + return CGContextGetTypePtr (cgc) == 4; + } + + /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */ + return CGBitmapContextGetBitsPerPixel (cgc) != 0; +} + +/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */ + +#define CG_MAX_HEIGHT SHRT_MAX +#define CG_MAX_WIDTH USHRT_MAX + +/* is the desired size of the surface within bounds? */ +cairo_bool_t +_cairo_quartz_verify_surface_size (int width, int height) +{ + /* hmmm, allow width, height == 0 ? */ + if (width < 0 || height < 0) + return FALSE; + + if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) + return FALSE; + + return TRUE; +} + +/* + * Cairo path -> Quartz path conversion helpers + */ + +/* cairo path -> execute in context */ +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))); + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + CGContextMoveToPoint (closure, x, y); + return CAIRO_STATUS_SUCCESS; +} + +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))); + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + CGContextAddLineToPoint (closure, x, y); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_path_to_quartz_context_curve_to (void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n", + // _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y), + // _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); + double y1 = _cairo_fixed_to_double (p1->y); + double x2 = _cairo_fixed_to_double (p2->x); + double y2 = _cairo_fixed_to_double (p2->y); + + 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")); + CGContextClosePath (closure); + return CAIRO_STATUS_SUCCESS; +} + +static void +_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_path_to_quartz_context_move_to, + _cairo_path_to_quartz_context_line_to, + _cairo_path_to_quartz_context_curve_to, + _cairo_path_to_quartz_context_close_path, + closure); + + assert (status == CAIRO_STATUS_SUCCESS); +} + +/* + * Misc helpers/callbacks + */ + +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050 +static PrivateCGCompositeMode +_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op) +{ + switch (op) { + case CAIRO_OPERATOR_CLEAR: + return kPrivateCGCompositeClear; + case CAIRO_OPERATOR_SOURCE: + return kPrivateCGCompositeCopy; + case CAIRO_OPERATOR_OVER: + return kPrivateCGCompositeSourceOver; + case CAIRO_OPERATOR_IN: + return kPrivateCGCompositeSourceIn; + case CAIRO_OPERATOR_OUT: + return kPrivateCGCompositeSourceOut; + case CAIRO_OPERATOR_ATOP: + return kPrivateCGCompositeSourceAtop; + case CAIRO_OPERATOR_DEST_OVER: + return kPrivateCGCompositeDestinationOver; + case CAIRO_OPERATOR_DEST_IN: + return kPrivateCGCompositeDestinationIn; + case CAIRO_OPERATOR_DEST_OUT: + return kPrivateCGCompositeDestinationOut; + case CAIRO_OPERATOR_DEST_ATOP: + return kPrivateCGCompositeDestinationAtop; + case CAIRO_OPERATOR_XOR: + return kPrivateCGCompositeXOR; + case CAIRO_OPERATOR_ADD: + return kPrivateCGCompositePlusLighter; + + case CAIRO_OPERATOR_DEST: + 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: + default: + 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; + } + return kCGBlendModeNormal; /* just to silence clang warning [-Wreturn-type] */ +} + +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) { + assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */ + + 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; + } + + 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) { + default: + ASSERT_NOT_REACHED; + + 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) { + default: + ASSERT_NOT_REACHED; + + 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: + case CAIRO_FILTER_FAST: + return kCGInterpolationNone; + + case CAIRO_FILTER_BEST: + return kCGInterpolationHigh; + + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BILINEAR: + return kCGInterpolationLow; + + case CAIRO_FILTER_GAUSSIAN: + return kCGInterpolationDefault; + + default: + ASSERT_NOT_REACHED; + return kCGInterpolationDefault; + } +} + +static inline void +_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src, + CGAffineTransform *dst) +{ + dst->a = src->xx; + dst->b = src->yx; + dst->c = src->xy; + dst->d = src->yy; + dst->tx = src->x0; + dst->ty = src->y0; +} + + +/* + * Source -> Quartz setup and finish functions + */ + +static void +ComputeGradientValue (void *info, + const cairo_quartz_float_t *in, + cairo_quartz_float_t *out) +{ + double fdist = *in; + const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info; + unsigned int i; + + /* Put fdist back in the 0.0..1.0 range if we're doing + * REPEAT/REFLECT + */ + if (grad->base.extend == CAIRO_EXTEND_REPEAT) { + fdist = fdist - floor (fdist); + } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) { + fdist = fmod (fabs (fdist), 2.0); + if (fdist > 1.0) + fdist = 2.0 - fdist; + } + + 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) + --i; + out[0] = grad->stops[i].color.red; + out[1] = grad->stops[i].color.green; + out[2] = grad->stops[i].color.blue; + out[3] = grad->stops[i].color.alpha; + } else { + cairo_quartz_float_t ax = grad->stops[i-1].offset; + cairo_quartz_float_t bx = grad->stops[i].offset - ax; + cairo_quartz_float_t bp = (fdist - ax)/bx; + cairo_quartz_float_t ap = 1.0 - bp; + + out[0] = + grad->stops[i-1].color.red * ap + + grad->stops[i].color.red * bp; + out[1] = + grad->stops[i-1].color.green * ap + + grad->stops[i].color.green * bp; + out[2] = + grad->stops[i-1].color.blue * ap + + grad->stops[i].color.blue * bp; + out[3] = + grad->stops[i-1].color.alpha * ap + + grad->stops[i].color.alpha * bp; + } +} + +static const cairo_quartz_float_t gradient_output_value_ranges[8] = { + 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f +}; +static const CGFunctionCallbacks gradient_callbacks = { + 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy +}; + +/* 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 +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]; + + 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; + } + + _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start); + _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end); + + if (_cairo_pattern_create_copy (&pat, &gradient->base)) + return NULL; + + return CGFunctionCreate (pat, + 1, + input_value_range, + 4, + gradient_output_value_ranges, + &gradient_callbacks); +} + +static void +DataProviderReleaseCallback (void *info, const void *data, size_t size) +{ + free (info); +} + +static cairo_status_t +_cairo_surface_to_cgimage (cairo_surface_t *source, + cairo_rectangle_int_t *extents, + cairo_format_t format, + cairo_matrix_t *matrix, + const cairo_clip_t *clip, + CGImageRef *image_out) +{ + cairo_status_t status; + 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; + *image_out = CGImageRetain (surface->image); + return CAIRO_STATUS_SUCCESS; + } + + if (_cairo_surface_is_quartz (source)) { + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source; + if (IS_EMPTY (surface)) { + *image_out = NULL; + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + + if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) { + *image_out = CGBitmapContextCreateImage (surface->cgContext); + if (*image_out) + return CAIRO_STATUS_SUCCESS; + } + } + + 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; + } + + 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); + + return status; + } + + image_data = _cairo_malloc_ab (image_surface->height, image_surface->stride); + if (unlikely (!image_data)) + { + if (acquired) + _cairo_surface_release_source_image (source, image_surface, image_extra); + else + cairo_surface_destroy (&image_surface->base); + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + // The last row of data may have less than stride bytes so make sure we + // only copy the minimum amount required from that row. + memcpy (image_data, image_surface->data, + (image_surface->height - 1) * image_surface->stride + + cairo_format_stride_for_width (image_surface->format, + image_surface->width)); + *image_out = CairoQuartzCreateCGImage (image_surface->format, + image_surface->width, + image_surface->height, + image_surface->stride, + image_data, + TRUE, + NULL, + DataProviderReleaseCallback, + image_data); + + /* 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; +} + +/* Generic #cairo_pattern_t -> CGPattern function */ + +typedef struct { + CGImageRef image; + CGRect imageBounds; + cairo_bool_t do_reflect; +} SurfacePatternDrawInfo; + +static void +SurfacePatternDrawFunc (void *ainfo, CGContextRef context) +{ + SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo; + + CGContextTranslateCTM (context, 0, info->imageBounds.size.height); + CGContextScaleCTM (context, 1, -1); + + CGContextDrawImage (context, info->imageBounds, info->image); + if (info->do_reflect) { + /* draw 3 more copies of the image, flipped. + * DrawImage draws the image according to the current Y-direction into the rectangle given + * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left + * of the base image position, and the Y axis is extending upwards. + */ + + /* Make the y axis extend downwards, and draw a flipped image below */ + CGContextScaleCTM (context, 1, -1); + CGContextDrawImage (context, info->imageBounds, info->image); + + /* Shift over to the right, and flip vertically (translation is 2x, + * since we'll be flipping and thus rendering the rectangle "backwards" + */ + CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0); + CGContextScaleCTM (context, -1, 1); + CGContextDrawImage (context, info->imageBounds, info->image); + + /* Then unflip the Y-axis again, and draw the image above the point. */ + CGContextScaleCTM (context, 1, -1); + CGContextDrawImage (context, info->imageBounds, info->image); + } +} + +static void +SurfacePatternReleaseInfoFunc (void *ainfo) +{ + SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo; + + CGImageRelease (info->image); + free (info); +} + +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; + CGAffineTransform ptransform, stransform; + CGPatternCallbacks cb = { 0, + SurfacePatternDrawFunc, + SurfacePatternReleaseInfoFunc }; + SurfacePatternDrawInfo *info; + cairo_quartz_float_t rw, rh; + cairo_status_t status; + cairo_bool_t is_bounded; + + cairo_matrix_t m; + + /* SURFACE is the only type we'll handle here */ + assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE); + + spattern = (cairo_surface_pattern_t *) apattern; + pat_surf = spattern->surface; + + 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); + + m = spattern->base.matrix; + status = _cairo_surface_to_cgimage (pat_surf, &extents, format, + &m, clip, &image); + if (unlikely (status)) + return status; + + 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 + * that the data will stick around for this image when the printer gets to it. + * Otherwise, the underlying data store may disappear from under us! + * + * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces, + * since the Quartz surfaces have a higher chance of sticking around. If the + * source is a quartz image surface, then it's set up to retain a ref to the + * image surface that it's backed by. + */ + info->image = image; + info->imageBounds = CGRectMake (0, 0, extents.width, extents.height); + info->do_reflect = FALSE; + + pbounds.origin.x = 0; + pbounds.origin.y = 0; + + if (spattern->base.extend == CAIRO_EXTEND_REFLECT) { + pbounds.size.width = 2.0 * extents.width; + pbounds.size.height = 2.0 * extents.height; + info->do_reflect = TRUE; + } else { + pbounds.size.width = extents.width; + pbounds.size.height = extents.height; + } + rw = pbounds.size.width; + rh = pbounds.size.height; + + cairo_matrix_invert (&m); + _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform); + + /* The pattern matrix is relative to the bottom left, again; the + * incoming cairo pattern matrix is relative to the upper left. + * 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); + +#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)); +#endif + + *cgpat = CGPatternCreate (info, + pbounds, + ptransform, + rw, rh, + kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */ + TRUE, + &cb); + + return CAIRO_STATUS_SUCCESS; +} + +/* State used during a drawing operation. */ +typedef struct { + /* 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; + + /* Destination rect */ + CGRect rect; + + /* Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE, DO_LAYER */ + CGAffineTransform transform; + + /* Used with DO_IMAGE and DO_TILED_IMAGE */ + CGImageRef image; + + /* Used with DO_SHADING */ + CGShadingRef shading; + + /* Temporary destination for unbounded operations */ + CGLayerRef layer; + CGRect clipRect; + + /* Source layer to be rendered when using DO_LAYER. + Unlike 'layer' above, this is not owned by the drawing state + but by the source surface. */ + CGLayerRef sourceLayer; +} cairo_quartz_drawing_state_t; + +/* +Quartz does not support repeating radients. We handle repeating gradients +by manually extending the gradient and repeating color stops. We need to +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. _cairo_quartz_create_gradient_function computes +the number of repetitions needed based on the extents. +*/ +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) +{ + cairo_matrix_t mat; + cairo_circle_double_t start, end; + CGFunctionRef gradFunc; + CGColorSpaceRef rgb; + bool extend = gradient->base.extend != CAIRO_EXTEND_NONE; + + assert (gradient->n_stops > 0); + + mat = gradient->base.matrix; + cairo_matrix_invert (&mat); + _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform); + + gradFunc = CairoQuartzCreateGradientFunction (gradient, extents, + &start, &end); + + if (unlikely (gradFunc == NULL)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + 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 { + 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); + } + + CGColorSpaceRelease (rgb); + CGFunctionRelease (gradFunc); + + state->action = DO_SHADING; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state, + cairo_composite_rectangles_t *composite) +{ + 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); + + 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; + } + + /* + * 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); + } + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + + 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); + + state->action = DO_DIRECT; + return CAIRO_STATUS_SUCCESS; + } + + if (source->type == CAIRO_PATTERN_TYPE_LINEAR || + source->type == CAIRO_PATTERN_TYPE_RADIAL) + { + 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); + } + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + (source->extend == CAIRO_EXTEND_NONE || + source->extend == CAIRO_EXTEND_PAD || + (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; + cairo_rectangle_int_t extents; + CGAffineTransform xform; + CGRect srcRect; + cairo_fixed_t fw, fh; + cairo_bool_t is_bounded; + + /* Draw nonrepeating CGLayer surface using DO_LAYER */ + if (source->extend != CAIRO_EXTEND_REPEAT && + cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) { + cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf; + if (quartz_surf->cgLayer) { + cairo_matrix_invert(&m); + _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform); + state->rect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height); + state->sourceLayer = quartz_surf->cgLayer; + state->action = DO_LAYER; + return CAIRO_STATUS_SUCCESS; + } + } + + _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; + + 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 || source->extend == CAIRO_EXTEND_PAD) { + 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 + * problem. Check if we're going to fill up the space (within some + * epsilon), and if not, fall back to the CGPattern type. + */ + + xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext), + state->transform); + + srcRect = CGRectApplyAffineTransform (srcRect, xform); + + fw = _cairo_fixed_from_double (srcRect.size.width); + fh = _cairo_fixed_from_double (srcRect.size.height); + + if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON && + (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON) + { + /* 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); + + xform = CGAffineTransformInvert (xform); + + srcRect = CGRectApplyAffineTransform (srcRect, xform); + + state->rect = srcRect; + state->action = DO_TILED_IMAGE; + return CAIRO_STATUS_SUCCESS; + } + + /* Fall through to generic SURFACE case */ + } + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_quartz_float_t patternAlpha = 1.0f; + CGColorSpaceRef patternSpace; + CGPatternRef pattern = NULL; + cairo_int_status_t status; + + 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; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state, + cairo_composite_rectangles_t *extents) +{ + 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->cgMaskContext) + CGContextRestoreGState (surface->cgContext); + + if (state->image) + CGImageRelease (state->image); + + if (state->shading) + CGShadingRelease (state->shading); +} + +static void +_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state, + cairo_operator_t op) +{ + CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone); + CGContextSetInterpolationQuality(state->cgDrawContext, state->filter); + + if (state->action == DO_DIRECT) { + CGContextFillRect (state->cgDrawContext, state->rect); + return; + } + + CGContextConcatCTM (state->cgDrawContext, state->transform); + + 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_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. + */ + assert (state->sourceLayer); + CGContextDrawLayerAtPoint (state->cgDrawContext, state->rect.origin, + state->sourceLayer); + } else 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; + + 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); +} + +static cairo_int_status_t +_cairo_quartz_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + + if (surface->imageSurfaceEquiv) + return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image); + + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return CAIRO_STATUS_SUCCESS; +} + + +/* + * get source/dest image implementation + */ + +/* 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) +{ + unsigned char *imageData; + cairo_image_surface_t *isurf; + + 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) { + CGContextFlush(surface->cgContext); + *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv); + return CAIRO_STATUS_SUCCESS; + } + + 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 + */ + +static cairo_status_t +_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)); + + if (IS_EMPTY (surface)) + return CAIRO_STATUS_SUCCESS; + + /* Restore our saved gstate that we use to reset clipping */ + CGContextRestoreGState (surface->cgContext); + _cairo_surface_clipper_reset (&surface->clipper); + + CGContextRelease (surface->cgContext); + + surface->cgContext = 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); + } + + surface->imageData = NULL; + + if (surface->cgLayer) { + CGLayerRelease (surface->cgLayer); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_quartz_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + 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; + + *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_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_quartz_surface_t *similar_quartz; + cairo_surface_t *similar; + cairo_format_t format; + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + + if (surface->cgLayer) + return cairo_quartz_surface_create_cg_layer (abstract_surface, content, + width, height); + + if (content == CAIRO_CONTENT_COLOR_ALPHA) + format = CAIRO_FORMAT_ARGB32; + else if (content == CAIRO_CONTENT_COLOR) + format = CAIRO_FORMAT_RGB24; + else if (content == CAIRO_CONTENT_ALPHA) + format = CAIRO_FORMAT_A8; + else + return NULL; + + // 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)); + } + + similar = cairo_quartz_surface_create (format, width, height); + if (unlikely (similar->status)) + return similar; + + surface = (cairo_quartz_surface_t *) abstract_surface; + similar_quartz = (cairo_quartz_surface_t *) similar; + similar_quartz->virtual_extents = surface->virtual_extents; + + return similar; +} + +static cairo_bool_t +_cairo_quartz_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + + *extents = surface->extents; + return TRUE; +} + +static cairo_int_status_t +_cairo_quartz_cg_paint (const cairo_compositor_t *compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_quartz_drawing_state_t state; + cairo_int_status_t rv; + + ND ((stderr, "%p _cairo_quartz_surface_paint 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; + + _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_cg_mask_with_surface (cairo_composite_rectangles_t *extents, + cairo_surface_t *mask_surf, + const cairo_matrix_t *mask_mat, + CGInterpolationQuality filter) +{ + CGRect rect; + CGImageRef img; + cairo_status_t status; + CGAffineTransform mask_matrix; + cairo_quartz_drawing_state_t state; + cairo_format_t format = _cairo_format_from_content (extents->surface->content); + cairo_rectangle_int_t dest_extents; + cairo_matrix_t m = *mask_mat; + + _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; + + status = _cairo_quartz_setup_state (&state, extents); + if (unlikely (status)) + goto BAIL; + + rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img)); + _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix); + + /* 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); + + state.filter = filter; + + CGContextSetInterpolationQuality (state.cgMaskContext, filter); + CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone); + + CGContextClipToMask (state.cgMaskContext, rect, img); + + CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0); + CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height); + CGContextConcatCTM (state.cgMaskContext, mask_matrix); + + _cairo_quartz_draw_source (&state, extents->op); + +BAIL: + _cairo_quartz_teardown_state (&state, extents); + + CGImageRelease (img); + + return status; +} + +static cairo_int_status_t +_cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface, + cairo_composite_rectangles_t *extents) +{ + cairo_quartz_drawing_state_t state; + double alpha = extents->mask_pattern.solid.color.alpha; + cairo_status_t status; + + status = _cairo_quartz_setup_state (&state, extents); + if (unlikely (status)) + return status; + + CGContextSetAlpha (surface->cgContext, alpha); + _cairo_quartz_draw_source (&state, extents->op); + + _cairo_quartz_teardown_state (&state, extents); + + return CAIRO_STATUS_SUCCESS; +} + +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.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) { + 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; + + 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; + } + + for (k = 0; k < max_dashes; k++) + fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes]; + + CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes); + if (fdash != sdash) + free (fdash); + } else + CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0); + + _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext); + + _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform); + CGContextConcatCTM (state.cgMaskContext, strokeTransform); + + if (state.action == DO_DIRECT) { + assert (state.cgDrawContext == state.cgMaskContext); + CGContextStrokePath (state.cgMaskContext); + } else { + CGContextReplacePathWithStrokedPath (state.cgMaskContext); + CGContextClip (state.cgMaskContext); + + _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform); + CGContextConcatCTM (state.cgMaskContext, invStrokeTransform); + + _cairo_quartz_draw_source (&state, extents->op); + } + +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_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, + cairo_bool_t permit_subpixel_antialiasing) +{ + 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]; + CGSize *cg_advances = &cg_advances_static[0]; + CGPoint *cg_positions; + COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize)); + COMPILE_TIME_ASSERT (sizeof (CGPoint) == sizeof (CGSize)); + + cairo_quartz_drawing_state_t state; + cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; + int i; + + cairo_bool_t didForceFontSmoothing = FALSE; + cairo_antialias_t effective_antialiasing; + + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) + return CAIRO_INT_STATUS_UNSUPPORTED; + + rv = _cairo_quartz_setup_state (&state, extents); + if (unlikely (rv)) + goto BAIL; + + if (state.action == DO_DIRECT) { + assert (state.cgDrawContext == state.cgMaskContext); + CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill); + } else { + CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip); + } + + if (!CTFontDrawGlyphsPtr) { + /* this doesn't addref */ + CGFontRef cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); + CGContextSetFont (state.cgMaskContext, cgfref); + CGContextSetFontSize (state.cgMaskContext, 1.0); + } + + effective_antialiasing = scaled_font->options.antialias; + if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL && + !permit_subpixel_antialiasing) { + effective_antialiasing = CAIRO_ANTIALIAS_GRAY; + } + switch (effective_antialiasing) { + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: + CGContextSetShouldAntialias (state.cgMaskContext, TRUE); + CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE); + if (CGContextSetAllowsFontSmoothingPtr && + !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext)) + { + didForceFontSmoothing = TRUE; + CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE); + } + break; + case CAIRO_ANTIALIAS_NONE: + CGContextSetShouldAntialias (state.cgMaskContext, FALSE); + break; + case CAIRO_ANTIALIAS_GRAY: + 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 > ARRAY_LENGTH (glyphs_static)) { + cg_advances = _cairo_malloc_ab (num_glyphs, + sizeof (CGSize) + sizeof (CGGlyph)); + + if (unlikely (cg_advances == NULL)) { + rv = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + cg_glyphs = (CGGlyph*) (cg_advances + num_glyphs); + } + + /* scale(1,-1) * scaled_font->scale */ + textTransform = CGAffineTransformMake (scaled_font->scale.xx, + scaled_font->scale.yx, + -scaled_font->scale.xy, + -scaled_font->scale.yy, + 0.0, 0.0); + + /* scaled_font->scale_inverse * scale(1,-1) */ + invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx, + -scaled_font->scale_inverse.yx, + scaled_font->scale_inverse.xy, + -scaled_font->scale_inverse.yy, + 0.0, 0.0); + + /* + * CGContextShowGlyphsWithAdvances does not transform the advances + * by the text matrix, if the drawing mode is kCGTextClip. Instead + * of trying to recompute the advances, make sure that the text + * matrix is the identity and rely on the CTM for the text + * transform. + */ + CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity); + CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y); + CGContextConcatCTM (state.cgMaskContext, textTransform); + + /* Convert glyph positions to glyph advances. */ + cg_glyphs[0] = glyphs[0].index; + for (i = 1; i < num_glyphs; i++) { + CGSize advance = CGSizeMake (glyphs[i].x - glyphs[i-1].x, + glyphs[i].y - glyphs[i-1].y); + cg_advances[i] = CGSizeApplyAffineTransform (advance, invTextTransform); + cg_glyphs[i] = glyphs[i].index; + } + + if (CTFontDrawGlyphsPtr) { + /* If CTFontDrawGlyphs is available, we want to use that + * instead of the deprecated CGContextShowGlyphsWithAdvances + * so that colored-bitmap fonts like Apple Color Emoji will + * render properly. */ + + /* Accumulate the glyph advances into glyph positions, + * overwriting them. Start at (0,0) because the CTM already + * takes into account the position of the first glyph. */ + CGPoint pos = CGPointMake (0, 0); + cg_positions = (CGPoint *) cg_advances; + cg_positions[0] = pos; + for (i = 1; i < num_glyphs; i++) { + pos.x += cg_advances[i].width; + pos.y += cg_advances[i].height; + cg_positions[i] = pos; + } + + CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font), + cg_glyphs, + cg_positions, + num_glyphs, + state.cgMaskContext); + } else { + CGContextShowGlyphsWithAdvances (state.cgMaskContext, + cg_glyphs, + cg_advances + 1, + num_glyphs); + } + + /* Revert the changes to the CTM. This fragment cannot rely on + * CG{Save,Restore}GState, as that would reset the clip. */ + CGContextConcatCTM (state.cgMaskContext, invTextTransform); + CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); + + if (state.action != DO_DIRECT) + _cairo_quartz_draw_source (&state, extents->op); + +BAIL: + if (didForceFontSmoothing) + CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE); + + _cairo_quartz_teardown_state (&state, extents); + + if (cg_advances != cg_advances_static) + free (cg_advances); + + return rv; +} +#endif /* CAIRO_HAS_QUARTZ_FONT */ + +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 + _cairo_quartz_cg_glyphs, +#else + NULL, +#endif +}; + +static cairo_int_status_t +_cairo_quartz_surface_paint (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + return _cairo_compositor_paint (&_cairo_quartz_cg_compositor, + surface, op, source, clip); +} + +static cairo_int_status_t +_cairo_quartz_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 (&_cairo_quartz_cg_compositor, + 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); +} + +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); +} + +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 +_cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + 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)); + + if (IS_EMPTY (surface)) + return CAIRO_STATUS_SUCCESS; + + if (path == NULL) { + /* If we're being asked to reset the clip, we can only do it + * by restoring the gstate to our previous saved one, and + * saving it again. + * + * Note that this assumes that ALL quartz surface creation + * functions will do a SaveGState first; we do this in create_internal. + */ + CGContextRestoreGState (surface->cgContext); + CGContextSaveGState (surface->cgContext); + } else { + CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); + + _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext); + + if (fill_rule == CAIRO_FILL_RULE_WINDING) + CGContextClip (surface->cgContext); + else + CGContextEOClip (surface->cgContext); + } + + ND ((stderr, "-- intersect_clip_path\n")); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_quartz_surface_link (cairo_quartz_surface_t *surface, + cairo_bool_t begin, + const char *attributes) +{ + cairo_link_attrs_t link_attrs; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + int i, num_rects; + + /* We only process the 'begin' tag, and expect a rect attribute; + using the extents of the drawing operations enclosed by the begin/end + link tags to define the clickable area is not implemented. */ + if (!begin) + return status; + + status = _cairo_tag_parse_link_attributes (attributes, &link_attrs); + if (unlikely (status)) + return status; + + num_rects = _cairo_array_num_elements (&link_attrs.rects); + if (num_rects > 0) { + /* Create either a named destination or a URL, depending which is present + in the link attributes. */ + CFURLRef url = NULL; + CFStringRef name = NULL; + if (link_attrs.uri && *link_attrs.uri) + url = CFURLCreateWithBytes (NULL, + (const UInt8 *) link_attrs.uri, + strlen (link_attrs.uri), + kCFStringEncodingUTF8, + NULL); + else if (link_attrs.dest && *link_attrs.dest) + name = CFStringCreateWithBytes (kCFAllocatorDefault, + (const UInt8 *) link_attrs.dest, + strlen (link_attrs.dest), + kCFStringEncodingUTF8, + FALSE); + else /* silently ignore link that doesn't have a usable target */ + goto cleanup; + + for (i = 0; i < num_rects; i++) { + CGRect link_rect; + cairo_rectangle_t rectf; + + _cairo_array_copy_element (&link_attrs.rects, i, &rectf); + + link_rect = + CGRectMake (rectf.x, + surface->extents.height - rectf.y - rectf.height, + rectf.width, + rectf.height); + + if (url) + CGPDFContextSetURLForRect (surface->cgContext, url, link_rect); + else + CGPDFContextSetDestinationForRect (surface->cgContext, name, link_rect); + } + + if (url) + CFRelease (url); + else + CFRelease (name); + } + +cleanup: + _cairo_array_fini (&link_attrs.rects); + free (link_attrs.dest); + free (link_attrs.uri); + free (link_attrs.file); + + return status; +} + +static cairo_int_status_t +_cairo_quartz_surface_dest (cairo_quartz_surface_t *surface, + cairo_bool_t begin, + const char *attributes) +{ + cairo_dest_attrs_t dest_attrs; + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + double x = 0, y = 0; + + /* We only process the 'begin' tag, and expect 'x' and 'y' attributes. */ + if (!begin) + return status; + + status = _cairo_tag_parse_dest_attributes (attributes, &dest_attrs); + if (unlikely (status)) + return status; + + if (unlikely (!dest_attrs.name || !strlen (dest_attrs.name))) + goto cleanup; + + CFStringRef name = CFStringCreateWithBytes (kCFAllocatorDefault, + (const UInt8 *) dest_attrs.name, + strlen (dest_attrs.name), + kCFStringEncodingUTF8, + FALSE); + + if (dest_attrs.x_valid) + x = dest_attrs.x; + if (dest_attrs.y_valid) + y = dest_attrs.y; + + CGPDFContextAddDestinationAtPoint (surface->cgContext, + name, + CGPointMake (x, surface->extents.height - y)); + CFRelease (name); + +cleanup: + free (dest_attrs.name); + + return status; +} + +static cairo_int_status_t +_cairo_quartz_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_link_attrs_t link_attrs; + int i, num_rects; + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + + /* Currently the only tags we support are CAIRO_TAG_LINK and CAIRO_TAG_DEST */ + if (!strcmp (tag_name, CAIRO_TAG_LINK)) + return _cairo_quartz_surface_link (surface, begin, attributes); + + if (!strcmp (tag_name, CAIRO_TAG_DEST)) + return _cairo_quartz_surface_dest (surface, begin, attributes); + + /* Unknown tag names are silently ignored here. */ + return CAIRO_INT_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_finish, + + _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, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_quartz_surface_get_extents, + NULL, /* get_font_options */ + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _cairo_quartz_surface_paint, + _cairo_quartz_surface_mask, + _cairo_quartz_surface_stroke, + _cairo_quartz_surface_fill, + NULL, /* fill-stroke */ + _cairo_quartz_surface_glyphs, + + NULL, /* has_show_text_glyphs */ + NULL, /* show_text_glyphs */ + NULL, /* get_supported_mime_types */ + _cairo_quartz_surface_tag /* tag */ +}; + +cairo_quartz_surface_t * +_cairo_quartz_surface_create_internal (CGContextRef cgContext, + cairo_content_t content, + unsigned int width, + unsigned int height) +{ + cairo_quartz_surface_t *surface; + + quartz_ensure_symbols (); + + /* Init the base surface */ + 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)); + + _cairo_surface_init (&surface->base, + &cairo_quartz_surface_backend, + NULL, /* device */ + content, + FALSE); /* is_vector */ + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_quartz_surface_clipper_intersect_clip_path); + + /* Save our extents */ + 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)) { + surface->cgContext = NULL; + surface->cgContextBaseCTM = CGAffineTransformIdentity; + surface->base.is_clear = TRUE; + return surface; + } + + /* Save so we can always get back to a known-good CGContext -- this is + * required for proper behaviour of intersect_clip_path(NULL) + */ + CGContextSaveGState (cgContext); + + surface->cgContext = cgContext; + surface->cgContextBaseCTM = CGContextGetCTM (cgContext); + + surface->ownsData = TRUE; + + return surface; +} + +/** + * 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 + * + * Creates a Quartz surface that wraps the given CGContext. The + * CGContext is assumed to be in the standard Cairo coordinate space + * (that is, with the origin at the upper left and the Y axis + * increasing downward). If the CGContext is in the Quartz coordinate + * space (with the origin at the bottom left), then it should be + * flipped before this function is called. The flip can be accomplished + * using a translate and a scale; for example: + * + * + * CGContextTranslateCTM (cgContext, 0.0, height); + * CGContextScaleCTM (cgContext, 1.0, -1.0); + * + * + * All Cairo operations are implemented in terms of Quartz operations, + * as long as Quartz-compatible elements are used (such as Quartz fonts). + * + * Return value: the newly created Cairo surface. + * + * Since: 1.6 + **/ + +cairo_surface_t * +cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, + unsigned int width, + unsigned int height) +{ + cairo_quartz_surface_t *surf; + + surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA, + width, height); + if (likely (!surf->base.status)) + CGContextRetain (cgContext); + + return &surf->base; +} + +/** + * cairo_quartz_surface_create_cg_layer + * @surface: The returned surface can be efficiently drawn into this + * destination surface (if tiling is not used)." + * @content: the content type of the surface + * @width: width of the surface, in pixels + * @height: height of the surface, in pixels + * + * Creates a Quartz surface backed by a CGLayer, if the given surface + * is a Quartz surface; the CGLayer is created to match the surface's + * Quartz context. Otherwise just calls cairo_surface_create_similar. + * The returned surface can be efficiently blitted to the given surface, + * but tiling and 'extend' modes other than NONE are not so efficient. + * + * Return value: the newly created surface. + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, + cairo_content_t content, + unsigned int width, + unsigned int height) +{ + cairo_quartz_surface_t *surf; + CGLayerRef layer; + CGContextRef ctx; + CGContextRef cgContext; + + cgContext = cairo_quartz_surface_get_cg_context (surface); + if (!cgContext) + return cairo_surface_create_similar (surface, content, + width, height); + + if (!_cairo_quartz_verify_surface_size(width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + /* If we pass zero width or height into CGLayerCreateWithContext below, + * it will fail. + */ + if (width == 0 || height == 0) { + return (cairo_surface_t*) + _cairo_quartz_surface_create_internal (NULL, content, + width, height); + } + + layer = CGLayerCreateWithContext (cgContext, + CGSizeMake (width, height), + NULL); + if (!layer) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + ctx = CGLayerGetContext (layer); + /* Flip it when we draw into it, so that when we finally composite it + * to a flipped target, the directions match and Quartz will optimize + * the composition properly + */ + CGContextTranslateCTM (ctx, 0, height); + CGContextScaleCTM (ctx, 1, -1); + + CGContextRetain (ctx); + surf = _cairo_quartz_surface_create_internal (ctx, content, + width, height); + if (surf->base.status) { + CGLayerRelease (layer); + // create_internal will have set an error + return (cairo_surface_t*) surf; + } + surf->cgLayer = layer; + + return (cairo_surface_t *) surf; +} + +/** + * 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 + * + * 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.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; + int bitsPerComponent; + + 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_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 + * 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)); + } + + /* The Apple docs say that for best performance, the stride and the data + * pointer should be 16-byte aligned. malloc already aligns to 16-bytes, + * so we don't have to anything special on allocation. + */ + stride = (stride + 15) & ~15; + + 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 (imageData, 0, height * stride); + + cgc = CGBitmapContextCreate (imageData, + width, + height, + bitsPerComponent, + stride, + cgColorspace, + bitinfo); + CGColorSpaceRelease (cgColorspace); + + if (!cgc) { + free (imageData); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + /* flip the Y axis */ + CGContextTranslateCTM (cgc, 0.0, height); + CGContextScaleCTM (cgc, 1.0, -1.0); + + surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format), + width, height); + if (surf->base.status) { + CGContextRelease (cgc); + free (imageData); + // create_internal will have set an error + return &surf->base; + } + + surf->base.is_clear = TRUE; + + surf->imageData = imageData; + surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride); + + // We created this data, so we can delete it. + surf->ownsData = TRUE; + + return &surf->base; +} + +/** + * 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.6 + **/ +CGContextRef +cairo_quartz_surface_get_cg_context (cairo_surface_t *surface) +{ + if (surface && _cairo_surface_is_quartz (surface)) { + cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface; + return quartz->cgContext; + } else + return NULL; +} + +/** + * _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) +{ + Handle dataRef = NULL; + OSType dataRefType; + CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII); + + GraphicsExportComponent grex = 0; + unsigned long sizeWritten; + + ComponentResult result; + + // create the data reference + result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle, + 0, &dataRef, &dataRefType); + + if (NULL != dataRef && noErr == result) { + // get the PNG exporter + result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG, + &grex); + + if (grex) { + // tell the exporter where to find its source image + result = GraphicsExportSetInputCGImage (grex, inImageRef); + + if (noErr == result) { + // tell the exporter where to save the exporter image + result = GraphicsExportSetOutputDataReference (grex, dataRef, + dataRefType); + + if (noErr == result) { + // write the PNG file + result = GraphicsExportDoExport (grex, &sizeWritten); + } + } + + // remember to close the component + CloseComponent (grex); + } + + // remember to dispose of the data reference handle + DisposeHandle (dataRef); + } +} + +void +quartz_image_to_png (CGImageRef imgref, char *dest) +{ + static int sctr = 0; + char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png"; + + if (dest == NULL) { + fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr); + sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr); + sctr++; + dest = sptr; + } + + ExportCGImageToPNGFile (imgref, dest); +} + +void +quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest) +{ + static int sctr = 0; + char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png"; + + if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) { + fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq); + return; + } + + if (dest == NULL) { + fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr); + sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr); + sctr++; + dest = sptr; + } + + CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext); + if (imgref == NULL) { + fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq); + return; + } + + ExportCGImageToPNGFile (imgref, dest); + + CGImageRelease (imgref); +} + +#endif /* QUARTZ_DEBUG */ diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h new file mode 100644 index 0000000000..2118d8ff9c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-quartz.h @@ -0,0 +1,100 @@ +/* cairo - a vector graphics library with display and print output + * + * 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 + * 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 Foundation. + * + * Contributor(s): + * Vladimir Vukicevic + */ + +#ifndef CAIRO_QUARTZ_H +#define CAIRO_QUARTZ_H + +#include "cairo.h" + +#if CAIRO_HAS_QUARTZ_SURFACE + +#if CAIRO_HAS_QUARTZ_CORE_GRAPHICS +#include +#include +#endif + +#if CAIRO_HAS_QUARTZ_APPLICATION_SERVICES +#include +#endif + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_quartz_surface_create (cairo_format_t format, + unsigned int width, + unsigned int height); + +cairo_public cairo_surface_t * +cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext, + unsigned int width, + unsigned int height); + +cairo_public cairo_surface_t * +cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface, + cairo_content_t content, + unsigned int width, + unsigned int height); + +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 + +/* + * Quartz font support + */ + +cairo_public cairo_font_face_t * +cairo_quartz_font_face_create_for_cgfont (CGFontRef font); + +#if CAIRO_HAS_QUARTZ_ATSUFONTID +cairo_public cairo_font_face_t * +cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id); +#endif /* CAIRO_HAS_QUARTZ_ATSUFONTID */ + +#endif /* CAIRO_HAS_QUARTZ_FONT */ + +CAIRO_END_DECLS + +#else + +# error Cairo was not compiled with support for the quartz backend + +#endif /* CAIRO_HAS_QUARTZ_SURFACE */ + +#endif /* CAIRO_QUARTZ_H */ 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 0000000000..64520feae5 --- /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 0000000000..9002ccd693 --- /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 new file mode 100644 index 0000000000..e8d98e8fa7 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-recording-surface-private.h @@ -0,0 +1,208 @@ +/* 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_H +#define CAIRO_RECORDING_SURFACE_H + +#include "cairoint.h" +#include "cairo-path-fixed-private.h" +#include "cairo-pattern-private.h" +#include "cairo-surface-backend-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, + + /* cairo_tag_begin()/cairo_tag_end() */ + CAIRO_COMMAND_TAG, +} cairo_command_type_t; + +typedef enum { + CAIRO_RECORDING_REGION_ALL, + CAIRO_RECORDING_REGION_NATIVE, + CAIRO_RECORDING_REGION_IMAGE_FALLBACK +} cairo_recording_region_type_t; + +typedef struct _cairo_command_header { + cairo_command_type_t type; + cairo_recording_region_type_t region; + cairo_operator_t op; + cairo_rectangle_int_t extents; + cairo_clip_t *clip; + + int index; + struct _cairo_command_header *chain; +} cairo_command_header_t; + +typedef struct _cairo_command_paint { + cairo_command_header_t header; + cairo_pattern_union_t source; +} cairo_command_paint_t; + +typedef struct _cairo_command_mask { + cairo_command_header_t header; + 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_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_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_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_tag { + cairo_command_header_t header; + cairo_bool_t begin; + char *tag_name; + char *attributes; +} cairo_command_tag_t; + +typedef union _cairo_command { + cairo_command_header_t header; + + 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; + cairo_command_tag_t tag; +} cairo_command_t; + +typedef struct _cairo_recording_surface { + cairo_surface_t base; + + /* 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. */ + cairo_rectangle_t extents_pixels; + cairo_rectangle_int_t extents; + cairo_bool_t unbounded; + + 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; + + 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); + +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_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, + 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, + cairo_surface_t *target, + cairo_recording_region_type_t region); + +cairo_private cairo_status_t +_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_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 new file mode 100644 index 0000000000..327bff3c39 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-recording-surface.c @@ -0,0 +1,2319 @@ +/* -*- 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 © 2007 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 Red Hat, Inc. + * + * Contributor(s): + * Kristian Høgsberg + * Carl Worth + * Adrian Johnson + */ + +/** + * SECTION:cairo-recording + * @Title: Recording Surfaces + * @Short_Description: Records all drawing operations + * @See_Also: #cairo_surface_t + * + * A recording surface is a surface that records all drawing operations at + * the highest level of the surface backend interface, (that is, the + * level of paint, mask, stroke, fill, and show_text_glyphs). The recording + * surface can then be "replayed" against any target surface by using it + * as a source surface. + * + * If you want to replay a surface so that the results in target will be + * identical to the results that would have been obtained if the original + * operations applied to the recording surface had instead been applied to the + * target surface, you can use code like this: + * + * cairo_t *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 + * on the size of the drawing surface. However, in practice this is rarely + * useful as you wish to replay against a particular target surface with + * known bounds. For this case, it is more efficient to specify the target + * extents to the recording surface upon creation. + * + * The recording phase of the recording surface is careful to snapshot all + * necessary objects (paths, patterns, etc.), in order to achieve + * accurate replay. The efficiency of the recording surface could be + * 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-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, + CAIRO_RECORDING_CREATE_REGIONS +} cairo_recording_replay_type_t; + +static const cairo_surface_backend_t cairo_recording_surface_backend; + +/** + * CAIRO_HAS_RECORDING_SURFACE: + * + * Defined if the recording surface backend is available. + * The recording surface backend is always built in. + * 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 + * recording-surface will ever be replayed. + * + * XXX: The naming of "pixels" in the size here is a misnomer. It's + * actually a size in whatever device-space units are desired (again, + * 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 + * @extents: the extents to record in pixels, can be %NULL to record + * unbounded operations. + * + * Creates a recording-surface which can be used to record all drawing operations + * at the highest level (that is, the level of paint, mask, stroke, fill + * and show_text_glyphs). The recording surface can then be "replayed" against + * any target surface by using it as a source to drawing operations. + * + * The recording phase of the recording surface is careful to snapshot all + * necessary objects (paths, patterns, etc.), in order to achieve + * accurate replay. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * Since: 1.10 + **/ +cairo_surface_t * +cairo_recording_surface_create (cairo_content_t content, + const cairo_rectangle_t *extents) +{ + cairo_recording_surface_t *surface; + + 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 (&surface->base, + &cairo_recording_surface_backend, + NULL, /* device */ + content, + TRUE); /* is_vector */ + + + surface->unbounded = TRUE; + + /* unbounded -> 'infinite' extents */ + if (extents != NULL) { + surface->extents_pixels = *extents; + + /* XXX check for overflow */ + 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; + + surface->unbounded = FALSE; + } + + _cairo_array_init (&surface->commands, sizeof (cairo_command_t *)); + + surface->base.is_clear = TRUE; + + 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); + +static cairo_surface_t * +_cairo_recording_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t extents; + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + return cairo_recording_surface_create (content, &extents); +} + +static cairo_status_t +_cairo_recording_surface_finish (void *abstract_surface) +{ + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_t **elements; + int i, num_elements; + + 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) { + case CAIRO_COMMAND_PAINT: + _cairo_pattern_fini (&command->paint.source.base); + break; + + case CAIRO_COMMAND_MASK: + _cairo_pattern_fini (&command->mask.source.base); + _cairo_pattern_fini (&command->mask.mask.base); + break; + + case CAIRO_COMMAND_STROKE: + _cairo_pattern_fini (&command->stroke.source.base); + _cairo_path_fixed_fini (&command->stroke.path); + _cairo_stroke_style_fini (&command->stroke.style); + break; + + case CAIRO_COMMAND_FILL: + _cairo_pattern_fini (&command->fill.source.base); + _cairo_path_fixed_fini (&command->fill.path); + break; + + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + _cairo_pattern_fini (&command->show_text_glyphs.source.base); + free (command->show_text_glyphs.utf8); + free (command->show_text_glyphs.glyphs); + free (command->show_text_glyphs.clusters); + cairo_scaled_font_destroy (command->show_text_glyphs.scaled_font); + break; + + case CAIRO_COMMAND_TAG: + free (command->tag.tag_name); + if (command->tag.begin) { + free (command->tag.attributes); + } + break; + + default: + ASSERT_NOT_REACHED; + } + + _cairo_clip_destroy (command->header.clip); + free (command); + } + + _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_recording_surface_t *surface = abstract_surface; + cairo_surface_t *image, *proxy; + cairo_status_t status; + + 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; + } + + 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; + + /* 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; + } + + *image_out = (cairo_image_surface_t *) image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_recording_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_command_init (cairo_recording_surface_t *surface, + cairo_command_header_t *command, + cairo_command_type_t type, + cairo_operator_t op, + cairo_composite_rectangles_t *composite) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + command->type = type; + command->op = op; + command->region = CAIRO_RECORDING_REGION_ALL; + + command->extents = composite ? composite->unbounded : _cairo_empty_rectangle; + command->chain = NULL; + command->index = surface->commands.num_elements; + + /* steal the clip */ + command->clip = NULL; + if (composite && ! _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, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_paint_t *command; + cairo_composite_rectangles_t composite; + + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + + 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; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_recording_surface_commit (surface, &command->header); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + _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_destroy (command->header.clip); + free (command); +CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); + return status; +} + +static cairo_int_status_t +_cairo_recording_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_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_mask_t *command; + cairo_composite_rectangles_t composite; + + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + + 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; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->mask.base, mask); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + 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: + _cairo_pattern_fini (&command->mask.base); + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _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_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_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_stroke_t *command; + cairo_composite_rectangles_t composite; + + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + + 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; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_path_fixed_init_copy (&command->path, path); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + status = _cairo_stroke_style_init_copy (&command->style, style); + if (unlikely (status)) + goto CLEANUP_PATH; + + command->ctm = *ctm; + command->ctm_inverse = *ctm_inverse; + command->tolerance = tolerance; + command->antialias = antialias; + + 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: + _cairo_stroke_style_fini (&command->style); + CLEANUP_PATH: + _cairo_path_fixed_fini (&command->path); + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _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_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_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_fill_t *command; + cairo_composite_rectangles_t composite; + + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + + 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; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_path_fixed_init_copy (&command->path, path); + if (unlikely (status)) + goto CLEANUP_SOURCE; + + command->fill_rule = fill_rule; + command->tolerance = tolerance; + command->antialias = antialias; + + 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: + _cairo_path_fixed_fini (&command->path); + CLEANUP_SOURCE: + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _cairo_clip_destroy (command->header.clip); + free (command); +CLEANUP_COMPOSITE: + _cairo_composite_rectangles_fini (&composite); + return status; +} + +static cairo_bool_t +_cairo_recording_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static cairo_int_status_t +_cairo_recording_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_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_show_text_glyphs_t *command; + cairo_composite_rectangles_t composite; + + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + + 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, &composite); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + status = _cairo_pattern_init_snapshot (&command->source.base, source); + if (unlikely (status)) + goto CLEANUP_COMMAND; + + command->utf8 = NULL; + command->utf8_len = utf8_len; + command->glyphs = NULL; + command->num_glyphs = num_glyphs; + command->clusters = NULL; + command->num_clusters = num_clusters; + + if (utf8_len) { + command->utf8 = _cairo_malloc (utf8_len); + if (unlikely (command->utf8 == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->utf8, utf8, utf8_len); + } + if (num_glyphs) { + command->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (glyphs[0])); + if (unlikely (command->glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->glyphs, glyphs, sizeof (glyphs[0]) * num_glyphs); + } + if (num_clusters) { + command->clusters = _cairo_malloc_ab (num_clusters, sizeof (clusters[0])); + if (unlikely (command->clusters == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_ARRAYS; + } + memcpy (command->clusters, clusters, sizeof (clusters[0]) * num_clusters); + } + + command->cluster_flags = cluster_flags; + + command->scaled_font = cairo_scaled_font_reference (scaled_font); + + 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: + cairo_scaled_font_destroy (command->scaled_font); + CLEANUP_ARRAYS: + free (command->utf8); + free (command->glyphs); + free (command->clusters); + + _cairo_pattern_fini (&command->source.base); + CLEANUP_COMMAND: + _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) +{ + cairo_status_t status; + cairo_recording_surface_t *surface = abstract_surface; + cairo_command_tag_t *command; + + TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id)); + + command = calloc (1, sizeof (cairo_command_tag_t)); + if (unlikely (command == NULL)) { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _command_init (surface, + &command->header, CAIRO_COMMAND_TAG, CAIRO_OPERATOR_SOURCE, + NULL); + 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_recording_surface_commit (surface, &command->header); + if (unlikely (status)) + goto CLEANUP_STRINGS; + + _cairo_recording_surface_destroy_bbtree (surface); + + return CAIRO_STATUS_SUCCESS; + + CLEANUP_STRINGS: + free (command->tag_name); + free (command->attributes); + CLEANUP_COMMAND: + _cairo_clip_destroy (command->header.clip); + free (command); + 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_recording_surface_commit (surface, &command->header); + if (unlikely (status)) + goto err_command; + + return CAIRO_STATUS_SUCCESS; + +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: + * @surface: a #cairo_surface_t which must be a recording surface + * + * Make an immutable copy of @surface. It is an error to call a + * surface-modifying function on the result of this function. + * + * The caller owns the return value and should call + * cairo_surface_destroy() when finished with it. This function will not + * return %NULL, but will return a nil surface instead. + * + * Return value: The snapshot surface. + **/ +static cairo_surface_t * +_cairo_recording_surface_snapshot (void *abstract_other) +{ + cairo_recording_surface_t *other = abstract_other; + cairo_recording_surface_t *surface; + cairo_status_t status; + + 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 (&surface->base, + &cairo_recording_surface_backend, + NULL, /* device */ + other->base.content, + other->base.is_vector); + + surface->extents_pixels = other->extents_pixels; + surface->extents = other->extents; + surface->unbounded = other->unbounded; + + surface->base.is_clear = other->base.is_clear; + + 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 = 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 (&surface->base); + return _cairo_surface_create_in_error (status); + } + + return &surface->base; +} + +static cairo_bool_t +_cairo_recording_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_recording_surface_t *surface = abstract_surface; + + if (surface->unbounded) + return FALSE; + + *rectangle = surface->extents; + return TRUE; +} + +static const cairo_surface_backend_t cairo_recording_surface_backend = { + CAIRO_SURFACE_TYPE_RECORDING, + _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, + _cairo_recording_surface_snapshot, + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_recording_surface_get_extents, + NULL, /* get_font_options */ + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + /* Here are the 5 basic drawing operations, (which are in some + * sense the only things that cairo_recording_surface should need to + * implement). However, we implement the more generic show_text_glyphs + * instead of show_glyphs. One or the other is eough. */ + + _cairo_recording_surface_paint, + _cairo_recording_surface_mask, + _cairo_recording_surface_stroke, + _cairo_recording_surface_fill, + NULL, /* fill-stroke */ + NULL, + _cairo_recording_surface_has_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 *abstract_surface, + cairo_path_fixed_t *path) +{ + cairo_recording_surface_t *surface; + cairo_command_t **elements; + int i, num_elements; + cairo_int_status_t status; + + if (unlikely (abstract_surface->status)) + return abstract_surface->status; + + surface = (cairo_recording_surface_t *) abstract_surface; + status = CAIRO_STATUS_SUCCESS; + + 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) { + case CAIRO_COMMAND_PAINT: + case CAIRO_COMMAND_MASK: + status = CAIRO_INT_STATUS_UNSUPPORTED; + break; + + case CAIRO_COMMAND_STROKE: + { + cairo_traps_t traps; + + _cairo_traps_init (&traps); + + /* XXX call cairo_stroke_to_path() when that is implemented */ + 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_INT_STATUS_SUCCESS) + status = _cairo_traps_path (&traps, path); + + _cairo_traps_fini (&traps); + break; + } + case CAIRO_COMMAND_FILL: + { + status = _cairo_path_fixed_append (path, + &command->fill.path, + 0, 0); + break; + } + case CAIRO_COMMAND_SHOW_TEXT_GLYPHS: + { + status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font, + command->show_text_glyphs.glyphs, + command->show_text_glyphs.num_glyphs, + path); + break; + } + + case CAIRO_COMMAND_TAG: + break; + + default: + ASSERT_NOT_REACHED; + } + + if (unlikely (status)) + break; + } + + 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; +} + +static cairo_status_t +_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_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->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); + + if (surface->base.is_clear) + return CAIRO_STATUS_SUCCESS; + + assert (_cairo_surface_is_recording (&surface->base)); + + _cairo_surface_wrapper_init (&wrapper, target); + 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); + + /* 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; + + surface->has_bilevel_alpha = TRUE; + surface->has_only_op_over = TRUE; + + 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; + } + + 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)) { + if (command->header.type != CAIRO_COMMAND_TAG) + continue; + } + + switch (command->header.type) { + case CAIRO_COMMAND_PAINT: + status = _cairo_surface_wrapper_paint (&wrapper, + command->header.op, + &command->paint.source.base, + 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: + status = _cairo_surface_wrapper_mask (&wrapper, + command->header.op, + &command->mask.source.base, + &command->mask.mask.base, + 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, + &command->stroke.path, + &command->stroke.style, + &command->stroke.ctm, + &command->stroke.ctm_inverse, + command->stroke.tolerance, + command->stroke.antialias, + 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: + 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]; + + 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 (status == CAIRO_INT_STATUS_UNSUPPORTED) { + 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); + 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: + 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); + 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); + break; + + default: + ASSERT_NOT_REACHED; + } + + /* It's possible that a degenerate clip caused the command to end up doing nothing when replayed. */ + if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO)) + status = CAIRO_INT_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_INT_STATUS_SUCCESS; + } else { + assert (_cairo_int_status_is_error (status)); + } + } + + if (unlikely (status)) + break; + } + +done: + _cairo_surface_wrapper_fini (&wrapper); + return _cairo_surface_set_error (&surface->base, status); +} + +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); + break; + + default: + ASSERT_NOT_REACHED; + } + + _cairo_surface_wrapper_fini (&wrapper); + return _cairo_surface_set_error (&surface->base, status); +} +/** + * _cairo_recording_surface_replay: + * @surface: the #cairo_recording_surface_t + * @target: a target #cairo_surface_t onto which to replay the operations + * @width_pixels: width of the surface, in pixels + * @height_pixels: height of the surface, in pixels + * + * A recording surface can be "replayed" against any target surface, + * after which the results in target will be identical to the results + * that would have been obtained if the original operations applied to + * the recording surface had instead been applied to the target surface. + **/ +cairo_status_t +_cairo_recording_surface_replay (cairo_surface_t *surface, + cairo_surface_t *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); +} + +/* Replay recording to surface. When the return status of each operation is + * one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or + * %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation + * will be stored in the recording surface. Any other status will abort the + * replay and return the status. + */ +cairo_status_t +_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface, + const cairo_matrix_t *surface_transform, + cairo_surface_t *target, + cairo_bool_t surface_is_unbounded) +{ + 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); +} + +cairo_status_t +_cairo_recording_surface_replay_region (cairo_surface_t *surface, + const cairo_rectangle_int_t *surface_extents, + cairo_surface_t *target, + cairo_recording_region_type_t region) +{ + return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, + surface_extents, NULL, + target, NULL, FALSE, + CAIRO_RECORDING_REPLAY, + region); +} + +static cairo_status_t +_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface, + cairo_box_t *bbox, + const cairo_matrix_t *transform) +{ + cairo_surface_t *null_surface; + cairo_surface_t *analysis_surface; + cairo_status_t status; + + null_surface = _cairo_null_surface_create (surface->base.content); + analysis_surface = _cairo_analysis_surface_create (null_surface); + cairo_surface_destroy (null_surface); + + status = analysis_surface->status; + if (unlikely (status)) + return status; + + if (transform != NULL) + _cairo_analysis_surface_set_ctm (analysis_surface, transform); + + status = _cairo_recording_surface_replay (&surface->base, analysis_surface); + _cairo_analysis_surface_get_bounding_box (analysis_surface, bbox); + cairo_surface_destroy (analysis_surface); + + return status; +} + +/** + * cairo_recording_surface_ink_extents: + * @surface: a #cairo_recording_surface_t + * @x0: the x-coordinate of the top-left of the ink bounding box + * @y0: the y-coordinate of the top-left of the ink bounding box + * @width: the width of the ink bounding box + * @height: the height of the ink bounding box + * + * Measures the extents of the operations stored within the recording-surface. + * This is useful to compute the required size of an image surface (or + * equivalent) into which to replay the full sequence of drawing operations. + * + * Since: 1.10 + **/ +void +cairo_recording_surface_ink_extents (cairo_surface_t *surface, + double *x0, + double *y0, + double *width, + double *height) +{ + cairo_status_t status; + cairo_box_t bbox; + + memset (&bbox, 0, sizeof (bbox)); + + if (surface->status || ! _cairo_surface_is_recording (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + goto DONE; + } + + status = _recording_surface_get_ink_bbox ((cairo_recording_surface_t *) surface, + &bbox, + NULL); + if (unlikely (status)) + status = _cairo_surface_set_error (surface, status); + +DONE: + if (x0) + *x0 = _cairo_fixed_to_double (bbox.p1.x); + if (y0) + *y0 = _cairo_fixed_to_double (bbox.p1.y); + if (width) + *width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x); + if (height) + *height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y); +} + +cairo_status_t +_cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface, + cairo_box_t *bbox, + const cairo_matrix_t *transform) +{ + if (! surface->unbounded) { + _cairo_box_from_rectangle (bbox, &surface->extents); + if (transform != NULL) + _cairo_matrix_transform_bounding_box_fixed (transform, bbox, NULL); + + return CAIRO_STATUS_SUCCESS; + } + + 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 new file mode 100644 index 0000000000..2d51d7b10a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-rectangle.c @@ -0,0 +1,299 @@ +/* -*- 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 © 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + */ + +#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, + double *x2, double *y2) +{ + box->p1.x = _cairo_fixed_from_double (*x1); + box->p1.y = _cairo_fixed_from_double (*y1); + box->p2.x = _cairo_fixed_from_double (*x2); + box->p2.y = _cairo_fixed_from_double (*y2); +} + +cairo_private void +_cairo_box_to_doubles (const cairo_box_t *box, + double *x1, double *y1, + double *x2, double *y2) +{ + *x1 = _cairo_fixed_to_double (box->p1.x); + *y1 = _cairo_fixed_to_double (box->p1.y); + *x2 = _cairo_fixed_to_double (box->p2.x); + *y2 = _cairo_fixed_to_double (box->p2.y); +} + +void +_cairo_box_from_rectangle (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); +} + +void +_cairo_boxes_get_extents (const cairo_box_t *boxes, + int num_boxes, + cairo_box_t *extents) +{ + assert (num_boxes > 0); + *extents = *boxes; + while (--num_boxes) + _cairo_box_add_box (extents, ++boxes); +} + +/* XXX We currently have a confusing mix of boxes and rectangles as + * exemplified by this function. A #cairo_box_t is a rectangular area + * represented by the coordinates of the upper left and lower right + * corners, expressed in fixed point numbers. A #cairo_rectangle_int_t is + * also a rectangular area, but represented by the upper left corner + * and the width and the height, as integer numbers. + * + * This function converts a #cairo_box_t to a #cairo_rectangle_int_t by + * increasing the area to the nearest integer coordinates. We should + * standardize on #cairo_rectangle_fixed_t and #cairo_rectangle_int_t, and + * this function could be renamed to the more reasonable + * _cairo_rectangle_fixed_round. + */ + +void +_cairo_box_round_to_rectangle (const cairo_box_t *box, + cairo_rectangle_int_t *rectangle) +{ + rectangle->x = _cairo_fixed_integer_floor (box->p1.x); + rectangle->y = _cairo_fixed_integer_floor (box->p1.y); + rectangle->width = _cairo_fixed_integer_ceil (box->p2.x) - rectangle->x; + rectangle->height = _cairo_fixed_integer_ceil (box->p2.y) - rectangle->y; +} + +cairo_bool_t +_cairo_rectangle_intersect (cairo_rectangle_int_t *dst, + const cairo_rectangle_int_t *src) +{ + int x1, y1, x2, y2; + + x1 = MAX (dst->x, src->x); + y1 = MAX (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 = MIN (dst->x + (int) dst->width, src->x + (int) src->width); + y2 = MIN (dst->y + (int) dst->height, src->y + (int) src->height); + + if (x1 >= x2 || y1 >= y2) { + dst->x = 0; + dst->y = 0; + dst->width = 0; + dst->height = 0; + + return FALSE; + } else { + dst->x = x1; + dst->y = y1; + dst->width = x2 - x1; + dst->height = y2 - y1; + + return TRUE; + } +} + +/* 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) +#define P2y (line->p2.y) +#define B1x (box->p1.x) +#define B1y (box->p1.y) +#define B2x (box->p2.x) +#define B2y (box->p2.y) + +/* + * Check whether any part of line intersects box. This function essentially + * computes whether the ray starting at line->p1 in the direction of line->p2 + * intersects the box before it reaches p2. Normally, this is done + * by dividing by the lengths of the line projected onto each axis. Because + * we're in fixed point, this function does a bit more work to avoid having to + * do the division -- we don't care about the actual intersection point, so + * it's of no interest to us. + */ + +cairo_bool_t +_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; + + cairo_fixed_t xlen, ylen; + + if (_cairo_box_contains_point (box, &line->p1) || + _cairo_box_contains_point (box, &line->p2)) + return TRUE; + + xlen = P2x - P1x; + ylen = P2y - P1y; + + if (xlen) { + if (xlen > 0) { + t1 = B1x - P1x; + t2 = B2x - P1x; + } else { + t1 = P1x - B2x; + t2 = P1x - B1x; + xlen = - xlen; + } + + if ((t1 < 0 || t1 > xlen) && + (t2 < 0 || t2 > xlen)) + return FALSE; + } else { + /* Fully vertical line -- check that X is in bounds */ + if (P1x < B1x || P1x > B2x) + return FALSE; + } + + if (ylen) { + if (ylen > 0) { + t3 = B1y - P1y; + t4 = B2y - P1y; + } else { + t3 = P1y - B2y; + t4 = P1y - B1y; + ylen = - ylen; + } + + if ((t3 < 0 || t3 > ylen) && + (t4 < 0 || t4 > ylen)) + return FALSE; + } else { + /* Fully horizontal line -- check Y */ + if (P1y < B1y || P1y > B2y) + return FALSE; + } + + /* If we had a horizontal or vertical line, then it's already been checked */ + if (P1x == P2x || P1y == P2y) + return TRUE; + + /* Check overlap. Note that t1 < t2 and t3 < t4 here. */ + t1y = _cairo_int32x32_64_mul (t1, ylen); + t2y = _cairo_int32x32_64_mul (t2, ylen); + t3x = _cairo_int32x32_64_mul (t3, xlen); + t4x = _cairo_int32x32_64_mul (t4, xlen); + + if (_cairo_int64_lt(t1y, t4x) && + _cairo_int64_lt(t3x, t2y)) + return TRUE; + + return FALSE; +} + +static cairo_status_t +_cairo_box_add_spline_point (void *closure, + const cairo_point_t *point, + const cairo_slope_t *tangent) +{ + _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 new file mode 100644 index 0000000000..e353b34e85 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-rectangular-scan-converter.c @@ -0,0 +1,791 @@ +/* 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-combsort-inline.h" +#include "cairo-error-private.h" +#include "cairo-freelist-private.h" +#include "cairo-list-private.h" +#include "cairo-spans-private.h" + +#include + +typedef struct _rectangle { + struct _rectangle *next, *prev; + cairo_fixed_t left, right; + cairo_fixed_t top, bottom; + int32_t top_y, bottom_y; + int dir; +} rectangle_t; + +#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 { + rectangle_t **start; + pqueue_t stop; + rectangle_t head, tail; + rectangle_t *insert_cursor; + int32_t current_y; + int32_t xmin, xmax; + + struct coverage { + struct cell { + struct cell *prev, *next; + int x, covered, uncovered; + } head, tail, *cursor; + unsigned int count; + cairo_freepool_t pool; + } coverage; + + cairo_half_open_span_t spans_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_half_open_span_t)]; + cairo_half_open_span_t *spans; + unsigned int num_spans; + unsigned int size_spans; + + jmp_buf jmpbuf; +} sweep_line_t; + +static inline int +rectangle_compare_start (const rectangle_t *a, + const rectangle_t *b) +{ + int cmp; + + cmp = a->top_y - b->top_y; + if (cmp) + return cmp; + + return a->left - b->left; +} + +static inline int +rectangle_compare_stop (const rectangle_t *a, + const rectangle_t *b) +{ + return a->bottom_y - b->bottom_y; +} + +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->stop.size + 1 == sweep->stop.max_size)) { + if (unlikely (! pqueue_grow (&sweep->stop))) + longjmp (sweep->jmpbuf, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + elements = sweep->stop.elements; + for (i = ++sweep->stop.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 * +peek_stop (sweep_line_t *sweep) +{ + return sweep->stop.elements[PQ_FIRST_ENTRY]; +} + +CAIRO_COMBSORT_DECLARE (rectangle_sort, rectangle_t *, rectangle_compare_start) + +static void +sweep_line_init (sweep_line_t *sweep) +{ + sweep->head.left = INT_MIN; + sweep->head.next = &sweep->tail; + sweep->tail.left = INT_MAX; + sweep->tail.prev = &sweep->head; + sweep->insert_cursor = &sweep->tail; + + _cairo_freepool_init (&sweep->coverage.pool, sizeof (struct cell)); + + sweep->spans = sweep->spans_stack; + sweep->size_spans = ARRAY_LENGTH (sweep->spans_stack); + + sweep->coverage.head.prev = NULL; + sweep->coverage.head.x = INT_MIN; + sweep->coverage.tail.next = NULL; + sweep->coverage.tail.x = INT_MAX; + + pqueue_init (&sweep->stop); +} + +static void +sweep_line_fini (sweep_line_t *sweep) +{ + _cairo_freepool_fini (&sweep->coverage.pool); + pqueue_fini (&sweep->stop); + + if (sweep->spans != sweep->spans_stack) + free (sweep->spans); +} + +static inline void +add_cell (sweep_line_t *sweep, int x, int covered, int uncovered) +{ + struct cell *cell; + + cell = sweep->coverage.cursor; + if (cell->x > x) { + do { + UNROLL3({ + if (cell->prev->x < x) + break; + cell = cell->prev; + }) + } while (TRUE); + } else { + if (cell->x == x) + goto found; + + do { + UNROLL3({ + cell = cell->next; + if (cell->x >= x) + break; + }) + } while (TRUE); + } + + if (x != cell->x) { + struct cell *c; + + sweep->coverage.count++; + + c = _cairo_freepool_alloc (&sweep->coverage.pool); + if (unlikely (c == NULL)) { + longjmp (sweep->jmpbuf, + _cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + cell->prev->next = c; + c->prev = cell->prev; + c->next = cell; + cell->prev = c; + + c->x = x; + c->covered = 0; + c->uncovered = 0; + + cell = c; + } + +found: + cell->covered += covered; + cell->uncovered += uncovered; + sweep->coverage.cursor = cell; +} + +static inline void +_active_edges_to_spans (sweep_line_t *sweep) +{ + int32_t y = sweep->current_y; + rectangle_t *rectangle; + int coverage, prev_coverage; + int prev_x; + struct cell *cell; + + sweep->num_spans = 0; + if (sweep->head.next == &sweep->tail) + return; + + sweep->coverage.head.next = &sweep->coverage.tail; + sweep->coverage.tail.prev = &sweep->coverage.head; + sweep->coverage.cursor = &sweep->coverage.tail; + sweep->coverage.count = 0; + + /* XXX cell coverage only changes when a rectangle appears or + * disappears. Try only modifying coverage at such times. + */ + for (rectangle = sweep->head.next; + rectangle != &sweep->tail; + rectangle = rectangle->next) + { + int height; + int frac, i; + + if (y == rectangle->bottom_y) { + height = rectangle->bottom & CAIRO_FIXED_FRAC_MASK; + if (height == 0) + continue; + } else + height = CAIRO_FIXED_ONE; + if (y == rectangle->top_y) + height -= rectangle->top & CAIRO_FIXED_FRAC_MASK; + height *= rectangle->dir; + + i = _cairo_fixed_integer_part (rectangle->left), + frac = _cairo_fixed_fractional_part (rectangle->left); + add_cell (sweep, i, + (CAIRO_FIXED_ONE-frac) * height, + frac * height); + + i = _cairo_fixed_integer_part (rectangle->right), + frac = _cairo_fixed_fractional_part (rectangle->right); + add_cell (sweep, i, + -(CAIRO_FIXED_ONE-frac) * height, + -frac * height); + } + + if (2*sweep->coverage.count >= sweep->size_spans) { + unsigned size; + + size = sweep->size_spans; + while (size <= 2*sweep->coverage.count) + size <<= 1; + + if (sweep->spans != sweep->spans_stack) + free (sweep->spans); + + sweep->spans = _cairo_malloc_ab (size, sizeof (cairo_half_open_span_t)); + if (unlikely (sweep->spans == NULL)) + longjmp (sweep->jmpbuf, _cairo_error (CAIRO_STATUS_NO_MEMORY)); + + sweep->size_spans = size; + } + + prev_coverage = coverage = 0; + prev_x = INT_MIN; + 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].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].inverse = 0; + sweep->spans[n].coverage = c - (c >> 8); + prev_coverage = coverage; + } + coverage += cell->uncovered; + prev_x = cell->x + 1; + } + _cairo_freepool_reset (&sweep->coverage.pool); + + 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].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; + } + } +} + +static inline void +sweep_line_delete (sweep_line_t *sweep, + rectangle_t *rectangle) +{ + if (sweep->insert_cursor == rectangle) + sweep->insert_cursor = rectangle->next; + + rectangle->prev->next = rectangle->next; + rectangle->next->prev = rectangle->prev; + + pqueue_pop (&sweep->stop); +} + +static inline void +sweep_line_insert (sweep_line_t *sweep, + rectangle_t *rectangle) +{ + rectangle_t *pos; + + pos = sweep->insert_cursor; + if (pos->left != rectangle->left) { + if (pos->left > rectangle->left) { + do { + UNROLL3({ + if (pos->prev->left < rectangle->left) + break; + pos = pos->prev; + }) + } while (TRUE); + } else { + do { + UNROLL3({ + pos = pos->next; + if (pos->left >= rectangle->left) + break; + }); + } while (TRUE); + } + } + + pos->prev->next = rectangle; + rectangle->prev = pos->prev; + rectangle->next = pos; + pos->prev = rectangle; + sweep->insert_cursor = rectangle; + + pqueue_push (sweep, rectangle); +} + +static void +render_rows (sweep_line_t *sweep_line, + cairo_span_renderer_t *renderer, + int height) +{ + cairo_status_t status; + + _active_edges_to_spans (sweep_line); + + status = renderer->render_rows (renderer, + sweep_line->current_y, height, + sweep_line->spans, + sweep_line->num_spans); + if (unlikely (status)) + longjmp (sweep_line->jmpbuf, status); +} + +static cairo_status_t +generate (cairo_rectangular_scan_converter_t *self, + cairo_span_renderer_t *renderer, + rectangle_t **rectangles) +{ + sweep_line_t sweep_line; + rectangle_t *start, *stop; + cairo_status_t status; + + sweep_line_init (&sweep_line); + 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 out; + + 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) { + render_rows (&sweep_line, renderer, + start->top_y - sweep_line.current_y); + sweep_line.current_y = start->top_y; + } + + do { + sweep_line_insert (&sweep_line, start); + start = *sweep_line.start++; + if (start == NULL) + goto end; + if (start->top_y != sweep_line.current_y) + break; + } while (TRUE); + + render_rows (&sweep_line, renderer, 1); + + stop = peek_stop (&sweep_line); + while (stop->bottom_y == sweep_line.current_y) { + sweep_line_delete (&sweep_line, stop); + stop = peek_stop (&sweep_line); + if (stop == NULL) + break; + } + + sweep_line.current_y++; + + while (stop != NULL && stop->bottom_y < start->top_y) { + if (stop->bottom_y != sweep_line.current_y) { + render_rows (&sweep_line, renderer, + stop->bottom_y - sweep_line.current_y); + sweep_line.current_y = stop->bottom_y; + } + + render_rows (&sweep_line, renderer, 1); + + do { + sweep_line_delete (&sweep_line, stop); + stop = peek_stop (&sweep_line); + } while (stop != NULL && stop->bottom_y == sweep_line.current_y); + + sweep_line.current_y++; + } + } while (TRUE); + + end: + render_rows (&sweep_line, renderer, 1); + + stop = peek_stop (&sweep_line); + while (stop->bottom_y == sweep_line.current_y) { + sweep_line_delete (&sweep_line, stop); + stop = peek_stop (&sweep_line); + if (stop == NULL) + goto out; + } + + 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); + sweep_line.current_y = stop->bottom_y; + } + + render_rows (&sweep_line, renderer, 1); + + do { + sweep_line_delete (&sweep_line, stop); + stop = peek_stop (&sweep_line); + if (stop == NULL) + goto out; + } while (stop->bottom_y == sweep_line.current_y); + + } + + out: + 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, + cairo_span_renderer_t *renderer) +{ + cairo_rectangular_scan_converter_t *self = converter; + rectangle_t *rectangles_stack[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *)]; + rectangle_t **rectangles; + struct _cairo_rectangular_scan_converter_chunk *chunk; + cairo_status_t status; + int i, j; + + if (unlikely (self->num_rectangles == 0)) { + return renderer->render_rows (renderer, + _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, + sizeof (rectangle_t *)); + if (unlikely (rectangles == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + j = 0; + for (chunk = &self->chunks; chunk != NULL; chunk = chunk->next) { + rectangle_t *rectangle; + + rectangle = chunk->base; + for (i = 0; i < chunk->count; i++) + rectangles[j++] = &rectangle[i]; + } + rectangle_sort (rectangles, j); + rectangles[j] = NULL; + + status = generate (self, renderer, rectangles); + + if (rectangles != rectangles_stack) + free (rectangles); + + return status; +} + +static rectangle_t * +_allocate_rectangle (cairo_rectangular_scan_converter_t *self) +{ + rectangle_t *rectangle; + struct _cairo_rectangular_scan_converter_chunk *chunk; + + chunk = self->tail; + if (chunk->count == chunk->size) { + int size; + + size = chunk->size * 2; + chunk->next = _cairo_malloc_ab_plus_c (size, + sizeof (rectangle_t), + sizeof (struct _cairo_rectangular_scan_converter_chunk)); + + if (unlikely (chunk->next == NULL)) + return NULL; + + chunk = chunk->next; + chunk->next = NULL; + chunk->count = 0; + chunk->size = size; + chunk->base = chunk + 1; + self->tail = chunk; + } + + rectangle = chunk->base; + return rectangle + chunk->count++; +} + +cairo_status_t +_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, + const cairo_box_t *box, + int dir) +{ + rectangle_t *rectangle; + + rectangle = _allocate_rectangle (self); + if (unlikely (rectangle == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + 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 = 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; +} + +static void +_cairo_rectangular_scan_converter_destroy (void *converter) +{ + cairo_rectangular_scan_converter_t *self = converter; + struct _cairo_rectangular_scan_converter_chunk *chunk, *next; + + for (chunk = self->chunks.next; chunk != NULL; chunk = next) { + next = chunk->next; + free (chunk); + } +} + +void +_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.generate = _cairo_rectangular_scan_converter_generate; + + _cairo_box_from_rectangle (&self->extents, extents); + + self->chunks.base = self->buf; + self->chunks.next = NULL; + self->chunks.count = 0; + self->chunks.size = sizeof (self->buf) / sizeof (rectangle_t); + self->tail = &self->chunks; + + self->num_rectangles = 0; +} diff --git a/gfx/cairo/cairo/src/cairo-reference-count-private.h b/gfx/cairo/cairo/src/cairo-reference-count-private.h new file mode 100644 index 0000000000..75fdf3538f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-reference-count-private.h @@ -0,0 +1,62 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_REFRENCE_COUNT_PRIVATE_H +#define CAIRO_REFRENCE_COUNT_PRIVATE_H + +#include "cairo-atomic-private.h" + +/* Encapsulate operations on the object's reference count */ +typedef struct { + cairo_atomic_int_t ref_count; +} 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 ((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) + +#define CAIRO_REFERENCE_COUNT_HAS_REFERENCE(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) > 0) + +#endif diff --git a/gfx/cairo/cairo/src/cairo-region-private.h b/gfx/cairo/cairo/src/cairo-region-private.h new file mode 100644 index 0000000000..549e508789 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-region-private.h @@ -0,0 +1,77 @@ +/* -*- 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 + * Vladimir Vukicevic + * Søren Sandmann + */ + +#ifndef CAIRO_REGION_PRIVATE_H +#define CAIRO_REGION_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-reference-count-private.h" + +#include + +CAIRO_BEGIN_DECLS + +struct _cairo_region { + cairo_reference_count_t ref_count; + cairo_status_t status; + + pixman_region32_t rgn; +}; + +cairo_private cairo_region_t * +_cairo_region_create_in_error (cairo_status_t status); + +cairo_private void +_cairo_region_init (cairo_region_t *region); + +cairo_private void +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); + +cairo_private void +_cairo_region_fini (cairo_region_t *region); + +cairo_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 new file mode 100644 index 0000000000..c1d35e174a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-region.c @@ -0,0 +1,943 @@ +/* -*- 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 + * Vladimir Vukicevic + * Søren Sandmann + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-region-private.h" + +/* XXX need to update pixman headers to be const as appropriate */ +#define CONST_CAST (pixman_region32_t *) + +/** + * SECTION:cairo-region + * @Title: Regions + * @Short_Description: Representing a pixel-aligned area + * + * 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 */ + CAIRO_STATUS_NO_MEMORY, /* status */ +}; + +cairo_region_t * +_cairo_region_create_in_error (cairo_status_t status) +{ + switch (status) { + case CAIRO_STATUS_NO_MEMORY: + return (cairo_region_t *) &_cairo_region_nil; + + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + /* fall-through */ + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_STATUS: + case CAIRO_STATUS_INVALID_CONTENT: + case CAIRO_STATUS_INVALID_FORMAT: + case CAIRO_STATUS_INVALID_VISUAL: + case CAIRO_STATUS_READ_ERROR: + case CAIRO_STATUS_WRITE_ERROR: + case CAIRO_STATUS_FILE_NOT_FOUND: + case CAIRO_STATUS_TEMP_FILE_ERROR: + case CAIRO_STATUS_INVALID_STRIDE: + case CAIRO_STATUS_INVALID_SIZE: + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: + case CAIRO_STATUS_DEVICE_ERROR: + case CAIRO_STATUS_INVALID_RESTORE: + case CAIRO_STATUS_INVALID_POP_GROUP: + case CAIRO_STATUS_NO_CURRENT_POINT: + case CAIRO_STATUS_INVALID_MATRIX: + case CAIRO_STATUS_NULL_POINTER: + case CAIRO_STATUS_INVALID_STRING: + case CAIRO_STATUS_INVALID_PATH_DATA: + case CAIRO_STATUS_SURFACE_FINISHED: + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_DASH: + case CAIRO_STATUS_INVALID_DSC_COMMENT: + case CAIRO_STATUS_INVALID_INDEX: + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: + case CAIRO_STATUS_FONT_TYPE_MISMATCH: + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + case CAIRO_STATUS_USER_FONT_ERROR: + case CAIRO_STATUS_NEGATIVE_COUNT: + case CAIRO_STATUS_INVALID_CLUSTERS: + 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; + } +} + +/** + * _cairo_region_set_error: + * @region: a region + * @status: a status value indicating an error + * + * Atomically sets region->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal + * status values. + * + * All assignments of an error status to region->status should happen + * through _cairo_region_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the + * nil objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +static cairo_status_t +_cairo_region_set_error (cairo_region_t *region, + cairo_status_t status) +{ + if (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 (®ion->status, status); + + return _cairo_error (status); +} + +void +_cairo_region_init (cairo_region_t *region) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); + + region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); + pixman_region32_init (®ion->rgn); +} + +void +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (region, sizeof (cairo_region_t))); + + region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 0); + pixman_region32_init_rect (®ion->rgn, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); +} + +void +_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_UNDEFINED (region, sizeof (cairo_region_t))); +} + +/** + * cairo_region_create: + * + * Allocates a new empty region object. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create (void) +{ + cairo_region_t *region; + + region = _cairo_malloc (sizeof (cairo_region_t)); + if (region == NULL) + return (cairo_region_t *) &_cairo_region_nil; + + region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); + + pixman_region32_init (®ion->rgn); + + return region; +} +slim_hidden_def (cairo_region_create); + +/** + * cairo_region_create_rectangles: + * @rects: an array of @count rectangles + * @count: number of rectangles + * + * Allocates a new region object containing the union of all given @rects. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, + int count) +{ + pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; + pixman_box32_t *pboxes = stack_pboxes; + cairo_region_t *region; + int i; + + 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 (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)) { + free (region); + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + } + + for (i = 0; i < count; i++) { + pboxes[i].x1 = rects[i].x; + pboxes[i].y1 = rects[i].y; + pboxes[i].x2 = rects[i].x + rects[i].width; + pboxes[i].y2 = rects[i].y + rects[i].height; + } + + i = pixman_region32_init_rects (®ion->rgn, pboxes, count); + + if (pboxes != stack_pboxes) + free (pboxes); + + if (unlikely (i == 0)) { + free (region); + return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + 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 + * + * Allocates a new region object containing @rectangle. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle) +{ + cairo_region_t *region; + + region = _cairo_malloc (sizeof (cairo_region_t)); + if (unlikely (region == NULL)) + return (cairo_region_t *) &_cairo_region_nil; + + region->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (®ion->ref_count, 1); + + pixman_region32_init_rect (®ion->rgn, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + return region; +} +slim_hidden_def (cairo_region_create_rectangle); + +/** + * cairo_region_copy: + * @original: a #cairo_region_t + * + * Allocates a new region object copying the area from @original. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_copy (const cairo_region_t *original) +{ + cairo_region_t *copy; + + if (original != NULL && original->status) + return (cairo_region_t *) &_cairo_region_nil; + + copy = cairo_region_create (); + if (unlikely (copy->status)) + return copy; + + if (original != NULL && + ! pixman_region32_copy (©->rgn, CONST_CAST &original->rgn)) + { + cairo_region_destroy (copy); + return (cairo_region_t *) &_cairo_region_nil; + } + + return copy; +} +slim_hidden_def (cairo_region_copy); + +/** + * cairo_region_reference: + * @region: a #cairo_region_t + * + * Increases the reference count on @region by one. This prevents + * @region from being destroyed until a matching call to + * cairo_region_destroy() is made. + * + * Return value: the referenced #cairo_region_t. + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_reference (cairo_region_t *region) +{ + if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) + return NULL; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); + + _cairo_reference_count_inc (®ion->ref_count); + return region; +} +slim_hidden_def (cairo_region_reference); + +/** + * cairo_region_destroy: + * @region: a #cairo_region_t + * + * Destroys a #cairo_region_t object created with + * cairo_region_create(), cairo_region_copy(), or + * or cairo_region_create_rectangle(). + * + * Since: 1.10 + **/ +void +cairo_region_destroy (cairo_region_t *region) +{ + if (region == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (®ion->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (®ion->ref_count)); + + if (! _cairo_reference_count_dec_and_test (®ion->ref_count)) + return; + + _cairo_region_fini (region); + free (region); +} +slim_hidden_def (cairo_region_destroy); + +/** + * cairo_region_num_rectangles: + * @region: a #cairo_region_t + * + * Returns the number of rectangles contained in @region. + * + * Return value: The number of rectangles contained in @region. + * + * Since: 1.10 + **/ +int +cairo_region_num_rectangles (const cairo_region_t *region) +{ + if (region->status) + return 0; + + return pixman_region32_n_rects (CONST_CAST ®ion->rgn); +} +slim_hidden_def (cairo_region_num_rectangles); + +/** + * cairo_region_get_rectangle: + * @region: a #cairo_region_t + * @nth: a number indicating which rectangle should be returned + * @rectangle: return location for a #cairo_rectangle_int_t + * + * Stores the @nth rectangle from the region in @rectangle. + * + * Since: 1.10 + **/ +void +cairo_region_get_rectangle (const cairo_region_t *region, + int nth, + cairo_rectangle_int_t *rectangle) +{ + pixman_box32_t *pbox; + + if (region->status) { + rectangle->x = rectangle->y = 0; + rectangle->width = rectangle->height = 0; + return; + } + + pbox = pixman_region32_rectangles (CONST_CAST ®ion->rgn, NULL) + nth; + + rectangle->x = pbox->x1; + rectangle->y = pbox->y1; + rectangle->width = pbox->x2 - pbox->x1; + rectangle->height = pbox->y2 - pbox->y1; +} +slim_hidden_def (cairo_region_get_rectangle); + +/** + * cairo_region_get_extents: + * @region: a #cairo_region_t + * @extents: rectangle into which to store the extents + * + * Gets the bounding rectangle of @region as a #cairo_rectangle_int_t + * + * Since: 1.10 + **/ +void +cairo_region_get_extents (const cairo_region_t *region, + cairo_rectangle_int_t *extents) +{ + pixman_box32_t *pextents; + + if (region->status) { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + return; + } + + pextents = pixman_region32_extents (CONST_CAST ®ion->rgn); + + extents->x = pextents->x1; + extents->y = pextents->y1; + extents->width = pextents->x2 - pextents->x1; + extents->height = pextents->y2 - pextents->y1; +} +slim_hidden_def (cairo_region_get_extents); + +/** + * cairo_region_status: + * @region: a #cairo_region_t + * + * Checks whether an error has previous occurred for this + * region object. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_status (const cairo_region_t *region) +{ + return region->status; +} +slim_hidden_def (cairo_region_status); + +/** + * cairo_region_subtract: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Subtracts @other from @dst and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_subtract (&dst->rgn, + &dst->rgn, + CONST_CAST &other->rgn)) + { + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + } + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_region_subtract); + +/** + * cairo_region_subtract_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Subtracts @rectangle from @dst and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_subtract_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_subtract_rectangle); + +/** + * cairo_region_intersect: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the intersection of @dst with @other and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_region_intersect); + +/** + * cairo_region_intersect_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the intersection of @dst with @rectangle and places the + * result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_intersect_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_intersect_rectangle); + +/** + * cairo_region_union: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the union of @dst with @other and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_union (cairo_region_t *dst, + const cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_union (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} +slim_hidden_def (cairo_region_union); + +/** + * cairo_region_union_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the union of @dst with @rectangle and places the result in @dst. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_union_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_union (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_union_rectangle); + +/** + * cairo_region_xor: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the exclusive difference of @dst with @other and places the + * result in @dst. That is, @dst will be set to contain all areas that + * are either in @dst or in @other, but not in both. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t tmp; + + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + pixman_region32_init (&tmp); + + /* XXX: get an xor function into pixman */ + if (! pixman_region32_subtract (&tmp, CONST_CAST &other->rgn, &dst->rgn) || + ! pixman_region32_subtract (&dst->rgn, &dst->rgn, CONST_CAST &other->rgn) || + ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (&tmp); + + return status; +} +slim_hidden_def (cairo_region_xor); + +/** + * cairo_region_xor_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the exclusive difference of @dst with @rectangle and places the + * result in @dst. That is, @dst will be set to contain all areas that are + * either in @dst or in @rectangle, but not in both. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_xor_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region, tmp; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + pixman_region32_init (&tmp); + + /* XXX: get an xor function into pixman */ + if (! pixman_region32_subtract (&tmp, ®ion, &dst->rgn) || + ! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion) || + ! pixman_region32_union (&dst->rgn, &dst->rgn, &tmp)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (&tmp); + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_xor_rectangle); + +/** + * cairo_region_is_empty: + * @region: a #cairo_region_t + * + * Checks whether @region is empty. + * + * Return value: %TRUE if @region is empty, %FALSE if it isn't. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_is_empty (const cairo_region_t *region) +{ + if (region->status) + return TRUE; + + return ! pixman_region32_not_empty (CONST_CAST ®ion->rgn); +} +slim_hidden_def (cairo_region_is_empty); + +/** + * cairo_region_translate: + * @region: a #cairo_region_t + * @dx: Amount to translate in the x direction + * @dy: Amount to translate in the y direction + * + * Translates @region by (@dx, @dy). + * + * Since: 1.10 + **/ +void +cairo_region_translate (cairo_region_t *region, + int dx, int dy) +{ + if (region->status) + return; + + pixman_region32_translate (®ion->rgn, dx, dy); +} +slim_hidden_def (cairo_region_translate); + +/** + * cairo_region_contains_rectangle: + * @region: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Checks whether @rectangle is inside, outside or partially contained + * in @region + * + * Return value: + * %CAIRO_REGION_OVERLAP_IN if @rectangle is entirely inside @region, + * %CAIRO_REGION_OVERLAP_OUT if @rectangle is entirely outside @region, or + * %CAIRO_REGION_OVERLAP_PART if @rectangle is partially inside and partially outside @region. + * + * Since: 1.10 + **/ +cairo_region_overlap_t +cairo_region_contains_rectangle (const cairo_region_t *region, + const cairo_rectangle_int_t *rectangle) +{ + pixman_box32_t pbox; + pixman_region_overlap_t poverlap; + + if (region->status) + return CAIRO_REGION_OVERLAP_OUT; + + pbox.x1 = rectangle->x; + pbox.y1 = rectangle->y; + pbox.x2 = rectangle->x + rectangle->width; + pbox.y2 = rectangle->y + rectangle->height; + + poverlap = pixman_region32_contains_rectangle (CONST_CAST ®ion->rgn, + &pbox); + switch (poverlap) { + default: + case PIXMAN_REGION_OUT: return CAIRO_REGION_OVERLAP_OUT; + case PIXMAN_REGION_IN: return CAIRO_REGION_OVERLAP_IN; + case PIXMAN_REGION_PART: return CAIRO_REGION_OVERLAP_PART; + } +} +slim_hidden_def (cairo_region_contains_rectangle); + +/** + * cairo_region_contains_point: + * @region: a #cairo_region_t + * @x: the x coordinate of a point + * @y: the y coordinate of a point + * + * Checks whether (@x, @y) is contained in @region. + * + * Return value: %TRUE if (@x, @y) is contained in @region, %FALSE if it is not. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_contains_point (const cairo_region_t *region, + int x, int y) +{ + pixman_box32_t box; + + if (region->status) + return FALSE; + + return pixman_region32_contains_point (CONST_CAST ®ion->rgn, x, y, &box); +} +slim_hidden_def (cairo_region_contains_point); + +/** + * cairo_region_equal: + * @a: a #cairo_region_t or %NULL + * @b: a #cairo_region_t or %NULL + * + * Compares whether region_a is equivalent to region_b. %NULL as an argument + * is equal to itself, but not to any non-%NULL region. + * + * Return value: %TRUE if both regions contained the same coverage, + * %FALSE if it is not or any region is in an error status. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_equal (const cairo_region_t *a, + const cairo_region_t *b) +{ + /* error objects are never equal */ + if ((a != NULL && a->status) || (b != NULL && b->status)) + return FALSE; + + if (a == b) + return TRUE; + + if (a == NULL || b == NULL) + return FALSE; + + return pixman_region32_equal (CONST_CAST &a->rgn, CONST_CAST &b->rgn); +} +slim_hidden_def (cairo_region_equal); diff --git a/gfx/cairo/cairo/src/cairo-rename.h b/gfx/cairo/cairo/src/cairo-rename.h new file mode 100644 index 0000000000..803934db0d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-rename.h @@ -0,0 +1,410 @@ +#define cairo_append_path _moz_cairo_append_path +#define cairo_arc _moz_cairo_arc +#define cairo_arc_negative _moz_cairo_arc_negative +#define cairo_arc_to _moz_cairo_arc_to +#define cairo_beos_surface_create _moz_cairo_beos_surface_create +#define cairo_beos_surface_create_for_bitmap _moz_cairo_beos_surface_create_for_bitmap +#define cairo_clip _moz_cairo_clip +#define cairo_clip_extents _moz_cairo_clip_extents +#define cairo_clip_preserve _moz_cairo_clip_preserve +#define cairo_close_path _moz_cairo_close_path +#define cairo_copy_clip_rectangle_list _moz_cairo_copy_clip_rectangle_list +#define cairo_copy_page _moz_cairo_copy_page +#define cairo_copy_path _moz_cairo_copy_path +#define cairo_copy_path_flat _moz_cairo_copy_path_flat +#define cairo_create _moz_cairo_create +#define cairo_curve_to _moz_cairo_curve_to +#define cairo_d2d_create_device _moz_cairo_d2d_create_device +#define cairo_d2d_create_device_from_d3d10device _moz_cairo_d2d_create_device_from_d3d10device +#define cairo_d2d_device_get_device _moz_cairo_d2d_device_get_device +#define cairo_d2d_get_dc _moz_cairo_d2d_get_dc +#define cairo_d2d_get_image_surface_cache_usage _moz_cairo_d2d_get_image_surface_cache_usage +#define cairo_d2d_get_surface_vram_usage _moz_cairo_d2d_get_surface_vram_usage +#define cairo_d2d_present_backbuffer _moz_cairo_d2d_present_backbuffer +#define cairo_d2d_release_dc _moz_cairo_d2d_release_dc +#define cairo_d2d_scroll _moz_cairo_d2d_scroll +#define cairo_d2d_surface_create _moz_cairo_d2d_surface_create +#define cairo_d2d_surface_create_for_handle _moz_cairo_d2d_surface_create_for_handle +#define cairo_d2d_surface_create_for_hwnd _moz_cairo_d2d_surface_create_for_hwnd +#define cairo_d2d_surface_create_for_texture _moz_cairo_d2d_surface_create_for_texture +#define cairo_d2d_surface_get_height _moz_cairo_d2d_surface_get_height +#define cairo_d2d_surface_get_texture _moz_cairo_d2d_surface_get_texture +#define cairo_d2d_surface_get_width _moz_cairo_d2d_surface_get_width +#define cairo_debug_reset_static_data _moz_cairo_debug_reset_static_data +#define cairo_destroy _moz_cairo_destroy +#define cairo_device_acquire _moz_cairo_device_acquire +#define cairo_device_destroy _moz_cairo_device_destroy +#define cairo_device_finish _moz_cairo_device_finish +#define cairo_device_flush _moz_cairo_device_flush +#define cairo_device_get_reference_count _moz_cairo_device_get_reference_count +#define cairo_device_get_type _moz_cairo_device_get_type +#define cairo_device_get_user_data _moz_cairo_device_get_user_data +#define cairo_device_release _moz_cairo_device_release +#define cairo_device_set_user_data _moz_cairo_device_set_user_data +#define cairo_device_status _moz_cairo_device_status +#define cairo_device_reference _moz_cairo_device_reference +#define cairo_device_to_user _moz_cairo_device_to_user +#define cairo_device_to_user_distance _moz_cairo_device_to_user_distance +#define cairo_directfb_surface_create _moz_cairo_directfb_surface_create +#define cairo_dwrite_font_face_create_for_dwrite_fontface _moz_cairo_dwrite_font_face_create_for_dwrite_fontface +#define cairo_dwrite_get_cleartype_rendering_mode _moz_cairo_dwrite_get_cleartype_rendering_mode +#define cairo_dwrite_scaled_font_get_force_GDI_classic _moz_cairo_dwrite_scaled_font_get_force_GDI_classic +#define cairo_dwrite_scaled_font_set_force_GDI_classic _moz_cairo_dwrite_scaled_font_set_force_GDI_classic +#define cairo_dwrite_set_cleartype_params _moz_cairo_dwrite_set_cleartype_params +#define cairo_fill _moz_cairo_fill +#define cairo_fill_extents _moz_cairo_fill_extents +#define cairo_fill_preserve _moz_cairo_fill_preserve +#define cairo_font_extents _moz_cairo_font_extents +#define cairo_font_face_destroy _moz_cairo_font_face_destroy +#define cairo_font_face_get_reference_count _moz_cairo_font_face_get_reference_count +#define cairo_font_face_get_type _moz_cairo_font_face_get_type +#define cairo_font_face_get_user_data _moz_cairo_font_face_get_user_data +#define cairo_font_face_reference _moz_cairo_font_face_reference +#define cairo_font_face_set_user_data _moz_cairo_font_face_set_user_data +#define cairo_font_face_status _moz_cairo_font_face_status +#define cairo_font_options_copy _moz_cairo_font_options_copy +#define cairo_font_options_create _moz_cairo_font_options_create +#define cairo_font_options_destroy _moz_cairo_font_options_destroy +#define cairo_font_options_equal _moz_cairo_font_options_equal +#define cairo_font_options_get_antialias _moz_cairo_font_options_get_antialias +#define cairo_font_options_get_hint_metrics _moz_cairo_font_options_get_hint_metrics +#define cairo_font_options_get_hint_style _moz_cairo_font_options_get_hint_style +#define cairo_font_options_get_lcd_filter _moz_cairo_font_options_get_lcd_filter +#define cairo_font_options_get_subpixel_order _moz_cairo_font_options_get_subpixel_order +#define cairo_font_options_hash _moz_cairo_font_options_hash +#define cairo_font_options_merge _moz_cairo_font_options_merge +#define cairo_font_options_set_antialias _moz_cairo_font_options_set_antialias +#define cairo_font_options_set_hint_metrics _moz_cairo_font_options_set_hint_metrics +#define cairo_font_options_set_hint_style _moz_cairo_font_options_set_hint_style +#define cairo_font_options_set_lcd_filter _moz_cairo_font_options_set_lcd_filter +#define cairo_font_options_set_subpixel_order _moz_cairo_font_options_set_subpixel_order +#define cairo_font_options_status _moz_cairo_font_options_status +#define cairo_format_stride_for_width _moz_cairo_format_stride_for_width +#define cairo_ft_font_face_create_for_ft_face _moz_cairo_ft_font_face_create_for_ft_face +#define cairo_ft_font_face_create_for_pattern _moz_cairo_ft_font_face_create_for_pattern +#define cairo_ft_font_face_set_synthesize _moz_cairo_ft_font_face_set_synthesize +#define cairo_ft_font_options_substitute _moz_cairo_ft_font_options_substitute +#define cairo_ft_scaled_font_lock_face _moz_cairo_ft_scaled_font_lock_face +#define cairo_ft_scaled_font_unlock_face _moz_cairo_ft_scaled_font_unlock_face +#define cairo_get_antialias _moz_cairo_get_antialias +#define cairo_get_current_point _moz_cairo_get_current_point +#define cairo_get_dash _moz_cairo_get_dash +#define cairo_get_dash_count _moz_cairo_get_dash_count +#define cairo_get_fill_rule _moz_cairo_get_fill_rule +#define cairo_get_font_face _moz_cairo_get_font_face +#define cairo_get_font_matrix _moz_cairo_get_font_matrix +#define cairo_get_font_options _moz_cairo_get_font_options +#define cairo_get_group_target _moz_cairo_get_group_target +#define cairo_get_line_cap _moz_cairo_get_line_cap +#define cairo_get_line_join _moz_cairo_get_line_join +#define cairo_get_line_width _moz_cairo_get_line_width +#define cairo_get_matrix _moz_cairo_get_matrix +#define cairo_get_miter_limit _moz_cairo_get_miter_limit +#define cairo_get_operator _moz_cairo_get_operator +#define cairo_get_reference_count _moz_cairo_get_reference_count +#define cairo_get_scaled_font _moz_cairo_get_scaled_font +#define cairo_get_source _moz_cairo_get_source +#define cairo_get_target _moz_cairo_get_target +#define cairo_get_tolerance _moz_cairo_get_tolerance +#define cairo_get_user_data _moz_cairo_get_user_data +#define cairo_glitz_surface_create _moz_cairo_glitz_surface_create +#define cairo_glyph_allocate _moz_cairo_glyph_allocate +#define cairo_glyph_extents _moz_cairo_glyph_extents +#define cairo_glyph_free _moz_cairo_glyph_free +#define cairo_glyph_path _moz_cairo_glyph_path +#define cairo_has_current_point _moz_cairo_has_current_point +#define cairo_has_show_text_glyphs _moz_cairo_has_show_text_glyphs +#define cairo_identity_matrix _moz_cairo_identity_matrix +#define cairo_image_surface_create _moz_cairo_image_surface_create +#define cairo_image_surface_create_for_data _moz_cairo_image_surface_create_for_data +#define cairo_image_surface_create_from_png _moz_cairo_image_surface_create_from_png +#define cairo_image_surface_create_from_png_stream _moz_cairo_image_surface_create_from_png_stream +#define cairo_image_surface_get_data _moz_cairo_image_surface_get_data +#define cairo_image_surface_get_format _moz_cairo_image_surface_get_format +#define cairo_image_surface_get_height _moz_cairo_image_surface_get_height +#define cairo_image_surface_get_stride _moz_cairo_image_surface_get_stride +#define cairo_image_surface_get_width _moz_cairo_image_surface_get_width +#define cairo_in_clip _moz_cairo_in_clip +#define cairo_in_fill _moz_cairo_in_fill +#define cairo_in_stroke _moz_cairo_in_stroke +#define cairo_line_to _moz_cairo_line_to +#define cairo_mask _moz_cairo_mask +#define cairo_mask_surface _moz_cairo_mask_surface +#define cairo_matrix_init _moz_cairo_matrix_init +#define cairo_matrix_init_identity _moz_cairo_matrix_init_identity +#define cairo_matrix_init_rotate _moz_cairo_matrix_init_rotate +#define cairo_matrix_init_scale _moz_cairo_matrix_init_scale +#define cairo_matrix_init_translate _moz_cairo_matrix_init_translate +#define cairo_matrix_invert _moz_cairo_matrix_invert +#define cairo_matrix_multiply _moz_cairo_matrix_multiply +#define cairo_matrix_rotate _moz_cairo_matrix_rotate +#define cairo_matrix_scale _moz_cairo_matrix_scale +#define cairo_matrix_transform_distance _moz_cairo_matrix_transform_distance +#define cairo_matrix_transform_point _moz_cairo_matrix_transform_point +#define cairo_matrix_translate _moz_cairo_matrix_translate +#define cairo_move_to _moz_cairo_move_to +#define cairo_new_path _moz_cairo_new_path +#define cairo_new_sub_path _moz_cairo_new_sub_path +#define cairo_null_surface_create _moz_cairo_null_surface_create +#define cairo_os2_fini _moz_cairo_os2_fini +#define cairo_os2_init _moz_cairo_os2_init +#define cairo_os2_surface_create _moz_cairo_os2_surface_create +#define cairo_os2_surface_create_for_window _moz_cairo_os2_surface_create_for_window +#define cairo_os2_surface_get_hps _moz_cairo_os2_surface_get_hps +#define cairo_os2_surface_get_manual_window_refresh _moz_cairo_os2_surface_get_manual_window_refresh +#define cairo_os2_surface_refresh_window _moz_cairo_os2_surface_refresh_window +#define cairo_os2_surface_set_hps _moz_cairo_os2_surface_set_hps +#define cairo_os2_surface_set_hwnd _moz_cairo_os2_surface_set_hwnd +#define cairo_os2_surface_set_manual_window_refresh _moz_cairo_os2_surface_set_manual_window_refresh +#define cairo_os2_surface_set_size _moz_cairo_os2_surface_set_size +#define cairo_paint _moz_cairo_paint +#define cairo_paint_with_alpha _moz_cairo_paint_with_alpha +#define cairo_path_destroy _moz_cairo_path_destroy +#define cairo_path_extents _moz_cairo_path_extents +#define cairo_pattern_add_color_stop_rgb _moz_cairo_pattern_add_color_stop_rgb +#define cairo_pattern_add_color_stop_rgba _moz_cairo_pattern_add_color_stop_rgba +#define cairo_pattern_create_for_surface _moz_cairo_pattern_create_for_surface +#define cairo_pattern_create_linear _moz_cairo_pattern_create_linear +#define cairo_pattern_create_radial _moz_cairo_pattern_create_radial +#define cairo_pattern_create_rgb _moz_cairo_pattern_create_rgb +#define cairo_pattern_create_rgba _moz_cairo_pattern_create_rgba +#define cairo_pattern_destroy _moz_cairo_pattern_destroy +#define cairo_pattern_get_color_stop_count _moz_cairo_pattern_get_color_stop_count +#define cairo_pattern_get_color_stop_rgba _moz_cairo_pattern_get_color_stop_rgba +#define cairo_pattern_get_extend _moz_cairo_pattern_get_extend +#define cairo_pattern_get_filter _moz_cairo_pattern_get_filter +#define cairo_pattern_get_linear_points _moz_cairo_pattern_get_linear_points +#define cairo_pattern_get_matrix _moz_cairo_pattern_get_matrix +#define cairo_pattern_get_radial_circles _moz_cairo_pattern_get_radial_circles +#define cairo_pattern_get_reference_count _moz_cairo_pattern_get_reference_count +#define cairo_pattern_get_rgba _moz_cairo_pattern_get_rgba +#define cairo_pattern_get_surface _moz_cairo_pattern_get_surface +#define cairo_pattern_get_type _moz_cairo_pattern_get_type +#define cairo_pattern_get_user_data _moz_cairo_pattern_get_user_data +#define cairo_pattern_reference _moz_cairo_pattern_reference +#define cairo_pattern_set_extend _moz_cairo_pattern_set_extend +#define cairo_pattern_set_filter _moz_cairo_pattern_set_filter +#define cairo_pattern_set_matrix _moz_cairo_pattern_set_matrix +#define cairo_pattern_set_user_data _moz_cairo_pattern_set_user_data +#define cairo_pattern_status _moz_cairo_pattern_status +#define cairo_pdf_get_versions _moz_cairo_pdf_get_versions +#define cairo_pdf_surface_create _moz_cairo_pdf_surface_create +#define cairo_pdf_surface_create_for_stream _moz_cairo_pdf_surface_create_for_stream +#define cairo_pdf_surface_restrict_to_version _moz_cairo_pdf_surface_restrict_to_version +#define cairo_pdf_surface_set_size _moz_cairo_pdf_surface_set_size +#define cairo_pdf_version_to_string _moz_cairo_pdf_version_to_string +#define cairo_pop_group _moz_cairo_pop_group +#define cairo_pop_group_to_source _moz_cairo_pop_group_to_source +#define cairo_ps_get_levels _moz_cairo_ps_get_levels +#define cairo_ps_level_to_string _moz_cairo_ps_level_to_string +#define cairo_ps_surface_create _moz_cairo_ps_surface_create +#define cairo_ps_surface_create_for_stream _moz_cairo_ps_surface_create_for_stream +#define cairo_ps_surface_dsc_begin_page_setup _moz_cairo_ps_surface_dsc_begin_page_setup +#define cairo_ps_surface_dsc_begin_setup _moz_cairo_ps_surface_dsc_begin_setup +#define cairo_ps_surface_dsc_comment _moz_cairo_ps_surface_dsc_comment +#define cairo_ps_surface_get_eps _moz_cairo_ps_surface_get_eps +#define cairo_ps_surface_restrict_to_level _moz_cairo_ps_surface_restrict_to_level +#define cairo_ps_surface_set_eps _moz_cairo_ps_surface_set_eps +#define cairo_ps_surface_set_size _moz_cairo_ps_surface_set_size +#define cairo_push_group _moz_cairo_push_group +#define cairo_push_group_with_content _moz_cairo_push_group_with_content +#define cairo_qpainter_surface_create _moz_cairo_qpainter_surface_create +#define cairo_qpainter_surface_create_with_qimage _moz_cairo_qpainter_surface_create_with_qimage +#define cairo_qpainter_surface_create_with_qpixmap _moz_cairo_qpainter_surface_create_with_qpixmap +#define cairo_qpainter_surface_get_image _moz_cairo_qpainter_surface_get_image +#define cairo_qpainter_surface_get_qimage _moz_cairo_qpainter_surface_get_qimage +#define cairo_qpainter_surface_get_qpainter _moz_cairo_qpainter_surface_get_qpainter +#define cairo_quartz_font_face_create_for_atsu_font_id _moz_cairo_quartz_font_face_create_for_atsu_font_id +#define cairo_quartz_font_face_create_for_cgfont _moz_cairo_quartz_font_face_create_for_cgfont +#define cairo_quartz_image_surface_create _moz_cairo_quartz_image_surface_create +#define cairo_quartz_image_surface_get_image _moz_cairo_quartz_image_surface_get_image +#define cairo_quartz_surface_create _moz_cairo_quartz_surface_create +#define cairo_quartz_surface_create_for_cg_context _moz_cairo_quartz_surface_create_for_cg_context +#define cairo_quartz_surface_get_cg_context _moz_cairo_quartz_surface_get_cg_context +#define cairo_quartz_surface_get_image _moz_cairo_quartz_surface_get_image +#define cairo_recording_surface_create _moz_cairo_recording_surface_create +#define cairo_recording_surface_ink_extents _moz_cairo_recording_surface_ink_extents +#define cairo_rectangle _moz_cairo_rectangle +#define cairo_rectangle_list_destroy _moz_cairo_rectangle_list_destroy +#define cairo_reference _moz_cairo_reference +#define cairo_region_contains_point _moz_cairo_region_contains_point +#define cairo_region_contains_rectangle _moz_cairo_region_contains_rectangle +#define cairo_region_copy _moz_cairo_region_copy +#define cairo_region_create _moz_cairo_region_create +#define cairo_region_create_rectangle _moz_cairo_region_create_rectangle +#define cairo_region_create_rectangles _moz_cairo_region_create_rectangles +#define cairo_region_destroy _moz_cairo_region_destroy +#define cairo_region_equal _moz_cairo_region_equal +#define cairo_region_get_extents _moz_cairo_region_get_extents +#define cairo_region_get_rectangle _moz_cairo_region_get_rectangle +#define cairo_region_intersect _moz_cairo_region_intersect +#define cairo_region_intersect_rectangle _moz_cairo_region_intersect_rectangle +#define cairo_region_is_empty _moz_cairo_region_is_empty +#define cairo_region_num_rectangles _moz_cairo_region_num_rectangles +#define cairo_region_reference _moz_cairo_region_reference +#define cairo_region_status _moz_cairo_region_status +#define cairo_region_subtract _moz_cairo_region_subtract +#define cairo_region_subtract_rectangle _moz_cairo_region_subtract_rectangle +#define cairo_region_translate _moz_cairo_region_translate +#define cairo_region_union _moz_cairo_region_union +#define cairo_region_union_rectangle _moz_cairo_region_union_rectangle +#define cairo_region_xor _moz_cairo_region_xor +#define cairo_region_xor_rectangle _moz_cairo_region_xor_rectangle +#define cairo_rel_curve_to _moz_cairo_rel_curve_to +#define cairo_rel_line_to _moz_cairo_rel_line_to +#define cairo_rel_move_to _moz_cairo_rel_move_to +#define cairo_release_device _moz_cairo_release_device +#define cairo_reset_clip _moz_cairo_reset_clip +#define cairo_restore _moz_cairo_restore +#define cairo_rotate _moz_cairo_rotate +#define cairo_save _moz_cairo_save +#define cairo_scale _moz_cairo_scale +#define cairo_scaled_font_create _moz_cairo_scaled_font_create +#define cairo_scaled_font_destroy _moz_cairo_scaled_font_destroy +#define cairo_scaled_font_extents _moz_cairo_scaled_font_extents +#define cairo_scaled_font_get_ctm _moz_cairo_scaled_font_get_ctm +#define cairo_scaled_font_get_font_face _moz_cairo_scaled_font_get_font_face +#define cairo_scaled_font_get_font_matrix _moz_cairo_scaled_font_get_font_matrix +#define cairo_scaled_font_get_font_options _moz_cairo_scaled_font_get_font_options +#define cairo_scaled_font_get_hint_metrics _moz_cairo_scaled_font_get_hint_metrics +#define cairo_scaled_font_get_reference_count _moz_cairo_scaled_font_get_reference_count +#define cairo_scaled_font_get_scale_matrix _moz_cairo_scaled_font_get_scale_matrix +#define cairo_scaled_font_get_type _moz_cairo_scaled_font_get_type +#define cairo_scaled_font_get_user_data _moz_cairo_scaled_font_get_user_data +#define cairo_scaled_font_glyph_extents _moz_cairo_scaled_font_glyph_extents +#define cairo_scaled_font_reference _moz_cairo_scaled_font_reference +#define cairo_scaled_font_set_user_data _moz_cairo_scaled_font_set_user_data +#define cairo_scaled_font_status _moz_cairo_scaled_font_status +#define cairo_scaled_font_text_extents _moz_cairo_scaled_font_text_extents +#define cairo_scaled_font_text_to_glyphs _moz_cairo_scaled_font_text_to_glyphs +#define cairo_select_font_face _moz_cairo_select_font_face +#define cairo_set_antialias _moz_cairo_set_antialias +#define cairo_set_dash _moz_cairo_set_dash +#define cairo_set_fill_rule _moz_cairo_set_fill_rule +#define cairo_set_font_face _moz_cairo_set_font_face +#define cairo_set_font_matrix _moz_cairo_set_font_matrix +#define cairo_set_font_options _moz_cairo_set_font_options +#define cairo_set_font_size _moz_cairo_set_font_size +#define cairo_set_line_cap _moz_cairo_set_line_cap +#define cairo_set_line_join _moz_cairo_set_line_join +#define cairo_set_line_width _moz_cairo_set_line_width +#define cairo_set_matrix _moz_cairo_set_matrix +#define cairo_set_miter_limit _moz_cairo_set_miter_limit +#define cairo_set_operator _moz_cairo_set_operator +#define cairo_set_scaled_font _moz_cairo_set_scaled_font +#define cairo_set_source _moz_cairo_set_source +#define cairo_set_source_rgb _moz_cairo_set_source_rgb +#define cairo_set_source_rgba _moz_cairo_set_source_rgba +#define cairo_set_source_surface _moz_cairo_set_source_surface +#define cairo_set_tolerance _moz_cairo_set_tolerance +#define cairo_set_user_data _moz_cairo_set_user_data +#define cairo_show_glyphs _moz_cairo_show_glyphs +#define cairo_show_page _moz_cairo_show_page +#define cairo_show_text _moz_cairo_show_text +#define cairo_show_text_glyphs _moz_cairo_show_text_glyphs +#define cairo_status _moz_cairo_status +#define cairo_status_to_string _moz_cairo_status_to_string +#define cairo_stroke _moz_cairo_stroke +#define cairo_stroke_extents _moz_cairo_stroke_extents +#define cairo_stroke_preserve _moz_cairo_stroke_preserve +#define cairo_stroke_to_path _moz_cairo_stroke_to_path +#define cairo_surface_attach_snapshot _moz_cairo_surface_attach_snapshot +#define cairo_surface_copy_page _moz_cairo_surface_copy_page +#define cairo_surface_create_for_rectangle _moz_cairo_surface_create_for_rectangle +#define cairo_surface_create_similar _moz_cairo_surface_create_similar +#define cairo_surface_detach_snapshot _moz_cairo_surface_detach_snapshot +#define cairo_surface_destroy _moz_cairo_surface_destroy +#define cairo_surface_finish _moz_cairo_surface_finish +#define cairo_surface_flush _moz_cairo_surface_flush +#define cairo_surface_get_content _moz_cairo_surface_get_content +#define cairo_surface_get_device _moz_cairo_surface_get_device +#define cairo_surface_get_device_offset _moz_cairo_surface_get_device_offset +#define cairo_surface_get_fallback_resolution _moz_cairo_surface_get_fallback_resolution +#define cairo_surface_get_font_options _moz_cairo_surface_get_font_options +#define cairo_surface_get_mime_data _moz_cairo_surface_get_mime_data +#define cairo_surface_get_reference_count _moz_cairo_surface_get_reference_count +#define cairo_surface_get_subpixel_antialiasing _moz_cairo_surface_get_subpixel_antialiasing +#define cairo_surface_get_type _moz_cairo_surface_get_type +#define cairo_surface_get_user_data _moz_cairo_surface_get_user_data +#define cairo_surface_has_show_text_glyphs _moz_cairo_surface_has_show_text_glyphs +#define cairo_surface_mark_dirty _moz_cairo_surface_mark_dirty +#define cairo_surface_mark_dirty_rectangle _moz_cairo_surface_mark_dirty_rectangle +#define cairo_surface_reference _moz_cairo_surface_reference +#define cairo_surface_set_device_offset _moz_cairo_surface_set_device_offset +#define cairo_surface_set_fallback_resolution _moz_cairo_surface_set_fallback_resolution +#define cairo_surface_set_mime_data _moz_cairo_surface_set_mime_data +#define cairo_surface_set_subpixel_antialiasing _moz_cairo_surface_set_subpixel_antialiasing +#define cairo_surface_set_user_data _moz_cairo_surface_set_user_data +#define cairo_surface_show_page _moz_cairo_surface_show_page +#define cairo_surface_status _moz_cairo_surface_status +#define cairo_surface_write_to_png _moz_cairo_surface_write_to_png +#define cairo_surface_write_to_png_stream _moz_cairo_surface_write_to_png_stream +#define cairo_svg_get_versions _moz_cairo_svg_get_versions +#define cairo_svg_surface_create _moz_cairo_svg_surface_create +#define cairo_svg_surface_create_for_stream _moz_cairo_svg_surface_create_for_stream +#define cairo_svg_surface_restrict_to_version _moz_cairo_svg_surface_restrict_to_version +#define cairo_svg_version_to_string _moz_cairo_svg_version_to_string +#define cairo_tee_surface_add _moz_cairo_tee_surface_add +#define cairo_tee_surface_create _moz_cairo_tee_surface_create +#define cairo_tee_surface_index _moz_cairo_tee_surface_index +#define cairo_tee_surface_remove _moz_cairo_tee_surface_remove +#define cairo_text_cluster_allocate _moz_cairo_text_cluster_allocate +#define cairo_text_cluster_free _moz_cairo_text_cluster_free +#define cairo_text_extents _moz_cairo_text_extents +#define cairo_text_path _moz_cairo_text_path +#define cairo_toy_font_face_create _moz_cairo_toy_font_face_create +#define cairo_toy_font_face_get_family _moz_cairo_toy_font_face_get_family +#define cairo_toy_font_face_get_slant _moz_cairo_toy_font_face_get_slant +#define cairo_toy_font_face_get_weight _moz_cairo_toy_font_face_get_weight +#define cairo_transform _moz_cairo_transform +#define cairo_translate _moz_cairo_translate +#define cairo_user_font_face_create _moz_cairo_user_font_face_create +#define cairo_user_font_face_get_init_func _moz_cairo_user_font_face_get_init_func +#define cairo_user_font_face_get_render_glyph_func _moz_cairo_user_font_face_get_render_glyph_func +#define cairo_user_font_face_get_text_to_glyphs_func _moz_cairo_user_font_face_get_text_to_glyphs_func +#define cairo_user_font_face_get_unicode_to_glyph_func _moz_cairo_user_font_face_get_unicode_to_glyph_func +#define cairo_user_font_face_set_init_func _moz_cairo_user_font_face_set_init_func +#define cairo_user_font_face_set_render_glyph_func _moz_cairo_user_font_face_set_render_glyph_func +#define cairo_user_font_face_set_text_to_glyphs_func _moz_cairo_user_font_face_set_text_to_glyphs_func +#define cairo_user_font_face_set_unicode_to_glyph_func _moz_cairo_user_font_face_set_unicode_to_glyph_func +#define cairo_user_to_device _moz_cairo_user_to_device +#define cairo_user_to_device_distance _moz_cairo_user_to_device_distance +#define cairo_version _moz_cairo_version +#define cairo_version_string _moz_cairo_version_string +#define cairo_win32_get_dc_with_clip _moz_cairo_win32_get_dc_with_clip +#define cairo_win32_get_system_text_quality _moz_cairo_win32_get_system_text_quality +#define cairo_win32_font_face_create_for_hfont _moz_cairo_win32_font_face_create_for_hfont +#define cairo_win32_font_face_create_for_logfontw _moz_cairo_win32_font_face_create_for_logfontw +#define cairo_win32_font_face_create_for_logfontw_hfont _moz_cairo_win32_font_face_create_for_logfontw_hfont +#define cairo_win32_printing_surface_create _moz_cairo_win32_printing_surface_create +#define cairo_win32_scaled_font_done_font _moz_cairo_win32_scaled_font_done_font +#define cairo_win32_scaled_font_get_device_to_logical _moz_cairo_win32_scaled_font_get_device_to_logical +#define cairo_win32_scaled_font_get_logical_to_device _moz_cairo_win32_scaled_font_get_logical_to_device +#define cairo_win32_scaled_font_get_metrics_factor _moz_cairo_win32_scaled_font_get_metrics_factor +#define cairo_win32_scaled_font_select_font _moz_cairo_win32_scaled_font_select_font +#define cairo_win32_surface_create _moz_cairo_win32_surface_create +#define cairo_win32_surface_create_with_format _moz_cairo_win32_surface_create_with_format +#define cairo_win32_surface_create_with_d3dsurface9 _moz_cairo_win32_surface_create_with_d3dsurface9 +#define cairo_win32_surface_create_with_ddb _moz_cairo_win32_surface_create_with_ddb +#define cairo_win32_surface_create_with_dib _moz_cairo_win32_surface_create_with_dib +#define cairo_win32_surface_get_dc _moz_cairo_win32_surface_get_dc +#define cairo_win32_surface_get_image _moz_cairo_win32_surface_get_image +#define cairo_win32_surface_get_size _moz_cairo_win32_surface_get_size +#define cairo_win32_surface_set_can_convert_to_dib _moz_cairo_win32_surface_set_can_convert_to_dib +#define cairo_xcb_surface_create _moz_cairo_xcb_surface_create +#define cairo_xcb_surface_create_for_bitmap _moz_cairo_xcb_surface_create_for_bitmap +#define cairo_xcb_surface_create_with_xrender_format _moz_cairo_xcb_surface_create_with_xrender_format +#define cairo_xcb_surface_set_size _moz_cairo_xcb_surface_set_size +#define cairo_xlib_surface_create _moz_cairo_xlib_surface_create +#define cairo_xlib_surface_create_for_bitmap _moz_cairo_xlib_surface_create_for_bitmap +#define cairo_xlib_surface_create_with_xrender_format _moz_cairo_xlib_surface_create_with_xrender_format +#define cairo_xlib_surface_get_depth _moz_cairo_xlib_surface_get_depth +#define cairo_xlib_surface_get_display _moz_cairo_xlib_surface_get_display +#define cairo_xlib_surface_get_drawable _moz_cairo_xlib_surface_get_drawable +#define cairo_xlib_surface_get_height _moz_cairo_xlib_surface_get_height +#define cairo_xlib_surface_get_screen _moz_cairo_xlib_surface_get_screen +#define cairo_xlib_surface_get_visual _moz_cairo_xlib_surface_get_visual +#define cairo_xlib_surface_get_width _moz_cairo_xlib_surface_get_width +#define cairo_xlib_surface_get_xrender_format _moz_cairo_xlib_surface_get_xrender_format +#define cairo_xlib_surface_set_drawable _moz_cairo_xlib_surface_set_drawable +#define cairo_xlib_surface_set_size _moz_cairo_xlib_surface_set_size diff --git a/gfx/cairo/cairo/src/cairo-rtree-private.h b/gfx/cairo/cairo/src/cairo-rtree-private.h new file mode 100644 index 0000000000..27806cab60 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-rtree-private.h @@ -0,0 +1,142 @@ +/* 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_RTREE_PRIVATE_H +#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-inline.h" + +enum { + CAIRO_RTREE_NODE_AVAILABLE, + CAIRO_RTREE_NODE_DIVIDED, + CAIRO_RTREE_NODE_OCCUPIED, +}; + +typedef struct _cairo_rtree_node { + struct _cairo_rtree_node *children[4], *parent; + cairo_list_t link; + uint16_t pinned; + uint16_t state; + uint16_t x, y; + uint16_t width, height; +} cairo_rtree_node_t; + +typedef struct _cairo_rtree { + cairo_rtree_node_t root; + int min_size; + 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; + +cairo_private cairo_rtree_node_t * +_cairo_rtree_node_create (cairo_rtree_t *rtree, + cairo_rtree_node_t *parent, + int x, + int y, + int width, + int height); + +cairo_private cairo_status_t +_cairo_rtree_node_insert (cairo_rtree_t *rtree, + cairo_rtree_node_t *node, + int width, + int height, + cairo_rtree_node_t **out); + +cairo_private void +_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node); + +cairo_private void +_cairo_rtree_init (cairo_rtree_t *rtree, + int width, + int height, + int min_size, + int node_size, + void (*destroy)(cairo_rtree_node_t *)); + +cairo_private cairo_int_status_t +_cairo_rtree_insert (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out); + +cairo_private cairo_int_status_t +_cairo_rtree_evict_random (cairo_rtree_t *rtree, + int width, + 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; + } + + return node; +} + +cairo_private void +_cairo_rtree_unpin (cairo_rtree_t *rtree); + +cairo_private void +_cairo_rtree_reset (cairo_rtree_t *rtree); + +cairo_private void +_cairo_rtree_fini (cairo_rtree_t *rtree); + +#endif /* CAIRO_RTREE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-rtree.c b/gfx/cairo/cairo/src/cairo-rtree.c new file mode 100644 index 0000000000..dbc040929a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-rtree.c @@ -0,0 +1,388 @@ +/* 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 + * + */ + +#include "cairoint.h" + +#include "cairo-error-private.h" +#include "cairo-rtree-private.h" + +cairo_rtree_node_t * +_cairo_rtree_node_create (cairo_rtree_t *rtree, + cairo_rtree_node_t *parent, + int x, + int y, + int width, + int height) +{ + cairo_rtree_node_t *node; + + node = _cairo_freepool_alloc (&rtree->node_freepool); + if (node == NULL) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + + node->children[0] = NULL; + node->parent = parent; + node->state = CAIRO_RTREE_NODE_AVAILABLE; + node->pinned = FALSE; + node->x = x; + node->y = y; + node->width = width; + node->height = height; + + cairo_list_add (&node->link, &rtree->available); + + return node; +} + +void +_cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + int i; + + cairo_list_del (&node->link); + + if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { + rtree->destroy (node); + } else { + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + } + + _cairo_freepool_free (&rtree->node_freepool, node); +} + +void +_cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node) +{ + int i; + + do { + assert (node->state == CAIRO_RTREE_NODE_DIVIDED); + + for (i = 0; i < 4 && node->children[i] != NULL; i++) + if (node->children[i]->state != CAIRO_RTREE_NODE_AVAILABLE) + return; + + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + + node->children[0] = NULL; + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + } while ((node = node->parent) != NULL); +} + +cairo_status_t +_cairo_rtree_node_insert (cairo_rtree_t *rtree, + cairo_rtree_node_t *node, + int width, + int height, + cairo_rtree_node_t **out) +{ + int w, h, i; + + assert (node->state == CAIRO_RTREE_NODE_AVAILABLE); + assert (node->pinned == FALSE); + + if (node->width - width > rtree->min_size || + node->height - height > rtree->min_size) + { + w = node->width - width; + h = node->height - height; + + i = 0; + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x, node->y, + width, height); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + + if (w > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x + width, + node->y, + w, height); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + } + + if (h > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x, + node->y + height, + width, h); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + + if (w > rtree->min_size) { + node->children[i] = _cairo_rtree_node_create (rtree, node, + node->x + width, + node->y + height, + w, h); + if (unlikely (node->children[i] == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + i++; + } + } + + if (i < 4) + node->children[i] = NULL; + + node->state = CAIRO_RTREE_NODE_DIVIDED; + cairo_list_move (&node->link, &rtree->evictable); + node = node->children[0]; + } + + node->state = CAIRO_RTREE_NODE_OCCUPIED; + cairo_list_move (&node->link, &rtree->evictable); + *out = node; + + return CAIRO_STATUS_SUCCESS; +} + +void +_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); + + _cairo_rtree_node_collapse (rtree, node->parent); +} + +cairo_int_status_t +_cairo_rtree_insert (cairo_rtree_t *rtree, + int width, + int height, + cairo_rtree_node_t **out) +{ + cairo_rtree_node_t *node; + + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->available, link) + { + if (node->width >= width && node->height >= height) + return _cairo_rtree_node_insert (rtree, node, width, height, out); + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +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 +} + +cairo_int_status_t +_cairo_rtree_evict_random (cairo_rtree_t *rtree, + int width, + 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) { + node = node->parent; + while (node && ! node->pinned) { + node->pinned = 1; + cairo_list_move (&node->link, &tmp_pinned); + node = node->parent; + } + } + + cnt = 0; + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->evictable, link) + { + if (node->width >= width && node->height >= height) + cnt++; + } + + if (cnt == 0) + goto out; + + cnt = hars_petruska_f54_1_random () % cnt; + cairo_list_foreach_entry (node, cairo_rtree_node_t, + &rtree->evictable, link) + { + if (node->width >= width && node->height >= height && cnt-- == 0) { + if (node->state == CAIRO_RTREE_NODE_OCCUPIED) { + rtree->destroy (node); + } else { + for (i = 0; i < 4 && node->children[i] != NULL; i++) + _cairo_rtree_node_destroy (rtree, node->children[i]); + node->children[0] = NULL; + } + + node->state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_move (&node->link, &rtree->available); + + *out = node; + ret = CAIRO_STATUS_SUCCESS; + break; + } + } + +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) +{ + 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); + } +} + +void +_cairo_rtree_init (cairo_rtree_t *rtree, + int width, + int height, + int min_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); + + cairo_list_init (&rtree->available); + cairo_list_init (&rtree->pinned); + cairo_list_init (&rtree->evictable); + + rtree->min_size = min_size; + rtree->destroy = destroy; + + memset (&rtree->root, 0, sizeof (rtree->root)); + rtree->root.width = width; + rtree->root.height = height; + rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; + cairo_list_add (&rtree->root.link, &rtree->available); +} + +void +_cairo_rtree_reset (cairo_rtree_t *rtree) +{ + int i; + + if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) { + 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]); + rtree->root.children[0] = NULL; + } + + cairo_list_init (&rtree->available); + cairo_list_init (&rtree->evictable); + cairo_list_init (&rtree->pinned); + + rtree->root.state = CAIRO_RTREE_NODE_AVAILABLE; + rtree->root.pinned = FALSE; + 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) { + 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]); + } + + _cairo_freepool_fini (&rtree->node_freepool); +} diff --git a/gfx/cairo/cairo/src/cairo-scaled-font-private.h b/gfx/cairo/cairo/src/cairo-scaled-font-private.h new file mode 100644 index 0000000000..317d211979 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-scaled-font-private.h @@ -0,0 +1,186 @@ +/* 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_SCALED_FONT_PRIVATE_H +#define CAIRO_SCALED_FONT_PRIVATE_H + +#include "cairo.h" + +#include "cairo-types-private.h" +#include "cairo-list-private.h" +#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 { + /* For most cairo objects, the rule for multiple threads is that + * the user is responsible for any locking if the same object is + * manipulated from multiple threads simultaneously. + * + * However, with the caching that cairo does for scaled fonts, a + * user can easily end up with the same cairo_scaled_font object + * being manipulated from multiple threads without the user ever + * being aware of this, (and in fact, unable to control it). + * + * So, as a special exception, the cairo implementation takes care + * of all locking needed for cairo_scaled_font_t. Most of what is + * in the scaled font is immutable, (which is what allows for the + * sharing in the first place). The things that are modified and + * the locks protecting them are as follows: + * + * 1. The reference count (scaled_font->ref_count) + * + * Modifications to the reference count are protected by the + * _cairo_scaled_font_map_mutex. This is because the reference + * count of a scaled font is intimately related with the font + * map itself, (and the magic holdovers array). + * + * 2. The cache of glyphs (scaled_font->glyphs) + * 3. The backend private data (scaled_font->surface_backend, + * scaled_font->surface_private) + * + * Modifications to these fields are protected with locks on + * scaled_font->mutex in the generic scaled_font code. + */ + + cairo_hash_entry_t hash_entry; + + /* useful bits for _cairo_scaled_font_nil */ + cairo_status_t status; + cairo_reference_count_t ref_count; + cairo_user_data_array_t user_data; + + cairo_font_face_t *original_font_face; /* may be NULL */ + + /* hash key members */ + cairo_font_face_t *font_face; /* may be NULL */ + cairo_matrix_t font_matrix; /* font space => user space */ + cairo_matrix_t ctm; /* user space => device space */ + cairo_font_options_t options; + + unsigned int placeholder : 1; /* protected by fontmap mutex */ + unsigned int holdover : 1; + unsigned int finished : 1; + + /* "live" scaled_font members */ + cairo_matrix_t scale; /* font space => device space */ + cairo_matrix_t scale_inverse; /* device space => font space */ + double max_scale; /* maximum x/y expansion of scale */ + cairo_font_extents_t extents; /* user space */ + cairo_font_extents_t fs_extents; /* font space */ + + /* The mutex protects modification to all subsequent fields. */ + cairo_mutex_t mutex; + + cairo_hash_table_t *glyphs; + cairo_list_t glyph_pages; + cairo_bool_t cache_frozen; + cairo_bool_t global_cache_frozen; + + 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_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 new file mode 100644 index 0000000000..e7809f03aa --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-scaled-font-subsets-private.h @@ -0,0 +1,740 @@ +/* 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + */ + +#ifndef CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H +#define CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H + +#include "cairoint.h" + +#if CAIRO_HAS_FONT_SUBSET + +typedef struct _cairo_scaled_font_subsets_glyph { + unsigned int font_id; + unsigned int subset_id; + 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; + uint32_t unicode; +} cairo_scaled_font_subsets_glyph_t; + +/** + * _cairo_scaled_font_subsets_create_scaled: + * + * Create a new #cairo_scaled_font_subsets_t object which can be used + * to create subsets of any number of #cairo_scaled_font_t + * objects. This allows the (arbitrarily large and sparse) glyph + * indices of a #cairo_scaled_font_t to be mapped to one or more font + * subsets with glyph indices packed into the range + * [0 .. max_glyphs_per_subset). + * + * Return value: a pointer to the newly creates font subsets. The + * caller owns this object and should call + * _cairo_scaled_font_subsets_destroy() when done with it. + **/ +cairo_private cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_scaled (void); + +/** + * _cairo_scaled_font_subsets_create_simple: + * + * Create a new #cairo_scaled_font_subsets_t object which can be used + * to create font subsets suitable for embedding as Postscript or PDF + * simple fonts. + * + * Glyphs with an outline path available will be mapped to one font + * subset for each font face. Glyphs from bitmap fonts will mapped to + * separate font subsets for each #cairo_scaled_font_t object. + * + * The maximum number of glyphs per subset is 256. Each subset + * reserves the first glyph for the .notdef glyph. + * + * Return value: a pointer to the newly creates font subsets. The + * caller owns this object and should call + * _cairo_scaled_font_subsets_destroy() when done with it. + **/ +cairo_private cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_simple (void); + +/** + * _cairo_scaled_font_subsets_create_composite: + * + * Create a new #cairo_scaled_font_subsets_t object which can be used + * to create font subsets suitable for embedding as Postscript or PDF + * composite fonts. + * + * Glyphs with an outline path available will be mapped to one font + * subset for each font face. Each unscaled subset has a maximum of + * 65536 glyphs except for Type1 fonts which have a maximum of 256 glyphs. + * + * Glyphs from bitmap fonts will mapped to separate font subsets for + * each #cairo_scaled_font_t object. Each unscaled subset has a maximum + * of 256 glyphs. + * + * Each subset reserves the first glyph for the .notdef glyph. + * + * Return value: a pointer to the newly creates font subsets. The + * caller owns this object and should call + * _cairo_scaled_font_subsets_destroy() when done with it. + **/ +cairo_private cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_composite (void); + +/** + * _cairo_scaled_font_subsets_destroy: + * @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed + * + * Destroys @font_subsets and all resources associated with it. + **/ +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 + * @scaled_font: the font of the glyph to be mapped + * @scaled_font_glyph_index: the index of the glyph to be mapped + * @utf8: a string of text encoded in UTF-8 + * @utf8_len: length of @utf8 in bytes + * @subset_glyph_ret: return structure containing subset font and glyph id + * + * Map a glyph from a #cairo_scaled_font to a new index within a + * subset of that font. The mapping performed is from the tuple: + * + * (scaled_font, scaled_font_glyph_index) + * + * to the tuple: + * + * (font_id, subset_id, subset_glyph_index) + * + * This mapping is 1:1. If the input tuple has previously mapped, the + * the output tuple previously returned will be returned again. + * + * Otherwise, the return tuple will be constructed as follows: + * + * 1) There is a 1:1 correspondence between the input scaled_font + * value and the output font_id value. If no mapping has been + * previously performed with the scaled_font value then the + * smallest unused font_id value will be returned. + * + * 2) Within the set of output tuples of the same font_id value the + * smallest value of subset_id will be returned such that + * subset_glyph_index does not exceed max_glyphs_per_subset (as + * passed to _cairo_scaled_font_subsets_create()) and that the + * resulting tuple is unique. + * + * 3) The smallest value of subset_glyph_index is returned such that + * the resulting tuple is unique. + * + * The net result is that any #cairo_scaled_font_t will be represented + * by one or more font subsets. Each subset is effectively a tuple of + * (scaled_font, font_id, subset_id) and within each subset there + * exists a mapping of scaled_glyph_font_index to subset_glyph_index. + * + * This final description of a font subset is the same representation + * used by #cairo_scaled_font_subset_t as provided by + * _cairo_scaled_font_subsets_foreach. + * + * @utf8 and @utf8_len specify a string of unicode characters that the + * glyph @scaled_font_glyph_index maps to. If @utf8_is_mapped in + * @subset_glyph_ret is %TRUE, the font subsetting will (where index to + * unicode mapping is supported) ensure that @scaled_font_glyph_index + * maps to @utf8. If @utf8_is_mapped is %FALSE, + * @scaled_font_glyph_index has already been mapped to a different + * unicode string. + * + * The returned values in the #cairo_scaled_font_subsets_glyph_t struct are: + * + * @font_id: The font ID of the mapped glyph + * @subset_id : The subset ID of the mapped glyph within the @font_id + * @subset_glyph_index: The index of the mapped glyph within the @subset_id subset + * @is_scaled: If true, the mapped glyph is from a bitmap font, and separate font + * subset is created for each font scale used. If false, the outline of the mapped glyph + * is available. One font subset for each font face is created. + * @x_advance, @y_advance: When @is_scaled is true, @x_advance and @y_advance contain + * the x and y advance for the mapped glyph in device space. + * When @is_scaled is false, @x_advance and @y_advance contain the x and y advance for + * the the mapped glyph from an unhinted 1 point font. + * @utf8_is_mapped: If true the utf8 string provided to _cairo_scaled_font_subsets_map_glyph() + * is (or already was) the utf8 string mapped to this glyph. If false the glyph is already + * mapped to a different utf8 string. + * @unicode: the unicode character mapped to this glyph by the font backend. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero + * value indicating an error. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_t *scaled_font, + unsigned long scaled_font_glyph_index, + const char * utf8, + int utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph_ret); + +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_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 + * + * Iterate over each unique scaled font subset as created by calls to + * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by + * unique pairs of (font_id, subset_id) as returned by + * _cairo_scaled_font_subsets_map_glyph(). + * + * For each subset, @font_subset_callback will be called and will be + * provided with both a #cairo_scaled_font_subset_t object containing + * all the glyphs in the subset as well as the value of @closure. + * + * The #cairo_scaled_font_subset_t object contains the scaled_font, + * the font_id, and the subset_id corresponding to all glyphs + * belonging to the subset. In addition, it contains an array providing + * a mapping between subset glyph indices and the original scaled font + * glyph indices. + * + * The index of the array corresponds to subset_glyph_index values + * returned by _cairo_scaled_font_subsets_map_glyph() while the + * values of the array correspond to the scaled_font_glyph_index + * values passed as input to the same function. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero + * value indicating an error. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure); + +/** + * _cairo_scaled_font_subsets_foreach_unscaled: + * @font_subsets: a #cairo_scaled_font_subsets_t + * @font_subset_callback: a function to be called for each font subset + * @closure: closure data for the callback function + * + * Iterate over each unique unscaled font subset as created by calls to + * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by + * unique pairs of (font_id, subset_id) as returned by + * _cairo_scaled_font_subsets_map_glyph(). + * + * For each subset, @font_subset_callback will be called and will be + * provided with both a #cairo_scaled_font_subset_t object containing + * all the glyphs in the subset as well as the value of @closure. + * + * The #cairo_scaled_font_subset_t object contains the scaled_font, + * the font_id, and the subset_id corresponding to all glyphs + * belonging to the subset. In addition, it contains an array providing + * a mapping between subset glyph indices and the original scaled font + * glyph indices. + * + * The index of the array corresponds to subset_glyph_index values + * returned by _cairo_scaled_font_subsets_map_glyph() while the + * values of the array correspond to the scaled_font_glyph_index + * values passed as input to the same function. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero + * value indicating an error. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure); + +/** + * _cairo_scaled_font_subsets_foreach_user: + * @font_subsets: a #cairo_scaled_font_subsets_t + * @font_subset_callback: a function to be called for each font subset + * @closure: closure data for the callback function + * + * Iterate over each unique scaled font subset as created by calls to + * _cairo_scaled_font_subsets_map_glyph(). A subset is determined by + * unique pairs of (font_id, subset_id) as returned by + * _cairo_scaled_font_subsets_map_glyph(). + * + * For each subset, @font_subset_callback will be called and will be + * provided with both a #cairo_scaled_font_subset_t object containing + * all the glyphs in the subset as well as the value of @closure. + * + * The #cairo_scaled_font_subset_t object contains the scaled_font, + * the font_id, and the subset_id corresponding to all glyphs + * belonging to the subset. In addition, it contains an array providing + * a mapping between subset glyph indices and the original scaled font + * glyph indices. + * + * The index of the array corresponds to subset_glyph_index values + * returned by _cairo_scaled_font_subsets_map_glyph() while the + * values of the array correspond to the scaled_font_glyph_index + * values passed as input to the same function. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, or a non-zero + * value indicating an error. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure); + +/** + * _cairo_scaled_font_subset_create_glyph_names: + * @font_subsets: a #cairo_scaled_font_subsets_t + * + * Create an array of strings containing the glyph name for each glyph + * in @font_subsets. The array as store in font_subsets->glyph_names. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font backend does not support + * mapping the glyph indices to unicode characters. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +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 *family_name_utf8; + char *ps_name; + double *widths; + double x_min, y_min, x_max, y_max; + double ascent, descent; + char *data; + unsigned long data_length; +} cairo_cff_subset_t; + +/** + * _cairo_cff_subset_init: + * @cff_subset: a #cairo_cff_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 + * cff file corresponding to @font_subset and initialize + * @cff_subset with information about the subset and the cff + * data. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a + * cff file, or an non-zero value indicating an error. Possible + * errors include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_cff_subset_fini: + * @cff_subset: a #cairo_cff_subset_t + * + * Free all resources associated with @cff_subset. After this + * call, @cff_subset should not be used again without a + * subsequent call to _cairo_cff_subset_init() again first. + **/ +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 + * @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 cff + * file corresponding to @font_subset and initialize @cff_subset + * with information about the subset and the cff data. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a + * cff file, or an non-zero value indicating an error. Possible + * errors include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_cff_fallback_fini: + * @cff_subset: a #cairo_cff_subset_t + * + * Free all resources associated with @cff_subset. After this + * call, @cff_subset should not be used again without a + * subsequent call to _cairo_cff_subset_init() again first. + **/ +cairo_private void +_cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset); + +typedef struct _cairo_truetype_subset { + char *family_name_utf8; + char *ps_name; + double *widths; + double x_min, y_min, x_max, y_max; + double ascent, descent; + unsigned char *data; + unsigned long data_length; + unsigned long *string_offsets; + unsigned long num_string_offsets; +} cairo_truetype_subset_t; + +/** + * _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 + * + * 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 + * PostScript. + * + * 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_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: + * @truetype_subset: a #cairo_truetype_subset_t + * + * Free all resources associated with @truetype_subset. After this + * call, @truetype_subset should not be used again without a + * subsequent call to _cairo_truetype_subset_init() again first. + **/ +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; + double *widths; + double x_min, y_min, x_max, y_max; + double ascent, descent; + char *data; + unsigned long header_length; + unsigned long data_length; + unsigned long trailer_length; +} cairo_type1_subset_t; + + +/** + * _cairo_type1_subset_init: + * @type1_subset: a #cairo_type1_subset_t to initialize + * @font_subset: the #cairo_scaled_font_subset_t to initialize from + * @hex_encode: if true the encrypted portion of the font is hex encoded + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a type1 + * file corresponding to @font_subset and initialize @type1_subset + * with information about the subset and the type1 data. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 + * file, or an non-zero value indicating an error. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_type1_subset_init (cairo_type1_subset_t *type_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset, + cairo_bool_t hex_encode); + +/** + * _cairo_type1_subset_fini: + * @type1_subset: a #cairo_type1_subset_t + * + * Free all resources associated with @type1_subset. After this call, + * @type1_subset should not be used again without a subsequent call to + * _cairo_truetype_type1_init() again first. + **/ +cairo_private void +_cairo_type1_subset_fini (cairo_type1_subset_t *subset); + +/** + * _cairo_type1_scaled_font_is_type1: + * @scaled_font: a #cairo_scaled_font_t + * + * Return %TRUE if @scaled_font is a Type 1 font, otherwise return %FALSE. + **/ +cairo_private cairo_bool_t +_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); + +/** + * _cairo_type1_fallback_init_binary: + * @type1_subset: a #cairo_type1_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 type1 + * file corresponding to @font_subset and initialize @type1_subset + * with information about the subset and the type1 data. The encrypted + * part of the font is binary encoded. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 + * file, or an non-zero value indicating an error. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset); + +/** + * _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 + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) generate a type1 + * file corresponding to @font_subset and initialize @type1_subset + * with information about the subset and the type1 data. The encrypted + * part of the font is hex encoded. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type1 + * file, or an non-zero value indicating an error. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type_subset, + const char *name, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_type1_fallback_fini: + * @type1_subset: a #cairo_type1_subset_t + * + * Free all resources associated with @type1_subset. After this call, + * @type1_subset should not be used again without a subsequent call to + * _cairo_truetype_type1_init() again first. + **/ +cairo_private void +_cairo_type1_fallback_fini (cairo_type1_subset_t *subset); + +typedef struct _cairo_type2_charstrings { + int *widths; + long x_min, y_min, x_max, y_max; + long ascent, descent; + cairo_array_t charstrings; +} cairo_type2_charstrings_t; + +/** + * _cairo_type2_charstrings_init: + * @type2_subset: a #cairo_type2_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 type2 + * charstrings to @font_subset and initialize @type2_subset + * with information about the subset. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a type2 + * charstrings, or an non-zero value indicating an error. Possible errors + * include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_status_t +_cairo_type2_charstrings_init (cairo_type2_charstrings_t *charstrings, + cairo_scaled_font_subset_t *font_subset); + +/** + * _cairo_type2_charstrings_fini: + * @subset: a #cairo_type2_charstrings_t + * + * Free all resources associated with @type2_charstring. After this call, + * @type2_charstring should not be used again without a subsequent call to + * _cairo_type2_charstring_init() again first. + **/ +cairo_private void +_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *charstrings); + +/** + * _cairo_truetype_index_to_ucs4: + * @scaled_font: the #cairo_scaled_font_t + * @index: the glyph index + * @ucs4: return value for the unicode value of the glyph + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) assign + * the unicode character of the glyph to @ucs4. + * + * If mapping glyph indices to unicode is supported but the unicode + * value of the specified glyph is not available, @ucs4 is set to -1. + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if mapping glyph indices to unicode + * is not supported. Possible errors include %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_int_status_t +_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, + unsigned long index, + uint32_t *ucs4); + +/** + * _cairo_truetype_read_font_name: + * @scaled_font: the #cairo_scaled_font_t + * @ps_name: returns the PostScript name of the font + * or %NULL if the name could not be found. + * @font_name: returns the font name or %NULL if the name could not be found. + * + * If possible (depending on the format of the underlying + * #cairo_scaled_font_t and the font backend in use) read the + * PostScript and Font names from a TrueType/OpenType font. + * + * The font name is the full name of the font eg "DejaVu Sans Bold". + * The PostScript name is a shortened name with spaces removed + * suitable for use as the font name in a PS or PDF file eg + * "DejaVuSans-Bold". + * + * Return value: %CAIRO_STATUS_SUCCESS if successful, + * %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType + * or the name table is not present. Possible errors include + * %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_private cairo_int_status_t +_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, + char **ps_name, + char **font_name); + +/** + * _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 new file mode 100644 index 0000000000..1f0e53577f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c @@ -0,0 +1,1381 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2003 University of Southern California + * Copyright © 2005 Red Hat, Inc + * Copyright © 2006 Keith Packard + * Copyright © 2006 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + * Keith Packard + * Adrian Johnson + */ + +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-user-font-private.h" + +#define MAX_GLYPHS_PER_SIMPLE_FONT 256 +#define MAX_GLYPHS_PER_COMPOSITE_FONT 65536 + +typedef enum { + CAIRO_SUBSETS_SCALED, + CAIRO_SUBSETS_SIMPLE, + CAIRO_SUBSETS_COMPOSITE +} cairo_subsets_type_t; + +typedef enum { + CAIRO_SUBSETS_FOREACH_UNSCALED, + CAIRO_SUBSETS_FOREACH_SCALED, + CAIRO_SUBSETS_FOREACH_USER +} cairo_subsets_foreach_type_t; + +typedef struct _cairo_sub_font { + cairo_hash_entry_t base; + + 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; +} cairo_sub_font_t; + +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; + cairo_sub_font_t *unscaled_sub_fonts_list; + cairo_sub_font_t *unscaled_sub_fonts_list_end; + + int max_glyphs_per_scaled_subset_used; + cairo_hash_table_t *scaled_sub_fonts; + cairo_sub_font_t *scaled_sub_fonts_list; + cairo_sub_font_t *scaled_sub_fonts_list_end; + + int num_sub_fonts; +}; + +typedef struct _cairo_sub_font_glyph { + cairo_hash_entry_t base; + + unsigned int subset_id; + unsigned int subset_glyph_index; + double x_advance; + double y_advance; + + cairo_bool_t is_latin; + int latin_character; + cairo_bool_t is_mapped; + uint32_t unicode; + char *utf8; + int utf8_len; +} cairo_sub_font_glyph_t; + +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; + + unsigned int subset_id; + + cairo_status_t status; + cairo_scaled_font_subset_callback_func_t font_subset_callback; + void *font_subset_callback_closure; +} cairo_sub_font_collection_t; + +typedef struct _cairo_string_entry { + cairo_hash_entry_t base; + char *string; +} cairo_string_entry_t; + +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, + cairo_scaled_font_subsets_glyph_t *subset_glyph); + +static void +_cairo_sub_font_glyph_init_key (cairo_sub_font_glyph_t *sub_font_glyph, + unsigned long scaled_font_glyph_index) +{ + sub_font_glyph->base.hash = scaled_font_glyph_index; +} + +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, + int latin_character, + uint32_t unicode, + char *utf8, + int utf8_len) +{ + cairo_sub_font_glyph_t *sub_font_glyph; + + 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; + } + + _cairo_sub_font_glyph_init_key (sub_font_glyph, scaled_font_glyph_index); + sub_font_glyph->subset_id = subset_id; + 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 = unicode; + sub_font_glyph->utf8 = utf8; + sub_font_glyph->utf8_len = utf8_len; + + return sub_font_glyph; +} + +static void +_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph) +{ + free (sub_font_glyph->utf8); + + free (sub_font_glyph); +} + +static void +_cairo_sub_font_glyph_pluck (void *entry, void *closure) +{ + cairo_sub_font_glyph_t *sub_font_glyph = entry; + cairo_hash_table_t *sub_font_glyphs = closure; + + _cairo_hash_table_remove (sub_font_glyphs, &sub_font_glyph->base); + _cairo_sub_font_glyph_destroy (sub_font_glyph); +} + +static void +_cairo_sub_font_glyph_collect (void *entry, void *closure) +{ + cairo_sub_font_glyph_t *sub_font_glyph = entry; + cairo_sub_font_collection_t *collection = closure; + unsigned long scaled_font_glyph_index; + unsigned int subset_glyph_index; + + if (sub_font_glyph->subset_id != collection->subset_id) + return; + + scaled_font_glyph_index = sub_font_glyph->base.hash; + subset_glyph_index = sub_font_glyph->subset_glyph_index; + + /* Ensure we don't exceed the allocated bounds. */ + assert (subset_glyph_index < collection->glyphs_size); + + 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; + + collection->num_glyphs++; +} + +static cairo_bool_t +_cairo_sub_fonts_equal (const void *key_a, const void *key_b) +{ + const cairo_sub_font_t *sub_font_a = key_a; + const cairo_sub_font_t *sub_font_b = key_b; + cairo_scaled_font_t *a = sub_font_a->scaled_font; + cairo_scaled_font_t *b = sub_font_b->scaled_font; + + if (sub_font_a->is_scaled) + return a == b; + else + return a->font_face == b->font_face || a->original_font_face == b->original_font_face; +} + +static void +_cairo_sub_font_init_key (cairo_sub_font_t *sub_font, + cairo_scaled_font_t *scaled_font) +{ + if (sub_font->is_scaled) + { + sub_font->base.hash = (unsigned long) scaled_font; + sub_font->scaled_font = scaled_font; + } + else + { + sub_font->base.hash = (unsigned long) scaled_font->font_face; + sub_font->scaled_font = scaled_font; + } +} + +static cairo_status_t +_cairo_sub_font_create (cairo_scaled_font_subsets_t *parent, + cairo_scaled_font_t *scaled_font, + unsigned int font_id, + int max_glyphs_per_subset, + cairo_bool_t is_scaled, + cairo_bool_t is_composite, + cairo_sub_font_t **sub_font_out) +{ + cairo_sub_font_t *sub_font; + int i; + + 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->use_latin_subset = parent->use_latin_subset; + + /* latin subsets of Type 3 and CID CFF fonts are not supported */ + if (sub_font->is_user || sub_font->is_scaled || + _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; + *sub_font_out = sub_font; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_sub_font_destroy (cairo_sub_font_t *sub_font) +{ + _cairo_hash_table_foreach (sub_font->sub_font_glyphs, + _cairo_sub_font_glyph_pluck, + sub_font->sub_font_glyphs); + _cairo_hash_table_destroy (sub_font->sub_font_glyphs); + cairo_scaled_font_destroy (sub_font->scaled_font); + free (sub_font); +} + +static void +_cairo_sub_font_pluck (void *entry, void *closure) +{ + cairo_sub_font_t *sub_font = entry; + cairo_hash_table_t *sub_fonts = closure; + + _cairo_hash_table_remove (sub_fonts, &sub_font->base); + _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_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]; + int len; + cairo_status_t status; + + /* Do a reverse lookup on the glyph index. unicode is -1 if the + * index could not be mapped to a unicode character. */ + unicode = -1; + status = _cairo_truetype_index_to_ucs4 (scaled_font, + scaled_font_glyph_index, + &unicode); + if (_cairo_status_is_error (status)) + return status; + + if (unicode == (uint32_t)-1 && scaled_font->backend->index_to_ucs4) { + status = scaled_font->backend->index_to_ucs4 (scaled_font, + scaled_font_glyph_index, + &unicode); + if (unlikely (status)) + return status; + } + + *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) { + *utf8_out = _cairo_malloc (len + 1); + if (unlikely (*utf8_out == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (*utf8_out, buf, len); + (*utf8_out)[len] = 0; + *utf8_len_out = len; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, + const char *utf8, + int utf8_len, + cairo_bool_t *is_mapped) +{ + *is_mapped = FALSE; + + if (utf8_len < 0) + return CAIRO_STATUS_SUCCESS; + + if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0') + utf8_len--; + + if (utf8 != NULL && utf8_len != 0) { + if (sub_font_glyph->utf8 != NULL) { + if (utf8_len == sub_font_glyph->utf8_len && + memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0) + { + /* Requested utf8 mapping matches the existing mapping */ + *is_mapped = TRUE; + } + } else { + /* No existing mapping. Use the requested mapping */ + sub_font_glyph->utf8 = _cairo_malloc (utf8_len + 1); + if (unlikely (sub_font_glyph->utf8 == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (sub_font_glyph->utf8, utf8, utf8_len); + sub_font_glyph->utf8[utf8_len] = 0; + sub_font_glyph->utf8_len = utf8_len; + *is_mapped = TRUE; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, + unsigned long scaled_font_glyph_index, + const char *utf8, + int utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + cairo_sub_font_glyph_t key, *sub_font_glyph; + cairo_int_status_t status; + + _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); + sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, + &key.base); + if (sub_font_glyph != NULL) { + subset_glyph->font_id = sub_font->font_id; + subset_glyph->subset_id = sub_font_glyph->subset_id; + 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, + &subset_glyph->utf8_is_mapped); + subset_glyph->unicode = sub_font_glyph->unicode; + + return status; + } + + 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 *text_utf8, + int text_utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + cairo_sub_font_glyph_t key, *sub_font_glyph; + cairo_status_t status; + + _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); + sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, + &key.base); + if (sub_font_glyph == NULL) { + uint32_t font_unicode; + char *font_utf8; + int font_utf8_len; + cairo_bool_t is_latin; + int latin_character; + + 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; + + /* 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; + + 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); + } + } + + /* 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; + } + } + } + + 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; + } + + subset_glyph->font_id = sub_font->font_id; + subset_glyph->subset_id = sub_font_glyph->subset_id; + 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, + text_utf8, text_utf8_len, + &subset_glyph->utf8_is_mapped); + subset_glyph->unicode = sub_font_glyph->unicode; + + return status; +} + +static void +_cairo_sub_font_collect (void *entry, void *closure) +{ + cairo_sub_font_t *sub_font = entry; + cairo_sub_font_collection_t *collection = closure; + cairo_scaled_font_subset_t subset; + int i; + unsigned int j; + + if (collection->status) + return; + + collection->status = sub_font->scaled_font->status; + if (collection->status) + return; + + for (i = 0; i <= sub_font->current_subset; i++) { + collection->subset_id = i; + collection->num_glyphs = 0; + 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); + + subset.scaled_font = sub_font->scaled_font; + subset.is_composite = sub_font->is_composite; + subset.is_scaled = sub_font->is_scaled; + subset.font_id = sub_font->font_id; + subset.subset_id = i; + subset.glyphs = collection->glyphs; + subset.utf8 = collection->utf8; + subset.num_glyphs = collection->num_glyphs; + subset.glyph_names = NULL; + + 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.glyph_names != NULL) { + for (j = 0; j < collection->num_glyphs; j++) + free (subset.glyph_names[j]); + free (subset.glyph_names); + } + + if (collection->status) + break; + } +} + +static cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_internal (cairo_subsets_type_t type) +{ + cairo_scaled_font_subsets_t *subsets; + + 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; + + subsets->unscaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); + if (! subsets->unscaled_sub_fonts) { + free (subsets); + return NULL; + } + subsets->unscaled_sub_fonts_list = NULL; + subsets->unscaled_sub_fonts_list_end = NULL; + + subsets->scaled_sub_fonts = _cairo_hash_table_create (_cairo_sub_fonts_equal); + if (! subsets->scaled_sub_fonts) { + _cairo_hash_table_destroy (subsets->unscaled_sub_fonts); + free (subsets); + return NULL; + } + subsets->scaled_sub_fonts_list = NULL; + subsets->scaled_sub_fonts_list_end = NULL; + + return subsets; +} + +cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_scaled (void) +{ + return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SCALED); +} + +cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_simple (void) +{ + return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_SIMPLE); +} + +cairo_scaled_font_subsets_t * +_cairo_scaled_font_subsets_create_composite (void) +{ + return _cairo_scaled_font_subsets_create_internal (CAIRO_SUBSETS_COMPOSITE); +} + +void +_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *subsets) +{ + _cairo_hash_table_foreach (subsets->scaled_sub_fonts, _cairo_sub_font_pluck, subsets->scaled_sub_fonts); + _cairo_hash_table_destroy (subsets->scaled_sub_fonts); + + _cairo_hash_table_foreach (subsets->unscaled_sub_fonts, _cairo_sub_font_pluck, subsets->unscaled_sub_fonts); + _cairo_hash_table_destroy (subsets->unscaled_sub_fonts); + + 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, + unsigned long scaled_font_glyph_index, + const char * utf8, + int utf8_len, + cairo_scaled_font_subsets_glyph_t *subset_glyph) +{ + cairo_sub_font_t key, *sub_font; + cairo_scaled_glyph_t *scaled_glyph; + cairo_font_face_t *font_face; + cairo_matrix_t identity; + cairo_font_options_t font_options; + cairo_scaled_font_t *unscaled_font; + cairo_int_status_t status; + int max_glyphs; + cairo_bool_t type1_font; + + /* Lookup glyph in unscaled subsets */ + if (subsets->type != CAIRO_SUBSETS_SCALED) { + key.is_scaled = FALSE; + _cairo_sub_font_init_key (&key, scaled_font); + sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, + &key.base); + if (sub_font != NULL) { + status = _cairo_sub_font_lookup_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + } + + /* Lookup glyph in scaled subsets */ + key.is_scaled = TRUE; + _cairo_sub_font_init_key (&key, scaled_font); + sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, + &key.base); + if (sub_font != NULL) { + status = _cairo_sub_font_lookup_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + } + + /* Glyph not found. Determine whether the glyph is outline or + * bitmap and add to the appropriate subset. + * + * glyph_index 0 (the .notdef glyph) is a special case. Some fonts + * will return CAIRO_INT_STATUS_UNSUPPORTED when doing a + * _scaled_glyph_lookup(_GLYPH_INFO_PATH). Type1-fallback creates + * empty glyphs in this case so we can put the glyph in a unscaled + * subset. */ + if (scaled_font_glyph_index == 0 || + _cairo_font_face_is_user (scaled_font->font_face)) { + status = CAIRO_STATUS_SUCCESS; + } else { + _cairo_scaled_font_freeze_cache (scaled_font); + status = _cairo_scaled_glyph_lookup (scaled_font, + scaled_font_glyph_index, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + _cairo_scaled_font_thaw_cache (scaled_font); + } + if (_cairo_int_status_is_error (status)) + return status; + + if (status == CAIRO_INT_STATUS_SUCCESS && + subsets->type != CAIRO_SUBSETS_SCALED && + ! _cairo_font_face_is_user (scaled_font->font_face)) + { + /* Path available. Add to unscaled subset. */ + key.is_scaled = FALSE; + _cairo_sub_font_init_key (&key, scaled_font); + sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, + &key.base); + if (sub_font == NULL) { + font_face = cairo_scaled_font_get_font_face (scaled_font); + cairo_matrix_init_identity (&identity); + _cairo_font_options_init_default (&font_options); + cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); + unscaled_font = cairo_scaled_font_create (font_face, + &identity, + &identity, + &font_options); + if (unlikely (unscaled_font->status)) + return unscaled_font->status; + + subset_glyph->is_scaled = FALSE; + type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font); + if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) { + max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT; + subset_glyph->is_composite = TRUE; + } else { + max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT; + subset_glyph->is_composite = FALSE; + } + + status = _cairo_sub_font_create (subsets, + unscaled_font, + subsets->num_sub_fonts, + max_glyphs, + subset_glyph->is_scaled, + subset_glyph->is_composite, + &sub_font); + + if (unlikely (status)) { + cairo_scaled_font_destroy (unscaled_font); + return status; + } + + status = _cairo_hash_table_insert (subsets->unscaled_sub_fonts, + &sub_font->base); + + if (unlikely (status)) { + _cairo_sub_font_destroy (sub_font); + return status; + } + if (!subsets->unscaled_sub_fonts_list) + subsets->unscaled_sub_fonts_list = sub_font; + else + subsets->unscaled_sub_fonts_list_end->next = sub_font; + subsets->unscaled_sub_fonts_list_end = sub_font; + subsets->num_sub_fonts++; + } + } else { + /* No path available. Add to scaled subset. */ + key.is_scaled = TRUE; + _cairo_sub_font_init_key (&key, scaled_font); + sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, + &key.base); + if (sub_font == NULL) { + subset_glyph->is_scaled = TRUE; + subset_glyph->is_composite = FALSE; + if (subsets->type == CAIRO_SUBSETS_SCALED) + max_glyphs = INT_MAX; + else + max_glyphs = MAX_GLYPHS_PER_SIMPLE_FONT; + + status = _cairo_sub_font_create (subsets, + cairo_scaled_font_reference (scaled_font), + subsets->num_sub_fonts, + max_glyphs, + subset_glyph->is_scaled, + subset_glyph->is_composite, + &sub_font); + if (unlikely (status)) { + cairo_scaled_font_destroy (scaled_font); + return status; + } + + status = _cairo_hash_table_insert (subsets->scaled_sub_fonts, + &sub_font->base); + if (unlikely (status)) { + _cairo_sub_font_destroy (sub_font); + return status; + } + if (!subsets->scaled_sub_fonts_list) + subsets->scaled_sub_fonts_list = sub_font; + else + subsets->scaled_sub_fonts_list_end->next = sub_font; + subsets->scaled_sub_fonts_list_end = sub_font; + subsets->num_sub_fonts++; + } + } + + return _cairo_sub_font_map_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); +} + +static cairo_status_t +_cairo_scaled_font_subsets_foreach_internal (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure, + cairo_subsets_foreach_type_t type) +{ + cairo_sub_font_collection_t collection; + cairo_sub_font_t *sub_font; + cairo_bool_t is_scaled, is_user; + + is_scaled = FALSE; + is_user = FALSE; + + if (type == CAIRO_SUBSETS_FOREACH_USER) + is_user = TRUE; + + if (type == CAIRO_SUBSETS_FOREACH_SCALED || + type == CAIRO_SUBSETS_FOREACH_USER) + { + is_scaled = TRUE; + } + + if (is_scaled) + collection.glyphs_size = font_subsets->max_glyphs_per_scaled_subset_used; + else + collection.glyphs_size = font_subsets->max_glyphs_per_unscaled_subset_used; + + if (! collection.glyphs_size) + return CAIRO_STATUS_SUCCESS; + + collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long)); + collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *)); + 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); + } + + collection.font_subset_callback = font_subset_callback; + collection.font_subset_callback_closure = closure; + collection.status = CAIRO_STATUS_SUCCESS; + + if (is_scaled) + sub_font = font_subsets->scaled_sub_fonts_list; + else + sub_font = font_subsets->unscaled_sub_fonts_list; + + while (sub_font) { + if (sub_font->is_user == is_user) + _cairo_sub_font_collect (sub_font, &collection); + + sub_font = sub_font->next; + } + free (collection.utf8); + free (collection.glyphs); + free (collection.to_latin_char); + free (collection.latin_to_subset_glyph_index); + + return collection.status; +} + +cairo_status_t +_cairo_scaled_font_subsets_foreach_scaled (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure) +{ + return _cairo_scaled_font_subsets_foreach_internal (font_subsets, + font_subset_callback, + closure, + CAIRO_SUBSETS_FOREACH_SCALED); +} + +cairo_status_t +_cairo_scaled_font_subsets_foreach_unscaled (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure) +{ + return _cairo_scaled_font_subsets_foreach_internal (font_subsets, + font_subset_callback, + closure, + CAIRO_SUBSETS_FOREACH_UNSCALED); +} + +cairo_status_t +_cairo_scaled_font_subsets_foreach_user (cairo_scaled_font_subsets_t *font_subsets, + cairo_scaled_font_subset_callback_func_t font_subset_callback, + void *closure) +{ + return _cairo_scaled_font_subsets_foreach_internal (font_subsets, + font_subset_callback, + closure, + CAIRO_SUBSETS_FOREACH_USER); +} + +static cairo_bool_t +_cairo_string_equal (const void *key_a, const void *key_b) +{ + const cairo_string_entry_t *a = key_a; + const cairo_string_entry_t *b = key_b; + + if (strcmp (a->string, b->string) == 0) + return TRUE; + else + 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) +{ + unsigned long sum = 0; + unsigned int i; + + for (i = 0; i < strlen(s); i++) + sum += s[i]; + key->base.hash = sum; + key->string = s; +} + +static cairo_status_t +create_string_entry (char *s, cairo_string_entry_t **entry) +{ + *entry = _cairo_malloc (sizeof (cairo_string_entry_t)); + if (unlikely (*entry == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_string_init_key (*entry, s); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_pluck_entry (void *entry, void *closure) +{ + _cairo_hash_table_remove (closure, entry); + free (entry); +} + +cairo_int_status_t +_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset) +{ + unsigned int i; + cairo_hash_table_t *names; + cairo_string_entry_t key, *entry; + char buf[30]; + char *utf8; + uint16_t *utf16; + int utf16_len; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + names = _cairo_hash_table_create (_cairo_string_equal); + if (unlikely (names == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + subset->glyph_names = calloc (subset->num_glyphs, sizeof (char *)); + if (unlikely (subset->glyph_names == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_HASH; + } + + i = 0; + if (! subset->is_scaled) { + subset->glyph_names[0] = strdup (".notdef"); + if (unlikely (subset->glyph_names[0] == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_HASH; + } + + status = create_string_entry (subset->glyph_names[0], &entry); + if (unlikely (status)) + goto CLEANUP_HASH; + + status = _cairo_hash_table_insert (names, &entry->base); + if (unlikely (status)) { + free (entry); + goto CLEANUP_HASH; + } + i++; + } + + for (; i < subset->num_glyphs; i++) { + utf8 = subset->utf8[i]; + utf16 = NULL; + utf16_len = 0; + if (utf8 && *utf8) { + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); + if (status == CAIRO_STATUS_INVALID_STRING) { + utf16 = NULL; + utf16_len = 0; + } else if (unlikely (status)) { + goto CLEANUP_HASH; + } + } + + if (utf16_len == 1) { + 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) + snprintf (buf, sizeof (buf), "g%d", i); + } else { + snprintf (buf, sizeof (buf), "g%d", i); + } + free (utf16); + + subset->glyph_names[i] = strdup (buf); + if (unlikely (subset->glyph_names[i] == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_HASH; + } + + status = create_string_entry (subset->glyph_names[i], &entry); + if (unlikely (status)) + goto CLEANUP_HASH; + + status = _cairo_hash_table_insert (names, &entry->base); + if (unlikely (status)) { + free (entry); + goto CLEANUP_HASH; + } + } + +CLEANUP_HASH: + _cairo_hash_table_foreach (names, _pluck_entry, names); + _cairo_hash_table_destroy (names); + + if (likely (status == CAIRO_STATUS_SUCCESS)) + return CAIRO_STATUS_SUCCESS; + + if (subset->glyph_names != NULL) { + for (i = 0; i < subset->num_glyphs; i++) { + free (subset->glyph_names[i]); + } + + free (subset->glyph_names); + subset->glyph_names = NULL; + } + + 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 new file mode 100755 index 0000000000..d53915fee0 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-scaled-font.c @@ -0,0 +1,3202 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* + * Copyright © 2005 Keith Packard + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Keith Packard + * Carl D. Worth + * Graydon Hoare + * Owen Taylor + * Behdad Esfahbod + * Chris Wilson + */ + +#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" +#include "cairo-surface-backend-private.h" + +/** + * SECTION:cairo-scaled-font + * @Title: cairo_scaled_font_t + * @Short_Description: Font face at particular size and options + * @See_Also: #cairo_font_face_t, #cairo_matrix_t, #cairo_font_options_t + * + * #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 + * + * We maintain a global pool of glyphs split between all active fonts. This + * allows a heavily used individual font to cache more glyphs than we could + * manage if we used per-font glyph caches, but at the same time maintains + * fairness across all fonts and provides a cap on the maximum number of + * global glyphs. + * + * The glyphs are allocated in pages, which are capped in the global pool. + * Using pages means we can reduce the frequency at which we have to probe the + * global pool and ameliorates the memory allocation pressure. + */ + +/* XXX: This number is arbitrary---we've never done any measurement of this. */ +#define MAX_GLYPH_PAGES_CACHED 512 +static cairo_cache_t cairo_scaled_glyph_page_cache; + +#define CAIRO_SCALED_GLYPH_PAGE_SIZE 32 +struct _cairo_scaled_glyph_page { + cairo_cache_entry_t cache_entry; + cairo_scaled_font_t *scaled_font; + cairo_list_t link; + + unsigned int num_glyphs; + cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE]; +}; + +/* + * Notes: + * + * To store rasterizations of glyphs, we use an image surface and the + * device offset to represent the glyph origin. + * + * A device_transform converts from device space (a conceptual space) to + * surface space. For simple cases of translation only, it's called a + * device_offset and is public API (cairo_surface_[gs]et_device_offset()). + * A possibly better name for those functions could have been + * cairo_surface_[gs]et_origin(). So, that's what they do: they set where + * the device-space origin (0,0) is in the surface. If the origin is inside + * the surface, device_offset values are positive. It may look like this: + * + * Device space: + * (-x,-y) <-- negative numbers + * +----------------+ + * | . | + * | . | + * |......(0,0) <---|-- device-space origin + * | | + * | | + * +----------------+ + * (width-x,height-y) + * + * Surface space: + * (0,0) <-- surface-space origin + * +---------------+ + * | . | + * | . | + * |......(x,y) <--|-- device_offset + * | | + * | | + * +---------------+ + * (width,height) + * + * In other words: device_offset is the coordinates of the device-space + * origin relative to the top-left of the surface. + * + * We use device offsets in a couple of places: + * + * - Public API: To let toolkits like Gtk+ give user a surface that + * only represents part of the final destination (say, the expose + * area), but has the same device space as the destination. In these + * cases device_offset is typically negative. Example: + * + * application window + * +---------------+ + * | . | + * | (x,y). | + * |......+---+ | + * | | | <--|-- expose area + * | +---+ | + * +---------------+ + * + * In this case, the user of cairo API can set the device_space on + * the expose area to (-x,-y) to move the device space origin to that + * of the application window, such that drawing in the expose area + * surface and painting it in the application window has the same + * effect as drawing in the application window directly. Gtk+ has + * been using this feature. + * + * - Glyph surfaces: In most font rendering systems, glyph surfaces + * have an origin at (0,0) and a bounding box that is typically + * represented as (x_bearing,y_bearing,width,height). Depending on + * which way y progresses in the system, y_bearing may typically be + * negative (for systems similar to cairo, with origin at top left), + * or be positive (in systems like PDF with origin at bottom left). + * No matter which is the case, it is important to note that + * (x_bearing,y_bearing) is the coordinates of top-left of the glyph + * relative to the glyph origin. That is, for example: + * + * Scaled-glyph space: + * + * (x_bearing,y_bearing) <-- negative numbers + * +----------------+ + * | . | + * | . | + * |......(0,0) <---|-- glyph origin + * | | + * | | + * +----------------+ + * (width+x_bearing,height+y_bearing) + * + * Note the similarity of the origin to the device space. That is + * exactly how we use the device_offset to represent scaled glyphs: + * to use the device-space origin as the glyph origin. + * + * Now compare the scaled-glyph space to device-space and surface-space + * and convince yourself that: + * + * (x_bearing,y_bearing) = (-x,-y) = - device_offset + * + * That's right. If you are not convinced yet, contrast the definition + * of the two: + * + * "(x_bearing,y_bearing) is the coordinates of top-left of the + * glyph relative to the glyph origin." + * + * "In other words: device_offset is the coordinates of the + * device-space origin relative to the top-left of the surface." + * + * and note that glyph origin = device-space origin. + */ + +static void +_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font); + +static void +_cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + 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); + } + + _cairo_image_scaled_glyph_fini (scaled_font, scaled_glyph); + + if (scaled_glyph->surface != NULL) + cairo_surface_destroy (&scaled_glyph->surface->base); + + if (scaled_glyph->path != NULL) + _cairo_path_fixed_destroy (scaled_glyph->path); + + if (scaled_glyph->recording_surface != NULL) { + 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 +static const cairo_scaled_font_t _cairo_scaled_font_nil = { + { ZOMBIE }, /* hash_entry */ + CAIRO_STATUS_NO_MEMORY, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL, /* original_font_face */ + NULL, /* font_face */ + { 1., 0., 0., 1., 0, 0}, /* font_matrix */ + { 1., 0., 0., 1., 0, 0}, /* ctm */ + { CAIRO_ANTIALIAS_DEFAULT, /* options */ + CAIRO_SUBPIXEL_ORDER_DEFAULT, + CAIRO_HINT_STYLE_DEFAULT, + CAIRO_HINT_METRICS_DEFAULT} , + FALSE, /* placeholder */ + FALSE, /* holdover */ + TRUE, /* finished */ + { 1., 0., 0., 1., 0, 0}, /* scale */ + { 1., 0., 0., 1., 0, 0}, /* scale_inverse */ + 1., /* max_scale */ + { 0., 0., 0., 0., 0. }, /* extents */ + { 0., 0., 0., 0., 0. }, /* fs_extents */ + CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */ + NULL, /* glyphs */ + { NULL, NULL }, /* pages */ + FALSE, /* cache_frozen */ + FALSE, /* global_cache_frozen */ + { NULL, NULL }, /* privates */ + NULL /* backend */ +}; + +/** + * _cairo_scaled_font_set_error: + * @scaled_font: a scaled_font + * @status: a status value indicating an error + * + * Atomically sets scaled_font->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS. + * + * All assignments of an error status to scaled_font->status should happen + * through _cairo_scaled_font_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the nil + * objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +cairo_status_t +_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&scaled_font->status, status); + + return _cairo_error (status); +} + +/** + * cairo_scaled_font_get_type: + * @scaled_font: a #cairo_scaled_font_t + * + * This function returns the type of the backend used to create + * a scaled font. See #cairo_font_type_t for available types. + * However, this function never returns %CAIRO_FONT_TYPE_TOY. + * + * Return value: The type of @scaled_font. + * + * Since: 1.2 + **/ +cairo_font_type_t +cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return CAIRO_FONT_TYPE_TOY; + + return scaled_font->backend->type; +} + +/** + * cairo_scaled_font_status: + * @scaled_font: a #cairo_scaled_font_t + * + * Checks whether an error has previously occurred for this + * 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) +{ + return scaled_font->status; +} +slim_hidden_def (cairo_scaled_font_status); + +/* Here we keep a unique mapping from + * font_face/matrix/ctm/font_options => #cairo_scaled_font_t. + * + * Here are the things that we want to map: + * + * a) All otherwise referenced #cairo_scaled_font_t's + * b) Some number of not otherwise referenced #cairo_scaled_font_t's + * + * The implementation uses a hash table which covers (a) + * completely. Then, for (b) we have an array of otherwise + * unreferenced fonts (holdovers) which are expired in + * least-recently-used order. + * + * The cairo_scaled_font_create() code gets to treat this like a regular + * hash table. All of the magic for the little holdover cache is in + * cairo_scaled_font_reference() and cairo_scaled_font_destroy(). + */ + +/* This defines the size of the holdover array ... that is, the number + * of scaled fonts we keep around even when not otherwise referenced + */ +#define CAIRO_SCALED_FONT_MAX_HOLDOVERS 256 + +typedef struct _cairo_scaled_font_map { + cairo_scaled_font_t *mru_scaled_font; + cairo_hash_table_t *hash_table; + cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS]; + int num_holdovers; +} cairo_scaled_font_map_t; + +static cairo_scaled_font_map_t *cairo_scaled_font_map; + +static int +_cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b); + +static cairo_scaled_font_map_t * +_cairo_scaled_font_map_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); + + if (cairo_scaled_font_map == NULL) { + cairo_scaled_font_map = _cairo_malloc (sizeof (cairo_scaled_font_map_t)); + if (unlikely (cairo_scaled_font_map == NULL)) + goto CLEANUP_MUTEX_LOCK; + + cairo_scaled_font_map->mru_scaled_font = NULL; + cairo_scaled_font_map->hash_table = + _cairo_hash_table_create (_cairo_scaled_font_keys_equal); + + if (unlikely (cairo_scaled_font_map->hash_table == NULL)) + goto CLEANUP_SCALED_FONT_MAP; + + cairo_scaled_font_map->num_holdovers = 0; + } + + return cairo_scaled_font_map; + + CLEANUP_SCALED_FONT_MAP: + free (cairo_scaled_font_map); + cairo_scaled_font_map = NULL; + CLEANUP_MUTEX_LOCK: + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; +} + +static void +_cairo_scaled_font_map_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); +} + +void +_cairo_scaled_font_map_destroy (void) +{ + cairo_scaled_font_map_t *font_map; + cairo_scaled_font_t *scaled_font; + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); + + font_map = cairo_scaled_font_map; + if (unlikely (font_map == NULL)) { + goto CLEANUP_MUTEX_LOCK; + } + + scaled_font = font_map->mru_scaled_font; + if (scaled_font != NULL) { + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + cairo_scaled_font_destroy (scaled_font); + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); + } + + /* remove scaled_fonts starting from the end so that font_map->holdovers + * is always in a consistent state when we release the mutex. */ + while (font_map->num_holdovers) { + scaled_font = font_map->holdovers[font_map->num_holdovers-1]; + assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); + _cairo_hash_table_remove (font_map->hash_table, + &scaled_font->hash_entry); + + font_map->num_holdovers--; + + /* This releases the font_map lock to avoid the possibility of a + * recursive deadlock when the scaled font destroy closure gets + * called + */ + _cairo_scaled_font_fini (scaled_font); + + free (scaled_font); + } + + _cairo_hash_table_destroy (font_map->hash_table); + + free (cairo_scaled_font_map); + cairo_scaled_font_map = NULL; + + CLEANUP_MUTEX_LOCK: + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); +} + +static void +_cairo_scaled_glyph_page_destroy (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_page_t *page) +{ + unsigned int n; + + 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); + _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]); + } + + 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. + * + * What we do is, we create a fake identical scaled font, and mark + * it as placeholder, lock its mutex, and insert that in the fontmap + * hash table. This makes other code trying to create an identical + * scaled font to just wait and retry. + * + * The reason we have to create a fake scaled font instead of just using + * scaled_font is for lifecycle management: we need to (or rather, + * other code needs to) reference the scaled_font in the hash table. + * We can't do that on the input scaled_font as it may be freed by + * font backend upon error. + */ + +cairo_status_t +_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font) +{ + cairo_status_t status; + cairo_scaled_font_t *placeholder_scaled_font; + + assert (CAIRO_MUTEX_IS_LOCKED (_cairo_scaled_font_map_mutex)); + + status = scaled_font->status; + if (unlikely (status)) + return status; + + placeholder_scaled_font = _cairo_malloc (sizeof (cairo_scaled_font_t)); + if (unlikely (placeholder_scaled_font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* full initialization is wasteful, but who cares... */ + status = _cairo_scaled_font_init (placeholder_scaled_font, + scaled_font->font_face, + &scaled_font->font_matrix, + &scaled_font->ctm, + &scaled_font->options, + NULL); + if (unlikely (status)) + goto FREE_PLACEHOLDER; + + 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)) + goto FINI_PLACEHOLDER; + + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex); + + return CAIRO_STATUS_SUCCESS; + + FINI_PLACEHOLDER: + _cairo_scaled_font_fini_internal (placeholder_scaled_font); + FREE_PLACEHOLDER: + free (placeholder_scaled_font); + + return _cairo_scaled_font_set_error (scaled_font, status); +} + +void +_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font) +{ + cairo_scaled_font_t *placeholder_scaled_font; + + 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); + assert (placeholder_scaled_font != NULL); + assert (placeholder_scaled_font->placeholder); + assert (CAIRO_MUTEX_IS_LOCKED (placeholder_scaled_font->mutex)); + + _cairo_hash_table_remove (cairo_scaled_font_map->hash_table, + &placeholder_scaled_font->hash_entry); + + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + + CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex); + cairo_scaled_font_destroy (placeholder_scaled_font); + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); +} + +static void +_cairo_scaled_font_placeholder_wait_for_creation_to_finish (cairo_scaled_font_t *placeholder_scaled_font) +{ + /* reference the place holder so it doesn't go away */ + cairo_scaled_font_reference (placeholder_scaled_font); + + /* now unlock the fontmap mutex so creation has a chance to finish */ + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + + /* wait on placeholder mutex until we are awaken */ + CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex); + + /* ok, creation done. just clean up and back out */ + CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex); + cairo_scaled_font_destroy (placeholder_scaled_font); + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex); +} + +/* Fowler / Noll / Vo (FNV) Hash (http://www.isthe.com/chongo/tech/comp/fnv/) + * + * Not necessarily better than a lot of other hashes, but should be OK, and + * well tested with binary data. + */ + +#define FNV_32_PRIME ((uint32_t)0x01000193) +#define FNV1_32_INIT ((uint32_t)0x811c9dc5) + +static uint32_t +_hash_matrix_fnv (const cairo_matrix_t *matrix, + uint32_t hval) +{ + const uint8_t *buffer = (const uint8_t *) matrix; + int len = sizeof (cairo_matrix_t); + do { + hval *= FNV_32_PRIME; + hval ^= *buffer++; + } while (--len); + + return hval; +} + +static uint32_t +_hash_mix_bits (uint32_t hash) +{ + hash += hash << 12; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; + return hash; +} + +static 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, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *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); + + scaled_font->hash_entry.hash = + _cairo_scaled_font_compute_hash (scaled_font); +} + +static cairo_bool_t +_cairo_scaled_font_keys_equal (const void *abstract_key_a, + const void *abstract_key_b) +{ + const cairo_scaled_font_t *key_a = abstract_key_a; + const cairo_scaled_font_t *key_b = abstract_key_b; + + return key_a->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 && + memcmp ((unsigned char *)(&key_a->ctm.xx), + (unsigned char *)(&key_b->ctm.xx), + sizeof(cairo_matrix_t)) == 0 && + cairo_font_options_equal (&key_a->options, &key_b->options); +} + +static cairo_bool_t +_cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font, + const cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + return scaled_font->original_font_face == font_face && + memcmp ((unsigned char *)(&scaled_font->font_matrix.xx), + (unsigned char *)(&font_matrix->xx), + sizeof(cairo_matrix_t)) == 0 && + memcmp ((unsigned char *)(&scaled_font->ctm.xx), + (unsigned char *)(&ctm->xx), + sizeof(cairo_matrix_t)) == 0 && + cairo_font_options_equal (&scaled_font->options, options); +} + +/* + * Basic #cairo_scaled_font_t object management + */ + +cairo_status_t +_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + const cairo_scaled_font_backend_t *backend) +{ + cairo_status_t status; + + status = cairo_font_options_status ((cairo_font_options_t *) options); + if (unlikely (status)) + return status; + + 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, + &scaled_font->ctm); + + scaled_font->max_scale = MAX (fabs (scaled_font->scale.xx) + fabs (scaled_font->scale.xy), + fabs (scaled_font->scale.yx) + fabs (scaled_font->scale.yy)); + scaled_font->scale_inverse = scaled_font->scale; + status = cairo_matrix_invert (&scaled_font->scale_inverse); + if (unlikely (status)) { + /* If the font scale matrix is rank 0, just using an all-zero inverse matrix + * makes everything work correctly. This make font size 0 work without + * producing an error. + * + * FIXME: If the scale is rank 1, we still go into error mode. But then + * again, that's what we do everywhere in cairo. + * + * Also, the check for == 0. below may be too harsh... + */ + if (_cairo_matrix_is_scale_0 (&scaled_font->scale)) { + cairo_matrix_init (&scaled_font->scale_inverse, + 0, 0, 0, 0, + -scaled_font->scale.x0, + -scaled_font->scale.y0); + } else + return status; + } + + scaled_font->glyphs = _cairo_hash_table_create (NULL); + if (unlikely (scaled_font->glyphs == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_list_init (&scaled_font->glyph_pages); + scaled_font->cache_frozen = FALSE; + scaled_font->global_cache_frozen = FALSE; + + scaled_font->holdover = FALSE; + scaled_font->finished = FALSE; + + CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1); + + _cairo_user_data_array_init (&scaled_font->user_data); + + cairo_font_face_reference (font_face); + scaled_font->original_font_face = NULL; + + CAIRO_MUTEX_INIT (scaled_font->mutex); + + cairo_list_init (&scaled_font->dev_privates); + + scaled_font->backend = backend; + cairo_list_init (&scaled_font->link); + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font) +{ + /* ensure we do not modify an error object */ + assert (scaled_font->status == CAIRO_STATUS_SUCCESS); + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + scaled_font->cache_frozen = TRUE; +} + +void +_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font) +{ + 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) +{ + 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); + + 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 +_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *fs_metrics) +{ + cairo_status_t status; + double font_scale_x, font_scale_y; + + scaled_font->fs_extents = *fs_metrics; + + status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->font_matrix, + &font_scale_x, &font_scale_y, + 1); + if (unlikely (status)) + return status; + + /* + * The font responded in unscaled units, scale by the font + * matrix scale factors to get to user space + */ + + scaled_font->extents.ascent = fs_metrics->ascent * font_scale_y; + scaled_font->extents.descent = fs_metrics->descent * font_scale_y; + scaled_font->extents.height = fs_metrics->height * font_scale_y; + scaled_font->extents.max_x_advance = fs_metrics->max_x_advance * font_scale_x; + scaled_font->extents.max_y_advance = fs_metrics->max_y_advance * font_scale_y; + + return CAIRO_STATUS_SUCCESS; +} + +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); + _cairo_hash_table_destroy (scaled_font->glyphs); + + cairo_font_face_destroy (scaled_font->font_face); + cairo_font_face_destroy (scaled_font->original_font_face); + + CAIRO_MUTEX_FINI (scaled_font->mutex); + + 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); + + _cairo_user_data_array_fini (&scaled_font->user_data); +} + +void +_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font) +{ + /* Release the lock to avoid the possibility of a recursive + * deadlock when the scaled font destroy closure gets called. */ + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex); + _cairo_scaled_font_fini_internal (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 + * @font_matrix: font space to user space transformation matrix for the + * font. In the simplest case of a N point font, this matrix is + * just a scale by N, but it can also be used to shear the font + * or stretch it unequally along the two axes. See + * cairo_set_font_matrix(). + * @ctm: user to device transformation matrix with which the font will + * be used. + * @options: options to use when getting metrics for the font and + * rendering with it. + * + * Creates a #cairo_scaled_font_t object from a font face and matrices that + * describe the size of the font and the environment in which it will + * be used. + * + * 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, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + cairo_status_t status; + cairo_scaled_font_map_t *font_map; + cairo_font_face_t *original_font_face = font_face; + cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL, *dead = NULL; + double det; + + status = font_face->status; + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + + det = _cairo_matrix_compute_determinant (font_matrix); + if (! ISFINITE (det)) + return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); + + det = _cairo_matrix_compute_determinant (ctm); + if (! ISFINITE (det)) + return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX)); + + status = cairo_font_options_status ((cairo_font_options_t *) options); + if (unlikely (status)) + return _cairo_scaled_font_create_in_error (status); + + /* Note that degenerate ctm or font_matrix *are* allowed. + * We want to support a font size of 0. */ + + font_map = _cairo_scaled_font_map_lock (); + if (unlikely (font_map == NULL)) + return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + scaled_font = font_map->mru_scaled_font; + if (scaled_font != NULL && + _cairo_scaled_font_matches (scaled_font, + font_face, font_matrix, ctm, options)) + { + assert (scaled_font->hash_entry.hash != ZOMBIE); + assert (! scaled_font->placeholder); + + 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_reference_count_inc (&scaled_font->ref_count); + _cairo_scaled_font_map_unlock (); + 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; + dead = scaled_font; + font_map->mru_scaled_font = NULL; + } + + _cairo_scaled_font_init_key (&key, font_face, font_matrix, ctm, options); + + while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table, + &key.hash_entry))) + { + if (! scaled_font->placeholder) + break; + + /* 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); + } + + 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; + } + } + + scaled_font->holdover = FALSE; + } + + /* reset any error status */ + scaled_font->status = CAIRO_STATUS_SUCCESS; + } + + if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) { + /* We increment the reference count manually here, (rather + * than calling into cairo_scaled_font_reference), since we + * must modify the reference count while our lock is still + * held. */ + + 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 (); + + cairo_scaled_font_destroy (old); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + + 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); + } + } + + status = font_face->backend->scaled_font_create (font_face, font_matrix, + ctm, options, &scaled_font); + /* Did we leave the backend in an error state? */ + if (unlikely (status)) { + _cairo_scaled_font_map_unlock (); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + + if (dead != NULL) + cairo_scaled_font_destroy (dead); + + status = _cairo_font_face_set_error (font_face, status); + return _cairo_scaled_font_create_in_error (status); + } + /* Or did we encounter an error whilst constructing the scaled font? */ + if (unlikely (scaled_font->status)) { + _cairo_scaled_font_map_unlock (); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + + if (dead != NULL) + cairo_scaled_font_destroy (dead); + + return scaled_font; + } + + /* Our caching above is defeated if the backend switches fonts on us - + * e.g. old incarnations of toy-font-face and lazily resolved + * ft-font-faces + */ + assert (scaled_font->font_face == font_face); + 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)) { + old = font_map->mru_scaled_font; + font_map->mru_scaled_font = scaled_font; + _cairo_reference_count_inc (&scaled_font->ref_count); + } + + _cairo_scaled_font_map_unlock (); + + cairo_scaled_font_destroy (old); + if (font_face != original_font_face) + cairo_font_face_destroy (font_face); + + if (dead != NULL) + cairo_scaled_font_destroy (dead); + + if (unlikely (status)) { + /* We can't call _cairo_scaled_font_destroy here since it expects + * that the font has already been successfully inserted into the + * hash table. */ + _cairo_scaled_font_fini_internal (scaled_font); + free (scaled_font); + return _cairo_scaled_font_create_in_error (status); + } + + return scaled_font; +} +slim_hidden_def (cairo_scaled_font_create); + +static cairo_scaled_font_t *_cairo_scaled_font_nil_objects[CAIRO_STATUS_LAST_STATUS + 1]; + +/* XXX This should disappear in favour of a common pool of error objects. */ +cairo_scaled_font_t * +_cairo_scaled_font_create_in_error (cairo_status_t status) +{ + cairo_scaled_font_t *scaled_font; + + assert (status != CAIRO_STATUS_SUCCESS); + + if (status == CAIRO_STATUS_NO_MEMORY) + return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); + scaled_font = _cairo_scaled_font_nil_objects[status]; + if (unlikely (scaled_font == NULL)) { + 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); + return (cairo_scaled_font_t *) &_cairo_scaled_font_nil; + } + + *scaled_font = _cairo_scaled_font_nil; + scaled_font->status = status; + _cairo_scaled_font_nil_objects[status] = scaled_font; + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); + + return scaled_font; +} + +void +_cairo_scaled_font_reset_static_data (void) +{ + int status; + + CAIRO_MUTEX_LOCK (_cairo_scaled_font_error_mutex); + for (status = CAIRO_STATUS_SUCCESS; + status <= CAIRO_STATUS_LAST_STATUS; + status++) + { + free (_cairo_scaled_font_nil_objects[status]); + _cairo_scaled_font_nil_objects[status] = NULL; + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex); + + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + if (cairo_scaled_glyph_page_cache.hash_table != NULL) { + _cairo_cache_fini (&cairo_scaled_glyph_page_cache); + cairo_scaled_glyph_page_cache.hash_table = NULL; + } + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); +} + +/** + * cairo_scaled_font_reference: + * @scaled_font: a #cairo_scaled_font_t, (may be %NULL in which case + * this function does nothing) + * + * Increases the reference count on @scaled_font by one. This prevents + * @scaled_font from being destroyed until a matching call to + * cairo_scaled_font_destroy() is made. + * + * 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) +{ + if (scaled_font == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return scaled_font; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); + + _cairo_reference_count_inc (&scaled_font->ref_count); + + return scaled_font; +} +slim_hidden_def (cairo_scaled_font_reference); + +/** + * cairo_scaled_font_destroy: + * @scaled_font: a #cairo_scaled_font_t + * + * 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) +{ + cairo_scaled_font_t *lru = NULL; + cairo_scaled_font_map_t *font_map; + + assert (CAIRO_MUTEX_IS_UNLOCKED (_cairo_scaled_font_map_mutex)); + + if (scaled_font == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)); + + 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 && + scaled_font->hash_entry.hash != ZOMBIE) + { + /* Another thread may have already inserted us into the holdovers */ + if (scaled_font->holdover) + goto unlock; + + /* Rather than immediately destroying this object, we put it into + * the font_map->holdovers array in case it will get used again + * soon (and is why we must hold the lock over the atomic op on + * the reference count). To make room for it, we do actually + * destroy the least-recently-used holdover. + */ + + if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) { + lru = font_map->holdovers[0]; + assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count)); + + _cairo_hash_table_remove (font_map->hash_table, + &lru->hash_entry); + + font_map->num_holdovers--; + memmove (&font_map->holdovers[0], + &font_map->holdovers[1], + font_map->num_holdovers * sizeof (cairo_scaled_font_t*)); + } + + font_map->holdovers[font_map->num_holdovers++] = scaled_font; + scaled_font->holdover = TRUE; + } else + lru = scaled_font; + } + + unlock: + _cairo_scaled_font_map_unlock (); + + /* If we pulled an item from the holdovers array, (while the font + * map lock was held, of course), then there is no way that anyone + * else could have acquired a reference to it. So we can now + * safely call fini on it without any lock held. This is desirable + * as we never want to call into any backend function with a lock + * held. */ + if (lru != NULL) { + _cairo_scaled_font_fini_internal (lru); + free (lru); + } +} +slim_hidden_def (cairo_scaled_font_destroy); + +/** + * cairo_scaled_font_get_reference_count: + * @scaled_font: a #cairo_scaled_font_t + * + * Returns the current reference count of @scaled_font. + * + * Return value: the current reference count of @scaled_font. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&scaled_font->ref_count); +} + +/** + * cairo_scaled_font_get_user_data: + * @scaled_font: a #cairo_scaled_font_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @scaled_font using the + * specified key. If no user data has been attached with the given + * key this function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + * + * Since: 1.4 + **/ +void * +cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, + const cairo_user_data_key_t *key) +{ + return _cairo_user_data_array_get_data (&scaled_font->user_data, + key); +} +slim_hidden_def (cairo_scaled_font_get_user_data); + +/** + * cairo_scaled_font_set_user_data: + * @scaled_font: a #cairo_scaled_font_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the #cairo_scaled_font_t + * @destroy: a #cairo_destroy_func_t which will be called when the + * #cairo_t is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @scaled_font. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count)) + return scaled_font->status; + + return _cairo_user_data_array_set_data (&scaled_font->user_data, + key, user_data, destroy); +} +slim_hidden_def (cairo_scaled_font_set_user_data); + +/* Public font API follows. */ + +/** + * cairo_scaled_font_extents: + * @scaled_font: a #cairo_scaled_font_t + * @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, + cairo_font_extents_t *extents) +{ + if (scaled_font->status) { + extents->ascent = 0.0; + extents->descent = 0.0; + extents->height = 0.0; + extents->max_x_advance = 0.0; + extents->max_y_advance = 0.0; + return; + } + + *extents = scaled_font->extents; +} +slim_hidden_def (cairo_scaled_font_extents); + +/** + * cairo_scaled_font_text_extents: + * @scaled_font: a #cairo_scaled_font_t + * @utf8: a NUL-terminated string of text, encoded in UTF-8 + * @extents: a #cairo_text_extents_t which to store the retrieved extents. + * + * Gets the extents for a string of text. The extents describe a + * user-space rectangle that encloses the "inked" portion of the text + * drawn at the origin (0,0) (as it would be drawn by cairo_show_text() + * if the cairo graphics state were set to the same font_face, + * font_matrix, ctm, and font_options as @scaled_font). Additionally, + * the x_advance and y_advance values indicate the amount by which the + * current point would be advanced by cairo_show_text(). + * + * Note that whitespace characters do not directly contribute to the + * size of the rectangle (extents.width and extents.height). They do + * contribute indirectly by changing the position of non-whitespace + * 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.2 + **/ +void +cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, + const char *utf8, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + cairo_glyph_t *glyphs = NULL; + int num_glyphs; + + if (scaled_font->status) + goto ZERO_EXTENTS; + + if (utf8 == NULL) + goto ZERO_EXTENTS; + + status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0., + utf8, -1, + &glyphs, &num_glyphs, + NULL, NULL, + NULL); + if (unlikely (status)) { + status = _cairo_scaled_font_set_error (scaled_font, status); + goto ZERO_EXTENTS; + } + + cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents); + free (glyphs); + + return; + +ZERO_EXTENTS: + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; +} + +/** + * cairo_scaled_font_glyph_extents: + * @scaled_font: a #cairo_scaled_font_t + * @glyphs: an array of glyph IDs with X and Y offsets. + * @num_glyphs: the number of glyphs in the @glyphs array + * @extents: a #cairo_text_extents_t which to store the retrieved extents. + * + * Gets the extents for an array of glyphs. The extents describe a + * user-space rectangle that encloses the "inked" portion of the + * glyphs, (as they would be drawn by cairo_show_glyphs() if the cairo + * graphics state were set to the same font_face, font_matrix, ctm, + * and font_options as @scaled_font). Additionally, the x_advance and + * y_advance values indicate the amount by which the current point + * would be advanced by cairo_show_glyphs(). + * + * 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, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + int i; + double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0; + cairo_bool_t visible = FALSE; + cairo_scaled_glyph_t *scaled_glyph = NULL; + + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + if (unlikely (scaled_font->status)) + goto ZERO_EXTENTS; + + if (num_glyphs == 0) + goto ZERO_EXTENTS; + + if (unlikely (num_glyphs < 0)) { + _cairo_error_throw (CAIRO_STATUS_NEGATIVE_COUNT); + /* XXX Can't propagate error */ + goto ZERO_EXTENTS; + } + + if (unlikely (glyphs == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); + /* XXX Can't propagate error */ + goto ZERO_EXTENTS; + } + + _cairo_scaled_font_freeze_cache (scaled_font); + + for (i = 0; i < num_glyphs; i++) { + double left, top, right, bottom; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) { + status = _cairo_scaled_font_set_error (scaled_font, status); + goto UNLOCK; + } + + /* "Ink" extents should skip "invisible" glyphs */ + if (scaled_glyph->metrics.width == 0 || scaled_glyph->metrics.height == 0) + continue; + + left = scaled_glyph->metrics.x_bearing + glyphs[i].x; + right = left + scaled_glyph->metrics.width; + top = scaled_glyph->metrics.y_bearing + glyphs[i].y; + bottom = top + scaled_glyph->metrics.height; + + if (!visible) { + visible = TRUE; + min_x = left; + max_x = right; + min_y = top; + max_y = bottom; + } else { + if (left < min_x) min_x = left; + if (right > max_x) max_x = right; + if (top < min_y) min_y = top; + if (bottom > max_y) max_y = bottom; + } + } + + if (visible) { + extents->x_bearing = min_x - glyphs[0].x; + extents->y_bearing = min_y - glyphs[0].y; + extents->width = max_x - min_x; + extents->height = max_y - min_y; + } else { + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + } + + if (num_glyphs) { + double x0, y0, x1, y1; + + x0 = glyphs[0].x; + y0 = glyphs[0].y; + + /* scaled_glyph contains the glyph for num_glyphs - 1 already. */ + x1 = glyphs[num_glyphs - 1].x + scaled_glyph->metrics.x_advance; + y1 = glyphs[num_glyphs - 1].y + scaled_glyph->metrics.y_advance; + + extents->x_advance = x1 - x0; + extents->y_advance = y1 - y0; + } else { + extents->x_advance = 0.0; + extents->y_advance = 0.0; + } + + UNLOCK: + _cairo_scaled_font_thaw_cache (scaled_font); + return; + +ZERO_EXTENTS: + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; +} +slim_hidden_def (cairo_scaled_font_glyph_extents); + +#define GLYPH_LUT_SIZE 64 +static cairo_status_t +cairo_scaled_font_text_to_glyphs_internal_cached (cairo_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t *glyphs, + cairo_text_cluster_t **clusters, + int num_chars) +{ + struct glyph_lut_elt { + unsigned long index; + double x_advance; + double y_advance; + } glyph_lut[GLYPH_LUT_SIZE]; + uint32_t glyph_lut_unicode[GLYPH_LUT_SIZE]; + cairo_status_t status; + const char *p; + int i; + + for (i = 0; i < GLYPH_LUT_SIZE; i++) + glyph_lut_unicode[i] = ~0U; + + p = utf8; + for (i = 0; i < num_chars; i++) { + int idx, num_bytes; + uint32_t unicode; + cairo_scaled_glyph_t *scaled_glyph; + struct glyph_lut_elt *glyph_slot; + + num_bytes = _cairo_utf8_get_char_validated (p, &unicode); + p += num_bytes; + + glyphs[i].x = x; + glyphs[i].y = y; + + idx = unicode % ARRAY_LENGTH (glyph_lut); + glyph_slot = &glyph_lut[idx]; + if (glyph_lut_unicode[idx] == unicode) { + glyphs[i].index = glyph_slot->index; + x += glyph_slot->x_advance; + y += glyph_slot->y_advance; + } else { + unsigned long g; + + g = scaled_font->backend->ucs4_to_index (scaled_font, unicode); + status = _cairo_scaled_glyph_lookup (scaled_font, + g, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + return status; + + x += scaled_glyph->metrics.x_advance; + y += scaled_glyph->metrics.y_advance; + + glyph_lut_unicode[idx] = unicode; + glyph_slot->index = g; + glyph_slot->x_advance = scaled_glyph->metrics.x_advance; + glyph_slot->y_advance = scaled_glyph->metrics.y_advance; + + glyphs[i].index = g; + } + + if (clusters) { + (*clusters)[i].num_bytes = num_bytes; + (*clusters)[i].num_glyphs = 1; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_scaled_font_text_to_glyphs_internal_uncached (cairo_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t *glyphs, + cairo_text_cluster_t **clusters, + int num_chars) +{ + const char *p; + int i; + + p = utf8; + for (i = 0; i < num_chars; i++) { + unsigned long g; + int num_bytes; + uint32_t unicode; + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + + num_bytes = _cairo_utf8_get_char_validated (p, &unicode); + p += num_bytes; + + glyphs[i].x = x; + glyphs[i].y = y; + + g = scaled_font->backend->ucs4_to_index (scaled_font, unicode); + + /* + * No advance needed for a single character string. So, let's speed up + * one-character strings by skipping glyph lookup. + */ + if (num_chars > 1) { + status = _cairo_scaled_glyph_lookup (scaled_font, + g, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + return status; + + x += scaled_glyph->metrics.x_advance; + y += scaled_glyph->metrics.y_advance; + } + + glyphs[i].index = g; + + if (clusters) { + (*clusters)[i].num_bytes = num_bytes; + (*clusters)[i].num_glyphs = 1; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_scaled_font_text_to_glyphs: + * @x: X position to place first glyph + * @y: Y position to place first glyph + * @scaled_font: a #cairo_scaled_font_t + * @utf8: a string of text encoded in UTF-8 + * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated + * @glyphs: pointer to array of glyphs to fill + * @num_glyphs: pointer to number of glyphs + * @clusters: pointer to array of cluster mapping information to fill, or %NULL + * @num_clusters: pointer to number of clusters, or %NULL + * @cluster_flags: pointer to location to store cluster flags corresponding to the + * output @clusters, or %NULL + * + * Converts UTF-8 text to an array of glyphs, optionally with cluster + * mapping, that can be used to render later using @scaled_font. + * + * If @glyphs initially points to a non-%NULL value, that array is used + * as a glyph buffer, and @num_glyphs should point to the number of glyph + * entries available there. If the provided glyph array is too short for + * the conversion, a new glyph array is allocated using cairo_glyph_allocate() + * and placed in @glyphs. Upon return, @num_glyphs always contains the + * number of generated glyphs. If the value @glyphs points to has changed + * after the call, the user is responsible for freeing the allocated glyph + * array using cairo_glyph_free(). This may happen even if the provided + * array was large enough. + * + * If @clusters is not %NULL, @num_clusters and @cluster_flags should not be %NULL, + * and cluster mapping will be computed. + * The semantics of how cluster array allocation works is similar to the glyph + * array. That is, + * if @clusters initially points to a non-%NULL value, that array is used + * as a cluster buffer, and @num_clusters should point to the number of cluster + * entries available there. If the provided cluster array is too short for + * the conversion, a new cluster array is allocated using cairo_text_cluster_allocate() + * and placed in @clusters. Upon return, @num_clusters always contains the + * number of generated clusters. If the value @clusters points at has changed + * after the call, the user is responsible for freeing the allocated cluster + * array using cairo_text_cluster_free(). This may happen even if the provided + * array was large enough. + * + * In the simplest case, @glyphs and @clusters can point to %NULL initially + * and a suitable array will be allocated. In code: + * + * cairo_status_t status; + * + * cairo_glyph_t *glyphs = NULL; + * int num_glyphs; + * cairo_text_cluster_t *clusters = NULL; + * int num_clusters; + * cairo_text_cluster_flags_t cluster_flags; + * + * status = cairo_scaled_font_text_to_glyphs (scaled_font, + * x, y, + * utf8, utf8_len, + * &glyphs, &num_glyphs, + * &clusters, &num_clusters, &cluster_flags); + * + * if (status == CAIRO_STATUS_SUCCESS) { + * cairo_show_text_glyphs (cr, + * utf8, utf8_len, + * glyphs, num_glyphs, + * clusters, num_clusters, cluster_flags); + * + * cairo_glyph_free (glyphs); + * cairo_text_cluster_free (clusters); + * } + * + * + * If no cluster mapping is needed: + * + * cairo_status_t status; + * + * cairo_glyph_t *glyphs = NULL; + * int num_glyphs; + * + * status = cairo_scaled_font_text_to_glyphs (scaled_font, + * x, y, + * utf8, utf8_len, + * &glyphs, &num_glyphs, + * NULL, NULL, + * NULL); + * + * if (status == CAIRO_STATUS_SUCCESS) { + * cairo_show_glyphs (cr, glyphs, num_glyphs); + * cairo_glyph_free (glyphs); + * } + * + * + * If stack-based glyph and cluster arrays are to be used for small + * arrays: + * + * cairo_status_t status; + * + * cairo_glyph_t stack_glyphs[40]; + * cairo_glyph_t *glyphs = stack_glyphs; + * int num_glyphs = sizeof (stack_glyphs) / sizeof (stack_glyphs[0]); + * cairo_text_cluster_t stack_clusters[40]; + * cairo_text_cluster_t *clusters = stack_clusters; + * int num_clusters = sizeof (stack_clusters) / sizeof (stack_clusters[0]); + * cairo_text_cluster_flags_t cluster_flags; + * + * status = cairo_scaled_font_text_to_glyphs (scaled_font, + * x, y, + * utf8, utf8_len, + * &glyphs, &num_glyphs, + * &clusters, &num_clusters, &cluster_flags); + * + * if (status == CAIRO_STATUS_SUCCESS) { + * cairo_show_text_glyphs (cr, + * utf8, utf8_len, + * glyphs, num_glyphs, + * clusters, num_clusters, cluster_flags); + * + * if (glyphs != stack_glyphs) + * cairo_glyph_free (glyphs); + * if (clusters != stack_clusters) + * cairo_text_cluster_free (clusters); + * } + * + * + * For details of how @clusters, @num_clusters, and @cluster_flags map input + * UTF-8 text to the output glyphs see cairo_show_text_glyphs(). + * + * The output values can be readily passed to cairo_show_text_glyphs() + * cairo_show_glyphs(), or related functions, assuming that the exact + * same @scaled_font is used for the operation. + * + * Return value: %CAIRO_STATUS_SUCCESS upon success, or an error status + * if the input values are wrong or if conversion failed. If the input + * values are correct but the conversion failed, the error status is also + * set on @scaled_font. + * + * Since: 1.8 + **/ +#define CACHING_THRESHOLD 16 +cairo_status_t +cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, + 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) +{ + int num_chars = 0; + cairo_int_status_t status; + cairo_glyph_t *orig_glyphs; + cairo_text_cluster_t *orig_clusters; + + status = scaled_font->status; + if (unlikely (status)) + return status; + + /* A slew of sanity checks */ + + /* glyphs and num_glyphs can't be NULL */ + if (glyphs == NULL || + num_glyphs == NULL) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto BAIL; + } + + /* Special case for NULL and -1 */ + if (utf8 == NULL && utf8_len == -1) + utf8_len = 0; + + /* No NULLs for non-NULLs! */ + if ((utf8_len && utf8 == NULL) || + (clusters && num_clusters == NULL) || + (clusters && cluster_flags == NULL)) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto BAIL; + } + + /* A -1 for utf8_len means NUL-terminated */ + if (utf8_len == -1) + utf8_len = strlen (utf8); + + /* A NULL *glyphs means no prealloced glyphs array */ + if (glyphs && *glyphs == NULL) + *num_glyphs = 0; + + /* A NULL *clusters means no prealloced clusters array */ + if (clusters && *clusters == NULL) + *num_clusters = 0; + + if (!clusters && num_clusters) { + num_clusters = NULL; + } + + if (cluster_flags) { + *cluster_flags = FALSE; + } + + if (!clusters && cluster_flags) { + cluster_flags = NULL; + } + + /* Apart from that, no negatives */ + if (utf8_len < 0 || + *num_glyphs < 0 || + (num_clusters && *num_clusters < 0)) { + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); + goto BAIL; + } + + if (utf8_len == 0) { + status = CAIRO_STATUS_SUCCESS; + goto BAIL; + } + + /* validate input so backend does not have to */ + status = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, &num_chars); + if (unlikely (status)) + goto BAIL; + + _cairo_scaled_font_freeze_cache (scaled_font); + + orig_glyphs = *glyphs; + orig_clusters = clusters ? *clusters : NULL; + + if (scaled_font->backend->text_to_glyphs) { + status = scaled_font->backend->text_to_glyphs (scaled_font, x, y, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + if (status == CAIRO_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. */ + + if (*num_glyphs < 0) { + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); + goto DONE; + } + if (*num_glyphs != 0 && *glyphs == NULL) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto DONE; + } + + if (clusters) { + if (*num_clusters < 0) { + status = _cairo_error (CAIRO_STATUS_NEGATIVE_COUNT); + goto DONE; + } + if (*num_clusters != 0 && *clusters == NULL) { + status = _cairo_error (CAIRO_STATUS_NULL_POINTER); + goto DONE; + } + + /* Don't trust the backend, validate clusters! */ + status = + _cairo_validate_text_clusters (utf8, utf8_len, + *glyphs, *num_glyphs, + *clusters, *num_clusters, + *cluster_flags); + } + } + + goto DONE; + } + } + + if (*num_glyphs < num_chars) { + *glyphs = cairo_glyph_allocate (num_chars); + if (unlikely (*glyphs == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto DONE; + } + } + *num_glyphs = num_chars; + + if (clusters) { + if (*num_clusters < num_chars) { + *clusters = cairo_text_cluster_allocate (num_chars); + if (unlikely (*clusters == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto DONE; + } + } + *num_clusters = num_chars; + } + + if (num_chars > CACHING_THRESHOLD) + status = cairo_scaled_font_text_to_glyphs_internal_cached (scaled_font, + x, y, + utf8, + *glyphs, + clusters, + num_chars); + else + status = cairo_scaled_font_text_to_glyphs_internal_uncached (scaled_font, + x, y, + utf8, + *glyphs, + clusters, + num_chars); + + DONE: /* error that should be logged on scaled_font happened */ + _cairo_scaled_font_thaw_cache (scaled_font); + + if (unlikely (status)) { + *num_glyphs = 0; + if (*glyphs != orig_glyphs) { + cairo_glyph_free (*glyphs); + *glyphs = orig_glyphs; + } + + if (clusters) { + *num_clusters = 0; + if (*clusters != orig_clusters) { + cairo_text_cluster_free (*clusters); + *clusters = orig_clusters; + } + } + } + + return _cairo_scaled_font_set_error (scaled_font, status); + + BAIL: /* error with input arguments */ + + if (num_glyphs) + *num_glyphs = 0; + + if (num_clusters) + *num_clusters = 0; + + return status; +} +slim_hidden_def (cairo_scaled_font_text_to_glyphs); + +static inline cairo_bool_t +_range_contains_glyph (const cairo_box_t *extents, + cairo_fixed_t left, + cairo_fixed_t top, + 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. + */ +cairo_status_t +_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_int_t *extents, + cairo_bool_t *overlap_out) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }}; + cairo_scaled_glyph_t *glyph_cache[64]; + cairo_bool_t overlap = overlap_out ? FALSE : TRUE; + cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options); + int i; + + 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)); + + for (i = 0; i < num_glyphs; i++) { + cairo_scaled_glyph_t *scaled_glyph; + cairo_fixed_t x, y, x1, y1, x2, y2; + int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache); + + scaled_glyph = glyph_cache[cache_index]; + if (scaled_glyph == NULL || + _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index) + { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + glyph_cache[cache_index] = scaled_glyph; + } + + if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON) + x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x)); + else + x = _cairo_fixed_from_double (glyphs[i].x); + x1 = x + scaled_glyph->bbox.p1.x; + x2 = x + scaled_glyph->bbox.p2.x; + + if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON) + y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y)); + else + y = _cairo_fixed_from_double (glyphs[i].y); + y1 = y + scaled_glyph->bbox.p1.y; + y2 = y + scaled_glyph->bbox.p2.y; + + if (overlap == FALSE) + overlap = _range_contains_glyph (&box, x1, y1, x2, y2); + + if (x1 < box.p1.x) box.p1.x = x1; + if (x2 > box.p2.x) box.p2.x = x2; + if (y1 < box.p1.y) box.p1.y = y1; + if (y2 > box.p2.y) box.p2.y = y2; + } + + _cairo_scaled_font_thaw_cache (scaled_font); + if (unlikely (status)) + return _cairo_scaled_font_set_error (scaled_font, status); + + if (box.p1.x < box.p2.x) { + _cairo_box_round_to_rectangle (&box, extents); + } else { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + } + + if (overlap_out != NULL) + *overlap_out = overlap; + + return CAIRO_STATUS_SUCCESS; +} + +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, x1, y0, y1, pad; + int 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; + if (g < x0) x0 = g; + if (g > x1) x1 = g; + + g = glyphs[i].y; + if (g < y0) y0 = g; + if (g > y1) y1 = g; + } + + pad = MAX(scaled_font->fs_extents.max_x_advance, + scaled_font->fs_extents.height); + pad *= scaled_font->max_scale; + + 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, + 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_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; + int i; + + /* 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 (scaled_font->status) + return scaled_font->status; + + if (!num_glyphs) + return CAIRO_STATUS_SUCCESS; + + if (scaled_font->backend->show_glyphs != NULL) { + int remaining_glyphs = num_glyphs; + status = scaled_font->backend->show_glyphs (scaled_font, + op, pattern, + surface, + source_x, source_y, + dest_x, dest_y, + width, height, + glyphs, num_glyphs, + clip_region, + &remaining_glyphs); + glyphs += num_glyphs - remaining_glyphs; + num_glyphs = remaining_glyphs; + if (remaining_glyphs == 0) + status = CAIRO_INT_STATUS_SUCCESS; + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return _cairo_scaled_font_set_error (scaled_font, status); + } + + /* Font display routine either does not exist or failed. */ + + _cairo_scaled_font_freeze_cache (scaled_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 (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + + if (unlikely (status)) + goto CLEANUP_MASK; + + glyph_surface = scaled_glyph->surface; + + /* 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->format; + mask = cairo_image_surface_create (mask_format, width, height); + status = mask->status; + if (unlikely (status)) + goto CLEANUP_MASK; + } + + /* If we have glyphs of different formats, we "upgrade" the mask + * to the wider of the formats. */ + if (glyph_surface->format != mask_format && + _cairo_format_bits_per_pixel (mask_format) < + _cairo_format_bits_per_pixel (glyph_surface->format) ) + { + cairo_surface_t *new_mask; + + switch (glyph_surface->format) { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_A8: + case CAIRO_FORMAT_A1: + mask_format = glyph_surface->format; + break; + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + mask_format = CAIRO_FORMAT_ARGB32; + break; + } + + new_mask = cairo_image_surface_create (mask_format, width, height); + status = new_mask->status; + if (unlikely (status)) { + cairo_surface_destroy (new_mask); + goto CLEANUP_MASK; + } + + _cairo_pattern_init_for_surface (&mask_pattern, mask); + /* Note that we only upgrade masks, i.e. A1 -> A8 -> ARGB32, so there is + * never any component alpha here. + */ + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + &mask_pattern.base, + new_mask, + 0, 0, + 0, 0, + 0, 0, + width, height, + NULL); + + _cairo_pattern_fini (&mask_pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (new_mask); + goto CLEANUP_MASK; + } + + cairo_surface_destroy (mask); + mask = new_mask; + } + + if (glyph_surface->width && glyph_surface->height) { + cairo_surface_pattern_t glyph_pattern; + + /* 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); + + _cairo_pattern_init_for_surface (&glyph_pattern, + &glyph_surface->base); + if (mask_format == CAIRO_FORMAT_ARGB32) + glyph_pattern.base.has_component_alpha = TRUE; + + status = _cairo_surface_composite (CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + &glyph_pattern.base, + mask, + 0, 0, + 0, 0, + x - dest_x, y - dest_y, + glyph_surface->width, + glyph_surface->height, + NULL); + + _cairo_pattern_fini (&glyph_pattern.base); + + if (unlikely (status)) + goto CLEANUP_MASK; + } + } + + _cairo_pattern_init_for_surface (&mask_pattern, mask); + if (mask_format == CAIRO_FORMAT_ARGB32) + mask_pattern.base.has_component_alpha = TRUE; + + status = _cairo_surface_composite (op, pattern, &mask_pattern.base, + surface, + source_x, source_y, + 0, 0, + dest_x, dest_y, + width, height, + clip_region); + + _cairo_pattern_fini (&mask_pattern.base); + +CLEANUP_MASK: + _cairo_scaled_font_thaw_cache (scaled_font); + + if (mask != NULL) + 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 +_add_unit_rectangle_to_path (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y) +{ + 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, + _cairo_fixed_from_int (1), + _cairo_fixed_from_int (0)); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (0), + _cairo_fixed_from_int (1)); + if (unlikely (status)) + return status; + + status = _cairo_path_fixed_rel_line_to (path, + _cairo_fixed_from_int (-1), + _cairo_fixed_from_int (0)); + if (unlikely (status)) + return status; + + return _cairo_path_fixed_close_path (path); +} + +/** + * _trace_mask_to_path: + * @bitmap: An alpha mask (either %CAIRO_FORMAT_A1 or %CAIRO_FORMAT_A8) + * @path: An initialized path to hold the result + * + * Given a mask surface, (an alpha image), fill out the provided path + * so that when filled it would result in something that approximates + * the mask. + * + * Note: The current tracing code here is extremely primitive. It + * operates only on an A1 surface, (converting an A8 surface to A1 if + * necessary), and performs the tracing by drawing a little square + * around each pixel that is on in the mask. We do not pretend that + * this is a high-quality result. But we are leaving it up to someone + * who cares enough about getting a better result to implement + * something more sophisticated. + **/ +static cairo_status_t +_trace_mask_to_path (cairo_image_surface_t *mask, + cairo_path_fixed_t *path, + double tx, double ty) +{ + const uint8_t *row; + int rows, cols, bytes_per_row; + int x, y, bit; + double xoff, yoff; + cairo_fixed_t x0, y0; + cairo_fixed_t px, py; + cairo_status_t status; + + mask = _cairo_image_surface_coerce_to_format (mask, CAIRO_FORMAT_A1); + status = mask->base.status; + if (unlikely (status)) + return status; + + cairo_surface_get_device_offset (&mask->base, &xoff, &yoff); + x0 = _cairo_fixed_from_double (tx - xoff); + y0 = _cairo_fixed_from_double (ty - yoff); + + bytes_per_row = (mask->width + 7) / 8; + row = mask->data; + for (y = 0, rows = mask->height; rows--; row += mask->stride, y++) { + const uint8_t *byte_ptr = row; + x = 0; + py = _cairo_fixed_from_int (y); + for (cols = bytes_per_row; cols--; ) { + uint8_t byte = *byte_ptr++; + if (byte == 0) { + x += 8; + continue; + } + + byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (byte); + for (bit = 1 << 7; bit && x < mask->width; bit >>= 1, x++) { + if (byte & bit) { + px = _cairo_fixed_from_int (x); + status = _add_unit_rectangle_to_path (path, + px + x0, + py + y0); + if (unlikely (status)) + goto BAIL; + } + } + } + } + +BAIL: + cairo_surface_destroy (&mask->base); + + return status; +} + +cairo_status_t +_cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path) +{ + cairo_int_status_t status; + int i; + + status = scaled_font->status; + if (unlikely (status)) + return status; + + _cairo_scaled_font_freeze_cache (scaled_font); + 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_PATH, + &scaled_glyph); + if (status == CAIRO_INT_STATUS_SUCCESS) { + status = _cairo_path_fixed_append (path, + scaled_glyph->path, + _cairo_fixed_from_double (glyphs[i].x), + _cairo_fixed_from_double (glyphs[i].y)); + + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + /* If the font is incapable of providing a path, then we'll + * have to trace our own from a surface. + */ + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + goto BAIL; + + status = _trace_mask_to_path (scaled_glyph->surface, path, + glyphs[i].x, glyphs[i].y); + } + + if (unlikely (status)) + goto BAIL; + } + BAIL: + _cairo_scaled_font_thaw_cache (scaled_font); + + return _cairo_scaled_font_set_error (scaled_font, status); +} + +/** + * _cairo_scaled_glyph_set_metrics: + * @scaled_glyph: a #cairo_scaled_glyph_t + * @scaled_font: a #cairo_scaled_font_t + * @fs_metrics: a #cairo_text_extents_t in font space + * + * _cairo_scaled_glyph_set_metrics() stores user space metrics + * for the specified glyph given font space metrics. It is + * called by the font backend when initializing a glyph with + * %CAIRO_SCALED_GLYPH_INFO_METRICS. + **/ +void +_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_text_extents_t *fs_metrics) +{ + cairo_bool_t first = TRUE; + double hm, wm; + double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0; + double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0; + double device_x_advance, device_y_advance; + + scaled_glyph->fs_metrics = *fs_metrics; + + for (hm = 0.0; hm <= 1.0; hm += 1.0) + for (wm = 0.0; wm <= 1.0; wm += 1.0) { + double x, y; + + /* Transform this corner to user space */ + x = fs_metrics->x_bearing + fs_metrics->width * wm; + y = fs_metrics->y_bearing + fs_metrics->height * hm; + cairo_matrix_transform_point (&scaled_font->font_matrix, + &x, &y); + if (first) { + min_user_x = max_user_x = x; + min_user_y = max_user_y = y; + } else { + if (x < min_user_x) min_user_x = x; + if (x > max_user_x) max_user_x = x; + if (y < min_user_y) min_user_y = y; + if (y > max_user_y) max_user_y = y; + } + + /* Transform this corner to device space from glyph origin */ + x = fs_metrics->x_bearing + fs_metrics->width * wm; + y = fs_metrics->y_bearing + fs_metrics->height * hm; + cairo_matrix_transform_distance (&scaled_font->scale, + &x, &y); + + if (first) { + min_device_x = max_device_x = x; + min_device_y = max_device_y = y; + } else { + if (x < min_device_x) min_device_x = x; + if (x > max_device_x) max_device_x = x; + if (y < min_device_y) min_device_y = y; + if (y > max_device_y) max_device_y = y; + } + first = FALSE; + } + scaled_glyph->metrics.x_bearing = min_user_x; + scaled_glyph->metrics.y_bearing = min_user_y; + scaled_glyph->metrics.width = max_user_x - min_user_x; + scaled_glyph->metrics.height = max_user_y - min_user_y; + + scaled_glyph->metrics.x_advance = fs_metrics->x_advance; + scaled_glyph->metrics.y_advance = fs_metrics->y_advance; + cairo_matrix_transform_distance (&scaled_font->font_matrix, + &scaled_glyph->metrics.x_advance, + &scaled_glyph->metrics.y_advance); + + device_x_advance = fs_metrics->x_advance; + device_y_advance = fs_metrics->y_advance; + cairo_matrix_transform_distance (&scaled_font->scale, + &device_x_advance, + &device_y_advance); + + scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x); + scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y); + scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x); + scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y); + + scaled_glyph->x_advance = _cairo_lround (device_x_advance); + scaled_glyph->y_advance = _cairo_lround (device_y_advance); + + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_METRICS; +} + +void +_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_image_surface_t *surface) +{ + if (scaled_glyph->surface != NULL) + cairo_surface_destroy (&scaled_glyph->surface->base); + + /* sanity check the backend glyph contents */ + _cairo_debug_check_image_surface_is_defined (&surface->base); + scaled_glyph->surface = surface; + + if (surface != NULL) + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_SURFACE; + else + scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_SURFACE; +} + +void +_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_path_fixed_t *path) +{ + if (scaled_glyph->path != NULL) + _cairo_path_fixed_destroy (scaled_glyph->path); + + scaled_glyph->path = path; + + if (path != NULL) + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_PATH; + else + scaled_glyph->has_info &= ~CAIRO_SCALED_GLYPH_INFO_PATH; +} + +void +_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface) +{ + if (scaled_glyph->recording_surface != NULL) { + cairo_surface_finish (scaled_glyph->recording_surface); + cairo_surface_destroy (scaled_glyph->recording_surface); + } + + scaled_glyph->recording_surface = recording_surface; + + if (recording_surface != NULL) + scaled_glyph->has_info |= CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE; + else + 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 = page->scaled_font; + return scaled_font->cache_frozen == 0; +} + +static cairo_status_t +_cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t **scaled_glyph) +{ + cairo_scaled_glyph_page_t *page; + cairo_status_t status; + + 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, + cairo_scaled_glyph_page_t, + link); + if (page->num_glyphs < CAIRO_SCALED_GLYPH_PAGE_SIZE) { + *scaled_glyph = &page->glyphs[page->num_glyphs++]; + return CAIRO_STATUS_SUCCESS; + } + } + + page = _cairo_malloc (sizeof (cairo_scaled_glyph_page_t)); + if (unlikely (page == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + page->cache_entry.hash = (unsigned long) scaled_font; + page->scaled_font = scaled_font; + page->cache_entry.size = 1; /* XXX occupancy weighting? */ + page->num_glyphs = 0; + + CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex); + if (scaled_font->global_cache_frozen == FALSE) { + if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) { + status = _cairo_cache_init (&cairo_scaled_glyph_page_cache, + NULL, + _cairo_scaled_glyph_page_can_remove, + _cairo_scaled_glyph_page_pluck, + MAX_GLYPH_PAGES_CACHED); + if (unlikely (status)) { + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + free (page); + return status; + } + } + + _cairo_cache_freeze (&cairo_scaled_glyph_page_cache); + scaled_font->global_cache_frozen = TRUE; + } + + status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache, + &page->cache_entry); + CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex); + if (unlikely (status)) { + free (page); + return status; + } + + cairo_list_add_tail (&page->link, &scaled_font->glyph_pages); + + *scaled_glyph = &page->glyphs[page->num_glyphs++]; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_scaled_glyph_page_t *page; + + 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, + link); + assert (scaled_glyph == &page->glyphs[page->num_glyphs-1]); + + _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); + } +} + +/** + * _cairo_scaled_glyph_lookup: + * @scaled_font: a #cairo_scaled_font_t + * @index: the glyph to create + * @info: a #cairo_scaled_glyph_info_t marking which portions of + * the glyph should be filled in. + * @scaled_glyph_ret: a #cairo_scaled_glyph_t where the glyph + * is returned. + * + * If the desired info is not available, (for example, when trying to + * get INFO_PATH with a bitmapped font), this function will return + * %CAIRO_INT_STATUS_UNSUPPORTED. + * + * Note: This function must be called with the scaled font frozen, and it must + * remain frozen for as long as the @scaled_glyph_ret is alive. (If the scaled + * font was not frozen, then there is no guarantee that the glyph would not be + * evicted before you tried to access it.) See + * _cairo_scaled_font_freeze_cache() and _cairo_scaled_font_thaw_cache(). + * + * Returns: a glyph with the requested portions filled in. Glyph + * lookup is cached and glyph will be automatically freed along + * with the scaled_font so no explicit free is required. + * @info can be one or more of: + * %CAIRO_SCALED_GLYPH_INFO_METRICS - glyph metrics and bounding box + * %CAIRO_SCALED_GLYPH_INFO_SURFACE - surface holding glyph image + * %CAIRO_SCALED_GLYPH_INFO_PATH - path holding glyph outline in device space + **/ +cairo_int_status_t +_cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, + unsigned long index, + cairo_scaled_glyph_info_t info, + cairo_scaled_glyph_t **scaled_glyph_ret) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph; + cairo_scaled_glyph_info_t need_info; + + *scaled_glyph_ret = NULL; + + 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); + + /* + * Check cache for glyph + */ + scaled_glyph = _cairo_hash_table_lookup (scaled_font->glyphs, + (cairo_hash_entry_t *) &index); + if (scaled_glyph == NULL) { + status = _cairo_scaled_font_allocate_glyph (scaled_font, &scaled_glyph); + if (unlikely (status)) + goto err; + + 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 = + scaled_font->backend->scaled_glyph_init (scaled_font, + scaled_glyph, + info | CAIRO_SCALED_GLYPH_INFO_METRICS); + if (unlikely (status)) { + _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); + goto err; + } + + status = _cairo_hash_table_insert (scaled_font->glyphs, + &scaled_glyph->hash_entry); + if (unlikely (status)) { + _cairo_scaled_font_free_last_glyph (scaled_font, scaled_glyph); + goto err; + } + } + + /* + * Check and see if the glyph, as provided, + * already has the requested data and amend it if not + */ + need_info = info & ~scaled_glyph->has_info; + if (need_info) { + status = scaled_font->backend->scaled_glyph_init (scaled_font, + scaled_glyph, + need_info); + if (unlikely (status)) + goto err; + + /* Don't trust the scaled_glyph_init() return value, the font + * backend may not even know about some of the info. For example, + * no backend other than the user-fonts knows about recording-surface + * glyph info. */ + if (info & ~scaled_glyph->has_info) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *scaled_glyph_ret = scaled_glyph; + return CAIRO_STATUS_SUCCESS; + +err: + /* It's not an error for the backend to not support the info we want. */ + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_scaled_font_set_error (scaled_font, status); + return status; +} + +double +_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->max_scale; +} + + +/** + * cairo_scaled_font_get_font_face: + * @scaled_font: a #cairo_scaled_font_t + * + * 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. This object is owned by cairo. To keep a reference to it, + * you must call cairo_scaled_font_reference(). + * + * Since: 1.2 + **/ +cairo_font_face_t * +cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font->status) + return (cairo_font_face_t*) &_cairo_font_face_nil; + + if (scaled_font->original_font_face != NULL) + return scaled_font->original_font_face; + + return scaled_font->font_face; +} +slim_hidden_def (cairo_scaled_font_get_font_face); + +/** + * cairo_scaled_font_get_font_matrix: + * @scaled_font: a #cairo_scaled_font_t + * @font_matrix: return value for the matrix + * + * Stores the font matrix with which @scaled_font was created into + * @matrix. + * + * Since: 1.2 + **/ +void +cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *font_matrix) +{ + if (scaled_font->status) { + cairo_matrix_init_identity (font_matrix); + return; + } + + *font_matrix = scaled_font->font_matrix; +} +slim_hidden_def (cairo_scaled_font_get_font_matrix); + +/** + * cairo_scaled_font_get_ctm: + * @scaled_font: a #cairo_scaled_font_t + * @ctm: return value for the CTM + * + * Stores the CTM with which @scaled_font was created into @ctm. + * Note that the translation offsets (x0, y0) of the CTM are ignored + * by cairo_scaled_font_create(). So, the matrix this + * function returns always has 0,0 as x0,y0. + * + * Since: 1.2 + **/ +void +cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *ctm) +{ + if (scaled_font->status) { + cairo_matrix_init_identity (ctm); + return; + } + + *ctm = scaled_font->ctm; +} +slim_hidden_def (cairo_scaled_font_get_ctm); + +/** + * cairo_scaled_font_get_scale_matrix: + * @scaled_font: a #cairo_scaled_font_t + * @scale_matrix: return value for the matrix + * + * Stores the scale matrix of @scaled_font into @matrix. + * The scale matrix is product of the font matrix and the ctm + * associated with the scaled font, and hence is the matrix mapping from + * font space to device space. + * + * Since: 1.8 + **/ +void +cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *scale_matrix) +{ + if (scaled_font->status) { + cairo_matrix_init_identity (scale_matrix); + return; + } + + *scale_matrix = scaled_font->scale; +} + +/** + * cairo_scaled_font_get_font_options: + * @scaled_font: a #cairo_scaled_font_t + * @options: return value for the font options + * + * Stores the font options with which @scaled_font was created into + * @options. + * + * Since: 1.2 + **/ +void +cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, + cairo_font_options_t *options) +{ + if (cairo_font_options_status (options)) + return; + + if (scaled_font->status) { + _cairo_font_options_init_default (options); + return; + } + + _cairo_font_options_init_copy (options, &scaled_font->options); +} +slim_hidden_def (cairo_scaled_font_get_font_options); + +cairo_bool_t +_cairo_scaled_font_has_color_glyphs (cairo_scaled_font_t *scaled_font) +{ + if (scaled_font->backend != NULL && scaled_font->backend->has_color_glyphs != NULL) + return scaled_font->backend->has_color_glyphs (scaled_font); + else + return FALSE; +} 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 0000000000..5b506f500c --- /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 new file mode 100644 index 0000000000..4d7778b990 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-script-surface.c @@ -0,0 +1,4023 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + */ + +/* The script surface is one that records all operations performed on + * it in the form of a procedural script, similar in fashion to + * PostScript but using Cairo's imaging model. In essence, this is + * equivalent to the recording-surface, but as there is no impedance mismatch + * between Cairo and CairoScript, we can generate output immediately + * without having to copy and hold the data in memory. + */ + +/** + * 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-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-inline.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-surface-wrapper-private.h" + +#if CAIRO_HAS_FT_FONT +#include "cairo-ft-private.h" +#endif + +#include + +#ifdef WORDS_BIGENDIAN +#define to_be32(x) x +#else +#define to_be32(x) bswap_32(x) +#endif + +#define _cairo_output_stream_puts(S, STR) \ + _cairo_output_stream_write ((S), (STR), strlen (STR)) + +#define static cairo_warn static + +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_font cairo_script_font_t; + +typedef struct _operand { + enum { + SURFACE, + DEFERRED, + } type; + cairo_list_t link; +} operand_t; + + +struct deferred_finish { + cairo_list_t link; + operand_t operand; +}; + +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; + + struct _bitmap { + unsigned long min; + unsigned long count; + unsigned int map[64]; + struct _bitmap *next; + } surface_id, font_id; + + cairo_list_t operands; + cairo_list_t deferred; + + cairo_list_t fonts; + cairo_list_t defines; +}; + +struct _cairo_script_font { + cairo_scaled_font_private_t base; + + cairo_bool_t has_sfnt; + unsigned long id; + unsigned long subset_glyph_index; + cairo_list_t link; + cairo_scaled_font_t *parent; +}; + +struct _cairo_script_implicit_context { + cairo_operator_t current_operator; + cairo_fill_rule_t current_fill_rule; + double current_tolerance; + cairo_antialias_t current_antialias; + cairo_stroke_style_t current_style; + cairo_pattern_union_t current_source; + cairo_matrix_t current_ctm; + cairo_matrix_t current_stroke_matrix; + cairo_matrix_t current_font_matrix; + cairo_font_options_t current_font_options; + cairo_scaled_font_t *current_scaled_font; + cairo_path_fixed_t current_path; + cairo_bool_t has_clip; +}; + +struct _cairo_script_surface { + cairo_surface_t base; + + cairo_surface_wrapper_t wrapper; + + cairo_surface_clipper_t clipper; + + operand_t operand; + cairo_bool_t emitted; + cairo_bool_t defined; + cairo_bool_t active; + + double width, height; + + /* implicit flattened context */ + cairo_script_implicit_context_t cr; +}; + +static const cairo_surface_backend_t _cairo_script_surface_backend; + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_context_t *ctx, + cairo_content_t content, + cairo_rectangle_t *extents, + cairo_surface_t *passthrough); + +static void +_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); + +static void +_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr); + +static void +_bitmap_release_id (struct _bitmap *b, unsigned long token) +{ + struct _bitmap **prev = NULL; + + do { + if (token < b->min + sizeof (b->map) * CHAR_BIT) { + unsigned int bit, elem; + + token -= b->min; + elem = token / (sizeof (b->map[0]) * CHAR_BIT); + bit = token % (sizeof (b->map[0]) * CHAR_BIT); + b->map[elem] &= ~(1 << bit); + if (! --b->count && prev) { + *prev = b->next; + free (b); + } + return; + } + prev = &b->next; + b = b->next; + } while (b != NULL); +} + +static cairo_status_t +_bitmap_next_id (struct _bitmap *b, + unsigned long *id) +{ + struct _bitmap *bb, **prev = NULL; + unsigned long min = 0; + + do { + if (b->min != min) + break; + + if (b->count < sizeof (b->map) * CHAR_BIT) { + unsigned int n, m, bit; + for (n = 0; n < ARRAY_LENGTH (b->map); n++) { + if (b->map[n] == (unsigned int) -1) + continue; + + for (m=0, bit=1; mmap[0])*CHAR_BIT; m++, bit<<=1) { + if ((b->map[n] & bit) == 0) { + b->map[n] |= bit; + b->count++; + *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min; + return CAIRO_STATUS_SUCCESS; + } + } + } + } + min += sizeof (b->map) * CHAR_BIT; + + prev = &b->next; + b = b->next; + } while (b != NULL); + assert (prev != NULL); + + bb = _cairo_malloc (sizeof (struct _bitmap)); + if (unlikely (bb == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + *prev = bb; + bb->next = b; + bb->min = min; + bb->count = 1; + bb->map[0] = 0x1; + memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0])); + *id = min; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_bitmap_fini (struct _bitmap *b) +{ + while (b != NULL) { + struct _bitmap *next = b->next; + free (b); + b = next; + } +} + +static const char * +_direction_to_string (cairo_bool_t backward) +{ + static const char *names[] = { + "FORWARD", + "BACKWARD" + }; + assert (backward < ARRAY_LENGTH (names)); + return names[backward]; +} + +static const char * +_operator_to_string (cairo_operator_t op) +{ + static const char *names[] = { + "CLEAR", /* CAIRO_OPERATOR_CLEAR */ + + "SOURCE", /* CAIRO_OPERATOR_SOURCE */ + "OVER", /* CAIRO_OPERATOR_OVER */ + "IN", /* CAIRO_OPERATOR_IN */ + "OUT", /* CAIRO_OPERATOR_OUT */ + "ATOP", /* CAIRO_OPERATOR_ATOP */ + + "DEST", /* CAIRO_OPERATOR_DEST */ + "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ + "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ + "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ + "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ + + "XOR", /* CAIRO_OPERATOR_XOR */ + "ADD", /* CAIRO_OPERATOR_ADD */ + "SATURATE", /* CAIRO_OPERATOR_SATURATE */ + + "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ + "SCREEN", /* CAIRO_OPERATOR_SCREEN */ + "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ + "DARKEN", /* CAIRO_OPERATOR_DARKEN */ + "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ + "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ + "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ + "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ + "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ + "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ + "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ + "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ + "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ + "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ + "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ + }; + assert (op < ARRAY_LENGTH (names)); + return names[op]; +} + +static const char * +_extend_to_string (cairo_extend_t extend) +{ + static const char *names[] = { + "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ + "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ + "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ + }; + assert (extend < ARRAY_LENGTH (names)); + return names[extend]; +} + +static const char * +_filter_to_string (cairo_filter_t filter) +{ + static const char *names[] = { + "FILTER_FAST", /* CAIRO_FILTER_FAST */ + "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_BEST", /* CAIRO_FILTER_BEST */ + "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ + "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ + "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ + }; + assert (filter < ARRAY_LENGTH (names)); + return names[filter]; +} + +static const char * +_fill_rule_to_string (cairo_fill_rule_t rule) +{ + static const char *names[] = { + "WINDING", /* CAIRO_FILL_RULE_WINDING */ + "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ + }; + assert (rule < ARRAY_LENGTH (names)); + return names[rule]; +} + +static const char * +_antialias_to_string (cairo_antialias_t antialias) +{ + static const char *names[] = { + "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ + "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */ + "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */ + "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]; +} + +static const char * +_line_cap_to_string (cairo_line_cap_t line_cap) +{ + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ + "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + }; + assert (line_cap < ARRAY_LENGTH (names)); + return names[line_cap]; +} + +static const char * +_line_join_to_string (cairo_line_join_t line_join) +{ + static const char *names[] = { + "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ + "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ + "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ + }; + assert (line_join < ARRAY_LENGTH (names)); + return names[line_join]; +} + +static inline cairo_script_context_t * +to_context (cairo_script_surface_t *surface) +{ + return (cairo_script_context_t *) surface->base.device; +} + +static cairo_bool_t +target_is_active (cairo_script_surface_t *surface) +{ + return cairo_list_is_first (&surface->operand.link, + &to_context (surface)->operands); +} + +static void +target_push (cairo_script_surface_t *surface) +{ + cairo_list_move (&surface->operand.link, &to_context (surface)->operands); +} + +static int +target_depth (cairo_script_surface_t *surface) +{ + cairo_list_t *link; + int depth = 0; + + cairo_list_foreach (link, &to_context (surface)->operands) { + if (link == &surface->operand.link) + break; + depth++; + } + + return depth; +} + +static void +_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)); + 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 { + 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 "); + } + } +} + +static const char * +_content_to_string (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return "ALPHA"; + case CAIRO_CONTENT_COLOR: return "COLOR"; + default: + case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; + } +} + +static cairo_status_t +_emit_surface (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = to_context (surface); + + _cairo_output_stream_printf (ctx->stream, + "<< /content //%s", + _content_to_string (surface->base.content)); + if (surface->width != -1 && surface->height != -1) { + _cairo_output_stream_printf (ctx->stream, + " /width %f /height %f", + surface->width, + surface->height); + } + + if (surface->base.x_fallback_resolution != + CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT || + surface->base.y_fallback_resolution != + CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT) + { + _cairo_output_stream_printf (ctx->stream, + " /fallback-resolution [%f %f]", + surface->base.x_fallback_resolution, + surface->base.y_fallback_resolution); + } + + if (surface->base.device_transform.x0 != 0. || + surface->base.device_transform.y0 != 0.) + { + /* XXX device offset is encoded into the pattern matrices etc. */ + if (0) { + _cairo_output_stream_printf (ctx->stream, + " /device-offset [%f %f]", + surface->base.device_transform.x0, + surface->base.device_transform.y0); + } + } + + _cairo_output_stream_puts (ctx->stream, " >> surface context\n"); + surface->emitted = TRUE; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_context (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = to_context (surface); + + if (target_is_active (surface)) + return CAIRO_STATUS_SUCCESS; + + while (! cairo_list_is_empty (&ctx->operands)) { + operand_t *op; + cairo_script_surface_t *old; + + op = cairo_list_first_entry (&ctx->operands, + operand_t, + link); + if (op->type == DEFERRED) + break; + + old = cairo_container_of (op, cairo_script_surface_t, operand); + if (old == surface) + break; + if (old->active) + break; + + if (! old->defined) { + assert (old->emitted); + _cairo_output_stream_printf (ctx->stream, + "/target get /s%u exch def pop\n", + old->base.unique_id); + old->defined = TRUE; + } else { + _cairo_output_stream_puts (ctx->stream, "pop\n"); + } + + cairo_list_del (&old->operand.link); + } + + if (target_is_active (surface)) + return CAIRO_STATUS_SUCCESS; + + if (! surface->emitted) { + cairo_status_t status; + + status = _emit_surface (surface); + if (unlikely (status)) + return status; + } else if (cairo_list_is_empty (&surface->operand.link)) { + assert (surface->defined); + _cairo_output_stream_printf (ctx->stream, + "s%u context\n", + surface->base.unique_id); + _cairo_script_implicit_context_reset (&surface->cr); + _cairo_surface_clipper_reset (&surface->clipper); + } else { + int depth = target_depth (surface); + if (depth == 1) { + _cairo_output_stream_puts (ctx->stream, "exch\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll\n", + depth); + } + } + target_push (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_operator (cairo_script_surface_t *surface, + cairo_operator_t op) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_operator == op) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_operator = op; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-operator\n", + _operator_to_string (op)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_fill_rule (cairo_script_surface_t *surface, + cairo_fill_rule_t fill_rule) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_fill_rule == fill_rule) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_fill_rule = fill_rule; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-fill-rule\n", + _fill_rule_to_string (fill_rule)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_tolerance (cairo_script_surface_t *surface, + double tolerance, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) && + surface->cr.current_tolerance == tolerance) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_tolerance = tolerance; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%f set-tolerance\n", + tolerance); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_antialias (cairo_script_surface_t *surface, + cairo_antialias_t antialias) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_antialias == antialias) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_antialias = antialias; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-antialias\n", + _antialias_to_string (antialias)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_width (cairo_script_surface_t *surface, + double line_width, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) && + surface->cr.current_style.line_width == line_width) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_style.line_width = line_width; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%f set-line-width\n", + line_width); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_cap (cairo_script_surface_t *surface, + cairo_line_cap_t line_cap) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_style.line_cap == line_cap) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_cap = line_cap; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-line-cap\n", + _line_cap_to_string (line_cap)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_line_join (cairo_script_surface_t *surface, + cairo_line_join_t line_join) +{ + assert (target_is_active (surface)); + + if (surface->cr.current_style.line_join == line_join) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_style.line_join = line_join; + + _cairo_output_stream_printf (to_context (surface)->stream, + "//%s set-line-join\n", + _line_join_to_string (line_join)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_miter_limit (cairo_script_surface_t *surface, + double miter_limit, + cairo_bool_t force) +{ + assert (target_is_active (surface)); + + if ((! force || + fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) && + surface->cr.current_style.miter_limit == miter_limit) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_style.miter_limit = miter_limit; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%f set-miter-limit\n", + miter_limit); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_dashes_equal (const double *a, const double *b, int num_dashes) +{ + while (num_dashes--) { + if (fabs (*a - *b) > 1e-5) + return FALSE; + a++, b++; + } + + return TRUE; +} + +static cairo_status_t +_emit_dash (cairo_script_surface_t *surface, + const double *dash, + unsigned int num_dashes, + double offset, + cairo_bool_t force) +{ + unsigned int n; + + assert (target_is_active (surface)); + + if (force && + num_dashes == 0 && + surface->cr.current_style.num_dashes == 0) + { + return CAIRO_STATUS_SUCCESS; + } + + if (! force && + (surface->cr.current_style.num_dashes == num_dashes && + (num_dashes == 0 || + (fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 && + _dashes_equal (surface->cr.current_style.dash, dash, num_dashes))))) + { + return CAIRO_STATUS_SUCCESS; + } + + + if (num_dashes) { + surface->cr.current_style.dash = _cairo_realloc_ab + (surface->cr.current_style.dash, num_dashes, sizeof (double)); + if (unlikely (surface->cr.current_style.dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (surface->cr.current_style.dash, dash, + sizeof (double) * num_dashes); + } else { + free (surface->cr.current_style.dash); + surface->cr.current_style.dash = NULL; + } + + surface->cr.current_style.num_dashes = num_dashes; + surface->cr.current_style.dash_offset = offset; + + _cairo_output_stream_puts (to_context (surface)->stream, "["); + for (n = 0; n < num_dashes; n++) { + _cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]); + if (n < num_dashes-1) + _cairo_output_stream_puts (to_context (surface)->stream, " "); + } + _cairo_output_stream_printf (to_context (surface)->stream, + "] %f set-dash\n", + offset); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_stroke_style (cairo_script_surface_t *surface, + const cairo_stroke_style_t *style, + cairo_bool_t force) +{ + cairo_status_t status; + + assert (target_is_active (surface)); + + status = _emit_line_width (surface, style->line_width, force); + if (unlikely (status)) + return status; + + status = _emit_line_cap (surface, style->line_cap); + if (unlikely (status)) + return status; + + status = _emit_line_join (surface, style->line_join); + if (unlikely (status)) + return status; + + status = _emit_miter_limit (surface, style->miter_limit, force); + if (unlikely (status)) + return status; + + status = _emit_dash (surface, + style->dash, style->num_dashes, style->dash_offset, + force); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static const char * +_format_to_string (cairo_format_t format) +{ + 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"; + case CAIRO_FORMAT_A1: return "A1"; + case CAIRO_FORMAT_INVALID: return "INVALID"; + } + ASSERT_NOT_REACHED; + return "INVALID"; +} + +static cairo_status_t +_emit_solid_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + cairo_script_context_t *ctx = to_context (surface); + + if (! CAIRO_COLOR_IS_OPAQUE (&solid->color)) + { + if (! (surface->base.content & CAIRO_CONTENT_COLOR) || + ((solid->color.red_short == 0 || solid->color.red_short == 0xffff) && + (solid->color.green_short == 0 || solid->color.green_short == 0xffff) && + (solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) )) + { + _cairo_output_stream_printf (ctx->stream, + "%f a", + solid->color.alpha); + } + else + { + _cairo_output_stream_printf (ctx->stream, + "%f %f %f %f rgba", + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + } + } + else + { + if (solid->color.red_short == solid->color.green_short && + solid->color.red_short == solid->color.blue_short) + { + _cairo_output_stream_printf (ctx->stream, + "%f g", + solid->color.red); + } + else + { + _cairo_output_stream_printf (ctx->stream, + "%f %f %f rgb", + solid->color.red, + solid->color.green, + solid->color.blue); + } + } + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient, + cairo_output_stream_t *output) +{ + unsigned int n; + + for (n = 0; n < gradient->n_stops; n++) { + _cairo_output_stream_printf (output, + "\n %f %f %f %f %f add-color-stop", + gradient->stops[n].offset, + gradient->stops[n].color.red, + gradient->stops[n].color.green, + gradient->stops[n].color.blue, + gradient->stops[n].color.alpha); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_linear_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_linear_pattern_t *linear; + + linear = (cairo_linear_pattern_t *) pattern; + + _cairo_output_stream_printf (ctx->stream, + "%f %f %f %f linear", + linear->pd1.x, linear->pd1.y, + linear->pd2.x, linear->pd2.y); + return _emit_gradient_color_stops (&linear->base, ctx->stream); +} + +static cairo_status_t +_emit_radial_pattern (cairo_script_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_radial_pattern_t *radial; + + radial = (cairo_radial_pattern_t *) pattern; + + _cairo_output_stream_printf (ctx->stream, + "%f %f %f %f %f %f radial", + 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; + + 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; + } + + extents = NULL; + if (_cairo_recording_surface_get_bounds (&source->base, &r)) + extents = &r; + + similar = _cairo_script_surface_create_internal (ctx, + source->base.content, + extents, + NULL); + if (unlikely (similar->base.status)) + return similar->base.status; + + similar->base.is_clear = TRUE; + + _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); + surface->cr = old_cr; + + if (unlikely (status)) { + cairo_surface_destroy (&similar->base); + return status; + } + + cairo_list_del (&similar->operand.link); + assert (target_is_active (surface)); + + _cairo_output_stream_puts (ctx->stream, "pop "); + cairo_surface_destroy (&similar->base); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_script_surface_pattern (cairo_script_surface_t *surface, + cairo_script_surface_t *source) +{ + _get_target (source); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_write_image_surface (cairo_output_stream_t *output, + const cairo_image_surface_t *image) +{ + int row, width; + ptrdiff_t stride; + uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE]; + uint8_t *rowdata; + uint8_t *data; + + stride = image->stride; + width = image->width; + data = image->data; +#if WORDS_BIGENDIAN + switch (image->format) { + case CAIRO_FORMAT_A1: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, (width+7)/8); + data += stride; + } + break; + case CAIRO_FORMAT_A8: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB16_565: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, 2*width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB24: + for (row = image->height; row--; ) { + int col; + rowdata = data; + for (col = width; col--; ) { + _cairo_output_stream_write (output, rowdata, 3); + rowdata+=4; + } + data += stride; + } + break; + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, 4*width); + data += stride; + } + break; + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + break; + } +#else + if (stride > ARRAY_LENGTH (row_stack)) { + rowdata = _cairo_malloc (stride); + if (unlikely (rowdata == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } else + rowdata = row_stack; + + switch (image->format) { + case CAIRO_FORMAT_A1: + for (row = image->height; row--; ) { + int col; + for (col = 0; col < (width + 7)/8; col++) + rowdata[col] = CAIRO_BITSWAP8 (data[col]); + _cairo_output_stream_write (output, rowdata, (width+7)/8); + data += stride; + } + break; + case CAIRO_FORMAT_A8: + for (row = image->height; row--; ) { + _cairo_output_stream_write (output, data, width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB16_565: + for (row = image->height; row--; ) { + uint16_t *src = (uint16_t *) data; + uint16_t *dst = (uint16_t *) rowdata; + int col; + for (col = 0; col < width; col++) + dst[col] = bswap_16 (src[col]); + _cairo_output_stream_write (output, rowdata, 2*width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB24: + for (row = image->height; row--; ) { + uint8_t *src = data; + int col; + for (col = 0; col < width; col++) { + rowdata[3*col+2] = *src++; + rowdata[3*col+1] = *src++; + rowdata[3*col+0] = *src++; + src++; + } + _cairo_output_stream_write (output, rowdata, 3*width); + data += stride; + } + break; + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_ARGB32: + for (row = image->height; row--; ) { + uint32_t *src = (uint32_t *) data; + uint32_t *dst = (uint32_t *) rowdata; + int col; + for (col = 0; col < width; col++) + dst[col] = bswap_32 (src[col]); + _cairo_output_stream_write (output, rowdata, 4*width); + data += stride; + } + break; + 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; + break; + } + if (rowdata != row_stack) + free (rowdata); +#endif + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_emit_png_surface (cairo_script_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_output_stream_t *base85_stream; + cairo_status_t status; + const uint8_t *mime_data; + unsigned long mime_data_length; + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_printf (ctx->stream, + "<< " + "/width %d " + "/height %d " + "/format //%s " + "/mime-type (image/png) " + "/source <~", + image->width, image->height, + _format_to_string (image->format)); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "~> >> image "); + return CAIRO_STATUS_SUCCESS; +} + +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_int_status_t status, status2; + cairo_surface_t *snapshot; + const uint8_t *mime_data; + unsigned long mime_data_length; + + 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_int_status_is_error (status)) { + return status; + } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_image_surface_t *clone; + uint32_t len; + + if (image->format == CAIRO_FORMAT_INVALID) { + clone = _cairo_image_surface_coerce (image); + } else { + clone = (cairo_image_surface_t *) + cairo_surface_reference (&image->base); + } + + _cairo_output_stream_printf (ctx->stream, + "<< " + "/width %d " + "/height %d " + "/format //%s " + "/source ", + clone->width, clone->height, + _format_to_string (clone->format)); + + switch (clone->format) { + case CAIRO_FORMAT_A1: + len = (clone->width + 7)/8; + break; + case CAIRO_FORMAT_A8: + len = clone->width; + break; + case CAIRO_FORMAT_RGB16_565: + len = clone->width * 2; + break; + 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; + + if (len > 24) { + _cairo_output_stream_puts (ctx->stream, "<|"); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + + len = to_be32 (len); + _cairo_output_stream_write (base85_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base85_stream); + status = _write_image_surface (zlib_stream, clone); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = status2; + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = status2; + if (unlikely (status)) + return status; + } else { + _cairo_output_stream_puts (ctx->stream, "<~"); + + 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_INT_STATUS_SUCCESS) + status = status2; + if (unlikely (status)) + return status; + } + _cairo_output_stream_puts (ctx->stream, "~> >> image "); + + cairo_surface_destroy (&clone->base); + } + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data != NULL) { + _cairo_output_stream_printf (ctx->stream, + "\n (%s) <~", + CAIRO_MIME_TYPE_JPEG); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); + } + + cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2, + &mime_data, &mime_data_length); + if (mime_data != NULL) { + _cairo_output_stream_printf (ctx->stream, + "\n (%s) <~", + CAIRO_MIME_TYPE_JP2); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + _cairo_output_stream_write (base85_stream, mime_data, mime_data_length); + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n"); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_emit_image_surface_pattern (cairo_script_surface_t *surface, + cairo_surface_t *source) +{ + cairo_image_surface_t *image; + cairo_status_t status; + void *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 (source, image, extra); + } + + return status; +} + +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_int_status_t status; + + switch ((int) source->backend->type) { + case CAIRO_SURFACE_TYPE_RECORDING: + status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); + break; + case CAIRO_SURFACE_TYPE_SCRIPT: + status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); + break; + default: + status = _emit_image_surface_pattern (surface, source); + break; + } + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (to_context (surface)->stream, + "%d %d %d %d subsurface ", + sub->extents.x, + sub->extents.y, + sub->extents.width, + sub->extents.height); + return CAIRO_INT_STATUS_SUCCESS; +} + +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, *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 (_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: + status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source); + break; + case CAIRO_SURFACE_TYPE_SCRIPT: + status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source); + break; + case CAIRO_SURFACE_TYPE_SUBSURFACE: + status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source); + break; + default: + status = _emit_image_surface_pattern (surface, source); + break; + } + cairo_surface_destroy (free_me); + if (unlikely (status)) + return status; + + if (take_snapshot) + attach_snapshot (ctx, take_snapshot); + + _cairo_output_stream_puts (ctx->stream, "pattern"); + return CAIRO_INT_STATUS_SUCCESS; +} + +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_int_status_t status; + cairo_bool_t is_default_extend; + cairo_bool_t need_newline = TRUE; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + /* solid colors do not need filter/extend/matrix */ + return _emit_solid_pattern (surface, pattern); + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _emit_linear_pattern (surface, pattern); + is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT; + break; + case CAIRO_PATTERN_TYPE_RADIAL: + 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; + status = CAIRO_INT_STATUS_UNSUPPORTED; + } + if (unlikely (status)) + return status; + + if (! _cairo_matrix_is_identity (&pattern->matrix)) { + if (need_newline) { + _cairo_output_stream_puts (ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (ctx->stream, + " [%f %f %f %f %f %f] set-matrix\n ", + pattern->matrix.xx, pattern->matrix.yx, + pattern->matrix.xy, pattern->matrix.yy, + pattern->matrix.x0, pattern->matrix.y0); + } + + /* XXX need to discriminate the user explicitly setting the default */ + if (pattern->filter != CAIRO_FILTER_DEFAULT) { + if (need_newline) { + _cairo_output_stream_puts (ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (ctx->stream, + " //%s set-filter\n ", + _filter_to_string (pattern->filter)); + } + if (! is_default_extend ){ + if (need_newline) { + _cairo_output_stream_puts (ctx->stream, "\n "); + need_newline = FALSE; + } + + _cairo_output_stream_printf (ctx->stream, + " //%s set-extend\n ", + _extend_to_string (pattern->extend)); + } + + if (need_newline) + _cairo_output_stream_puts (ctx->stream, "\n "); + + return CAIRO_INT_STATUS_SUCCESS; +} + +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_INT_STATUS_SUCCESS; + + _cairo_output_stream_puts (to_context (surface)->stream, + "identity set-matrix\n"); + + *matrix_updated = TRUE; + cairo_matrix_init_identity (&surface->cr.current_ctm); + + return CAIRO_INT_STATUS_SUCCESS; +} + +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_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_INT_STATUS_SUCCESS; + } + + if (_cairo_pattern_equal (&surface->cr.current_source.base, source)) + return CAIRO_INT_STATUS_SUCCESS; + + _cairo_pattern_fini (&surface->cr.current_source.base); + status = _cairo_pattern_init_copy (&surface->cr.current_source.base, + source); + if (unlikely (status)) + return status; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_pattern (surface, source); + if (unlikely (status)) + return status; + + assert (target_is_active (surface)); + _cairo_output_stream_puts (to_context (surface)->stream, + " set-source\n"); + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_status_t +_path_move_to (void *closure, + const cairo_point_t *point) +{ + _cairo_output_stream_printf (closure, + " %f %f m", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_line_to (void *closure, + const cairo_point_t *point) +{ + _cairo_output_stream_printf (closure, + " %f %f l", + _cairo_fixed_to_double (point->x), + _cairo_fixed_to_double (point->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + _cairo_output_stream_printf (closure, + " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_path_close (void *closure) +{ + _cairo_output_stream_printf (closure, + " h"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_path_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, + const cairo_path_fixed_t *path, + cairo_bool_t is_fill) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_box_t box; + cairo_int_status_t status; + + assert (target_is_active (surface)); + assert (_cairo_matrix_is_identity (&surface->cr.current_ctm)); + + if (_cairo_path_fixed_equal (&surface->cr.current_path, path)) + return CAIRO_STATUS_SUCCESS; + + _cairo_path_fixed_fini (&surface->cr.current_path); + + _cairo_output_stream_puts (ctx->stream, "n"); + + if (path == NULL) { + _cairo_path_fixed_init (&surface->cr.current_path); + _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); + + assert (x1 > -9999); + + _cairo_output_stream_printf (ctx->stream, + " %f %f %f %f rectangle", + x1, y1, x2 - x1, y2 - y1); + 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, + _path_move_to, + _path_line_to, + _path_curve_to, + _path_close, + ctx->stream); + } + + _cairo_output_stream_puts (ctx->stream, "\n"); + + return status; +} +static cairo_bool_t +_scaling_matrix_equal (const cairo_matrix_t *a, + const cairo_matrix_t *b) +{ + return fabs (a->xx - b->xx) < 1e-5 && + fabs (a->xy - b->xy) < 1e-5 && + fabs (a->yx - b->yx) < 1e-5 && + fabs (a->yy - b->yy) < 1e-5; +} + +static cairo_status_t +_emit_scaling_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *ctm, + cairo_bool_t *matrix_updated) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_bool_t was_identity; + assert (target_is_active (surface)); + + if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm)) + return CAIRO_STATUS_SUCCESS; + + was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm); + + *matrix_updated = TRUE; + surface->cr.current_ctm = *ctm; + surface->cr.current_ctm.x0 = 0.; + surface->cr.current_ctm.y0 = 0.; + + if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) { + _cairo_output_stream_puts (ctx->stream, + "identity set-matrix\n"); + } else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) { + _cairo_output_stream_printf (ctx->stream, + "%f %f scale\n", + ctm->xx, ctm->yy); + } else { + _cairo_output_stream_printf (ctx->stream, + "[%f %f %f %f 0 0] set-matrix\n", + ctm->xx, ctm->yx, + ctm->xy, ctm->yy); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_font_matrix (cairo_script_surface_t *surface, + const cairo_matrix_t *font_matrix) +{ + cairo_script_context_t *ctx = to_context (surface); + assert (target_is_active (surface)); + + if (memcmp (&surface->cr.current_font_matrix, + font_matrix, + sizeof (cairo_matrix_t)) == 0) + { + return CAIRO_STATUS_SUCCESS; + } + + surface->cr.current_font_matrix = *font_matrix; + + if (_cairo_matrix_is_identity (font_matrix)) { + _cairo_output_stream_puts (ctx->stream, + "identity set-font-matrix\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "[%f %f %f %f %f %f] set-font-matrix\n", + font_matrix->xx, font_matrix->yx, + font_matrix->xy, font_matrix->yy, + font_matrix->x0, font_matrix->y0); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_script_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_script_surface_t *surface, *other = abstract_surface; + cairo_surface_t *passthrough = NULL; + cairo_script_context_t *ctx; + cairo_rectangle_t extents; + cairo_status_t status; + + ctx = to_context (other); + + status = cairo_device_acquire (&ctx->base); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + if (! other->emitted) { + status = _emit_surface (other); + if (unlikely (status)) { + cairo_device_release (&ctx->base); + return _cairo_surface_create_in_error (status); + } + + target_push (other); + } + + if (_cairo_surface_wrapper_is_active (&other->wrapper)) { + passthrough = + _cairo_surface_wrapper_create_similar (&other->wrapper, + content, width, height); + if (unlikely (passthrough->status)) { + cairo_device_release (&ctx->base); + return 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)) { + cairo_device_release (&ctx->base); + return &surface->base; + } + + _get_target (other); + _cairo_output_stream_printf (ctx->stream, + "%u %u //%s similar dup /s%u exch def context\n", + width, height, + _content_to_string (content), + surface->base.unique_id); + + surface->emitted = TRUE; + surface->defined = TRUE; + surface->base.is_clear = TRUE; + target_push (surface); + + cairo_device_release (&ctx->base); + return &surface->base; +} + +static cairo_status_t +_device_flush (void *abstract_device) +{ + cairo_script_context_t *ctx = abstract_device; + + return _cairo_output_stream_flush (ctx->stream); +} + +static void +_device_destroy (void *abstract_device) +{ + cairo_script_context_t *ctx = abstract_device; + cairo_status_t status; + + while (! cairo_list_is_empty (&ctx->fonts)) { + cairo_script_font_t *font; + + font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link); + cairo_list_del (&font->base.link); + cairo_list_del (&font->link); + free (font); + } + + _bitmap_fini (ctx->surface_id.next); + _bitmap_fini (ctx->font_id.next); + + 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, + void **image_extra) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper, + image_out, + image_extra); + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_cairo_script_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_script_surface_t *surface = abstract_surface; + + assert (_cairo_surface_wrapper_is_active (&surface->wrapper)); + _cairo_surface_wrapper_release_source_image (&surface->wrapper, + image, + image_extra); +} + +static cairo_status_t +_cairo_script_surface_finish (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_script_context_t *ctx = to_context (surface); + cairo_status_t status = CAIRO_STATUS_SUCCESS, status2; + + _cairo_surface_wrapper_fini (&surface->wrapper); + + 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); + + status = cairo_device_acquire (&ctx->base); + if (unlikely (status)) + return status; + + if (surface->emitted) { + assert (! surface->active); + + if (! cairo_list_is_empty (&surface->operand.link)) { + if (! ctx->active) { + if (target_is_active (surface)) { + _cairo_output_stream_printf (ctx->stream, + "pop\n"); + } else { + int depth = target_depth (surface); + if (depth == 1) { + _cairo_output_stream_printf (ctx->stream, + "exch pop\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll pop\n", + depth); + } + } + cairo_list_del (&surface->operand.link); + } else { + struct deferred_finish *link = _cairo_malloc (sizeof (*link)); + if (link == NULL) { + status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + cairo_list_del (&surface->operand.link); + } else { + link->operand.type = DEFERRED; + cairo_list_swap (&link->operand.link, + &surface->operand.link); + cairo_list_add (&link->link, &ctx->deferred); + } + } + } + + if (surface->defined) { + _cairo_output_stream_printf (ctx->stream, + "/s%u undef\n", + surface->base.unique_id); + } + } + + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_output_stream_flush (to_context (surface)->stream); + + cairo_device_release (&ctx->base); + + return status; +} + +static cairo_int_status_t +_cairo_script_surface_copy_page (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = cairo_device_acquire (surface->base.device); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n"); + +BAIL: + cairo_device_release (surface->base.device); + return status; +} + +static cairo_int_status_t +_cairo_script_surface_show_page (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = cairo_device_acquire (surface->base.device); + if (unlikely (status)) + return status; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, "show-page\n"); + +BAIL: + cairo_device_release (surface->base.device); + return status; +} + +static cairo_status_t +_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_script_surface_t *surface = cairo_container_of (clipper, + cairo_script_surface_t, + clipper); + cairo_script_context_t *ctx = to_context (surface); + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_box_t box; + + status = _emit_context (surface); + if (unlikely (status)) + return status; + + if (path == NULL) { + if (surface->cr.has_clip) { + _cairo_output_stream_puts (ctx->stream, "reset-clip\n"); + surface->cr.has_clip = FALSE; + } + return CAIRO_STATUS_SUCCESS; + } + + /* skip the trivial clip covering the surface extents */ + 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 >= _cairo_fixed_from_double (surface->width) && + box.p2.y >= _cairo_fixed_from_double (surface->height)) + { + return CAIRO_STATUS_SUCCESS; + } + } + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + return status; + + status = _emit_fill_rule (surface, fill_rule); + if (unlikely (status)) + return status; + + if (path->has_curve_to) { + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + return status; + } + + if (! _cairo_path_fixed_fill_maybe_region (path)) { + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + return status; + } + + status = _emit_path (surface, path, TRUE); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "clip+\n"); + surface->cr.has_clip = TRUE; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +active (cairo_script_surface_t *surface) +{ + cairo_status_t status; + + status = cairo_device_acquire (surface->base.device); + if (unlikely (status)) + return status; + + if (surface->active++ == 0) + to_context (surface)->active++; + + return CAIRO_STATUS_SUCCESS; +} + +static void +inactive (cairo_script_surface_t *surface) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_list_t sorted; + + assert (surface->active > 0); + if (--surface->active) + goto DONE; + + assert (ctx->active > 0); + if (--ctx->active) + goto DONE; + + cairo_list_init (&sorted); + while (! cairo_list_is_empty (&ctx->deferred)) { + struct deferred_finish *df; + cairo_list_t *operand; + int depth; + + df = cairo_list_first_entry (&ctx->deferred, + struct deferred_finish, + link); + + depth = 0; + cairo_list_foreach (operand, &ctx->operands) { + if (operand == &df->operand.link) + break; + depth++; + } + + df->operand.type = depth; + + if (cairo_list_is_empty (&sorted)) { + cairo_list_move (&df->link, &sorted); + } else { + struct deferred_finish *pos; + + cairo_list_foreach_entry (pos, struct deferred_finish, + &sorted, + link) + { + if (df->operand.type < pos->operand.type) + break; + } + cairo_list_move_tail (&df->link, &pos->link); + } + } + + while (! cairo_list_is_empty (&sorted)) { + struct deferred_finish *df; + cairo_list_t *operand; + int depth; + + df = cairo_list_first_entry (&sorted, + struct deferred_finish, + link); + + depth = 0; + cairo_list_foreach (operand, &ctx->operands) { + if (operand == &df->operand.link) + break; + depth++; + } + + if (depth == 0) { + _cairo_output_stream_printf (ctx->stream, + "pop\n"); + } else if (depth == 1) { + _cairo_output_stream_printf (ctx->stream, + "exch pop\n"); + } else { + _cairo_output_stream_printf (ctx->stream, + "%d -1 roll pop\n", + depth); + } + + cairo_list_del (&df->operand.link); + cairo_list_del (&df->link); + free (df); + } + +DONE: + cairo_device_release (surface->base.device); +} + +static cairo_int_status_t +_cairo_script_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, + "paint\n"); + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_paint (&surface->wrapper, + op, source, clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_int_status_t +_cairo_script_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + if (_cairo_pattern_equal (source, mask)) { + _cairo_output_stream_puts (to_context (surface)->stream, "/source get"); + } else { + status = _emit_pattern (surface, mask); + if (unlikely (status)) + goto BAIL; + } + + assert (surface->cr.current_operator == op); + + _cairo_output_stream_puts (to_context (surface)->stream, + " mask\n"); + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_mask (&surface->wrapper, + op, source, mask, clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_int_status_t +_cairo_script_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_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + goto BAIL; + + status = _emit_path (surface, path, FALSE); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + status = _emit_scaling_matrix (surface, ctm, &matrix_updated); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + if (_scaling_matrix_equal (&surface->cr.current_ctm, + &surface->cr.current_stroke_matrix)) + { + matrix_updated = FALSE; + } + else + { + matrix_updated = TRUE; + surface->cr.current_stroke_matrix = surface->cr.current_ctm; + } + + status = _emit_stroke_style (surface, style, 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)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n"); + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_stroke (&surface->wrapper, + op, source, path, + style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_int_status_t +_cairo_script_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_script_surface_t *surface = abstract_surface; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_box_t box; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_identity (surface, &matrix_updated); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + if (! _cairo_path_fixed_is_box (path, &box)) { + status = _emit_fill_rule (surface, fill_rule); + if (unlikely (status)) + goto BAIL; + } + + if (path->has_curve_to) { + status = _emit_tolerance (surface, tolerance, matrix_updated); + if (unlikely (status)) + goto BAIL; + } + + if (! _cairo_path_fixed_fill_maybe_region (path)) { + status = _emit_antialias (surface, antialias); + if (unlikely (status)) + goto BAIL; + } + + status = _emit_path (surface, path, TRUE); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (to_context (surface)->stream, "fill+\n"); + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_fill (&surface->wrapper, + op, source, path, + fill_rule, + tolerance, + antialias, + clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_surface_t * +_cairo_script_surface_snapshot (void *abstract_surface) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) + return _cairo_surface_wrapper_snapshot (&surface->wrapper); + + return NULL; +} + +static cairo_bool_t +_cairo_script_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static const char * +_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order) +{ + static const char *names[] = { + "SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */ + "SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */ + "SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */ + "SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */ + "SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */ + }; + return names[subpixel_order]; +} +static const char * +_hint_style_to_string (cairo_hint_style_t hint_style) +{ + static const char *names[] = { + "HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */ + "HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */ + "HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */ + "HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */ + "HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */ + }; + return names[hint_style]; +} +static const char * +_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics) +{ + static const char *names[] = { + "HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */ + "HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */ + "HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */ + }; + return names[hint_metrics]; +} + +static cairo_status_t +_emit_font_options (cairo_script_surface_t *surface, + cairo_font_options_t *font_options) +{ + cairo_script_context_t *ctx = to_context (surface); + + if (cairo_font_options_equal (&surface->cr.current_font_options, + font_options)) + { + return CAIRO_STATUS_SUCCESS; + } + + _cairo_output_stream_printf (ctx->stream, "<<"); + + if (font_options->antialias != surface->cr.current_font_options.antialias) { + _cairo_output_stream_printf (ctx->stream, + " /antialias //%s", + _antialias_to_string (font_options->antialias)); + } + + if (font_options->subpixel_order != + surface->cr.current_font_options.subpixel_order) + { + _cairo_output_stream_printf (ctx->stream, + " /subpixel-order //%s", + _subpixel_order_to_string (font_options->subpixel_order)); + } + + if (font_options->hint_style != + surface->cr.current_font_options.hint_style) + { + _cairo_output_stream_printf (ctx->stream, + " /hint-style //%s", + _hint_style_to_string (font_options->hint_style)); + } + + if (font_options->hint_metrics != + surface->cr.current_font_options.hint_metrics) + { + _cairo_output_stream_printf (ctx->stream, + " /hint-metrics //%s", + _hint_metrics_to_string (font_options->hint_metrics)); + } + + _cairo_output_stream_printf (ctx->stream, + " >> set-font-options\n"); + + surface->cr.current_font_options = *font_options; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private, + cairo_scaled_font_t *scaled_font) +{ + 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; + + 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); + + _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 +_emit_type42_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_context_t *ctx = to_context (surface); + const cairo_scaled_font_backend_t *backend; + cairo_output_stream_t *base85_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + unsigned long size; + unsigned int load_flags; + uint32_t len; + uint8_t *buf; + + backend = scaled_font->backend; + if (backend->load_truetype_table == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); + if (unlikely (status)) + return status; + + buf = _cairo_malloc (size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); + if (unlikely (status)) { + free (buf); + return status; + } + +#if CAIRO_HAS_FT_FONT + load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font); +#else + load_flags = 0; +#endif + _cairo_output_stream_printf (ctx->stream, + "<< " + "/type 42 " + "/index 0 " + "/flags %d " + "/source <|", + load_flags); + + base85_stream = _cairo_base85_stream_create (ctx->stream); + len = to_be32 (size); + _cairo_output_stream_write (base85_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base85_stream); + + _cairo_output_stream_write (zlib_stream, buf, size); + free (buf); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_output_stream_printf (ctx->stream, + "~> >> font dup /f%lu exch def set-font-face", + _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_script_font_t **font_out) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_script_font_t *font_private; + cairo_int_status_t status; + + font_private = _cairo_malloc (sizeof (cairo_script_font_t)); + if (unlikely (font_private == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _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; + + cairo_list_add (&font_private->link, &ctx->fonts); + + status = _bitmap_next_id (&ctx->font_id, + &font_private->id); + if (unlikely (status)) { + free (font_private); + return status; + } + + status = _emit_context (surface); + if (unlikely (status)) { + free (font_private); + return status; + } + + status = _emit_type42_font (surface, scaled_font); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *font_out = font_private; + return status; + } + + font_private->has_sfnt = FALSE; + _cairo_output_stream_printf (ctx->stream, + "dict\n" + " /type 3 set\n" + " /metrics [%f %f %f %f %f] set\n" + " /glyphs array set\n" + " font dup /f%lu exch def set-font-face", + scaled_font->fs_extents.ascent, + scaled_font->fs_extents.descent, + scaled_font->fs_extents.height, + scaled_font->fs_extents.max_x_advance, + scaled_font->fs_extents.max_y_advance, + font_private->id); + + *font_out = font_private; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_font (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_matrix_t matrix; + cairo_font_options_t options; + cairo_bool_t matrix_updated = FALSE; + cairo_status_t status; + cairo_script_font_t *font_private; + + cairo_scaled_font_get_ctm (scaled_font, &matrix); + status = _emit_scaling_matrix (surface, &matrix, &matrix_updated); + if (unlikely (status)) + return status; + + if (! matrix_updated && surface->cr.current_scaled_font == scaled_font) + return CAIRO_STATUS_SUCCESS; + + surface->cr.current_scaled_font = scaled_font; + + 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); + if (unlikely (status)) + return status; + + cairo_scaled_font_get_font_options (scaled_font, &options); + status = _emit_font_options (surface, &options); + if (unlikely (status)) + return status; + + status = _emit_scaled_font_init (surface, scaled_font, &font_private); + if (unlikely (status)) + return status; + + assert (target_is_active (surface)); + _cairo_output_stream_printf (ctx->stream, + " /scaled-font get /sf%lu exch def\n", + font_private->id); + } else { + _cairo_output_stream_printf (ctx->stream, + "sf%lu set-scaled-font\n", + font_private->id); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_vector (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_script_font_t *font_private, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_script_context_t *ctx = to_context (surface); + cairo_script_implicit_context_t old_cr; + cairo_status_t status; + unsigned long index; + + index = ++font_private->subset_glyph_index; + scaled_glyph->dev_private_key = ctx; + scaled_glyph->dev_private = (void *) index; + + _cairo_output_stream_printf (ctx->stream, + "%lu <<\n" + " /metrics [%f %f %f %f %f %f]\n" + " /render {\n", + index, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing, + scaled_glyph->fs_metrics.width, + scaled_glyph->fs_metrics.height, + scaled_glyph->fs_metrics.x_advance, + scaled_glyph->fs_metrics.y_advance); + + if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) { + _cairo_output_stream_printf (ctx->stream, + "[%f %f %f %f %f %f] transform\n", + scaled_font->scale_inverse.xx, + scaled_font->scale_inverse.yx, + scaled_font->scale_inverse.xy, + scaled_font->scale_inverse.yy, + scaled_font->scale_inverse.x0, + scaled_font->scale_inverse.y0); + } + + old_cr = surface->cr; + _cairo_script_implicit_context_init (&surface->cr); + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); + surface->cr = old_cr; + + _cairo_output_stream_puts (ctx->stream, "} >> set\n"); + + return status; +} + +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_status_t status; + unsigned long index; + + index = ++font_private->subset_glyph_index; + scaled_glyph->dev_private_key = ctx; + scaled_glyph->dev_private = (void *) index; + + _cairo_output_stream_printf (ctx->stream, + "%lu <<\n" + " /metrics [%f %f %f %f %f %f]\n" + " /render {\n" + "%f %f translate\n", + index, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing, + scaled_glyph->fs_metrics.width, + scaled_glyph->fs_metrics.height, + scaled_glyph->fs_metrics.x_advance, + scaled_glyph->fs_metrics.y_advance, + scaled_glyph->fs_metrics.x_bearing, + scaled_glyph->fs_metrics.y_bearing); + + status = _emit_image_surface (surface, scaled_glyph->surface); + if (unlikely (status)) + return status; + + _cairo_output_stream_puts (ctx->stream, "pattern "); + + if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) { + _cairo_output_stream_printf (ctx->stream, + "\n [%f %f %f %f %f %f] set-matrix\n", + scaled_font->font_matrix.xx, + scaled_font->font_matrix.yx, + scaled_font->font_matrix.xy, + scaled_font->font_matrix.yy, + scaled_font->font_matrix.x0, + scaled_font->font_matrix.y0); + } + _cairo_output_stream_puts (ctx->stream, + "mask\n} >> set\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyph_prologue (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font) +{ + cairo_script_context_t *ctx = to_context (surface); + + _cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n", + _cairo_script_font_id (ctx, scaled_font)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_emit_scaled_glyphs (cairo_script_surface_t *surface, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + unsigned int num_glyphs) +{ + cairo_script_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; + + if (num_glyphs == 0) + return CAIRO_STATUS_SUCCESS; + + font_private = _cairo_script_font_get (ctx, scaled_font); + if (font_private->has_sfnt) + return CAIRO_STATUS_SUCCESS; + + _cairo_scaled_font_freeze_cache (scaled_font); + for (n = 0; n < num_glyphs; n++) { + cairo_scaled_glyph_t *scaled_glyph; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) + break; + + if (scaled_glyph->dev_private_key == ctx) + continue; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + &scaled_glyph); + if (_cairo_status_is_error (status)) + break; + + if (status == CAIRO_STATUS_SUCCESS) { + if (! have_glyph_prologue) { + status = _emit_scaled_glyph_prologue (surface, scaled_font); + if (unlikely (status)) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_vector (surface, + scaled_font, font_private, + scaled_glyph); + if (unlikely (status)) + break; + + continue; + } + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (_cairo_status_is_error (status)) + break; + + if (status == CAIRO_STATUS_SUCCESS) { + if (! have_glyph_prologue) { + status = _emit_scaled_glyph_prologue (surface, scaled_font); + if (unlikely (status)) + break; + + have_glyph_prologue = TRUE; + } + + status = _emit_scaled_glyph_bitmap (surface, + scaled_font, + font_private, + scaled_glyph); + if (unlikely (status)) + break; + + continue; + } + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (have_glyph_prologue) { + _cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n"); + } + + return status; +} + +static void +to_octal (int value, char *buf, size_t size) +{ + do { + buf[--size] = '0' + (value & 7); + value >>= 3; + } while (size); +} + +static void +_emit_string_literal (cairo_script_surface_t *surface, + const char *utf8, int len) +{ + cairo_script_context_t *ctx = to_context (surface); + char c; + const char *end; + + _cairo_output_stream_puts (ctx->stream, "("); + + if (utf8 == NULL) { + end = utf8; + } else { + if (len < 0) + len = strlen (utf8); + end = utf8 + len; + } + + while (utf8 < end) { + switch ((c = *utf8++)) { + case '\n': + c = 'n'; + goto ESCAPED_CHAR; + case '\r': + c = 'r'; + goto ESCAPED_CHAR; + case '\t': + c = 't'; + goto ESCAPED_CHAR; + case '\b': + c = 'b'; + goto ESCAPED_CHAR; + case '\f': + c = 'f'; + goto ESCAPED_CHAR; + case '\\': + case '(': + case ')': +ESCAPED_CHAR: + _cairo_output_stream_printf (ctx->stream, "\\%c", c); + break; + default: + if (isprint (c) || isspace (c)) { + _cairo_output_stream_printf (ctx->stream, "%c", c); + } else { + char buf[4] = { '\\' }; + + to_octal (c, buf+1, 3); + _cairo_output_stream_write (ctx->stream, buf, 4); + } + break; + } + } + _cairo_output_stream_puts (ctx->stream, ")"); +} + +static cairo_int_status_t +_cairo_script_surface_show_text_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const char *utf8, + int utf8_len, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_text_cluster_t *clusters, + int num_clusters, + cairo_text_cluster_flags_t backward, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) +{ + cairo_script_surface_t *surface = abstract_surface; + cairo_script_context_t *ctx = to_context (surface); + cairo_script_font_t *font_private; + cairo_scaled_glyph_t *scaled_glyph; + cairo_matrix_t matrix; + cairo_status_t status; + double x, y, ix, iy; + int n; + cairo_output_stream_t *base85_stream = NULL; + + status = active (surface); + if (unlikely (status)) + return status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + goto BAIL; + + status = _emit_context (surface); + if (unlikely (status)) + goto BAIL; + + status = _emit_source (surface, op, source); + if (unlikely (status)) + goto BAIL; + + status = _emit_scaled_font (surface, scaled_font); + if (unlikely (status)) + goto BAIL; + + status = _emit_operator (surface, op); + if (unlikely (status)) + goto BAIL; + + status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs); + if (unlikely (status)) + goto BAIL; + + /* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */ + /* [cx cy [glyphs]] show_glyphs */ + + if (utf8 != NULL && clusters != NULL) { + _emit_string_literal (surface, utf8, utf8_len); + _cairo_output_stream_puts (ctx->stream, " "); + } + + matrix = surface->cr.current_ctm; + status = cairo_matrix_invert (&matrix); + assert (status == CAIRO_STATUS_SUCCESS); + + ix = x = glyphs[0].x; + iy = y = glyphs[0].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + + _cairo_scaled_font_freeze_cache (scaled_font); + font_private = _cairo_script_font_get (ctx, scaled_font); + + _cairo_output_stream_printf (ctx->stream, + "[%f %f ", + ix, iy); + + for (n = 0; n < num_glyphs; n++) { + if (font_private->has_sfnt) { + if (glyphs[n].index > 256) + break; + } else { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) { + _cairo_scaled_font_thaw_cache (scaled_font); + goto BAIL; + } + + if ((long unsigned) scaled_glyph->dev_private > 256) + break; + } + } + + if (n == num_glyphs) { + _cairo_output_stream_puts (ctx->stream, "<~"); + base85_stream = _cairo_base85_stream_create (ctx->stream); + } else + _cairo_output_stream_puts (ctx->stream, "["); + + for (n = 0; n < num_glyphs; n++) { + double dx, dy; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[n].index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (unlikely (status)) { + _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) { + if (base85_stream != NULL) { + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) { + base85_stream = NULL; + break; + } + + _cairo_output_stream_printf (ctx->stream, + "~> %f <~", glyphs[n].x - x); + base85_stream = _cairo_base85_stream_create (ctx->stream); + } else { + _cairo_output_stream_printf (ctx->stream, + " ] %f [ ", glyphs[n].x - x); + } + + x = glyphs[n].x; + } else { + ix = x = glyphs[n].x; + iy = y = glyphs[n].y; + cairo_matrix_transform_point (&matrix, &ix, &iy); + ix -= scaled_font->font_matrix.x0; + iy -= scaled_font->font_matrix.y0; + if (base85_stream != NULL) { + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) { + base85_stream = NULL; + break; + } + + _cairo_output_stream_printf (ctx->stream, + "~> %f %f <~", + ix, iy); + base85_stream = _cairo_base85_stream_create (ctx->stream); + } else { + _cairo_output_stream_printf (ctx->stream, + " ] %f %f [ ", + ix, iy); + } + } + } + if (base85_stream != NULL) { + uint8_t c; + + if (font_private->has_sfnt) + c = glyphs[n].index; + else + c = (uint8_t) (long unsigned) scaled_glyph->dev_private; + + _cairo_output_stream_write (base85_stream, &c, 1); + } else { + if (font_private->has_sfnt) + _cairo_output_stream_printf (ctx->stream, " %lu", + glyphs[n].index); + else + _cairo_output_stream_printf (ctx->stream, " %lu", + (long unsigned) scaled_glyph->dev_private); + } + + dx = scaled_glyph->metrics.x_advance; + dy = scaled_glyph->metrics.y_advance; + cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy); + x += dx; + y += dy; + } + _cairo_scaled_font_thaw_cache (scaled_font); + + if (base85_stream != NULL) { + cairo_status_t status2; + + status2 = _cairo_output_stream_destroy (base85_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_output_stream_printf (ctx->stream, "~>"); + } else { + _cairo_output_stream_puts (ctx->stream, " ]"); + } + if (unlikely (status)) + return status; + + if (utf8 != NULL && clusters != NULL) { + for (n = 0; n < num_clusters; n++) { + if (clusters[n].num_bytes > UCHAR_MAX || + clusters[n].num_glyphs > UCHAR_MAX) + { + break; + } + } + + if (n < num_clusters) { + _cairo_output_stream_puts (ctx->stream, "] [ "); + for (n = 0; n < num_clusters; n++) { + _cairo_output_stream_printf (ctx->stream, + "%d %d ", + clusters[n].num_bytes, + clusters[n].num_glyphs); + } + _cairo_output_stream_puts (ctx->stream, "]"); + } + else + { + _cairo_output_stream_puts (ctx->stream, "] <~"); + base85_stream = _cairo_base85_stream_create (ctx->stream); + for (n = 0; n < num_clusters; n++) { + uint8_t c[2]; + c[0] = clusters[n].num_bytes; + c[1] = clusters[n].num_glyphs; + _cairo_output_stream_write (base85_stream, c, 2); + } + status = _cairo_output_stream_destroy (base85_stream); + if (unlikely (status)) + goto BAIL; + + _cairo_output_stream_puts (ctx->stream, "~>"); + } + + _cairo_output_stream_printf (ctx->stream, + " //%s show-text-glyphs\n", + _direction_to_string (backward)); + } else { + _cairo_output_stream_puts (ctx->stream, + "] show-glyphs\n"); + } + + inactive (surface); + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)){ + return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper, + op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + backward, + scaled_font, + clip); + } + + return CAIRO_STATUS_SUCCESS; + +BAIL: + inactive (surface); + return status; +} + +static cairo_bool_t +_cairo_script_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_script_surface_t *surface = abstract_surface; + + if (_cairo_surface_wrapper_is_active (&surface->wrapper)) { + return _cairo_surface_wrapper_get_extents (&surface->wrapper, + rectangle); + } + + if (surface->width < 0 || surface->height < 0) + return FALSE; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static const cairo_surface_backend_t +_cairo_script_surface_backend = { + CAIRO_SURFACE_TYPE_SCRIPT, + _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, + _cairo_script_surface_snapshot, + + _cairo_script_surface_copy_page, + _cairo_script_surface_show_page, + + _cairo_script_surface_get_extents, + NULL, /* get_font_options */ + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _cairo_script_surface_paint, + _cairo_script_surface_mask, + _cairo_script_surface_stroke, + _cairo_script_surface_fill, + NULL, /* fill/stroke */ + NULL, /* glyphs */ + _cairo_script_surface_has_show_text_glyphs, + _cairo_script_surface_show_text_glyphs +}; + +static void +_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr) +{ + cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT; + cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; + cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; + cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT; + _cairo_stroke_style_init (&cr->current_style); + _cairo_pattern_init_solid (&cr->current_source.solid, + CAIRO_COLOR_BLACK); + _cairo_path_fixed_init (&cr->current_path); + cairo_matrix_init_identity (&cr->current_ctm); + cairo_matrix_init_identity (&cr->current_stroke_matrix); + cairo_matrix_init_identity (&cr->current_font_matrix); + _cairo_font_options_init_default (&cr->current_font_options); + cr->current_scaled_font = NULL; + cr->has_clip = FALSE; +} + +static void +_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr) +{ + free (cr->current_style.dash); + cr->current_style.dash = NULL; + + _cairo_pattern_fini (&cr->current_source.base); + _cairo_path_fixed_fini (&cr->current_path); + + _cairo_script_implicit_context_init (cr); +} + +static cairo_script_surface_t * +_cairo_script_surface_create_internal (cairo_script_context_t *ctx, + cairo_content_t content, + cairo_rectangle_t *extents, + cairo_surface_t *passthrough) +{ + cairo_script_surface_t *surface; + + if (unlikely (ctx == NULL)) + return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER)); + + 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, + 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 = 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; + surface->active = FALSE; + surface->operand.type = SURFACE; + cairo_list_init (&surface->operand.link); + + _cairo_script_implicit_context_init (&surface->cr); + + return surface; +} + +static const cairo_device_backend_t _cairo_script_device_backend = { + CAIRO_DEVICE_TYPE_SCRIPT, + + NULL, NULL, /* lock, unlock */ + + _device_flush, /* flush */ + NULL, /* finish */ + _device_destroy +}; + +cairo_device_t * +_cairo_script_context_create_internal (cairo_output_stream_t *stream) +{ + cairo_script_context_t *ctx; + + ctx = _cairo_malloc (sizeof (cairo_script_context_t)); + if (unlikely (ctx == NULL)) + return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + memset (ctx, 0, sizeof (cairo_script_context_t)); + + _cairo_device_init (&ctx->base, &_cairo_script_device_backend); + + cairo_list_init (&ctx->operands); + cairo_list_init (&ctx->deferred); + ctx->stream = stream; + ctx->mode = CAIRO_SCRIPT_MODE_ASCII; + + cairo_list_init (&ctx->fonts); + cairo_list_init (&ctx->defines); + + 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) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create_for_filename (filename); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_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) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_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 *script, + const char *comment, + int len) +{ + cairo_script_context_t *context = (cairo_script_context_t *) script; + + if (len < 0) + len = strlen (comment); + + _cairo_output_stream_puts (context->stream, "% "); + _cairo_output_stream_write (context->stream, comment, len); + _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 *script, + cairo_script_mode_t mode) +{ + 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 *script) +{ + 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 *script, + cairo_content_t content, + double width, + double height) +{ + 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 (script->status)) + return _cairo_surface_create_in_error (script->status); + + 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 *script, + cairo_surface_t *target) +{ + cairo_rectangle_int_t extents; + cairo_rectangle_t rect, *r; + + if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) + return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (script->status)) + return _cairo_surface_create_in_error (script->status); + + if (unlikely (target->status)) + return _cairo_surface_create_in_error (target->status); + + 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 *script, + cairo_surface_t *recording_surface) +{ + cairo_rectangle_t r, *extents; + cairo_surface_t *surface; + cairo_status_t status; + + if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT)) + return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (script->status)) + return _cairo_error (script->status); + + if (unlikely (recording_surface->status)) + return recording_surface->status; + + if (unlikely (! _cairo_surface_is_recording (recording_surface))) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + extents = NULL; + if (_cairo_recording_surface_get_bounds (recording_surface, &r)) + extents = &r; + + surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) script, + recording_surface->content, + extents, + NULL)->base; + if (unlikely (surface->status)) + return surface->status; + + status = _cairo_recording_surface_replay (recording_surface, surface); + cairo_surface_destroy (surface); + + return status; +} diff --git a/gfx/cairo/cairo/src/cairo-script.h b/gfx/cairo/cairo/src/cairo-script.h new file mode 100644 index 0000000000..b5a8cf32d0 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-script.h @@ -0,0 +1,98 @@ +/* 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_H +#define CAIRO_SCRIPT_H + +#include "cairo.h" + +#if CAIRO_HAS_SCRIPT_SURFACE + +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_ASCII, + CAIRO_SCRIPT_MODE_BINARY +} cairo_script_mode_t; + +cairo_public cairo_device_t * +cairo_script_create (const char *filename); + +cairo_public cairo_device_t * +cairo_script_create_for_stream (cairo_write_func_t write_func, + void *closure); + +cairo_public void +cairo_script_write_comment (cairo_device_t *script, + const char *comment, + int len); + +cairo_public void +cairo_script_set_mode (cairo_device_t *script, + cairo_script_mode_t mode); + +cairo_public cairo_script_mode_t +cairo_script_get_mode (cairo_device_t *script); + +cairo_public cairo_surface_t * +cairo_script_surface_create (cairo_device_t *script, + cairo_content_t content, + double width, + double height); + +cairo_public cairo_surface_t * +cairo_script_surface_create_for_target (cairo_device_t *script, + cairo_surface_t *target); + +cairo_public cairo_status_t +cairo_script_from_recording_surface (cairo_device_t *script, + cairo_surface_t *recording_surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_SCRIPT_SURFACE*/ +# error Cairo was not compiled with support for the CairoScript backend +#endif /*CAIRO_HAS_SCRIPT_SURFACE*/ + +#endif /*CAIRO_SCRIPT_H*/ diff --git a/gfx/cairo/cairo/src/cairo-shape-mask-compositor.c b/gfx/cairo/cairo/src/cairo-shape-mask-compositor.c new file mode 100644 index 0000000000..3117267ccc --- /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-private.h b/gfx/cairo/cairo/src/cairo-slope-private.h new file mode 100644 index 0000000000..6a58c9f456 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-slope-private.h @@ -0,0 +1,72 @@ +/* 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_SLOPE_PRIVATE_H +#define _CAIRO_SLOPE_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-fixed-private.h" + +static inline void +_cairo_slope_init (cairo_slope_t *slope, + const cairo_point_t *a, + const cairo_point_t *b) +{ + slope->dx = b->x - a->x; + slope->dy = b->y - a->y; +} + +static inline cairo_bool_t +_cairo_slope_equal (const cairo_slope_t *a, const cairo_slope_t *b) +{ + return _cairo_int64_eq (_cairo_int32x32_64_mul (a->dy, b->dx), + _cairo_int32x32_64_mul (b->dy, a->dx)); +} + +static inline cairo_bool_t +_cairo_slope_backwards (const cairo_slope_t *a, const cairo_slope_t *b) +{ + return _cairo_int64_negative (_cairo_int64_add (_cairo_int32x32_64_mul (a->dx, b->dx), + _cairo_int32x32_64_mul (a->dy, b->dy))); +} + +cairo_private int +_cairo_slope_compare (const cairo_slope_t *a, + const cairo_slope_t *b) cairo_pure; + + +#endif /* _CAIRO_SLOPE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-slope.c b/gfx/cairo/cairo/src/cairo-slope.c new file mode 100644 index 0000000000..cc5f30cb0b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-slope.c @@ -0,0 +1,99 @@ +/* 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 + */ + +#include "cairoint.h" + +#include "cairo-slope-private.h" + +/* Compare two slopes. Slope angles begin at 0 in the direction of the + positive X axis and increase in the direction of the positive Y + axis. + + This function always compares the slope vectors based on the + smaller angular difference between them, (that is based on an + angular difference that is strictly less than pi). To break ties + when comparing slope vectors with an angular difference of exactly + pi, the vector with a positive dx (or positive dy if dx's are zero) + is considered to be more positive than the other. + + Also, all slope vectors with both dx==0 and dy==0 are considered + equal and more positive than any non-zero vector. + + < 0 => a less positive than b + == 0 => a equal to b + > 0 => a more positive than b +*/ +int +_cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b) +{ + cairo_int64_t ady_bdx = _cairo_int32x32_64_mul (a->dy, b->dx); + cairo_int64_t bdy_adx = _cairo_int32x32_64_mul (b->dy, a->dx); + int cmp; + + cmp = _cairo_int64_cmp (ady_bdx, bdy_adx); + if (cmp) + return cmp; + + /* special-case zero vectors. the intended logic here is: + * zero vectors all compare equal, and more positive than any + * non-zero vector. + */ + if (a->dx == 0 && a->dy == 0 && b->dx == 0 && b->dy ==0) + return 0; + if (a->dx == 0 && a->dy == 0) + return 1; + if (b->dx == 0 && b->dy ==0) + return -1; + + /* Finally, we're looking at two vectors that are either equal or + * that differ by exactly pi. We can identify the "differ by pi" + * case by looking for a change in sign in either dx or dy between + * a and b. + * + * And in these cases, we eliminate the ambiguity by reducing the angle + * of b by an infinitesimally small amount, (that is, 'a' will + * always be considered less than '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; + } + + /* Finally, for identical slopes, we obviously return 0. */ + 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 0000000000..0babebd26b --- /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 0000000000..50c92b25c5 --- /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 new file mode 100644 index 0000000000..653183fb1c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-spans-private.h @@ -0,0 +1,210 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2008 M Joonas Pihlaja + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef CAIRO_SPANS_PRIVATE_H +#define CAIRO_SPANS_PRIVATE_H +#include "cairo-types-private.h" +#include "cairo-compiler-private.h" + +/* Number of bits of precision used for alpha. */ +#define CAIRO_SPANS_UNIT_COVERAGE_BITS 8 +#define CAIRO_SPANS_UNIT_COVERAGE ((1 << CAIRO_SPANS_UNIT_COVERAGE_BITS)-1) + +/* A structure representing an open-ended horizontal span of constant + * pixel coverage. */ +typedef struct _cairo_half_open_span { + 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 + * surfaces if they want to composite spans instead of trapezoids. */ +typedef struct _cairo_span_renderer cairo_span_renderer_t; +struct _cairo_span_renderer { + /* Private status variable. */ + cairo_status_t status; + + /* Called to destroy the renderer. */ + cairo_destroy_func_t destroy; + + /* Render the spans on row y of the destination by whatever compositing + * method is required. */ + cairo_status_t + (*render_rows) (void *abstract_renderer, + int y, int height, + const cairo_half_open_span_t *coverages, + unsigned num_coverages); + + /* Called after all rows have been rendered to perform whatever + * final rendering step is required. This function is called just + * once before the renderer is destroyed. */ + cairo_status_t (*finish) (void *abstract_renderer); +}; + +/* Scan converter interface. */ +typedef struct _cairo_scan_converter cairo_scan_converter_t; +struct _cairo_scan_converter { + /* Destroy this scan converter. */ + cairo_destroy_func_t destroy; + + /* Generates coverage spans for rows for the added edges and calls + * the renderer function for each row. After generating spans the + * only valid thing to do with the converter is to destroy it. */ + cairo_status_t (*generate) (void *abstract_converter, + cairo_span_renderer_t *renderer); + + /* Private status. Read with _cairo_scan_converter_status(). */ + cairo_status_t status; +}; + +/* Scan converter constructors. */ + +cairo_private cairo_scan_converter_t * +_cairo_tor_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule, + cairo_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; + + cairo_box_t extents; + + struct _cairo_rectangular_scan_converter_chunk { + struct _cairo_rectangular_scan_converter_chunk *next; + void *base; + int count; + int size; + } chunks, *tail; + char buf[CAIRO_STACK_BUFFER_SIZE]; + int num_rectangles; +} cairo_rectangular_scan_converter_t; + +cairo_private void +_cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self, + const cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *self, + const cairo_box_t *box, + int dir); + +typedef struct _cairo_botor_scan_converter { + cairo_scan_converter_t base; + + cairo_box_t extents; + cairo_fill_rule_t fill_rule; + + int xmin, xmax; + + struct _cairo_botor_scan_converter_chunk { + struct _cairo_botor_scan_converter_chunk *next; + void *base; + int count; + int size; + } chunks, *tail; + char buf[CAIRO_STACK_BUFFER_SIZE]; + int num_edges; +} cairo_botor_scan_converter_t; + +cairo_private void +_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 * +_cairo_scan_converter_create_in_error (cairo_status_t error); + +cairo_private cairo_status_t +_cairo_scan_converter_status (void *abstract_converter); + +cairo_private cairo_status_t +_cairo_scan_converter_set_error (void *abstract_converter, + cairo_status_t error); + +cairo_private cairo_span_renderer_t * +_cairo_span_renderer_create_in_error (cairo_status_t error); + +cairo_private cairo_status_t +_cairo_span_renderer_status (void *abstract_renderer); + +/* Set the renderer into an error state. This sets all the method + * pointers except ->destroy() of the renderer to no-op + * implementations that just return the error status. */ +cairo_private cairo_status_t +_cairo_span_renderer_set_error (void *abstract_renderer, + cairo_status_t error); + +cairo_private cairo_status_t +_cairo_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); + +#endif /* CAIRO_SPANS_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-spans.c b/gfx/cairo/cairo/src/cairo-spans.c new file mode 100644 index 0000000000..59452c0ba6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-spans.c @@ -0,0 +1,258 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright (c) 2008 M Joonas Pihlaja + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "cairoint.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-clip-private.h" +#include "cairo-error-private.h" +#include "cairo-fixed-private.h" +#include "cairo-types-private.h" + +static void +_cairo_nil_destroy (void *abstract) +{ + (void) abstract; +} + +static cairo_status_t +_cairo_nil_scan_converter_generate (void *abstract_converter, + cairo_span_renderer_t *renderer) +{ + (void) abstract_converter; + (void) renderer; + return _cairo_scan_converter_status (abstract_converter); +} + +cairo_status_t +_cairo_scan_converter_status (void *abstract_converter) +{ + cairo_scan_converter_t *converter = abstract_converter; + return converter->status; +} + +cairo_status_t +_cairo_scan_converter_set_error (void *abstract_converter, + cairo_status_t error) +{ + cairo_scan_converter_t *converter = abstract_converter; + if (error == CAIRO_STATUS_SUCCESS) + ASSERT_NOT_REACHED; + if (converter->status == CAIRO_STATUS_SUCCESS) { + converter->generate = _cairo_nil_scan_converter_generate; + converter->status = error; + } + return converter->status; +} + +static void +_cairo_nil_scan_converter_init (cairo_scan_converter_t *converter, + cairo_status_t status) +{ + converter->destroy = _cairo_nil_destroy; + converter->status = CAIRO_STATUS_SUCCESS; + status = _cairo_scan_converter_set_error (converter, status); +} + +cairo_scan_converter_t * +_cairo_scan_converter_create_in_error (cairo_status_t status) +{ +#define RETURN_NIL {\ + static cairo_scan_converter_t nil;\ + _cairo_nil_scan_converter_init (&nil, status);\ + return &nil;\ + } + switch (status) { + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + break; + case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; + case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; + case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; + case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; + case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; + case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; + case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; + case CAIRO_STATUS_READ_ERROR: RETURN_NIL; + case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; + case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; + case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; + case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; + case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; + case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; + case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; + case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; + case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; + case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; + case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; + case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; + case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; + case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; + 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; + } + status = CAIRO_STATUS_NO_MEMORY; + RETURN_NIL; +#undef RETURN_NIL +} + +static cairo_status_t +_cairo_nil_span_renderer_render_rows ( + void *abstract_renderer, + int y, + int height, + const cairo_half_open_span_t *coverages, + unsigned num_coverages) +{ + (void) y; + (void) height; + (void) coverages; + (void) num_coverages; + return _cairo_span_renderer_status (abstract_renderer); +} + +static cairo_status_t +_cairo_nil_span_renderer_finish (void *abstract_renderer) +{ + return _cairo_span_renderer_status (abstract_renderer); +} + +cairo_status_t +_cairo_span_renderer_status (void *abstract_renderer) +{ + cairo_span_renderer_t *renderer = abstract_renderer; + return renderer->status; +} + +cairo_status_t +_cairo_span_renderer_set_error ( + void *abstract_renderer, + cairo_status_t error) +{ + cairo_span_renderer_t *renderer = abstract_renderer; + if (error == CAIRO_STATUS_SUCCESS) { + ASSERT_NOT_REACHED; + } + if (renderer->status == CAIRO_STATUS_SUCCESS) { + renderer->render_rows = _cairo_nil_span_renderer_render_rows; + renderer->finish = _cairo_nil_span_renderer_finish; + renderer->status = error; + } + return renderer->status; +} + +static void +_cairo_nil_span_renderer_init (cairo_span_renderer_t *renderer, + cairo_status_t status) +{ + renderer->destroy = _cairo_nil_destroy; + renderer->status = CAIRO_STATUS_SUCCESS; + status = _cairo_span_renderer_set_error (renderer, status); +} + +cairo_span_renderer_t * +_cairo_span_renderer_create_in_error (cairo_status_t status) +{ +#define RETURN_NIL {\ + static cairo_span_renderer_t nil;\ + _cairo_nil_span_renderer_init (&nil, status);\ + return &nil;\ + } + switch (status) { + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + break; + case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; + case CAIRO_STATUS_INVALID_POP_GROUP: RETURN_NIL; + case CAIRO_STATUS_NO_CURRENT_POINT: RETURN_NIL; + case CAIRO_STATUS_INVALID_MATRIX: RETURN_NIL; + case CAIRO_STATUS_INVALID_STATUS: RETURN_NIL; + case CAIRO_STATUS_NULL_POINTER: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRING: RETURN_NIL; + case CAIRO_STATUS_INVALID_PATH_DATA: RETURN_NIL; + case CAIRO_STATUS_READ_ERROR: RETURN_NIL; + case CAIRO_STATUS_WRITE_ERROR: RETURN_NIL; + case CAIRO_STATUS_SURFACE_FINISHED: RETURN_NIL; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_INVALID_CONTENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_FORMAT: RETURN_NIL; + case CAIRO_STATUS_INVALID_VISUAL: RETURN_NIL; + case CAIRO_STATUS_FILE_NOT_FOUND: RETURN_NIL; + case CAIRO_STATUS_INVALID_DASH: RETURN_NIL; + case CAIRO_STATUS_INVALID_DSC_COMMENT: RETURN_NIL; + case CAIRO_STATUS_INVALID_INDEX: RETURN_NIL; + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: RETURN_NIL; + case CAIRO_STATUS_TEMP_FILE_ERROR: RETURN_NIL; + case CAIRO_STATUS_INVALID_STRIDE: RETURN_NIL; + case CAIRO_STATUS_FONT_TYPE_MISMATCH: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_IMMUTABLE: RETURN_NIL; + case CAIRO_STATUS_USER_FONT_ERROR: RETURN_NIL; + case CAIRO_STATUS_NEGATIVE_COUNT: RETURN_NIL; + case CAIRO_STATUS_INVALID_CLUSTERS: RETURN_NIL; + case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL; + case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL; + case CAIRO_STATUS_NO_MEMORY: RETURN_NIL; + case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL; + 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; + } + status = CAIRO_STATUS_NO_MEMORY; + RETURN_NIL; +#undef RETURN_NIL +} diff --git a/gfx/cairo/cairo/src/cairo-spline.c b/gfx/cairo/cairo/src/cairo-spline.c new file mode 100644 index 0000000000..44634faec8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-spline.c @@ -0,0 +1,424 @@ +/* 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 + */ + +#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, + void *closure, + 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; + + spline->knots.a = *a; + spline->knots.b = *b; + spline->knots.c = *c; + spline->knots.d = *d; + + if (a->x != b->x || a->y != b->y) + _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.b); + else if (a->x != c->x || a->y != c->y) + _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.c); + else if (a->x != d->x || a->y != d->y) + _cairo_slope_init (&spline->initial_slope, &spline->knots.a, &spline->knots.d); + else + return FALSE; + + if (c->x != d->x || c->y != d->y) + _cairo_slope_init (&spline->final_slope, &spline->knots.c, &spline->knots.d); + else if (b->x != d->x || b->y != d->y) + _cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d); + else + 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, + 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, &slope); +} + +static void +_lerp_half (const cairo_point_t *a, const cairo_point_t *b, cairo_point_t *result) +{ + result->x = a->x + ((b->x - a->x) >> 1); + result->y = a->y + ((b->y - a->y) >> 1); +} + +static void +_de_casteljau (cairo_spline_knots_t *s1, cairo_spline_knots_t *s2) +{ + cairo_point_t ab, bc, cd; + cairo_point_t abbc, bccd; + cairo_point_t final; + + _lerp_half (&s1->a, &s1->b, &ab); + _lerp_half (&s1->b, &s1->c, &bc); + _lerp_half (&s1->c, &s1->d, &cd); + _lerp_half (&ab, &bc, &abbc); + _lerp_half (&bc, &cd, &bccd); + _lerp_half (&abbc, &bccd, &final); + + s2->a = final; + s2->b = bccd; + s2->c = cd; + s2->d = s1->d; + + s1->b = ab; + s1->c = abbc; + s1->d = final; +} + +/* Return an upper bound on the error (squared) that could result from + * approximating a spline as a line segment connecting the two endpoints. */ +static double +_cairo_spline_error_squared (const cairo_spline_knots_t *knots) +{ + double bdx, bdy, berr; + double cdx, cdy, cerr; + + /* We are going to compute the distance (squared) between each of the the b + * and c control points and the segment a-b. The maximum of these two + * distances will be our approximation error. */ + + bdx = _cairo_fixed_to_double (knots->b.x - knots->a.x); + bdy = _cairo_fixed_to_double (knots->b.y - knots->a.y); + + cdx = _cairo_fixed_to_double (knots->c.x - knots->a.x); + cdy = _cairo_fixed_to_double (knots->c.y - knots->a.y); + + if (knots->a.x != knots->d.x || knots->a.y != knots->d.y) { + /* Intersection point (px): + * px = p1 + u(p2 - p1) + * (p - px) ∙ (p2 - p1) = 0 + * Thus: + * u = ((p - p1) ∙ (p2 - p1)) / ∥p2 - p1∥²; + */ + + double dx, dy, u, v; + + dx = _cairo_fixed_to_double (knots->d.x - knots->a.x); + dy = _cairo_fixed_to_double (knots->d.y - knots->a.y); + v = dx * dx + dy * dy; + + u = bdx * dx + bdy * dy; + if (u <= 0) { + /* bdx -= 0; + * bdy -= 0; + */ + } else if (u >= v) { + bdx -= dx; + bdy -= dy; + } else { + bdx -= u/v * dx; + bdy -= u/v * dy; + } + + u = cdx * dx + cdy * dy; + if (u <= 0) { + /* cdx -= 0; + * cdy -= 0; + */ + } else if (u >= v) { + cdx -= dx; + cdy -= dy; + } else { + cdx -= u/v * dx; + cdy -= u/v * dy; + } + } + + berr = bdx * bdx + bdy * bdy; + cerr = cdx * cdx + cdy * cdy; + if (berr > cerr) + return berr; + else + return cerr; +} + +static cairo_status_t +_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, &s1->b); + + _de_casteljau (s1, &s2); + + status = _cairo_spline_decompose_into (s1, tolerance_squared, result); + if (unlikely (status)) + return status; + + return _cairo_spline_decompose_into (&s2, tolerance_squared, result); +} + +cairo_status_t +_cairo_spline_decompose (cairo_spline_t *spline, double tolerance) +{ + cairo_spline_knots_t s1; + cairo_status_t status; + + s1 = spline->knots; + spline->last_point = s1.a; + status = _cairo_spline_decompose_into (&s1, tolerance * tolerance, spline); + if (unlikely (status)) + return status; + + 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. */ +cairo_status_t +_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *p0, const cairo_point_t *p1, + const cairo_point_t *p2, const cairo_point_t *p3) +{ + double x0, x1, x2, x3; + double y0, y1, y2, y3; + double a, b, c; + double t[4]; + int t_num = 0, i; + cairo_status_t status; + + x0 = _cairo_fixed_to_double (p0->x); + y0 = _cairo_fixed_to_double (p0->y); + x1 = _cairo_fixed_to_double (p1->x); + y1 = _cairo_fixed_to_double (p1->y); + x2 = _cairo_fixed_to_double (p2->x); + y2 = _cairo_fixed_to_double (p2->y); + x3 = _cairo_fixed_to_double (p3->x); + y3 = _cairo_fixed_to_double (p3->y); + + /* The spline can be written as a polynomial of the four points: + * + * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3 + * + * for 0≤t≤1. Now, the X and Y components of the spline follow the + * same polynomial but with x and y replaced for p. To find the + * bounds of the spline, we just need to find the X and Y bounds. + * To find the bound, we take the derivative and equal it to zero, + * and solve to find the t's that give the extreme points. + * + * Here is the derivative of the curve, sorted on t: + * + * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1 + * + * Let: + * + * a = -p0+3p1-3p2+p3 + * b = p0-2p1+p2 + * c = -p0+p1 + * + * Gives: + * + * a.t² + 2b.t + c = 0 + * + * With: + * + * delta = b*b - a*c + * + * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if + * delta is positive, and at -b/a if delta is zero. + */ + +#define ADD(t0) \ + { \ + double _t0 = (t0); \ + if (0 < _t0 && _t0 < 1) \ + t[t_num++] = _t0; \ + } + +#define FIND_EXTREMES(a,b,c) \ + { \ + if (a == 0) { \ + if (b != 0) \ + ADD (-c / (2*b)); \ + } else { \ + double b2 = b * b; \ + double delta = b2 - a * c; \ + if (delta > 0) { \ + cairo_bool_t feasible; \ + double _2ab = 2 * a * b; \ + /* We are only interested in solutions t that satisfy 0= 0) \ + feasible = delta > b2 && delta < a*a + b2 + _2ab; \ + else if (-b / a >= 1) \ + feasible = delta < b2 && delta > a*a + b2 + _2ab; \ + else \ + feasible = delta < b2 || delta < a*a + b2 + _2ab; \ + \ + if (unlikely (feasible)) { \ + double sqrt_delta = sqrt (delta); \ + ADD ((-b - sqrt_delta) / a); \ + ADD ((-b + sqrt_delta) / a); \ + } \ + } else if (delta == 0) { \ + ADD (-b / a); \ + } \ + } \ + } + + /* Find X extremes */ + a = -x0 + 3*x1 - 3*x2 + x3; + b = x0 - 2*x1 + x2; + c = -x0 + x1; + FIND_EXTREMES (a, b, c); + + /* Find Y extremes */ + a = -y0 + 3*y1 - 3*y2 + y3; + b = y0 - 2*y1 + y2; + c = -y0 + y1; + FIND_EXTREMES (a, b, c); + + status = add_point_func (closure, p0, NULL); + if (unlikely (status)) + return status; + + for (i = 0; i < t_num; i++) { + cairo_point_t p; + double x, y; + double t_1_0, t_0_1; + double t_2_0, t_0_2; + double t_3_0, t_2_1_3, t_1_2_3, t_0_3; + + t_1_0 = t[i]; /* t */ + t_0_1 = 1 - t_1_0; /* (1 - t) */ + + t_2_0 = t_1_0 * t_1_0; /* t * t */ + t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */ + + t_3_0 = t_2_0 * t_1_0; /* t * t * t */ + t_2_1_3 = t_2_0 * t_0_1 * 3; /* t * t * (1 - t) * 3 */ + t_1_2_3 = t_1_0 * t_0_2 * 3; /* t * (1 - t) * (1 - t) * 3 */ + t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */ + + /* Bezier polynomial */ + x = x0 * t_0_3 + + x1 * t_1_2_3 + + x2 * t_2_1_3 + + x3 * t_3_0; + y = y0 * t_0_3 + + y1 * t_1_2_3 + + y2 * t_2_1_3 + + y3 * t_3_0; + + p.x = _cairo_fixed_from_double (x); + p.y = _cairo_fixed_from_double (y); + status = add_point_func (closure, &p, NULL); + if (unlikely (status)) + return status; + } + + return add_point_func (closure, p3, NULL); +} diff --git a/gfx/cairo/cairo/src/cairo-stroke-dash-private.h b/gfx/cairo/cairo/src/cairo-stroke-dash-private.h new file mode 100644 index 0000000000..75c000cd73 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-stroke-dash-private.h @@ -0,0 +1,70 @@ +/* -*- 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 + */ + +#ifndef CAIRO_STROKE_DASH_PRIVATE_H +#define CAIRO_STROKE_DASH_PRIVATE_H + +#include "cairoint.h" + +CAIRO_BEGIN_DECLS + +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; + +cairo_private void +_cairo_stroker_dash_init (cairo_stroker_dash_t *dash, + const cairo_stroke_style_t *style); + +cairo_private void +_cairo_stroker_dash_start (cairo_stroker_dash_t *dash); + +cairo_private void +_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step); + +CAIRO_END_DECLS + +#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 0000000000..9494010f56 --- /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 new file mode 100644 index 0000000000..9c373c3332 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-stroke-style.c @@ -0,0 +1,354 @@ +/* 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 + */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +void +_cairo_stroke_style_init (cairo_stroke_style_t *style) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); + + style->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT; + style->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT; + style->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT; + style->miter_limit = CAIRO_GSTATE_MITER_LIMIT_DEFAULT; + + style->dash = NULL; + style->num_dashes = 0; + style->dash_offset = 0.0; +} + +cairo_status_t +_cairo_stroke_style_init_copy (cairo_stroke_style_t *style, + const cairo_stroke_style_t *other) +{ + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); + + style->line_width = other->line_width; + style->line_cap = other->line_cap; + style->line_join = other->line_join; + style->miter_limit = other->miter_limit; + + style->num_dashes = other->num_dashes; + + if (other->dash == NULL) { + style->dash = NULL; + } else { + style->dash = _cairo_malloc_ab (style->num_dashes, sizeof (double)); + if (unlikely (style->dash == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (style->dash, other->dash, + style->num_dashes * sizeof (double)); + } + + style->dash_offset = other->dash_offset; + + return CAIRO_STATUS_SUCCESS; +} + +void +_cairo_stroke_style_fini (cairo_stroke_style_t *style) +{ + free (style->dash); + style->dash = NULL; + + style->num_dashes = 0; + + VG (VALGRIND_MAKE_MEM_UNDEFINED (style, sizeof (cairo_stroke_style_t))); +} + +/* + * For a stroke in the given style, compute the maximum distance + * from the path that vertices could be generated. In the case + * of rotation in the ctm, the distance will not be exact. + */ +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) +{ + double style_expansion = 0.5; + + if (style->line_cap == CAIRO_LINE_CAP_SQUARE) + style_expansion = M_SQRT1_2; + + if (style->line_join == CAIRO_LINE_JOIN_MITER && + ! 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); + } +} + +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. + */ +double +_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style) +{ + double period; + unsigned int i; + + period = 0.0; + for (i = 0; i < style->num_dashes; i++) + period += style->dash[i]; + + if (style->num_dashes & 1) + period *= 2.0; + + return period; +} + +/* + * Coefficient of the linear approximation (minimizing square difference) + * of the surface covered by round caps + * + * This can be computed in the following way: + * the area inside the circle with radius w/2 and the region -d/2 <= x <= d/2 is: + * f(w,d) = 2 * integrate (sqrt (w*w/4 - x*x), x, -d/2, d/2) + * The square difference to a generic linear approximation (c*d) in the range (0,w) would be: + * integrate ((f(w,d) - c*d)^2, d, 0, w) + * To minimize this difference it is sufficient to find a solution of the differential with + * respect to c: + * solve ( diff (integrate ((f(w,d) - c*d)^2, d, 0, w), c), c) + * Which leads to c = 9/32*pi*w + * Since we're not interested in the true area, but just in a coverage extimate, + * we always divide the real area by the line width (w). + * The same computation for square caps would be + * f(w,d) = 2 * integrate(w/2, x, -d/2, d/2) + * c = 1*w + * but in this case it would not be an approximation, since f is already linear in d. + */ +#define ROUND_MINSQ_APPROXIMATION (9*M_PI/32) + +/* + * Computes the length of the "on" part of a dashed stroke style, + * taking into account also line caps. + * Returns 0 for non-dashed styles. + */ +double +_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style) +{ + double stroked, cap_scale; + unsigned int i; + + switch (style->line_cap) { + default: ASSERT_NOT_REACHED; + case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break; + case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break; + case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break; + } + + stroked = 0.0; + if (style->num_dashes & 1) { + /* Each dash element is used both as on and as off. The order in which they are summed is + * irrelevant, so sum the coverage of one dash element, taken both on and off at each iteration */ + for (i = 0; i < style->num_dashes; i++) + stroked += style->dash[i] + cap_scale * MIN (style->dash[i], style->line_width); + } 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 + 1 < style->num_dashes; i += 2) + stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width); + } + + return stroked; +} + +/* + * Verifies if _cairo_stroke_style_dash_approximate should be used to generate + * an approximation of the dash pattern in the specified style, when used for + * stroking a path with the given CTM and tolerance. + * Always %FALSE for non-dashed styles. + */ +cairo_bool_t +_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance) +{ + double period; + + if (! style->num_dashes) + return FALSE; + + period = _cairo_stroke_style_dash_period (style); + return _cairo_matrix_transformed_circle_major_axis (ctm, period) < tolerance; +} + +/* + * Create a 2-dashes approximation of a dashed style, by making the "on" and "off" + * parts respect the original ratio. + */ +void +_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance, + double *dash_offset, + double *dashes, + unsigned int *num_dashes) +{ + double coverage, scale, offset; + cairo_bool_t on = TRUE; + unsigned int i = 0; + + coverage = _cairo_stroke_style_dash_stroked (style) / _cairo_stroke_style_dash_period (style); + coverage = MIN (coverage, 1.0); + scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0); + + /* 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. */ + offset = style->dash_offset; + while (offset > 0.0 && offset >= style->dash[i]) { + offset -= style->dash[i]; + on = !on; + if (++i == style->num_dashes) + i = 0; + } + + *num_dashes = 2; + + /* + * We want to create a new dash pattern with the same relative coverage, + * but composed of just 2 elements with total length equal to scale. + * Based on the formula in _cairo_stroke_style_dash_stroked: + * scale * coverage = dashes[0] + cap_scale * MIN (dashes[1], line_width) + * = MIN (dashes[0] + cap_scale * (scale - dashes[0]), + * dashes[0] + cap_scale * line_width) = + * = MIN (dashes[0] * (1 - cap_scale) + cap_scale * scale, + * dashes[0] + cap_scale * line_width) + * + * Solving both cases we get: + * dashes[0] = scale * (coverage - cap_scale) / (1 - cap_scale) + * when scale - dashes[0] <= line_width + * dashes[0] = scale * coverage - cap_scale * line_width + * when scale - dashes[0] > line_width. + * + * Comparing the two cases we get: + * second > first + * second > scale * (coverage - cap_scale) / (1 - cap_scale) + * second - cap_scale * second - scale * coverage + scale * cap_scale > 0 + * (scale * coverage - cap_scale * line_width) - cap_scale * second - scale * coverage + scale * cap_scale > 0 + * - line_width - second + scale > 0 + * scale - second > line_width + * which is the condition for the second solution to be the valid one. + * So when second > first, the second solution is the correct one (i.e. + * the solution is always MAX (first, second). + */ + switch (style->line_cap) { + default: + ASSERT_NOT_REACHED; + dashes[0] = 0.0; + break; + + case CAIRO_LINE_CAP_BUTT: + /* Simplified formula (substituting 0 for cap_scale): */ + dashes[0] = scale * coverage; + break; + + case CAIRO_LINE_CAP_ROUND: + dashes[0] = MAX(scale * (coverage - ROUND_MINSQ_APPROXIMATION) / (1.0 - ROUND_MINSQ_APPROXIMATION), + scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width); + break; + + case CAIRO_LINE_CAP_SQUARE: + /* + * Special attention is needed to handle the case cap_scale == 1 (since the first solution + * is either indeterminate or -inf in this case). Since dash lengths are always >=0, using + * 0 as first solution always leads to the correct solution. + */ + dashes[0] = MAX(0.0, scale * coverage - style->line_width); + break; + } + + dashes[1] = scale - dashes[0]; + + *dash_offset = on ? 0.0 : dashes[0]; +} 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 0000000000..d31655be89 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-backend-private.h @@ -0,0 +1,228 @@ +/* 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); + +}; + +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 new file mode 100644 index 0000000000..e5b00af7c6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-clipper-private.h @@ -0,0 +1,71 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_SURFACE_CLIPPER_PRIVATE_H +#define CAIRO_SURFACE_CLIPPER_PRIVATE_H + +#include "cairo-types-private.h" +#include "cairo-clip-private.h" + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_surface_clipper cairo_surface_clipper_t; + +typedef cairo_status_t +(*cairo_surface_clipper_intersect_clip_path_func_t) (cairo_surface_clipper_t *, + cairo_path_fixed_t *, + cairo_fill_rule_t, + double, + cairo_antialias_t); +struct _cairo_surface_clipper { + 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, + const cairo_clip_t *clip); + +cairo_private void +_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, + cairo_surface_clipper_intersect_clip_path_func_t intersect); + +cairo_private void +_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper); + +CAIRO_END_DECLS + +#endif /* CAIRO_SURFACE_CLIPPER_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-clipper.c b/gfx/cairo/cairo/src/cairo-surface-clipper.c new file mode 100644 index 0000000000..5309362c68 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-clipper.c @@ -0,0 +1,196 @@ +/* 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 Red Hat, Inc. + * + * Contributor(s): + * Chris Wilson + */ + +#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_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_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, + end); + if (unlikely (status)) + return status; + } + + return clipper->intersect_clip_path (clipper, + &clip_path->path, + clip_path->fill_rule, + clip_path->tolerance, + clip_path->antialias); +} + +cairo_status_t +_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_bool_t incremental = FALSE; + + if (_cairo_clip_equal (clip, clipper->clip)) + return CAIRO_STATUS_SUCCESS; + + /* all clipped out state should never propagate this far */ + assert (!_cairo_clip_is_all_clipped (clip)); + + /* 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; + + if (clip_path) { + incremental = TRUE; + status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper, + clip->path, + clipper->clip->path); + } + } + + _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; +} + +void +_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper, + cairo_surface_clipper_intersect_clip_path_func_t func) +{ + clipper->clip = NULL; + clipper->intersect_clip_path = func; +} + +void +_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper) +{ + _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 new file mode 100644 index 0000000000..ecf7b0edf2 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-fallback-private.h @@ -0,0 +1,95 @@ +/* -*- 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 + */ + +#ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H +#define CAIRO_SURFACE_FALLBACK_PRIVATE_H + +#include "cairoint.h" + +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, + const cairo_clip_t *clip); + +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, + const cairo_clip_t *clip); + +cairo_private cairo_int_status_t +_cairo_surface_fallback_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_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_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_END_DECLS + +#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 new file mode 100644 index 0000000000..a0af159692 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-fallback.c @@ -0,0 +1,115 @@ +/* -*- 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-surface-fallback-private.h" + +cairo_int_status_t +_cairo_surface_fallback_paint (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + return _cairo_compositor_paint (&_cairo_fallback_compositor, + surface, op, source, clip); +} + +cairo_int_status_t +_cairo_surface_fallback_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 (&_cairo_fallback_compositor, + surface, op, source, mask, clip); +} + +cairo_int_status_t +_cairo_surface_fallback_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_fallback_compositor, + surface, op, source, path, + style, ctm,ctm_inverse, + tolerance, antialias, 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) +{ + return _cairo_compositor_fill (&_cairo_fallback_compositor, + surface, op, source, path, + fill_rule, tolerance, antialias, + clip); +} + +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) +{ + return _cairo_compositor_glyphs (&_cairo_fallback_compositor, + surface, op, source, + glyphs, num_glyphs, scaled_font, + clip); +} diff --git a/gfx/cairo/cairo/src/cairo-surface-inline.h b/gfx/cairo/cairo/src/cairo-surface-inline.h new file mode 100644 index 0000000000..85fbc9192b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-inline.h @@ -0,0 +1,60 @@ +/* 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_INLINE_H +#define CAIRO_SURFACE_INLINE_H + +#include "cairo-surface-private.h" + +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; +} + +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; +} + +#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 0000000000..07b94770d4 --- /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 0000000000..6ed0c18d17 --- /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 0000000000..9c4432e246 --- /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 new file mode 100644 index 0000000000..310ba5691c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-offset-private.h @@ -0,0 +1,95 @@ +/* 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): + * Chris Wilson + */ + +#ifndef CAIRO_SURFACE_OFFSET_PRIVATE_H +#define CAIRO_SURFACE_OFFSET_PRIVATE_H + +#include "cairo-types-private.h" + +CAIRO_BEGIN_DECLS + +cairo_private cairo_status_t +_cairo_surface_offset_paint (cairo_surface_t *target, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_offset_mask (cairo_surface_t *target, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + 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, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_offset_fill (cairo_surface_t *surface, + int x, int y, + 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_offset_glyphs (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + 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 new file mode 100644 index 0000000000..98f57f2980 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-offset.c @@ -0,0 +1,308 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2007 Adrian Johnson + * 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 Red Hat, Inc. + * + * Contributor(s): + * Chris Wilson + */ + +#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. */ + +static void +_copy_transformed_pattern (cairo_pattern_t *pattern, + const cairo_pattern_t *original, + const cairo_matrix_t *ctm_inverse) +{ + _cairo_pattern_init_static_copy (pattern, original); + + if (! _cairo_matrix_is_identity (ctm_inverse)) + _cairo_pattern_transform (pattern, ctm_inverse); +} + +cairo_status_t +_cairo_surface_offset_paint (cairo_surface_t *target, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t *dev_clip = (cairo_clip_t *) clip; + cairo_pattern_union_t source_copy; + + if (unlikely (target->status)) + return target->status; + + 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); + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_paint (target, op, source, dev_clip); + + if (dev_clip != clip) + _cairo_clip_destroy (dev_clip); + + return status; +} + +cairo_status_t +_cairo_surface_offset_mask (cairo_surface_t *target, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_status_t status; + 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 (_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); + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + _copy_transformed_pattern (&mask_copy.base, mask, &m); + source = &source_copy.base; + mask = &mask_copy.base; + } + + status = _cairo_surface_mask (target, op, + source, mask, + dev_clip); + + if (dev_clip != clip) + _cairo_clip_destroy (dev_clip); + + return status; +} + +cairo_status_t +_cairo_surface_offset_stroke (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t*stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_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; + cairo_status_t status; + + if (unlikely (surface->status)) + return surface->status; + + 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; + + _cairo_path_fixed_translate (&path_copy, + _cairo_fixed_from_int (-x), + _cairo_fixed_from_int (-y)); + dev_path = &path_copy; + + cairo_matrix_init_translate (&m, -x, -y); + cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m); + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + cairo_matrix_multiply (&dev_ctm_inverse, &m, &dev_ctm_inverse); + } + + status = _cairo_surface_stroke (surface, op, source, + dev_path, stroke_style, + &dev_ctm, &dev_ctm_inverse, + tolerance, antialias, + dev_clip); + +FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (dev_clip != clip) + _cairo_clip_destroy (dev_clip); + + return status; +} + +cairo_status_t +_cairo_surface_offset_fill (cairo_surface_t *surface, + int x, int y, + 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_status_t status; + 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 (_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; + + _cairo_path_fixed_translate (&path_copy, + _cairo_fixed_from_int (-x), + _cairo_fixed_from_int (-y)); + dev_path = &path_copy; + + cairo_matrix_init_translate (&m, x, y); + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_fill (surface, op, source, + dev_path, fill_rule, + tolerance, antialias, + dev_clip); + +FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + if (dev_clip != clip) + _cairo_clip_destroy (dev_clip); + + return status; +} + +cairo_status_t +_cairo_surface_offset_glyphs (cairo_surface_t *surface, + int x, int y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t *dev_clip = (cairo_clip_t *) clip; + cairo_pattern_union_t source_copy; + cairo_glyph_t *dev_glyphs; + int i; + + if (unlikely (surface->status)) + return surface->status; + + if (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (dev_glyphs == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs); + + if (x | y) { + cairo_matrix_t m; + + 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); + source = &source_copy.base; + + for (i = 0; i < num_glyphs; i++) { + dev_glyphs[i].x -= x; + dev_glyphs[i].y -= y; + } + } + + status = _cairo_surface_show_text_glyphs (surface, op, source, + NULL, 0, + dev_glyphs, num_glyphs, + NULL, 0, 0, + scaled_font, + dev_clip); + + if (dev_clip != 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 new file mode 100644 index 0000000000..41f2f2cad8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-private.h @@ -0,0 +1,123 @@ +/* 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_PRIVATE_H +#define CAIRO_SURFACE_PRIVATE_H + +#include "cairo.h" + +#include "cairo-types-private.h" +#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; + cairo_device_t *device; + + /* We allow surfaces to override the backend->type by shoving something + * else into surface->type. This is for "wrapper" surfaces that want to + * hide their internal type from the user-level API. */ + cairo_surface_type_t type; + + cairo_content_t content; + + 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 is_vector : 1; + unsigned permit_subpixel_antialiasing : 1; + + cairo_user_data_array_t user_data; + cairo_user_data_array_t mime_data; + + cairo_matrix_t device_transform; + cairo_matrix_t device_transform_inverse; + cairo_list_t device_transform_observers; + + /* The actual resolution of the device, in dots per inch. */ + double x_resolution; + double y_resolution; + + /* The resolution that should be used when generating image-based + * fallback; generally only used by the analysis/paginated + * surfaces + */ + double x_fallback_resolution; + double y_fallback_resolution; + + /* A "snapshot" surface is immutable. See _cairo_surface_snapshot. */ + cairo_surface_t *snapshot_of; + cairo_surface_func_t snapshot_detach; + /* current snapshots of this surface*/ + cairo_list_t snapshots; + /* place upon snapshot list */ + cairo_list_t snapshot; + + /* + * Surface font options, falling back to backend's default options, + * and set using _cairo_surface_set_font_options(), and propagated by + * cairo_surface_create_similar(). + */ + 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 0000000000..bf89c772b4 --- /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 new file mode 100644 index 0000000000..58bee7b49d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-snapshot-private.h @@ -0,0 +1,51 @@ +/* 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_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; +}; + +#endif /* CAIRO_SURFACE_SNAPSHOT_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-surface-snapshot.c b/gfx/cairo/cairo/src/cairo-surface-snapshot.c new file mode 100644 index 0000000000..b2908f6bc5 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-snapshot.c @@ -0,0 +1,295 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * 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 University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-error-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) +{ + 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; + + 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; + + 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) +{ + struct snapshot_extra *extra = _extra; + + _cairo_surface_release_source_image (extra->target, image, extra->extra); + cairo_surface_destroy (extra->target); + free (extra); +} + +static cairo_bool_t +_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; + + 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 */ + 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, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_surface_snapshot_get_extents, + NULL, /* get-font-options */ + + _cairo_surface_snapshot_flush, +}; + +static void +_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_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); + goto unlock; + } + clone = image->base.backend->snapshot (&image->base); + _cairo_surface_release_source_image (snapshot->target, image, extra); + +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: + * @surface: a #cairo_surface_t + * + * Make an immutable reference to @surface. It is an error to call a + * surface-modifying function on the result of this function. The + * resulting 'snapshot' is a lazily copied-on-write surface i.e. it + * remains a reference to the original surface until that surface is + * written to again, at which time a copy is made of the original surface + * and the snapshot then points to that instead. Multiple snapshots of the + * same unmodified surface point to the same copy. + * + * The caller owns the return value and should call + * cairo_surface_destroy() when finished with it. This function will not + * return %NULL, but will return a nil surface instead. + * + * Return value: The snapshot surface. Note that the return surface + * may not necessarily be of the same type as @surface. + **/ +cairo_surface_t * +_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 (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 (_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 = _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->is_vector); + snapshot->base.type = surface->type; + + CAIRO_MUTEX_INIT (snapshot->mutex); + snapshot->target = surface; + snapshot->clone = NULL; + + status = _cairo_surface_copy_mime_data (&snapshot->base, surface); + if (unlikely (status)) { + cairo_surface_destroy (&snapshot->base); + return _cairo_surface_create_in_error (status); + } + + snapshot->base.device_transform = surface->device_transform; + snapshot->base.device_transform_inverse = surface->device_transform_inverse; + + _cairo_surface_attach_snapshot (surface, + &snapshot->base, + _cairo_surface_snapshot_copy_on_write); + + return &snapshot->base; +} 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 0000000000..0cd09e63e7 --- /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 new file mode 100644 index 0000000000..89c5cc01b9 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-subsurface-private.h @@ -0,0 +1,55 @@ +/* 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_PRIVATE_H +#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; + + 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 new file mode 100644 index 0000000000..b2a10e9bce --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-subsurface.c @@ -0,0 +1,589 @@ +/* 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 + */ + +#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; + +static cairo_status_t +_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; +} + +static cairo_surface_t * +_cairo_surface_subsurface_create_similar (void *other, + cairo_content_t content, + 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, + 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; + + 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); + _cairo_clip_destroy (target_clip); + return status; +} + +static cairo_int_status_t +_cairo_surface_subsurface_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_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; + + 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); + _cairo_clip_destroy (target_clip); + return status; +} + +static cairo_int_status_t +_cairo_surface_subsurface_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_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; + + 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); + _cairo_clip_destroy (target_clip); + return status; +} + +static cairo_int_status_t +_cairo_surface_subsurface_stroke (void *abstract_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, + 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; + + 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); + _cairo_clip_destroy (target_clip); + return status; +} + +static cairo_int_status_t +_cairo_surface_subsurface_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_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; + + 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); + _cairo_clip_destroy (target_clip); + return status; +} + +static cairo_status_t +_cairo_surface_subsurface_flush (void *abstract_surface, unsigned flags) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + return _cairo_surface_flush (surface->target, flags); +} + +static cairo_status_t +_cairo_surface_subsurface_mark_dirty (void *abstract_surface, + int x, int y, + int width, int height) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_status_t status; + + status = CAIRO_STATUS_SUCCESS; + if (surface->target->backend->mark_dirty_rectangle != NULL) { + cairo_rectangle_int_t rect, extents; + + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + + extents.x = extents.y = 0; + extents.width = surface->extents.width; + extents.height = surface->extents.height; + + if (_cairo_rectangle_intersect (&rect, &extents)) { + status = surface->target->backend->mark_dirty_rectangle (surface->target, + rect.x + surface->extents.x, + rect.y + surface->extents.y, + rect.width, rect.height); + } + } + + return status; +} + +static cairo_bool_t +_cairo_surface_subsurface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + + extents->x = 0; + extents->y = 0; + extents->width = surface->extents.width; + extents->height = surface->extents.height; + + return TRUE; +} + +static void +_cairo_surface_subsurface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_surface_subsurface_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_subsurface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_surface_t *source; + + source = _cairo_surface_get_source (surface->target, extents); + if (extents) + *extents = surface->extents; + + return source; +} + +static cairo_status_t +_cairo_surface_subsurface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **extra_out) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_surface_pattern_t pattern; + cairo_surface_t *image; + cairo_status_t status; + + image = _cairo_image_surface_create_with_content (surface->base.content, + surface->extents.width, + surface->extents.height); + if (unlikely (image->status)) + return image->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 (image, + CAIRO_OPERATOR_SOURCE, + &pattern.base, NULL); + _cairo_pattern_fini (&pattern.base); + if (unlikely (status)) { + cairo_surface_destroy (image); + return status; + } + + *image_out = (cairo_image_surface_t *)image; + *extra_out = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_surface_subsurface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *abstract_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_surface_t * +_cairo_surface_subsurface_snapshot (void *abstract_surface) +{ + cairo_surface_subsurface_t *surface = abstract_surface; + cairo_surface_pattern_t pattern; + cairo_surface_t *clone; + cairo_status_t status; + + TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->target->unique_id)); + + clone = _cairo_surface_create_scratch (surface->target, + surface->target->content, + surface->extents.width, + surface->extents.height, + NULL); + if (unlikely (clone->status)) + return clone; + + _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); + } + + return clone; +} + +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_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, + _cairo_surface_subsurface_snapshot, + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_surface_subsurface_get_extents, + _cairo_surface_subsurface_get_font_options, + + _cairo_surface_subsurface_flush, + _cairo_surface_subsurface_mark_dirty, + + _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_create_for_rectangle: + * @target: an existing surface for which the sub-surface will point to + * @x: the x-origin of the sub-surface from the top-left of the target surface (in device-space units) + * @y: the y-origin of the sub-surface from the top-left of the target surface (in device-space units) + * @width: width of the sub-surface (in device-space units) + * @height: height of the sub-surface (in device-space units) + * + * Create a new surface that is a rectangle within the target surface. + * All operations drawn to this surface are then clipped and translated + * onto the target surface. Nothing drawn via this sub-surface outside of + * its bounds is drawn onto the target surface, making this a useful method + * for passing constrained child surfaces to library routines that draw + * directly onto the parent surface, i.e. with no further backend allocations, + * double buffering or copies. + * + * The semantics of subsurfaces have not been finalized yet + * unless the rectangle is in full device units, is contained within + * the extents of the target surface, and the target or subsurface's + * device transforms are not changed. + * + * 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.10 + **/ +cairo_surface_t * +cairo_surface_create_for_rectangle (cairo_surface_t *target, + double x, double y, + double width, double height) +{ + 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 = _cairo_malloc (sizeof (cairo_surface_subsurface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + 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->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 */ + cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) target; + surface->extents.x += sub->extents.x; + surface->extents.y += sub->extents.y; + target = sub->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 new file mode 100644 index 0000000000..380ba099dc --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h @@ -0,0 +1,200 @@ +/* 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): + * Chris Wilson + */ + +#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 +_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper, + cairo_surface_t *target); + +cairo_private void +_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, + void **image_extra); + +cairo_private void +_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t *image, + void *image_extra); + + +cairo_private cairo_status_t +_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + const cairo_path_fixed_t*path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_scaled_font_t *scaled_font, + 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); + +cairo_private cairo_surface_t * +_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, + cairo_content_t content, + int width, + int height); +cairo_private cairo_bool_t +_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper, + cairo_font_options_t *options); + +cairo_private cairo_surface_t * +_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); + +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 new file mode 100644 index 0000000000..7fb417a202 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface-wrapper.c @@ -0,0 +1,695 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2007 Adrian Johnson + * 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 Red Hat, Inc. + * + * Contributor(s): + * Chris Wilson + */ + +#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 */ + +static void +_copy_transformed_pattern (cairo_pattern_t *pattern, + const cairo_pattern_t *original, + const cairo_matrix_t *ctm_inverse) +{ + _cairo_pattern_init_static_copy (pattern, original); + + if (! _cairo_matrix_is_identity (ctm_inverse)) + _cairo_pattern_transform (pattern, ctm_inverse); +} + +cairo_status_t +_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t **image_out, + void **image_extra) +{ + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + return _cairo_surface_acquire_source_image (wrapper->target, + image_out, image_extra); +} + +void +_cairo_surface_wrapper_release_source_image (cairo_surface_wrapper_t *wrapper, + cairo_image_surface_t *image, + void *image_extra) +{ + _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, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t *dev_clip; + 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 (_cairo_clip_is_all_clipped (dev_clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + if (wrapper->needs_transform) { + cairo_matrix_t m; + + _cairo_surface_wrapper_get_transform (wrapper, &m); + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_paint (wrapper->target, op, source, 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, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_clip_t *dev_clip; + cairo_pattern_union_t source_copy; + cairo_pattern_union_t mask_copy; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + 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->needs_transform) { + cairo_matrix_t m; + + _cairo_surface_wrapper_get_transform (wrapper, &m); + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + + _copy_transformed_pattern (&mask_copy.base, mask, &m); + mask = &mask_copy.base; + } + + status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip); + + _cairo_clip_destroy (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_status_t status; + 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; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + 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->needs_transform) { + cairo_matrix_t m; + + _cairo_surface_wrapper_get_transform (wrapper, &m); + + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &m); + dev_path = &path_copy; + + 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_stroke (wrapper->target, op, source, + dev_path, stroke_style, + &dev_ctm, &dev_ctm_inverse, + tolerance, antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + _cairo_clip_destroy (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper, + cairo_operator_t fill_op, + const cairo_pattern_t *fill_source, + cairo_fill_rule_t fill_rule, + double fill_tolerance, + cairo_antialias_t fill_antialias, + const cairo_path_fixed_t*path, + cairo_operator_t stroke_op, + const cairo_pattern_t *stroke_source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double stroke_tolerance, + cairo_antialias_t stroke_antialias, + const cairo_clip_t *clip) +{ + cairo_status_t status; + 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; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + 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->needs_transform) { + cairo_matrix_t m; + + _cairo_surface_wrapper_get_transform (wrapper, &m); + + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &m); + dev_path = &path_copy; + + 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 (&stroke_source_copy.base, stroke_source, &m); + stroke_source = &stroke_source_copy.base; + + _copy_transformed_pattern (&fill_source_copy.base, fill_source, &m); + fill_source = &fill_source_copy.base; + } + + status = _cairo_surface_fill_stroke (wrapper->target, + fill_op, fill_source, fill_rule, + fill_tolerance, fill_antialias, + dev_path, + stroke_op, stroke_source, + stroke_style, + &dev_ctm, &dev_ctm_inverse, + stroke_tolerance, stroke_antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + _cairo_clip_destroy (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path; + cairo_pattern_union_t source_copy; + cairo_clip_t *dev_clip; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + 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->needs_transform) { + cairo_matrix_t m; + + _cairo_surface_wrapper_get_transform (wrapper, &m); + + status = _cairo_path_fixed_init_copy (&path_copy, dev_path); + if (unlikely (status)) + goto FINISH; + + _cairo_path_fixed_transform (&path_copy, &m); + dev_path = &path_copy; + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } + + status = _cairo_surface_fill (wrapper->target, op, source, + dev_path, fill_rule, + tolerance, antialias, + dev_clip); + + FINISH: + if (dev_path != path) + _cairo_path_fixed_fini (dev_path); + _cairo_clip_destroy (dev_clip); + return status; +} + +cairo_status_t +_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_scaled_font_t *scaled_font, + const cairo_clip_t *clip) +{ + cairo_status_t status; + 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_font_options_t options; + + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip); + if (_cairo_clip_is_all_clipped (dev_clip)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + cairo_surface_get_font_options (wrapper->target, &options); + cairo_font_options_merge (&options, &scaled_font->options); + + if (wrapper->needs_transform) { + cairo_matrix_t m; + int i; + + _cairo_surface_wrapper_get_transform (wrapper, &m); + + if (! _cairo_matrix_is_translation (&m)) { + cairo_matrix_t ctm; + + _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); + } + + 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); + } + + status = cairo_matrix_invert (&m); + assert (status == CAIRO_STATUS_SUCCESS); + + _copy_transformed_pattern (&source_copy.base, source, &m); + source = &source_copy.base; + } 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, + utf8, utf8_len, + dev_glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + dev_scaled_font, + dev_clip); + FINISH: + _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) +{ + if (unlikely (wrapper->target->status)) + return wrapper->target->status; + + + return _cairo_surface_tag (wrapper->target, begin, tag_name, attributes); +} + +cairo_surface_t * +_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper, + cairo_content_t content, + int width, + int height) +{ + return _cairo_surface_create_scratch (wrapper->target, + content, width, height, NULL); +} + +cairo_bool_t +_cairo_surface_wrapper_get_extents (cairo_surface_wrapper_t *wrapper, + cairo_rectangle_int_t *extents) +{ + if (wrapper->has_extents) { + if (_cairo_surface_get_extents (wrapper->target, extents)) + _cairo_rectangle_intersect (extents, &wrapper->extents); + else + *extents = wrapper->extents; + + return TRUE; + } else { + return _cairo_surface_get_extents (wrapper->target, extents); + } +} + +static cairo_bool_t +_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper) +{ + 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->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) +{ + cairo_surface_get_font_options (wrapper->target, options); +} + +cairo_surface_t * +_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper) +{ + if (wrapper->target->backend->snapshot) + return wrapper->target->backend->snapshot (wrapper->target); + + return NULL; +} + +cairo_bool_t +_cairo_surface_wrapper_has_show_text_glyphs (cairo_surface_wrapper_t *wrapper) +{ + return cairo_surface_has_show_text_glyphs (wrapper->target); +} + +void +_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 +_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper) +{ + cairo_surface_destroy (wrapper->target); +} + +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_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; + } +} diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c new file mode 100644 index 0000000000..32a9e825cd --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-surface.c @@ -0,0 +1,3130 @@ +/* -*- 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. + * + * 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-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" + +/** + * SECTION:cairo-surface + * @Title: cairo_surface_t + * @Short_Description: Base class for surfaces + * @See_Also: #cairo_t, #cairo_pattern_t + * + * #cairo_surface_t is the abstract type representing all different drawing + * targets that cairo can render to. The actual drawings are + * performed using a cairo context. + * + * A cairo surface is created by using backend-specific + * constructors, typically of the form + * 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 + * cairo_surface_flush() before reading from or writing to the surface and that + * you must use cairo_surface_mark_dirty() after modifying it. + * + * Directly modifying an image surface + * + * void + * modify_image_surface (cairo_surface_t *surface) + * { + * unsigned char *data; + * int width, height, stride; + * + * // flush to ensure all writing to the image was done + * cairo_surface_flush (surface); + * + * // modify the image + * data = cairo_image_surface_get_data (surface); + * width = cairo_image_surface_get_width (surface); + * height = cairo_image_surface_get_height (surface); + * stride = cairo_image_surface_get_stride (surface); + * modify_image_data (data, width, height, stride); + * + * // mark the image dirty so Cairo clears its caches. + * cairo_surface_mark_dirty (surface); + * } + * + * + * 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 = { \ + NULL, /* backend */ \ + NULL, /* device */ \ + CAIRO_SURFACE_TYPE_IMAGE, /* type */ \ + CAIRO_CONTENT_COLOR, /* content */ \ + 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, /* is_vector */ \ + FALSE, /* permit_subpixel_antialiasing */ \ + { 0, 0, 0, NULL, }, /* user_data */ \ + { 0, 0, 0, NULL, }, /* mime_data */ \ + { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \ + { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform_inverse */ \ + { NULL, NULL }, /* device_transform_observers */ \ + 0.0, /* x_resolution */ \ + 0.0, /* y_resolution */ \ + 0.0, /* x_fallback_resolution */ \ + 0.0, /* y_fallback_resolution */ \ + NULL, /* snapshot_of */ \ + NULL, /* snapshot_detach */ \ + { NULL, NULL }, /* snapshots */ \ + { NULL, NULL }, /* snapshot */ \ + { CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \ + CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \ + CAIRO_LCD_FILTER_DEFAULT, /* lcd_filter */ \ + CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \ + CAIRO_HINT_METRICS_DEFAULT, /* hint_metrics */ \ + CAIRO_ROUND_GLYPH_POS_DEFAULT /* round_glyph_positions */ \ + } /* font_options */ \ +} + +/* XXX error object! */ + +static DEFINE_NIL_SURFACE(CAIRO_STATUS_NO_MEMORY, _cairo_surface_nil); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_SURFACE_TYPE_MISMATCH, _cairo_surface_nil_surface_type_mismatch); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STATUS, _cairo_surface_nil_invalid_status); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_CONTENT, _cairo_surface_nil_invalid_content); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_FORMAT, _cairo_surface_nil_invalid_format); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_VISUAL, _cairo_surface_nil_invalid_visual); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_FILE_NOT_FOUND, _cairo_surface_nil_file_not_found); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_TEMP_FILE_ERROR, _cairo_surface_nil_temp_file_error); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_READ_ERROR, _cairo_surface_nil_read_error); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_error); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride); +static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size); +static 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 + * @status: a status value indicating an error + * + * Atomically sets surface->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal + * status values. + * + * All assignments of an error status to surface->status should happen + * through _cairo_surface_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the + * nil objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +cairo_int_status_t +_cairo_surface_set_error (cairo_surface_t *surface, + 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_INT_STATUS_SUCCESS; + + 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, (cairo_status_t)status); + + return _cairo_error (status); +} + +/** + * cairo_surface_get_type: + * @surface: a #cairo_surface_t + * + * This function returns the type of the backend used to create + * a surface. See #cairo_surface_type_t for available types. + * + * Return value: The type of @surface. + * + * Since: 1.2 + **/ +cairo_surface_type_t +cairo_surface_get_type (cairo_surface_t *surface) +{ + /* We don't use surface->backend->type here so that some of the + * special "wrapper" surfaces such as cairo_paginated_surface_t + * can override surface->type with the type of the "child" + * surface. */ + return surface->type; +} + +/** + * cairo_surface_get_content: + * @surface: a #cairo_surface_t + * + * This function returns the content type of @surface which indicates + * whether the surface contains color and/or alpha information. See + * #cairo_content_t. + * + * Return value: The content type of @surface. + * + * Since: 1.2 + **/ +cairo_content_t +cairo_surface_get_content (cairo_surface_t *surface) +{ + return surface->content; +} + +/** + * cairo_surface_status: + * @surface: a #cairo_surface_t + * + * Checks whether an error has previously occurred for this + * surface. + * + * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NULL_POINTER, + * %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) +{ + return surface->status; +} +slim_hidden_def (cairo_surface_status); + +static unsigned int +_cairo_surface_allocate_unique_id (void) +{ + static cairo_atomic_int_t unique_id; + +#if CAIRO_NO_MUTEX + if (++unique_id == 0) + unique_id = 1; + return unique_id; +#else + cairo_atomic_int_t old, id; + + do { + old = _cairo_atomic_uint_get (&unique_id); + id = old + 1; + if (id == 0) + id = 1; + } while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id)); + + return id; +#endif +} + +/** + * cairo_surface_get_device: + * @surface: a #cairo_surface_t + * + * This function returns the device for a @surface. + * See #cairo_device_t. + * + * Return value: The device for @surface or %NULL if the surface does + * not have an associated device. + * + * Since: 1.10 + **/ +cairo_device_t * +cairo_surface_get_device (cairo_surface_t *surface) +{ + if (unlikely (surface->status)) + return _cairo_device_create_in_error (surface->status); + + return surface->device; +} + +static cairo_bool_t +_cairo_surface_has_snapshots (cairo_surface_t *surface) +{ + return ! cairo_list_is_empty (&surface->snapshots); +} + +static cairo_bool_t +_cairo_surface_has_mime_data (cairo_surface_t *surface) +{ + return surface->mime_data.num_elements != 0; +} + +static void +_cairo_surface_detach_mime_data (cairo_surface_t *surface) +{ + if (! _cairo_surface_has_mime_data (surface)) + return; + + _cairo_user_data_array_fini (&surface->mime_data); + _cairo_user_data_array_init (&surface->mime_data); +} + +static void +_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_t, + snapshot)); + } +} + +void +_cairo_surface_detach_snapshot (cairo_surface_t *snapshot) +{ + assert (snapshot->snapshot_of != NULL); + + snapshot->snapshot_of = NULL; + cairo_list_del (&snapshot->snapshot); + + if (snapshot->snapshot_detach != NULL) + snapshot->snapshot_detach (snapshot); + + cairo_surface_destroy (snapshot); +} + +void +_cairo_surface_attach_snapshot (cairo_surface_t *surface, + cairo_surface_t *snapshot, + cairo_surface_func_t detach_func) +{ + assert (surface != snapshot); + assert (snapshot->snapshot_of != surface); + + cairo_surface_reference (snapshot); + + if (snapshot->snapshot_of != NULL) + _cairo_surface_detach_snapshot (snapshot); + + snapshot->snapshot_of = surface; + snapshot->snapshot_detach = detach_func; + + cairo_list_add (&snapshot->snapshot, &surface->snapshots); + + assert (_cairo_surface_has_snapshot (surface, snapshot->backend) == snapshot); +} + +cairo_surface_t * +_cairo_surface_has_snapshot (cairo_surface_t *surface, + const cairo_surface_backend_t *backend) +{ + cairo_surface_t *snapshot; + + cairo_list_foreach_entry (snapshot, cairo_surface_t, + &surface->snapshots, snapshot) + { + if (snapshot->backend == backend) + return snapshot; + } + + return NULL; +} + +cairo_status_t +_cairo_surface_begin_modification (cairo_surface_t *surface) +{ + assert (surface->status == CAIRO_STATUS_SUCCESS); + assert (! surface->finished); + + 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_bool_t is_vector) +{ + CAIRO_MUTEX_INITIALIZE (); + + surface->backend = backend; + 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->permit_subpixel_antialiasing = TRUE; + + _cairo_user_data_array_init (&surface->user_data); + _cairo_user_data_array_init (&surface->mime_data); + + cairo_matrix_init_identity (&surface->device_transform); + cairo_matrix_init_identity (&surface->device_transform_inverse); + cairo_list_init (&surface->device_transform_observers); + + surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; + surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT; + + surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; + surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT; + + cairo_list_init (&surface->snapshots); + surface->snapshot_of = NULL; + + surface->has_font_options = FALSE; +} + +static void +_cairo_surface_copy_similar_properties (cairo_surface_t *surface, + cairo_surface_t *other) +{ + if (other->has_font_options || other->backend != surface->backend) { + cairo_font_options_t options; + + cairo_surface_get_font_options (other, &options); + _cairo_surface_set_font_options (surface, &options); + } + + surface->permit_subpixel_antialiasing = other->permit_subpixel_antialiasing; + + cairo_surface_set_fallback_resolution (surface, + other->x_fallback_resolution, + other->y_fallback_resolution); +} + +/** + * cairo_surface_create_similar: + * @other: an existing surface used to select the backend of the new surface + * @content: the content for the new surface + * @width: width of the new surface, (in device-space units) + * @height: height of the new surface (in device-space units) + * + * Create a new surface that is as compatible as possible with an + * existing surface. For example the new surface will have the same + * 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. + * + * 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, + cairo_content_t content, + 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_STATUS_INVALID_CONTENT); + + /* We inherit the device scale, so create a larger surface */ + width = width * other->device_transform.xx; + height = height * other->device_transform.yy; + + 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); + 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, + 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_similar_image (cairo_surface_t *other, + cairo_format_t format, + int width, + int height) +{ + cairo_surface_t *image; + + 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_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_unmap_image (cairo_surface_t *surface, + cairo_image_surface_t *image) +{ + 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; + } + + /* 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; +} + +/** + * cairo_surface_reference: + * @surface: a #cairo_surface_t + * + * Increases the reference count on @surface by one. This prevents + * @surface from being destroyed until a matching call to + * cairo_surface_destroy() is made. + * + * 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) +{ + if (surface == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return surface; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); + + _cairo_reference_count_inc (&surface->ref_count); + + return surface; +} +slim_hidden_def (cairo_surface_reference); + +/** + * cairo_surface_destroy: + * @surface: a #cairo_surface_t + * + * 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) +{ + if (surface == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&surface->ref_count)) + return; + + assert (surface->snapshot_of == NULL); + + 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; + + _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); + + 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); + +/** + * cairo_surface_get_reference_count: + * @surface: a #cairo_surface_t + * + * Returns the current reference count of @surface. + * + * Return value: the current reference count of @surface. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_surface_get_reference_count (cairo_surface_t *surface) +{ + if (surface == NULL || + CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return 0; + + 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 + * + * This function finishes the surface and drops all references to + * 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 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. + * + * When the last call to cairo_surface_destroy() decreases the + * 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) +{ + if (surface == NULL) + return; + + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count)) + return; + + if (surface->finished) + return; + + /* We have to be careful when decoupling potential reference cycles */ + cairo_surface_reference (surface); + + _cairo_surface_finish_snapshots (surface); + /* XXX need to block and wait for snapshot references */ + _cairo_surface_finish (surface); + + cairo_surface_destroy (surface); +} +slim_hidden_def (cairo_surface_finish); + +/** + * _cairo_surface_release_device_reference: + * @surface: a #cairo_surface_t + * + * This function makes @surface release the reference to its device. The + * function is intended to be used for avoiding cycling references for + * surfaces that are owned by their device, for example cache surfaces. + * Note that the @surface will still assume that the device is available. + * So it is the caller's responsibility to ensure the device stays around + * until the @surface is destroyed. Just calling cairo_surface_finish() is + * not enough. + **/ +void +_cairo_surface_release_device_reference (cairo_surface_t *surface) +{ + assert (surface->owns_device); + + cairo_device_destroy (surface->device); + surface->owns_device = FALSE; +} + +/** + * cairo_surface_get_user_data: + * @surface: a #cairo_surface_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @surface using the specified + * key. If no user data has been attached with the given key this + * 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) +{ + /* 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); +} + +/** + * cairo_surface_set_user_data: + * @surface: a #cairo_surface_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the surface + * @destroy: a #cairo_destroy_func_t which will be called when the + * surface is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @surface. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @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_surface_set_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + 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); +} + +/** + * cairo_surface_get_mime_data: + * @surface: a #cairo_surface_t + * @mime_type: the mime type of the image data + * @data: the image data to attached to the surface + * @length: the length of the image data + * + * Return mime data previously attached to @surface using the + * specified mime type. If no data has been attached with the given + * mime type, @data is set %NULL. + * + * Since: 1.10 + **/ +void +cairo_surface_get_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char **data, + unsigned long *length) +{ + cairo_user_data_slot_t *slots; + int i, num_slots; + + *data = NULL; + *length = 0; + + /* 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, + * 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 && strcmp ((char *) slots[i].key, mime_type) == 0) { + cairo_mime_data_t *mime_data = slots[i].user_data; + + *data = mime_data->data; + *length = mime_data->length; + return; + } + } +} +slim_hidden_def (cairo_surface_get_mime_data); + +static void +_cairo_mime_data_destroy (void *ptr) +{ + cairo_mime_data_t *mime_data = ptr; + + if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count)) + return; + + if (mime_data->destroy && mime_data->closure) + mime_data->destroy (mime_data->closure); + + free (mime_data); +} + + +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 + **/ + +/** + * CAIRO_MIME_TYPE_JPEG: + * + * The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1). + * + * Since: 1.10 + **/ + +/** + * CAIRO_MIME_TYPE_PNG: + * + * The Portable Network Graphics image file format (ISO/IEC 15948). + * + * Since: 1.10 + **/ + +/** + * CAIRO_MIME_TYPE_URI: + * + * URI for an image file (unofficial MIME type). + * + * 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: + * @surface: a #cairo_surface_t + * @mime_type: the MIME type of the image data + * @data: the image data to attach to the surface + * @length: the length of the image data + * @destroy: a #cairo_destroy_func_t which will be called when the + * surface is destroyed or when new image data is attached using the + * same mime type. + * @closure: the data to be passed to the @destroy notifier + * + * Attach an image in the format @mime_type to @surface. To remove + * the data from a surface, call this function with same mime type + * and %NULL for @data. + * + * The attached image (or filename) data can later be used by backends + * which support it (currently: PDF, PS, SVG and Win32 Printing + * surfaces) to emit this data instead of making a snapshot of the + * @surface. This approach tends to be faster and requires less + * 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_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. + * + * 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, + const char *mime_type, + const unsigned char *data, + unsigned long length, + cairo_destroy_func_t destroy, + void *closure) +{ + 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 (unlikely (surface->finished)) + return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + status = _cairo_intern_string (&mime_type, -1); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + if (data != NULL) { + 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)); + + CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1); + + mime_data->data = (unsigned char *) data; + mime_data->length = length; + mime_data->destroy = destroy; + mime_data->closure = closure; + } else + mime_data = NULL; + + status = _cairo_user_data_array_set_data (&surface->mime_data, + (cairo_user_data_key_t *) mime_type, + mime_data, + _cairo_mime_data_destroy); + if (unlikely (status)) { + 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) +{ + cairo_mime_data_t *mime_data = elt; + + _cairo_reference_count_inc (&mime_data->ref_count); +} + +cairo_status_t +_cairo_surface_copy_mime_data (cairo_surface_t *dst, + cairo_surface_t *src) +{ + cairo_status_t status; + + if (dst->status) + return dst->status; + + if (src->status) + return _cairo_surface_set_error (dst, src->status); + + /* first copy the mime-data, discarding any already set on dst */ + status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data); + if (unlikely (status)) + return _cairo_surface_set_error (dst, status); + + /* now increment the reference counters for the copies */ + _cairo_user_data_array_foreach (&dst->mime_data, + _cairo_mime_data_reference, + NULL); + + dst->is_clear = FALSE; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_surface_set_font_options: + * @surface: a #cairo_surface_t + * @options: a #cairo_font_options_t object that contains the + * options to use for this surface instead of backend's default + * font options. + * + * Sets the default font rendering options for the surface. + * This is useful to correctly propagate default font options when + * falling back to an image surface in a backend implementation. + * This affects the options returned in cairo_surface_get_font_options(). + * + * If @options is %NULL the surface options are reset to those of + * the backend default. + **/ +void +_cairo_surface_set_font_options (cairo_surface_t *surface, + cairo_font_options_t *options) +{ + if (surface->status) + return; + + assert (surface->snapshot_of == NULL); + + if (surface->finished) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (options) { + surface->has_font_options = TRUE; + _cairo_font_options_init_copy (&surface->font_options, options); + } else { + surface->has_font_options = FALSE; + } +} + +/** + * cairo_surface_get_font_options: + * @surface: a #cairo_surface_t + * @options: a #cairo_font_options_t object into which to store + * the retrieved options. All existing values are overwritten + * + * Retrieves the default font rendering options for the surface. + * This allows display surfaces to report the correct subpixel order + * 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, + cairo_font_options_t *options) +{ + if (cairo_font_options_status (options)) + return; + + if (surface->status) { + _cairo_font_options_init_default (options); + return; + } + + if (! surface->has_font_options) { + surface->has_font_options = TRUE; + + _cairo_font_options_init_default (&surface->font_options); + + if (!surface->finished && surface->backend->get_font_options) { + surface->backend->get_font_options (surface, &surface->font_options); + } + } + + _cairo_font_options_init_copy (options, &surface->font_options); +} +slim_hidden_def (cairo_surface_get_font_options); + +/** + * 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. + **/ +void +cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface, + cairo_subpixel_antialiasing_t enabled) +{ + if (surface->status) + return; + + if (surface->finished) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return; + } + + surface->permit_subpixel_antialiasing = + enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; +} + +/** + * 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. + **/ +cairo_subpixel_antialiasing_t +cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface) +{ + if (surface->status) + return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; + + return surface->permit_subpixel_antialiasing ? + CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED; +} + +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, 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) +{ + cairo_status_t status; + + if (surface->status) + return; + + if (surface->finished) + return; + + status = _cairo_surface_flush (surface, 0); + if (unlikely (status)) + _cairo_surface_set_error (surface, status); +} +slim_hidden_def (cairo_surface_flush); + +/** + * cairo_surface_mark_dirty: + * @surface: a #cairo_surface_t + * + * 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_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); + +/** + * cairo_surface_mark_dirty_rectangle: + * @surface: a #cairo_surface_t + * @x: X coordinate of dirty rectangle + * @y: Y coordinate of dirty rectangle + * @width: width of dirty rectangle + * @height: height of dirty rectangle + * + * Like cairo_surface_mark_dirty(), but drawing has been done only to + * the specified rectangle, so that cairo can retain cached contents + * for other parts of the surface. + * + * Any cached clip set on the surface will be reset by this function, + * to make sure that future cairo calls have the clip set that they + * expect. + * + * Since: 1.0 + **/ +void +cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, + int x, + int y, + int width, + int height) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return; + + assert (surface->snapshot_of == NULL); + + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + /* The application *should* have called cairo_surface_flush() before + * modifying the surface independently of cairo (and thus having to + * call mark_dirty()). */ + assert (! _cairo_surface_has_snapshots (surface)); + 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 + * device_transform here. I don't know what the right thing to + * do would actually be if there were some scaling here, but + * we avoid this since device_transfom scaling is not exported + * publicly and mark_dirty is not used internally. */ + status = surface->backend->mark_dirty_rectangle (surface, + x + surface->device_transform.x0, + y + surface->device_transform.y0, + width, height); + + if (unlikely (status)) + _cairo_surface_set_error (surface, status); + } +} +slim_hidden_def (cairo_surface_mark_dirty_rectangle); + +/** + * cairo_surface_set_device_scale: + * @surface: a #cairo_surface_t + * @x_scale: a scale factor in the X direction + * @y_scale: a scale factor in the Y direction + * + * 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. + * + * 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 x_scale, + double y_scale) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return; + + assert (surface->snapshot_of == NULL); + + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) { + _cairo_surface_set_error (surface, status); + return; + } + + surface->device_transform.xx = x_scale; + surface->device_transform.yy = y_scale; + surface->device_transform.xy = 0.0; + surface->device_transform.yx = 0.0; + + surface->device_transform_inverse = surface->device_transform; + status = cairo_matrix_invert (&surface->device_transform_inverse); + /* should always be invertible unless given pathological input */ + assert (status == CAIRO_STATUS_SUCCESS); + + _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: + * @surface: a #cairo_surface_t + * @x_offset: the offset in the X direction, in device units + * @y_offset: the offset in the Y direction, in device units + * + * Sets an offset that is added to the device coordinates determined + * by the CTM when drawing to @surface. One use case for this function + * is when we want to create a #cairo_surface_t that redirects drawing + * for a portion of an onscreen surface to an offscreen surface in a + * way that is completely invisible to the user of the cairo + * API. Setting a transformation via cairo_translate() isn't + * sufficient to do this, since functions like + * cairo_device_to_user() will expose the hidden offset. + * + * 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, + double x_offset, + double y_offset) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return; + + assert (surface->snapshot_of == NULL); + + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + 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; + + surface->device_transform_inverse = surface->device_transform; + status = cairo_matrix_invert (&surface->device_transform_inverse); + /* should always be invertible unless given pathological input */ + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_observers_notify (&surface->device_transform_observers, surface); +} +slim_hidden_def (cairo_surface_set_device_offset); + +/** + * cairo_surface_get_device_offset: + * @surface: a #cairo_surface_t + * @x_offset: the offset in the X direction, in device units + * @y_offset: the offset in the Y direction, in device units + * + * This function returns the previous device offset set by + * cairo_surface_set_device_offset(). + * + * Since: 1.2 + **/ +void +cairo_surface_get_device_offset (cairo_surface_t *surface, + double *x_offset, + double *y_offset) +{ + if (x_offset) + *x_offset = surface->device_transform.x0; + if (y_offset) + *y_offset = surface->device_transform.y0; +} +slim_hidden_def (cairo_surface_get_device_offset); + +/** + * cairo_surface_set_fallback_resolution: + * @surface: a #cairo_surface_t + * @x_pixels_per_inch: horizontal setting for pixels per inch + * @y_pixels_per_inch: vertical setting for pixels per inch + * + * Set the horizontal and vertical resolution for image fallbacks. + * + * When certain operations aren't supported natively by a backend, + * cairo will fallback by rendering operations to an image and then + * overlaying that image onto the output. For backends that are + * natively vector-oriented, this function can be used to set the + * resolution used for these image fallbacks, (larger values will + * result in more detailed images, but also larger file sizes). + * + * Some examples of natively vector-oriented backends are the ps, pdf, + * and svg backends. + * + * For backends that are natively raster-oriented, image fallbacks are + * still possible, but they are always performed at the native + * device resolution. So this function has no effect on those + * backends. + * + * Note: The fallback resolution only takes effect at the time of + * completing a page (with cairo_show_page() or cairo_copy_page()) so + * there is currently no way to have more than one fallback resolution + * in effect on a single page. + * + * The default fallback resolution is 300 pixels per inch in both + * dimensions. + * + * Since: 1.2 + **/ +void +cairo_surface_set_fallback_resolution (cairo_surface_t *surface, + double x_pixels_per_inch, + double y_pixels_per_inch) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return; + + assert (surface->snapshot_of == NULL); + + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; + } + + if (x_pixels_per_inch <= 0 || y_pixels_per_inch <= 0) { + /* XXX Could delay raising the error until we fallback, but throwing + * the error here means that we can catch the real culprit. + */ + _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX); + return; + } + + 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; +} +slim_hidden_def (cairo_surface_set_fallback_resolution); + +/** + * cairo_surface_get_fallback_resolution: + * @surface: a #cairo_surface_t + * @x_pixels_per_inch: horizontal pixels per inch + * @y_pixels_per_inch: vertical pixels per inch + * + * This function returns the previous fallback resolution set by + * cairo_surface_set_fallback_resolution(), or default fallback + * resolution if never set. + * + * Since: 1.8 + **/ +void +cairo_surface_get_fallback_resolution (cairo_surface_t *surface, + double *x_pixels_per_inch, + double *y_pixels_per_inch) +{ + if (x_pixels_per_inch) + *x_pixels_per_inch = surface->x_fallback_resolution; + if (y_pixels_per_inch) + *y_pixels_per_inch = surface->y_fallback_resolution; +} + +cairo_bool_t +_cairo_surface_has_device_transform (cairo_surface_t *surface) +{ + return ! _cairo_matrix_is_identity (&surface->device_transform); +} + +/** + * _cairo_surface_acquire_source_image: + * @surface: a #cairo_surface_t + * @image_out: location to store a pointer to an image surface that + * has identical contents to @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. + * @image_extra: location to store image specific backend data + * + * Gets an image surface to use when drawing as a fallback when drawing with + * @surface as a source. _cairo_surface_release_source_image() must be called + * when finished. + * + * Return value: %CAIRO_STATUS_SUCCESS if an image was stored in @image_out. + * %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified + * surface. Or %CAIRO_STATUS_NO_MEMORY. + **/ +cairo_status_t +_cairo_surface_acquire_source_image (cairo_surface_t *surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return surface->status; + + assert (!surface->finished); + + if (surface->backend->acquire_source_image == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = surface->backend->acquire_source_image (surface, + image_out, image_extra); + if (unlikely (status)) + return _cairo_surface_set_error (surface, status); + + _cairo_debug_check_image_surface_is_defined (&(*image_out)->base); + + return CAIRO_STATUS_SUCCESS; +} + +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 + * @image_extra: same as return from the matching _cairo_surface_acquire_source_image() + * + * Releases any resources obtained with _cairo_surface_acquire_source_image() + **/ +void +_cairo_surface_release_source_image (cairo_surface_t *surface, + cairo_image_surface_t *image, + void *image_extra) +{ + assert (!surface->finished); + + if (surface->backend->release_source_image) + surface->backend->release_source_image (surface, image, image_extra); +} + +void +_cairo_surface_default_release_source_image (void *surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_status_t ignored; + + ignored = _cairo_surface_unmap_image (surface, image); + (void)ignored; +} + + +cairo_surface_t * +_cairo_surface_get_source (cairo_surface_t *surface, + cairo_rectangle_int_t *extents) +{ + assert (surface->backend->source); + return surface->backend->source (surface, extents); +} + +cairo_surface_t * +_cairo_surface_default_source (void *surface, + cairo_rectangle_int_t *extents) +{ + if (extents) + _cairo_surface_get_extents(surface, extents); + return surface; +} + +static cairo_status_t +_pattern_has_error (const cairo_pattern_t *pattern) +{ + const cairo_surface_pattern_t *spattern; + + if (unlikely (pattern->status)) + return pattern->status; + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_STATUS_SUCCESS; + + spattern = (const cairo_surface_pattern_t *) pattern; + if (unlikely (spattern->surface->status)) + return spattern->surface->status; + + if (unlikely (spattern->surface->finished)) + return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +nothing_to_do (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source) +{ + if (_cairo_pattern_is_clear (source)) { + if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD) + return TRUE; + + if (op == CAIRO_OPERATOR_SOURCE) + op = CAIRO_OPERATOR_CLEAR; + } + + if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear) + return TRUE; + + 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; + + 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->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++; + } + + return _cairo_surface_set_error (surface, status); +} + +cairo_status_t +_cairo_surface_mask (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + 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 (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + /* If the mask is blank, this is just an expensive no-op */ + if (_cairo_pattern_is_clear (mask) && + _cairo_operator_bounded_by_mask (op)) + { + return CAIRO_STATUS_SUCCESS; + } + + status = _pattern_has_error (source); + if (unlikely (status)) + return status; + + status = _pattern_has_error (mask); + 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->mask (surface, op, source, mask, clip); + 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_fill_stroke (cairo_surface_t *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, + 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 (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + if (surface->is_clear && + fill_op == CAIRO_OPERATOR_CLEAR && + stroke_op == CAIRO_OPERATOR_CLEAR) + { + return CAIRO_STATUS_SUCCESS; + } + + status = _pattern_has_error (fill_source); + if (unlikely (status)) + return status; + + status = _pattern_has_error (stroke_source); + if (unlikely (status)) + return status; + + status = _cairo_surface_begin_modification (surface); + if (unlikely (status)) + return status; + + if (surface->backend->fill_stroke) { + cairo_matrix_t dev_ctm = *stroke_ctm; + cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse; + + status = surface->backend->fill_stroke (surface, + fill_op, fill_source, fill_rule, + fill_tolerance, fill_antialias, + path, + stroke_op, stroke_source, + stroke_style, + &dev_ctm, &dev_ctm_inverse, + stroke_tolerance, stroke_antialias, + clip); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + goto FINISH; + } + + status = _cairo_surface_fill (surface, fill_op, fill_source, path, + fill_rule, fill_tolerance, fill_antialias, + clip); + if (unlikely (status)) + goto FINISH; + + status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path, + stroke_style, stroke_ctm, stroke_ctm_inverse, + stroke_tolerance, stroke_antialias, + clip); + if (unlikely (status)) + goto FINISH; + + FINISH: + 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, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_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 (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + 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->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 (surface, status); +} + +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) +{ + 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 (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + 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++; + } + + return _cairo_surface_set_error (surface, status); +} + +/** + * cairo_surface_copy_page: + * @surface: a #cairo_surface_t + * + * Emits the current page for backends that support multiple pages, + * but doesn't clear it, so that the contents of the current page will + * be retained for the next page. Use cairo_surface_show_page() if you + * want to get an empty page after the emission. + * + * There is a convenience function for this that takes a #cairo_t, + * namely cairo_copy_page(). + * + * Since: 1.6 + **/ +void +cairo_surface_copy_page (cairo_surface_t *surface) +{ + if (unlikely (surface->status)) + return; + + assert (surface->snapshot_of == NULL); + + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return; + } + + /* It's fine if some backends don't implement copy_page */ + if (surface->backend->copy_page == NULL) + return; + + _cairo_surface_set_error (surface, surface->backend->copy_page (surface)); +} +slim_hidden_def (cairo_surface_copy_page); + +/** + * cairo_surface_show_page: + * @surface: a #cairo_Surface_t + * + * Emits and clears the current page for backends that support multiple + * pages. Use cairo_surface_copy_page() if you don't want to clear the page. + * + * There is a convenience function for this that takes a #cairo_t, + * namely cairo_show_page(). + * + * Since: 1.6 + **/ +void +cairo_surface_show_page (cairo_surface_t *surface) +{ + cairo_status_t status; + + if (unlikely (surface->status)) + return; + + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return; + } + + 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; + + _cairo_surface_set_error (surface, surface->backend->show_page (surface)); +} +slim_hidden_def (cairo_surface_show_page); + +/** + * _cairo_surface_get_extents: + * @surface: the #cairo_surface_t to fetch extents for + * + * This function returns a bounding box for the surface. The surface + * bounds are defined as a region beyond which no rendering will + * possibly be recorded, in other words, it is the maximum extent of + * potentially usable coordinates. + * + * For vector surfaces, (PDF, PS, SVG and recording-surfaces), the surface + * might be conceived as unbounded, but we force the user to provide a + * maximum size at the time of surface_create. So get_extents uses + * that size. + * + * Note: The coordinates returned are in "backend" space rather than + * "surface" space. That is, they are relative to the true (0,0) + * origin rather than the device_transform origin. This might seem a + * bit inconsistent with other #cairo_surface_t interfaces, but all + * current callers are within the surface layer where backend space is + * desired. + * + * 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->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; +} + +/** + * cairo_surface_has_show_text_glyphs: + * @surface: a #cairo_surface_t + * + * Returns whether the surface supports + * sophisticated cairo_show_text_glyphs() operations. That is, + * whether it actually uses the provided text and cluster data + * to a cairo_show_text_glyphs() call. + * + * Note: Even if this function returns %FALSE, a + * cairo_show_text_glyphs() operation targeted at @surface will + * still succeed. It just will + * act like a cairo_show_glyphs() operation. Users can use this + * function to avoid computing UTF-8 text and cluster mapping if the + * target surface does not use it. + * + * Return value: %TRUE if @surface supports + * cairo_show_text_glyphs(), %FALSE otherwise + * + * Since: 1.8 + **/ +cairo_bool_t +cairo_surface_has_show_text_glyphs (cairo_surface_t *surface) +{ + if (unlikely (surface->status)) + return FALSE; + + if (unlikely (surface->finished)) { + _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED); + return FALSE; + } + + if (surface->backend->has_show_text_glyphs) + return surface->backend->has_show_text_glyphs (surface); + else + return surface->backend->show_text_glyphs != NULL; +} +slim_hidden_def (cairo_surface_has_show_text_glyphs); + +#define GLYPH_CACHE_SIZE 64 + +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; + } + + return status; +} + +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) +{ + cairo_int_status_t status; + cairo_image_surface_t *glyph_surface; + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + 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; +} + +/* 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 + * avoid copying the array again and again, and edit it in-place. + * Backends are in fact free to use the array as a generic buffer as they + * see fit. + * + * For show_glyphs backend method, and NOT for show_text_glyphs method, + * when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify + * that they have successfully rendered some of the glyphs (from the beginning + * of the array), but not all. If they don't touch remaining_glyphs, it + * defaults to all glyphs. + * + * See commits 5a9642c5746fd677aed35ce620ce90b1029b1a0c and + * 1781e6018c17909311295a9cc74b70500c6b4d0a for the rationale. + */ +cairo_status_t +_cairo_surface_show_text_glyphs (cairo_surface_t *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_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 (_cairo_clip_is_all_clipped (clip)) + return CAIRO_STATUS_SUCCESS; + + 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 = 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) { + /* A real show_text_glyphs call. Try show_text_glyphs backend + * method first */ + if (surface->backend->show_text_glyphs != NULL) { + status = surface->backend->show_text_glyphs (surface, op, + source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags, + scaled_font, + clip); + } + if (status == CAIRO_INT_STATUS_UNSUPPORTED && + surface->backend->show_glyphs) + { + status = surface->backend->show_glyphs (surface, op, + source, + glyphs, num_glyphs, + scaled_font, + clip); + } + } else { + /* A mere show_glyphs call. Try show_glyphs backend method first */ + if (surface->backend->show_glyphs != NULL) { + status = surface->backend->show_glyphs (surface, op, + source, + glyphs, num_glyphs, + 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 + * both methods implemented, we don't fallback from show_glyphs to + * show_text_glyphs, and hence the backend can assume in its + * show_text_glyphs call that clusters is not NULL (which also + * implies that UTF-8 is not NULL, unless the text is + * zero-length). + */ + status = surface->backend->show_text_glyphs (surface, op, + source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags, + scaled_font, + clip); + } + } + +DONE: + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + surface->is_clear = FALSE; + surface->serial++; + } + + 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) +{ + 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; + + status = surface->backend->tag (surface, begin, tag_name, attributes); + surface->is_clear = FALSE; + + return _cairo_surface_set_error (surface, status); +} + + +/** + * _cairo_surface_set_resolution: + * @surface: the surface + * @x_res: x resolution, in dpi + * @y_res: y resolution, in dpi + * + * 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, + double y_res) +{ + if (surface->status) + return; + + surface->x_resolution = x_res; + surface->y_resolution = y_res; +} + +/** + * _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; + case CAIRO_STATUS_SURFACE_TYPE_MISMATCH: + return (cairo_surface_t *) &_cairo_surface_nil_surface_type_mismatch; + case CAIRO_STATUS_INVALID_STATUS: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_status; + case CAIRO_STATUS_INVALID_CONTENT: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_content; + case CAIRO_STATUS_INVALID_FORMAT: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_format; + case CAIRO_STATUS_INVALID_VISUAL: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_visual; + case CAIRO_STATUS_READ_ERROR: + return (cairo_surface_t *) &_cairo_surface_nil_read_error; + case CAIRO_STATUS_WRITE_ERROR: + return (cairo_surface_t *) &_cairo_surface_nil_write_error; + case CAIRO_STATUS_FILE_NOT_FOUND: + return (cairo_surface_t *) &_cairo_surface_nil_file_not_found; + case CAIRO_STATUS_TEMP_FILE_ERROR: + return (cairo_surface_t *) &_cairo_surface_nil_temp_file_error; + case CAIRO_STATUS_INVALID_STRIDE: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride; + case CAIRO_STATUS_INVALID_SIZE: + return (cairo_surface_t *) &_cairo_surface_nil_invalid_size; + case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: + return (cairo_surface_t *) &_cairo_surface_nil_device_type_mismatch; + case CAIRO_STATUS_DEVICE_ERROR: + return (cairo_surface_t *) &_cairo_surface_nil_device_error; + case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: + ASSERT_NOT_REACHED; + /* fall-through */ + case CAIRO_STATUS_INVALID_RESTORE: + case CAIRO_STATUS_INVALID_POP_GROUP: + case CAIRO_STATUS_NO_CURRENT_POINT: + case CAIRO_STATUS_INVALID_MATRIX: + case CAIRO_STATUS_NULL_POINTER: + case CAIRO_STATUS_INVALID_STRING: + case CAIRO_STATUS_INVALID_PATH_DATA: + case CAIRO_STATUS_SURFACE_FINISHED: + case CAIRO_STATUS_PATTERN_TYPE_MISMATCH: + case CAIRO_STATUS_INVALID_DASH: + case CAIRO_STATUS_INVALID_DSC_COMMENT: + case CAIRO_STATUS_INVALID_INDEX: + case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: + case CAIRO_STATUS_FONT_TYPE_MISMATCH: + case CAIRO_STATUS_USER_FONT_IMMUTABLE: + case CAIRO_STATUS_USER_FONT_ERROR: + case CAIRO_STATUS_NEGATIVE_COUNT: + case CAIRO_STATUS_INVALID_CLUSTERS: + 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; + } +} + +/* LocalWords: rasterized + */ diff --git a/gfx/cairo/cairo/src/cairo-svg-surface-private.h b/gfx/cairo/cairo/src/cairo-svg-surface-private.h new file mode 100644 index 0000000000..6f693252a8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-svg-surface-private.h @@ -0,0 +1,83 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Red Hat, Inc + * Copyright © 2005-2006 Emmanuel Pacaud + * 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 University of Southern + * California. + * + * Contributor(s): + * Kristian Høgsberg + * Emmanuel Pacaud + * Carl Worth + */ + +#ifndef CAIRO_SVG_SURFACE_PRIVATE_H +#define CAIRO_SVG_SURFACE_PRIVATE_H + +#include "cairo-svg.h" + +#include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" + +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; + + cairo_content_t content; + + 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; + unsigned int base_clip; + cairo_bool_t is_base_clip_emitted; + + cairo_paginated_mode_t paginated_mode; + + cairo_bool_t force_fallbacks; +} cairo_svg_surface_t; + +#endif /* CAIRO_SVG_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-svg-surface.c b/gfx/cairo/cairo/src/cairo-svg-surface.c new file mode 100644 index 0000000000..7e7051eb68 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-svg-surface.c @@ -0,0 +1,3093 @@ +/* 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 + * Copyright © 2005-2007 Emmanuel Pacaud + * 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 University of Southern + * California. + * + * Contributor(s): + * Kristian Høgsberg + * Emmanuel Pacaud + * Carl Worth + */ + +#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-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" + +/** + * SECTION:cairo-svg + * @Title: SVG Surfaces + * @Short_Description: Rendering SVG documents + * @See_Also: #cairo_surface_t + * + * 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; + +static const int invalid_pattern_id = -1; + +static const cairo_svg_version_t _cairo_svg_versions[] = +{ + CAIRO_SVG_VERSION_1_1, + CAIRO_SVG_VERSION_1_2 +}; + +#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, + const cairo_path_fixed_t *path, + const cairo_matrix_t *ctm_inverse); + +static cairo_bool_t +_cairo_svg_version_has_page_set_support (cairo_svg_version_t version) +{ + return version > CAIRO_SVG_VERSION_1_1; +} + +static const char * _cairo_svg_version_strings[CAIRO_SVG_VERSION_LAST] = +{ + "SVG 1.1", + "SVG 1.2" +}; + +static const char * _cairo_svg_internal_version_strings[CAIRO_SVG_VERSION_LAST] = +{ + "1.1", + "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; + cairo_output_stream_t *xml_node; +}; + +struct cairo_svg_document { + cairo_output_stream_t *output_stream; + unsigned long refcount; + cairo_surface_t *owner; + cairo_bool_t finished; + + double width; + double height; + cairo_svg_unit_t unit; + + cairo_output_stream_t *xml_node_defs; + cairo_output_stream_t *xml_node_glyphs; + + unsigned int linear_pattern_id; + unsigned int radial_pattern_id; + unsigned int pattern_id; + unsigned int filter_id; + unsigned int clip_id; + unsigned int mask_id; + + cairo_bool_t alpha_filter; + + cairo_svg_version_t svg_version; + + cairo_scaled_font_subsets_t *font_subsets; +}; + +static cairo_status_t +_cairo_svg_document_create (cairo_output_stream_t *stream, + double width, + double height, + cairo_svg_version_t version, + cairo_svg_document_t **document_out); + +static cairo_status_t +_cairo_svg_document_destroy (cairo_svg_document_t *document); + +static cairo_status_t +_cairo_svg_document_finish (cairo_svg_document_t *document); + +static cairo_svg_document_t * +_cairo_svg_document_reference (cairo_svg_document_t *document); + +static unsigned int +_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document); + +static cairo_surface_t * +_cairo_svg_surface_create_for_document (cairo_svg_document_t *document, + cairo_content_t content, + double width, + double height, + cairo_bool_t bounded); + +static cairo_surface_t * +_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width, + double height, + cairo_svg_version_t version); + +static const cairo_surface_backend_t cairo_svg_surface_backend; +static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend; + +/** + * cairo_svg_surface_create_for_stream: + * @write_func: a #cairo_write_func_t to accept the output data, may be %NULL + * to indicate a no-op @write_func. With a no-op @write_func, + * the surface may be queried or used as a source without + * generating any temporary files. + * @closure: the closure argument for @write_func + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a SVG surface of the specified size in points to be written + * incrementally to the stream represented by @write_func and @closure. + * + * Return value: a pointer to the newly created surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a "nil" surface if an error such as out of memory + * occurs. You can use cairo_surface_status() to check for this. + * + * Since: 1.2 + **/ +cairo_surface_t * +cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); +} + +/** + * cairo_svg_surface_create: + * @filename: a filename for the SVG output (must be writable), %NULL may be + * used to specify no output. This will generate a SVG surface that + * may be queried and used as a source, without generating a + * temporary file. + * @width_in_points: width of the surface, in points (1 point == 1/72.0 inch) + * @height_in_points: height of the surface, in points (1 point == 1/72.0 inch) + * + * Creates a SVG surface of the specified size in points to be written + * to @filename. + * + * The SVG surface backend recognizes the following MIME types for the + * data attached to a surface (see cairo_surface_set_mime_data()) when + * it is used as a source pattern for drawing on this surface: + * %CAIRO_MIME_TYPE_JPEG, %CAIRO_MIME_TYPE_PNG, + * %CAIRO_MIME_TYPE_URI. If any of them is specified, the SVG backend + * emits a href with the content of MIME data instead of a surface + * snapshot (PNG, Base64-encoded) in the corresponding image tag. + * + * The unofficial MIME type %CAIRO_MIME_TYPE_URI is examined + * first. If present, the URI is emitted as is: assuring the + * correctness of URI is left to the client code. + * + * If %CAIRO_MIME_TYPE_URI is not present, but %CAIRO_MIME_TYPE_JPEG + * 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. + * + * 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.2 + **/ +cairo_surface_t * +cairo_svg_surface_create (const char *filename, + double width, + double height) +{ + cairo_output_stream_t *stream; + + stream = _cairo_output_stream_create_for_filename (filename); + if (_cairo_output_stream_get_status (stream)) + return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream)); + + return _cairo_svg_surface_create_for_stream_internal (stream, width, height, CAIRO_SVG_VERSION_1_1); +} + +static cairo_bool_t +_cairo_surface_is_svg (cairo_surface_t *surface) +{ + return surface->backend == &cairo_svg_surface_backend; +} + +/* If the abstract_surface is a paginated surface, and that paginated + * surface's target is a svg_surface, then set svg_surface to that + * target. Otherwise return FALSE. + */ +static cairo_bool_t +_extract_svg_surface (cairo_surface_t *surface, + cairo_svg_surface_t **svg_surface) +{ + cairo_surface_t *target; + cairo_status_t status_ignored; + + if (surface->status) + return FALSE; + if (surface->finished) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_paginated (surface)) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + target = _cairo_paginated_surface_get_target (surface); + if (target->status) { + status_ignored = _cairo_surface_set_error (surface, + target->status); + return FALSE; + } + if (target->finished) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return FALSE; + } + + if (! _cairo_surface_is_svg (target)) { + status_ignored = _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return FALSE; + } + + *svg_surface = (cairo_svg_surface_t *) target; + return TRUE; +} + +/** + * cairo_svg_surface_restrict_to_version: + * @surface: a SVG #cairo_surface_t + * @version: SVG version + * + * Restricts the generated SVG file to @version. See cairo_svg_get_versions() + * for a list of available version values that can be used here. + * + * This function should only be called before any drawing operations + * have been performed on the given surface. The simplest way to do + * this is to call this function immediately after creating the + * surface. + * + * Since: 1.2 + **/ +void +cairo_svg_surface_restrict_to_version (cairo_surface_t *abstract_surface, + cairo_svg_version_t version) +{ + cairo_svg_surface_t *surface = NULL; /* hide compiler warning */ + + if (! _extract_svg_surface (abstract_surface, &surface)) + return; + + if (version < CAIRO_SVG_VERSION_LAST) + surface->document->svg_version = version; +} + +/** + * cairo_svg_get_versions: + * @versions: supported version list + * @num_versions: list length + * + * Used to retrieve the list of supported versions. See + * cairo_svg_surface_restrict_to_version(). + * + * Since: 1.2 + **/ +void +cairo_svg_get_versions (cairo_svg_version_t const **versions, + int *num_versions) +{ + if (versions != NULL) + *versions = _cairo_svg_versions; + + if (num_versions != NULL) + *num_versions = CAIRO_SVG_VERSION_LAST; +} + +/** + * cairo_svg_version_to_string: + * @version: a version id + * + * Get the string representation of the given @version id. This function + * will return %NULL if @version isn't valid. See cairo_svg_get_versions() + * for a way to get the list of valid version ids. + * + * Return value: the string associated to given version. + * + * Since: 1.2 + **/ +const char * +cairo_svg_version_to_string (cairo_svg_version_t version) +{ + if (version >= CAIRO_SVG_VERSION_LAST) + return NULL; + + 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) +{ + cairo_box_t box; + + if (_cairo_path_fixed_is_box (path, &box)) { + if (box.p1.x <= 0 && + box.p1.y <= 0 && + _cairo_fixed_to_double (box.p2.x) >= surface->width && + _cairo_fixed_to_double (box.p2.y) >= surface->height) + { + return TRUE; + } + } + + return FALSE; +} + +static cairo_status_t +_cairo_svg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_svg_surface_t *surface = cairo_container_of (clipper, + cairo_svg_surface_t, + clipper); + cairo_svg_document_t *document = surface->document; + unsigned int i; + + if (path == NULL) { + for (i = 0; i < surface->clip_level; i++) + _cairo_output_stream_printf (surface->xml_node, "\n"); + + surface->clip_level = 0; + return CAIRO_STATUS_SUCCESS; + } + + /* skip trivial whole-page clips */ + if (_cliprect_covers_surface (surface, path)) + return CAIRO_STATUS_SUCCESS; + + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " clip_id); + _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL); + + _cairo_output_stream_printf (document->xml_node_defs, + "/>\n" + "\n"); + + _cairo_output_stream_printf (surface->xml_node, + "\n", + document->clip_id, + fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? + "evenodd" : "nonzero"); + + document->clip_id++; + surface->clip_level++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_svg_surface_create_for_document (cairo_svg_document_t *document, + cairo_content_t content, + double width, + double height, + cairo_bool_t bounded) +{ + cairo_svg_surface_t *surface; + cairo_surface_t *paginated; + cairo_status_t status, status_ignored; + + 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, + TRUE); /* is_vector */ + + surface->width = width; + surface->height = height; + surface->surface_bounded = bounded; + + surface->document = _cairo_svg_document_reference (document); + + surface->clip_level = 0; + _cairo_surface_clipper_init (&surface->clipper, + _cairo_svg_surface_clipper_intersect_clip_path); + + surface->base_clip = document->clip_id++; + surface->is_base_clip_emitted = FALSE; + + surface->xml_node = _cairo_memory_stream_create (); + status = _cairo_output_stream_get_status (surface->xml_node); + if (unlikely (status)) + goto CLEANUP; + + _cairo_array_init (&surface->page_set, sizeof (cairo_svg_page_t)); + + if (content == CAIRO_CONTENT_COLOR) { + _cairo_output_stream_printf (surface->xml_node, + "\n", + width, height); + status = _cairo_output_stream_get_status (surface->xml_node); + if (unlikely (status)) + goto CLEANUP; + } + + surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE; + 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); + status = paginated->status; + if (status == CAIRO_STATUS_SUCCESS) { + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + return paginated; + } + + /* ignore status as we are on the error path */ +CLEANUP: + status_ignored = _cairo_output_stream_destroy (surface->xml_node); + status_ignored = _cairo_svg_document_destroy (document); + + free (surface); + + return _cairo_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_svg_surface_create_for_stream_internal (cairo_output_stream_t *stream, + double width, + double height, + cairo_svg_version_t version) +{ + cairo_svg_document_t *document = NULL; /* silence compiler */ + cairo_surface_t *surface; + cairo_status_t status; + + status = _cairo_svg_document_create (stream, + width, height, version, + &document); + if (unlikely (status)) { + surface = _cairo_surface_create_in_error (status); + /* consume the output stream on behalf of caller */ + status = _cairo_output_stream_destroy (stream); + return surface; + } + + surface = _cairo_svg_surface_create_for_document (document, CAIRO_CONTENT_COLOR_ALPHA, + width, height, TRUE); + if (surface->status) { + status = _cairo_svg_document_destroy (document); + return surface; + } + + document->owner = surface; + status = _cairo_svg_document_destroy (document); + /* the ref count should be 2 at this point */ + assert (status == CAIRO_STATUS_SUCCESS); + + return surface; +} + +static cairo_svg_page_t * +_cairo_svg_surface_store_page (cairo_svg_surface_t *surface) +{ + cairo_svg_page_t page; + cairo_output_stream_t *stream; + cairo_int_status_t status; + unsigned int i; + + stream = _cairo_memory_stream_create (); + if (_cairo_output_stream_get_status (stream)) { + status = _cairo_output_stream_destroy (stream); + return NULL; + } + + page.surface_id = surface->base.unique_id; + page.clip_level = surface->clip_level; + page.xml_node = surface->xml_node; + + if (_cairo_array_append (&surface->page_set, &page)) { + status = _cairo_output_stream_destroy (stream); + return NULL; + } + + surface->xml_node = stream; + surface->clip_level = 0; + for (i = 0; i < page.clip_level; i++) + _cairo_output_stream_printf (page.xml_node, "\n"); + + _cairo_surface_clipper_reset (&surface->clipper); + + return _cairo_array_index (&surface->page_set, + surface->page_set.num_elements - 1); +} + +static cairo_int_status_t +_cairo_svg_surface_copy_page (void *abstract_surface) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_page_t *page; + + page = _cairo_svg_surface_store_page (surface); + if (unlikely (page == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_memory_stream_copy (page->xml_node, surface->xml_node); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_show_page (void *abstract_surface) +{ + cairo_svg_surface_t *surface = abstract_surface; + + if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_svg_surface_emit_transform (cairo_output_stream_t *output, + char const *attribute_str, + const cairo_matrix_t *object_matrix, + const cairo_matrix_t *parent_matrix) +{ + cairo_matrix_t matrix = *object_matrix; + + if (parent_matrix != NULL) + cairo_matrix_multiply (&matrix, &matrix, parent_matrix); + + if (!_cairo_matrix_is_identity (&matrix)) + _cairo_output_stream_printf (output, + "%s=\"matrix(%f,%f,%f,%f,%f,%f)\"", + attribute_str, + matrix.xx, matrix.yx, + matrix.xy, matrix.yy, + matrix.x0, matrix.y0); +} + +typedef struct { + cairo_output_stream_t *output; + const cairo_matrix_t *ctm_inverse; +} svg_path_info_t; + +static cairo_status_t +_cairo_svg_path_move_to (void *closure, + const cairo_point_t *point) +{ + svg_path_info_t *info = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (info->ctm_inverse) + cairo_matrix_transform_point (info->ctm_inverse, &x, &y); + + _cairo_output_stream_printf (info->output, "M %f %f ", x, y); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_path_line_to (void *closure, + const cairo_point_t *point) +{ + svg_path_info_t *info = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (info->ctm_inverse) + cairo_matrix_transform_point (info->ctm_inverse, &x, &y); + + _cairo_output_stream_printf (info->output, "L %f %f ", x, y); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_path_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + svg_path_info_t *info = closure; + double bx = _cairo_fixed_to_double (b->x); + double by = _cairo_fixed_to_double (b->y); + double cx = _cairo_fixed_to_double (c->x); + double cy = _cairo_fixed_to_double (c->y); + double dx = _cairo_fixed_to_double (d->x); + double dy = _cairo_fixed_to_double (d->y); + + if (info->ctm_inverse) { + cairo_matrix_transform_point (info->ctm_inverse, &bx, &by); + cairo_matrix_transform_point (info->ctm_inverse, &cx, &cy); + cairo_matrix_transform_point (info->ctm_inverse, &dx, &dy); + } + + _cairo_output_stream_printf (info->output, + "C %f %f %f %f %f %f ", + bx, by, cx, cy, dx, dy); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_path_close_path (void *closure) +{ + svg_path_info_t *info = closure; + + _cairo_output_stream_printf (info->output, "Z "); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_svg_surface_emit_path (cairo_output_stream_t *output, + const cairo_path_fixed_t *path, + const cairo_matrix_t *ctm_inverse) +{ + cairo_status_t status; + svg_path_info_t info; + + _cairo_output_stream_printf (output, "d=\""); + + info.output = output; + info.ctm_inverse = ctm_inverse; + status = _cairo_path_fixed_interpret (path, + _cairo_svg_path_move_to, + _cairo_svg_path_line_to, + _cairo_svg_path_curve_to, + _cairo_svg_path_close_path, + &info); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_output_stream_printf (output, "\""); +} + +static cairo_int_status_t +_cairo_svg_document_emit_outline_glyph_data (cairo_svg_document_t *document, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_int_status_t status; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (document->xml_node_glyphs, + "xml_node_glyphs, + scaled_glyph->path, NULL); + + _cairo_output_stream_printf (document->xml_node_glyphs, + "/>\n"); + + return status; +} + +static cairo_int_status_t +_cairo_svg_document_emit_bitmap_glyph_data (cairo_svg_document_t *document, + cairo_scaled_font_t *scaled_font, + unsigned long glyph_index) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_image_surface_t *image; + cairo_status_t status; + uint8_t *row, *byte; + int rows, cols; + int x, y, bit; + + status = _cairo_scaled_glyph_lookup (scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + return status; + + image = _cairo_image_surface_coerce_to_format (scaled_glyph->surface, + CAIRO_FORMAT_A1); + status = image->base.status; + if (unlikely (status)) + return status; + + _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"); + + 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--) { + uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte); + for (bit = 7; bit >= 0 && x < image->width; bit--, x++) { + if (output_byte & (1 << bit)) { + _cairo_output_stream_printf (document->xml_node_glyphs, + "\n", + x, y); + } + } + } + } + _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); + + cairo_surface_destroy (&image->base); + + return CAIRO_STATUS_SUCCESS; +} + +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_int_status_t status; + + _cairo_output_stream_printf (document->xml_node_glyphs, + "\n", + font_id, + subset_glyph_index); + + status = _cairo_svg_document_emit_outline_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + status = _cairo_svg_document_emit_bitmap_glyph_data (document, + scaled_font, + scaled_font_glyph_index); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (document->xml_node_glyphs, "\n"); + + return CAIRO_INT_STATUS_SUCCESS; +} + +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_scaled_font_freeze_cache (font_subset->scaled_font); + for (i = 0; i < font_subset->num_glyphs; i++) { + status = _cairo_svg_document_emit_glyph (document, + font_subset->scaled_font, + font_subset->glyphs[i], + font_subset->font_id, i); + if (unlikely (status)) + break; + } + _cairo_scaled_font_thaw_cache (font_subset->scaled_font); + + return status; +} + +static cairo_status_t +_cairo_svg_document_emit_font_subsets (cairo_svg_document_t *document) +{ + cairo_status_t status; + + status = _cairo_scaled_font_subsets_foreach_scaled (document->font_subsets, + _cairo_svg_document_emit_font_subset, + document); + if (unlikely (status)) + goto FAIL; + + status = _cairo_scaled_font_subsets_foreach_user (document->font_subsets, + _cairo_svg_document_emit_font_subset, + document); + + FAIL: + _cairo_scaled_font_subsets_destroy (document->font_subsets); + document->font_subsets = NULL; + + return status; +} + +static char const * +_cairo_svg_surface_operators[] = { + "clear", + + "src", "src-over", "src-in", + "src-out", "src-atop", + + "dst", "dst-over", "dst-in", + "dst-out", "dst-atop", + + "xor", "plus", + "color-dodge", /* FIXME: saturate ? */ + + "multiply", "screen", "overlay", + "darken", "lighten", + "color-dodge", "color-burn", + "hard-light", "soft-light", + "difference", "exclusion" +}; + +static cairo_bool_t +_cairo_svg_surface_analyze_operator (cairo_svg_surface_t *surface, + cairo_operator_t op) +{ + /* guard against newly added operators */ + if (op >= ARRAY_LENGTH (_cairo_svg_surface_operators)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* allow operators being NULL if they are unsupported */ + if (_cairo_svg_surface_operators[op] == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_analyze_operation (cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + cairo_svg_document_t *document = surface->document; + + if (surface->force_fallbacks && + surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + { + 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) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (document->svg_version >= CAIRO_SVG_VERSION_1_2) + return _cairo_svg_surface_analyze_operator (surface, op); + + if (op == CAIRO_OPERATOR_OVER) + return CAIRO_STATUS_SUCCESS; + + /* The SOURCE operator is only supported if there is nothing + * painted underneath. */ + if (op == CAIRO_OPERATOR_SOURCE) + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_int_status_t +_cairo_svg_surface_operation_supported (cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + return _cairo_svg_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +_cairo_svg_surface_finish (void *abstract_surface) +{ + cairo_status_t status, status2; + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_document_t *document = surface->document; + cairo_svg_page_t *page; + unsigned int i; + + if (_cairo_paginated_surface_get_target (document->owner) == &surface->base) + status = _cairo_svg_document_finish (document); + else + status = CAIRO_STATUS_SUCCESS; + + if (surface->xml_node != NULL) { + status2 = _cairo_output_stream_destroy (surface->xml_node); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + + for (i = 0; i < surface->page_set.num_elements; i++) { + page = _cairo_array_index (&surface->page_set, i); + status2 = _cairo_output_stream_destroy (page->xml_node); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + } + _cairo_array_fini (&surface->page_set); + + _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; + + return status; +} + + +static void +_cairo_svg_surface_emit_alpha_filter (cairo_svg_document_t *document) +{ + if (document->alpha_filter) + return; + + _cairo_output_stream_printf (document->xml_node_defs, + "\n" + " \n" + "\n"); + + document->alpha_filter = TRUE; +} + +typedef struct { + cairo_output_stream_t *output; + unsigned int in_mem; + unsigned int trailing; + unsigned char src[3]; +} base64_write_closure_t; + +static char const base64_table[64] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static cairo_status_t +base64_write_func (void *closure, + const unsigned char *data, + unsigned int length) +{ + base64_write_closure_t *info = (base64_write_closure_t *) closure; + unsigned int i; + unsigned char *src; + + src = info->src; + + if (info->in_mem + length < 3) { + for (i = 0; i < length; i++) { + src[i + info->in_mem] = *data++; + } + info->in_mem += length; + return CAIRO_STATUS_SUCCESS; + } + + do { + unsigned char dst[4]; + + for (i = info->in_mem; i < 3; i++) { + src[i] = *data++; + length--; + } + info->in_mem = 0; + + dst[0] = base64_table[src[0] >> 2]; + dst[1] = base64_table[(src[0] & 0x03) << 4 | src[1] >> 4]; + dst[2] = base64_table[(src[1] & 0x0f) << 2 | src[2] >> 6]; + dst[3] = base64_table[src[2] & 0xfc >> 2]; + /* Special case for the last missing bits */ + switch (info->trailing) { + case 2: + dst[2] = '='; + /* fall through */ + case 1: + dst[3] = '='; + default: + break; + } + _cairo_output_stream_write (info->output, dst, 4); + } while (length >= 3); + + for (i = 0; i < length; i++) { + src[i] = *data++; + } + info->in_mem = length; + + return _cairo_output_stream_get_status (info->output); +} + +static cairo_int_status_t +_cairo_surface_base64_encode_jpeg (cairo_surface_t *surface, + cairo_output_stream_t *output) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_image_info_t image_info; + base64_write_closure_t info; + cairo_status_t status; + + cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (&image_info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (image_info.num_components == 4) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_printf (output, "data:image/jpeg;base64,"); + + info.output = output; + info.in_mem = 0; + info.trailing = 0; + + status = base64_write_func (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.in_mem > 0) { + memset (info.src + info.in_mem, 0, 3 - info.in_mem); + info.trailing = 3 - info.in_mem; + info.in_mem = 3; + status = base64_write_func (&info, NULL, 0); + } + + return status; +} + +static cairo_int_status_t +_cairo_surface_base64_encode_png (cairo_surface_t *surface, + cairo_output_stream_t *output) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + base64_write_closure_t info; + cairo_status_t status; + + cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (unlikely (surface->status)) + return surface->status; + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_printf (output, "data:image/png;base64,"); + + info.output = output; + info.in_mem = 0; + info.trailing = 0; + + status = base64_write_func (&info, mime_data, mime_data_length); + if (unlikely (status)) + return status; + + if (info.in_mem > 0) { + memset (info.src + info.in_mem, 0, 3 - info.in_mem); + info.trailing = 3 - info.in_mem; + info.in_mem = 3; + status = base64_write_func (&info, NULL, 0); + } + + return status; +} + +static cairo_int_status_t +_cairo_surface_base64_encode (cairo_surface_t *surface, + cairo_output_stream_t *output) +{ + cairo_int_status_t status; + base64_write_closure_t info; + + status = _cairo_surface_base64_encode_jpeg (surface, output); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + status = _cairo_surface_base64_encode_png (surface, output); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + info.output = output; + info.in_mem = 0; + info.trailing = 0; + + _cairo_output_stream_printf (info.output, "data:image/png;base64,"); + + status = cairo_surface_write_to_png_stream (surface, base64_write_func, + (void *) &info); + + if (unlikely (status)) + return status; + + if (info.in_mem > 0) { + memset (info.src + info.in_mem, 0, 3 - info.in_mem); + info.trailing = 3 - info.in_mem; + info.in_mem = 3; + status = base64_write_func (&info, NULL, 0); + } + + return status; +} + +static void +_cairo_svg_surface_emit_operator (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op) +{ + if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && + op != CAIRO_OPERATOR_OVER) { + _cairo_output_stream_printf (output, " comp-op=\"%s\"", _cairo_svg_surface_operators[op]); + if (!_cairo_operator_bounded_by_source (op)) + _cairo_output_stream_printf (output, " clip-to-self=\"true\""); + } +} + +static void +_cairo_svg_surface_emit_operator_for_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op) +{ + if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2 && + op != CAIRO_OPERATOR_OVER) { + _cairo_output_stream_printf (output, "comp-op:%s;", _cairo_svg_surface_operators[op]); + if (!_cairo_operator_bounded_by_source (op)) + _cairo_output_stream_printf (output, "clip-to-self:true;"); + } +} + +/** + * _cairo_svg_surface_emit_attr_value: + * + * Write the value to output the stream as a sequence of characters, + * while escaping those which have special meaning in the XML + * attribute's value context: & and ". + **/ +static void +_cairo_svg_surface_emit_attr_value (cairo_output_stream_t *stream, + const unsigned char *value, + unsigned int length) +{ + const unsigned char *p; + const unsigned char *q; + unsigned int i; + + /* we'll accumulate non-special chars in [q, p) range */ + p = value; + q = p; + for (i = 0; i < length; i++, p++) { + if (*p == '&' || *p == '"') { + /* flush what's left before special char */ + if (p != q) { + _cairo_output_stream_write (stream, q, p - q); + q = p + 1; + } + + if (*p == '&') + _cairo_output_stream_printf (stream, "&"); + else // p == '"' + _cairo_output_stream_printf (stream, """); + } + } + + /* flush the trailing chars if any */ + if (p != q) + _cairo_output_stream_write (stream, q, p - q); +} + +static cairo_status_t +_cairo_svg_surface_emit_surface (cairo_svg_document_t *document, + cairo_surface_t *surface, + int source_id) +{ + cairo_rectangle_int_t extents; + cairo_bool_t is_bounded; + cairo_status_t status; + const unsigned char *uri; + unsigned long uri_len; + + is_bounded = _cairo_surface_get_extents (surface, &extents); + assert (is_bounded); + + _cairo_output_stream_printf (document->xml_node_defs, + "xml_node_defs, " xlink:href=\""); + + cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_URI, + &uri, &uri_len); + if (uri != NULL) { + _cairo_svg_surface_emit_attr_value (document->xml_node_defs, + uri, uri_len); + } else { + status = _cairo_surface_base64_encode (surface, + document->xml_node_defs); + if (unlikely (status)) + return status; + } + + _cairo_output_stream_printf (document->xml_node_defs, "\"/>\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_composite_surface_pattern (cairo_output_stream_t *output, + cairo_svg_surface_t *svg_surface, + cairo_operator_t op, + cairo_surface_pattern_t *pattern, + int pattern_id, + const cairo_matrix_t *parent_matrix, + const char *extra_attributes) +{ + 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_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; + + is_bounded = _cairo_surface_get_extents (pattern->surface, &extents); + assert (is_bounded); + + _cairo_output_stream_printf (output, + "\n "); + } + + _cairo_output_stream_printf (output, + "\n"); + + + if (pattern_id != invalid_pattern_id) + _cairo_output_stream_printf (output, "\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_recording_surface (cairo_svg_document_t *document, + 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; + + bounded = _cairo_surface_get_extents (&source->base, &extents); + paginated_surface = _cairo_svg_surface_create_for_document (document, + source->base.content, + extents.width, + extents.height, + bounded); + if (unlikely (paginated_surface->status)) + return paginated_surface->status; + + svg_surface = (cairo_svg_surface_t *) + _cairo_paginated_surface_get_target (paginated_surface); + cairo_surface_set_fallback_resolution (paginated_surface, + document->owner->x_fallback_resolution, + document->owner->y_fallback_resolution); + cairo_surface_set_device_offset (&svg_surface->base, + -source->extents_pixels.x, + -source->extents_pixels.y); + + status = _cairo_recording_surface_replay (&source->base, paginated_surface); + if (unlikely (status)) { + cairo_surface_destroy (paginated_surface); + return status; + } + + cairo_surface_show_page (paginated_surface); + status = cairo_surface_status (paginated_surface); + if (unlikely (status)) { + cairo_surface_destroy (paginated_surface); + return status; + } + + if (! svg_surface->is_base_clip_emitted) { + svg_surface->is_base_clip_emitted = TRUE; + 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->base.content == CAIRO_CONTENT_ALPHA) { + _cairo_svg_surface_emit_alpha_filter (document); + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + source_id, + svg_surface->base_clip); + } else { + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + source_id, + svg_surface->base_clip); + } + + contents = svg_surface->xml_node; + page_set = &svg_surface->page_set; + + if (_cairo_memory_stream_length (contents) > 0) { + if (unlikely (_cairo_svg_surface_store_page (svg_surface) == NULL)) { + cairo_surface_destroy (paginated_surface); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + if (page_set->num_elements > 0) { + cairo_svg_page_t *page; + + page = _cairo_array_index (page_set, page_set->num_elements - 1); + _cairo_memory_stream_copy (page->xml_node, document->xml_node_defs); + } + + _cairo_output_stream_printf (document->xml_node_defs, "\n"); + + status = cairo_surface_status (paginated_surface); + cairo_surface_destroy (paginated_surface); + + return status; +} + +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 +_cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + cairo_surface_pattern_t *pattern, + int pattern_id, + const cairo_matrix_t *parent_matrix, + const char *extra_attributes) +{ + cairo_svg_document_t *document = surface->document; + 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); + + 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, + "extents.width, + recording_surface->extents.height); + _cairo_svg_surface_emit_transform (output, " patternTransform", &p2u, parent_matrix); + _cairo_output_stream_printf (output, ">\n"); + } + + _cairo_output_stream_printf (output, + "\n"); + + if (pattern_id != invalid_pattern_id) + _cairo_output_stream_printf (output, "\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_composite_pattern (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + cairo_surface_pattern_t *pattern, + int pattern_id, + const cairo_matrix_t *parent_matrix, + const char *extra_attributes) +{ + + if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + return _cairo_svg_surface_emit_composite_recording_pattern (output, surface, + op, pattern, + pattern_id, + parent_matrix, + extra_attributes); + } + + return _cairo_svg_surface_emit_composite_surface_pattern (output, surface, + op, pattern, + pattern_id, + parent_matrix, + extra_attributes); +} + +static cairo_status_t +_cairo_svg_surface_emit_solid_pattern (cairo_svg_surface_t *surface, + cairo_solid_pattern_t *pattern, + cairo_output_stream_t *style, + cairo_bool_t is_stroke) +{ + _cairo_output_stream_printf (style, is_stroke ? + "stroke:rgb(%f%%,%f%%,%f%%);stroke-opacity:%f;": + "fill:rgb(%f%%,%f%%,%f%%);fill-opacity:%f;", + pattern->color.red * 100.0, + pattern->color.green * 100.0, + pattern->color.blue * 100.0, + pattern->color.alpha); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_surface_pattern (cairo_svg_surface_t *surface, + cairo_surface_pattern_t *pattern, + cairo_output_stream_t *style, + cairo_bool_t is_stroke, + const cairo_matrix_t *parent_matrix) +{ + cairo_svg_document_t *document = surface->document; + cairo_status_t status; + int pattern_id; + + pattern_id = document->pattern_id++; + status = _cairo_svg_surface_emit_composite_pattern (document->xml_node_defs, + surface, CAIRO_OPERATOR_SOURCE, pattern, + pattern_id, parent_matrix, NULL); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (style, + "%s:url(#pattern%d);", + is_stroke ? "stroke" : "fill", + pattern_id); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_pattern_stops (cairo_output_stream_t *output, + cairo_gradient_pattern_t const *pattern, + double start_offset, + cairo_bool_t reverse_stops, + cairo_bool_t emulate_reflect) +{ + cairo_gradient_stop_t *stops; + double offset; + unsigned int n_stops; + unsigned int i; + + if (pattern->n_stops < 1) + return CAIRO_STATUS_SUCCESS; + + if (pattern->n_stops == 1) { + _cairo_output_stream_printf (output, + "\n", + pattern->stops[0].offset, + pattern->stops[0].color.red * 100.0, + pattern->stops[0].color.green * 100.0, + pattern->stops[0].color.blue * 100.0, + pattern->stops[0].color.alpha); + return CAIRO_STATUS_SUCCESS; + } + + if (emulate_reflect || reverse_stops) { + n_stops = emulate_reflect ? pattern->n_stops * 2 - 2: pattern->n_stops; + stops = _cairo_malloc_ab (n_stops, sizeof (cairo_gradient_stop_t)); + if (unlikely (stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + for (i = 0; i < pattern->n_stops; i++) { + if (reverse_stops) { + stops[i] = pattern->stops[pattern->n_stops - i - 1]; + stops[i].offset = 1.0 - stops[i].offset; + } else + stops[i] = pattern->stops[i]; + if (emulate_reflect) { + stops[i].offset /= 2; + if (i > 0 && i < (pattern->n_stops - 1)) { + if (reverse_stops) { + stops[i + pattern->n_stops - 1] = pattern->stops[i]; + stops[i + pattern->n_stops - 1].offset = + 0.5 + 0.5 * stops[i + pattern->n_stops - 1].offset; + } else { + stops[i + pattern->n_stops - 1] = pattern->stops[pattern->n_stops - i - 1]; + stops[i + pattern->n_stops - 1].offset = + 1 - 0.5 * stops[i + pattern->n_stops - 1].offset; + } + } + } + } + } else { + n_stops = pattern->n_stops; + stops = pattern->stops; + } + + if (start_offset >= 0.0) + for (i = 0; i < n_stops; i++) { + offset = start_offset + (1 - start_offset ) * stops[i].offset; + _cairo_output_stream_printf (output, + "\n", + offset, + stops[i].color.red * 100.0, + stops[i].color.green * 100.0, + stops[i].color.blue * 100.0, + stops[i].color.alpha); + } + else { + cairo_bool_t found = FALSE; + unsigned int offset_index; + cairo_color_stop_t offset_color_start, offset_color_stop; + + for (i = 0; i < n_stops; i++) { + if (stops[i].offset >= -start_offset) { + if (i > 0) { + if (stops[i].offset != stops[i-1].offset) { + double x0, x1; + cairo_color_stop_t *color0, *color1; + + x0 = stops[i-1].offset; + x1 = stops[i].offset; + color0 = &stops[i-1].color; + color1 = &stops[i].color; + offset_color_start.red = color0->red + (color1->red - color0->red) + * (-start_offset - x0) / (x1 - x0); + offset_color_start.green = color0->green + (color1->green - color0->green) + * (-start_offset - x0) / (x1 - x0); + offset_color_start.blue = color0->blue + (color1->blue - color0->blue) + * (-start_offset - x0) / (x1 - x0); + offset_color_start.alpha = color0->alpha + (color1->alpha - color0->alpha) + * (-start_offset - x0) / (x1 - x0); + offset_color_stop = offset_color_start; + } else { + offset_color_stop = stops[i-1].color; + offset_color_start = stops[i].color; + } + } else + offset_color_stop = offset_color_start = stops[i].color; + offset_index = i; + found = TRUE; + break; + } + } + + if (!found) { + offset_index = n_stops - 1; + offset_color_stop = offset_color_start = stops[offset_index].color; + } + + _cairo_output_stream_printf (output, + "\n", + offset_color_start.red * 100.0, + offset_color_start.green * 100.0, + offset_color_start.blue * 100.0, + offset_color_start.alpha); + for (i = offset_index; i < n_stops; i++) { + _cairo_output_stream_printf (output, + "\n", + stops[i].offset + start_offset, + stops[i].color.red * 100.0, + stops[i].color.green * 100.0, + stops[i].color.blue * 100.0, + stops[i].color.alpha); + } + for (i = 0; i < offset_index; i++) { + _cairo_output_stream_printf (output, + "\n", + 1.0 + stops[i].offset + start_offset, + stops[i].color.red * 100.0, + stops[i].color.green * 100.0, + stops[i].color.blue * 100.0, + stops[i].color.alpha); + } + + _cairo_output_stream_printf (output, + "\n", + offset_color_stop.red * 100.0, + offset_color_stop.green * 100.0, + offset_color_stop.blue * 100.0, + offset_color_stop.alpha); + + } + + if (reverse_stops || emulate_reflect) + free (stops); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_svg_surface_emit_pattern_extend (cairo_output_stream_t *output, + cairo_pattern_t *pattern) +{ + switch (pattern->extend) { + case CAIRO_EXTEND_REPEAT: + _cairo_output_stream_printf (output, "spreadMethod=\"repeat\" "); + break; + case CAIRO_EXTEND_REFLECT: + _cairo_output_stream_printf (output, "spreadMethod=\"reflect\" "); + break; + case CAIRO_EXTEND_NONE: + case CAIRO_EXTEND_PAD: + break; + } +} + +static cairo_status_t +_cairo_svg_surface_emit_linear_pattern (cairo_svg_surface_t *surface, + cairo_linear_pattern_t *pattern, + cairo_output_stream_t *style, + cairo_bool_t is_stroke, + const cairo_matrix_t *parent_matrix) +{ + cairo_svg_document_t *document = surface->document; + cairo_matrix_t p2u; + cairo_status_t status; + + p2u = pattern->base.base.matrix; + status = cairo_matrix_invert (&p2u); + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_output_stream_printf (document->xml_node_defs, + "linear_pattern_id, + 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); + _cairo_output_stream_printf (document->xml_node_defs, ">\n"); + + status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, + &pattern->base, 0.0, + FALSE, FALSE); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + + _cairo_output_stream_printf (style, + "%s:url(#linear%d);", + is_stroke ? "stroke" : "fill", + document->linear_pattern_id); + + document->linear_pattern_id++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_radial_pattern (cairo_svg_surface_t *surface, + cairo_radial_pattern_t *pattern, + cairo_output_stream_t *style, + cairo_bool_t is_stroke, + const cairo_matrix_t *parent_matrix) +{ + cairo_svg_document_t *document = surface->document; + cairo_matrix_t p2u; + cairo_extend_t extend; + double x0, y0, x1, y1, r0, r1; + double fx, fy; + cairo_bool_t reverse_stops; + cairo_status_t status; + cairo_circle_double_t *c0, *c1; + + extend = pattern->base.base.extend; + + if (pattern->cd1.radius < pattern->cd2.radius) { + c0 = &pattern->cd1; + c1 = &pattern->cd2; + reverse_stops = FALSE; + } else { + c0 = &pattern->cd2; + c1 = &pattern->cd1; + reverse_stops = TRUE; + } + + 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 (r0 == r1) { + unsigned int n_stops = pattern->base.n_stops; + + _cairo_output_stream_printf (document->xml_node_defs, + "radial_pattern_id, + x1, y1, + x1, y1, r1); + _cairo_svg_surface_emit_transform (document->xml_node_defs, + "gradientTransform", + &p2u, parent_matrix); + _cairo_output_stream_printf (document->xml_node_defs, ">\n"); + + if (extend == CAIRO_EXTEND_NONE || n_stops < 1) + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + else { + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + pattern->base.stops[0].color.red * 100.0, + pattern->base.stops[0].color.green * 100.0, + pattern->base.stops[0].color.blue * 100.0, + pattern->base.stops[0].color.alpha); + if (n_stops > 1) + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + pattern->base.stops[n_stops - 1].color.red * 100.0, + pattern->base.stops[n_stops - 1].color.green * 100.0, + pattern->base.stops[n_stops - 1].color.blue * 100.0, + pattern->base.stops[n_stops - 1].color.alpha); + } + + } else { + double offset, r, x, y; + cairo_bool_t emulate_reflect = FALSE; + + fx = (r1 * x0 - r0 * x1) / (r1 - r0); + fy = (r1 * y0 - r0 * y1) / (r1 - r0); + + /* SVG doesn't support the inner circle and use instead a gradient focal. + * That means we need to emulate the cairo behaviour by processing the + * cairo gradient stops. + * The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle, + * it's just a matter of stop position translation and calculation of + * the corresponding SVG radial gradient focal. + * The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new + * radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT + * case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop + * list that maps to the original cairo stop list. + */ + if ((extend == CAIRO_EXTEND_REFLECT + || extend == CAIRO_EXTEND_REPEAT) + && r0 > 0.0) { + double r_org = r1; + + if (extend == CAIRO_EXTEND_REFLECT) { + r1 = 2 * r1 - r0; + emulate_reflect = TRUE; + } + + offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0; + r = r1 - r0; + + /* New position of outer circle. */ + x = r * (x1 - fx) / r_org + fx; + y = r * (y1 - fy) / r_org + fy; + + x1 = x; + y1 = y; + r1 = r; + r0 = 0.0; + } else { + offset = r0 / r1; + } + + _cairo_output_stream_printf (document->xml_node_defs, + "radial_pattern_id, + x1, y1, + fx, fy, r1); + + if (emulate_reflect) + _cairo_output_stream_printf (document->xml_node_defs, "spreadMethod=\"repeat\" "); + else + _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); + _cairo_output_stream_printf (document->xml_node_defs, ">\n"); + + /* To support cairo's EXTEND_NONE, (for which SVG has no similar + * notion), we add transparent color stops on either end of the + * user-provided stops. */ + if (extend == CAIRO_EXTEND_NONE) { + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + if (r0 != 0.0) + _cairo_output_stream_printf (document->xml_node_defs, + "\n", + r0 / r1); + } + status = _cairo_svg_surface_emit_pattern_stops (document->xml_node_defs, + &pattern->base, offset, + reverse_stops, + emulate_reflect); + if (unlikely (status)) + return status; + + if (pattern->base.base.extend == CAIRO_EXTEND_NONE) + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + } + + _cairo_output_stream_printf (document->xml_node_defs, + "\n"); + + _cairo_output_stream_printf (style, + "%s:url(#radial%d);", + is_stroke ? "stroke" : "fill", + document->radial_pattern_id); + + document->radial_pattern_id++; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_svg_surface_emit_pattern (cairo_svg_surface_t *surface, + const cairo_pattern_t *pattern, + cairo_output_stream_t *output, + cairo_bool_t is_stroke, + const cairo_matrix_t *parent_matrix) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _cairo_svg_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern, + output, is_stroke); + + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_svg_surface_emit_surface_pattern (surface, (cairo_surface_pattern_t *) pattern, + output, is_stroke, parent_matrix); + + case CAIRO_PATTERN_TYPE_LINEAR: + return _cairo_svg_surface_emit_linear_pattern (surface, (cairo_linear_pattern_t *) pattern, + output, is_stroke, parent_matrix); + + 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); +} + +static cairo_status_t +_cairo_svg_surface_emit_fill_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_fill_rule_t fill_rule, + const cairo_matrix_t *parent_matrix) +{ + _cairo_output_stream_printf (output, + "fill-rule:%s;", + fill_rule == CAIRO_FILL_RULE_EVEN_ODD ? + "evenodd" : "nonzero"); + _cairo_svg_surface_emit_operator_for_style (output, surface, op); + return _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, parent_matrix); +} + +static cairo_status_t +_cairo_svg_surface_emit_stroke_style (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *parent_matrix) +{ + cairo_status_t status; + const char *line_cap, *line_join; + unsigned int i; + + switch (stroke_style->line_cap) { + case CAIRO_LINE_CAP_BUTT: + line_cap = "butt"; + break; + case CAIRO_LINE_CAP_ROUND: + line_cap = "round"; + break; + case CAIRO_LINE_CAP_SQUARE: + line_cap = "square"; + break; + default: + ASSERT_NOT_REACHED; + } + + switch (stroke_style->line_join) { + case CAIRO_LINE_JOIN_MITER: + line_join = "miter"; + break; + case CAIRO_LINE_JOIN_ROUND: + line_join = "round"; + break; + case CAIRO_LINE_JOIN_BEVEL: + line_join = "bevel"; + break; + default: + ASSERT_NOT_REACHED; + } + + _cairo_output_stream_printf (output, + "stroke-width:%f;" + "stroke-linecap:%s;" + "stroke-linejoin:%s;", + stroke_style->line_width, + line_cap, + line_join); + + status = _cairo_svg_surface_emit_pattern (surface, source, output, TRUE, parent_matrix); + if (unlikely (status)) + return status; + + _cairo_svg_surface_emit_operator_for_style (output, surface, op); + + if (stroke_style->num_dashes > 0) { + _cairo_output_stream_printf (output, "stroke-dasharray:"); + for (i = 0; i < stroke_style->num_dashes; i++) { + _cairo_output_stream_printf (output, "%f", + stroke_style->dash[i]); + if (i + 1 < stroke_style->num_dashes) + _cairo_output_stream_printf (output, ","); + else + _cairo_output_stream_printf (output, ";"); + } + if (stroke_style->dash_offset != 0.0) { + _cairo_output_stream_printf (output, + "stroke-dashoffset:%f;", + stroke_style->dash_offset); + } + } + + _cairo_output_stream_printf (output, + "stroke-miterlimit:%f;", + stroke_style->miter_limit); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_fill_stroke (void *abstract_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_svg_surface_t *surface = abstract_surface; + cairo_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, fill_op, + fill_source, fill_rule, stroke_ctm_inverse); + if (unlikely (status)) + return status; + + status = _cairo_svg_surface_emit_stroke_style (surface->xml_node, surface, stroke_op, + stroke_source, stroke_style, stroke_ctm_inverse); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "\" "); + + _cairo_svg_surface_emit_path (surface->xml_node, path, stroke_ctm_inverse); + + _cairo_svg_surface_emit_transform (surface->xml_node, " transform", stroke_ctm, NULL); + _cairo_output_stream_printf (surface->xml_node, "/>\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_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_svg_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, source); + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, op, source, fill_rule, NULL); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "\" "); + + _cairo_svg_surface_emit_path (surface->xml_node, path, NULL); + + _cairo_output_stream_printf (surface->xml_node, "/>\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_svg_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_svg_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + + /* 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 surface->surface_bounded; +} + +static cairo_status_t +_cairo_svg_surface_emit_paint (cairo_output_stream_t *output, + cairo_svg_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask_source, + const char *extra_attributes) +{ + cairo_status_t status; + + if (source->type == CAIRO_PATTERN_TYPE_SURFACE && + source->extend == CAIRO_EXTEND_NONE) + return _cairo_svg_surface_emit_composite_pattern (output, + surface, + op, + (cairo_surface_pattern_t *) source, + invalid_pattern_id, + mask_source ? &mask_source->matrix :NULL, + extra_attributes); + + _cairo_output_stream_printf (output, + "width, surface->height); + _cairo_svg_surface_emit_operator_for_style (output, surface, op); + status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (output, "stroke:none;\""); + + if (extra_attributes) + _cairo_output_stream_printf (output, " %s", extra_attributes); + + _cairo_output_stream_printf (output, "/>\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_status_t status; + cairo_svg_surface_t *surface = abstract_surface; + + /* Emulation of clear and source operators, when no clipping region + * is defined. We just delete existing content of surface root node, + * and exit early if operator is clear. + */ + if ((op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) && + clip == NULL) + { + switch (surface->paginated_mode) { + case CAIRO_PAGINATED_MODE_FALLBACK: + ASSERT_NOT_REACHED; + case CAIRO_PAGINATED_MODE_ANALYZE: + return CAIRO_STATUS_SUCCESS; + + case CAIRO_PAGINATED_MODE_RENDER: + status = _cairo_output_stream_destroy (surface->xml_node); + if (unlikely (status)) { + surface->xml_node = NULL; + return status; + } + + surface->xml_node = _cairo_memory_stream_create (); + if (_cairo_output_stream_get_status (surface->xml_node)) { + status = _cairo_output_stream_destroy (surface->xml_node); + surface->xml_node = NULL; + return status; + } + + if (op == CAIRO_OPERATOR_CLEAR) { + if (surface->content == CAIRO_CONTENT_COLOR) { + _cairo_output_stream_printf (surface->xml_node, + "\n", + surface->width, surface->height); + } + return CAIRO_STATUS_SUCCESS; + } + break; + } + } else { + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, source); + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + return _cairo_svg_surface_emit_paint (surface->xml_node, + surface, op, source, 0, NULL); +} + +static cairo_int_status_t +_cairo_svg_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_status_t status; + cairo_svg_surface_t *surface = abstract_surface; + cairo_svg_document_t *document = surface->document; + cairo_output_stream_t *mask_stream; + char buffer[64]; + cairo_bool_t discard_filter = FALSE; + unsigned int mask_id; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + cairo_status_t source_status, mask_status; + + source_status = _cairo_svg_surface_analyze_operation (surface, op, source); + if (_cairo_status_is_error (source_status)) + return source_status; + + if (mask->has_component_alpha) { + mask_status = CAIRO_INT_STATUS_UNSUPPORTED; + } else { + mask_status = _cairo_svg_surface_analyze_operation (surface, op, mask); + if (_cairo_status_is_error (mask_status)) + return mask_status; + } + + return _cairo_analysis_surface_merge_status (source_status, + mask_status); + } + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); + assert (_cairo_svg_surface_operation_supported (surface, CAIRO_OPERATOR_OVER, mask)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask; + cairo_content_t content = surface_pattern->surface->content; + if (content == CAIRO_CONTENT_ALPHA) + discard_filter = TRUE; + } + + if (!discard_filter) + _cairo_svg_surface_emit_alpha_filter (document); + + /* _cairo_svg_surface_emit_paint() will output a pattern definition to + * document->xml_node_defs so we need to write the mask element to + * a temporary stream and then copy that to xml_node_defs. */ + mask_stream = _cairo_memory_stream_create (); + if (_cairo_output_stream_get_status (mask_stream)) + return _cairo_output_stream_destroy (mask_stream); + + mask_id = _cairo_svg_document_allocate_mask_id (document); + + _cairo_output_stream_printf (mask_stream, + "\n" + "%s", + mask_id, + discard_filter ? "" : " \n"); + status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL); + if (unlikely (status)) { + cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream); + return status; + (void) ignore; + } + + _cairo_output_stream_printf (mask_stream, + "%s" + "\n", + discard_filter ? "" : " \n"); + _cairo_memory_stream_copy (mask_stream, document->xml_node_defs); + + status = _cairo_output_stream_destroy (mask_stream); + if (unlikely (status)) + return status; + + snprintf (buffer, sizeof buffer, "mask=\"url(#mask%d)\"", + mask_id); + status = _cairo_svg_surface_emit_paint (surface->xml_node, surface, op, source, 0, buffer); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_stroke (void *abstract_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, + const cairo_clip_t *clip) +{ + cairo_svg_surface_t *surface = abstract_dst; + cairo_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, source); + + assert (_cairo_svg_surface_operation_supported (surface, op, source)); + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "xml_node, surface, op, + source, stroke_style, ctm_inverse); + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, "\" "); + + _cairo_svg_surface_emit_path (surface->xml_node, path, ctm_inverse); + + _cairo_svg_surface_emit_transform (surface->xml_node, " transform", ctm, NULL); + _cairo_output_stream_printf (surface->xml_node, "/>\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_svg_surface_show_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + 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_int_status_t status; + cairo_scaled_font_subsets_glyph_t subset_glyph; + int i; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_svg_surface_analyze_operation (surface, op, pattern); + + assert (_cairo_svg_surface_operation_supported (surface, op, pattern)); + + if (num_glyphs <= 0) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + /* FIXME it's probably possible to apply a pattern of a gradient to + * a group of symbols, but I don't know how yet. Gradients or patterns + * are translated by x and y properties of use element. */ + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + goto FALLBACK; + + _cairo_output_stream_printf (surface->xml_node, "xml_node, FALSE, NULL); + if (unlikely (status)) + return status; + + _cairo_svg_surface_emit_operator_for_style (surface->xml_node, surface, op); + + _cairo_output_stream_printf (surface->xml_node, "\">\n"); + + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_font_subsets_map_glyph (document->font_subsets, + scaled_font, glyphs[i].index, + NULL, 0, + &subset_glyph); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_output_stream_printf (surface->xml_node, "\n"); + + glyphs += i; + num_glyphs -= i; + goto FALLBACK; + } + + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->xml_node, + " \n", + subset_glyph.font_id, + subset_glyph.subset_glyph_index, + glyphs[i].x, glyphs[i].y); + } + + _cairo_output_stream_printf (surface->xml_node, "\n"); + + return CAIRO_STATUS_SUCCESS; + +FALLBACK: + _cairo_path_fixed_init (&path); + + status = _cairo_scaled_font_glyph_path (scaled_font, + (cairo_glyph_t *) glyphs, + num_glyphs, &path); + + if (unlikely (status)) { + _cairo_path_fixed_fini (&path); + return status; + } + + status = _cairo_svg_surface_fill (abstract_surface, op, pattern, + &path, CAIRO_FILL_RULE_WINDING, + 0.0, CAIRO_ANTIALIAS_SUBPIXEL, + clip); + + _cairo_path_fixed_fini (&path); + + return status; +} + +static void +_cairo_svg_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + 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, + _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, /* snapshot */ + + _cairo_svg_surface_copy_page, + _cairo_svg_surface_show_page, + + _cairo_svg_surface_get_extents, + _cairo_svg_surface_get_font_options, + + NULL, /* flush */ + NULL, /* mark dirty rectangle */ + + _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, /* has_show_text_glyphs */ + NULL, /* show_text_glyphs */ + _cairo_svg_surface_get_supported_mime_types, +}; + +static cairo_status_t +_cairo_svg_document_create (cairo_output_stream_t *output_stream, + double width, + double height, + cairo_svg_version_t version, + cairo_svg_document_t **document_out) +{ + cairo_svg_document_t *document; + cairo_status_t status, status_ignored; + + if (output_stream->status) + return output_stream->status; + + document = _cairo_malloc (sizeof (cairo_svg_document_t)); + if (unlikely (document == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* The use of defs for font glyphs imposes no per-subset limit. */ + document->font_subsets = _cairo_scaled_font_subsets_create_scaled (); + if (unlikely (document->font_subsets == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_DOCUMENT; + } + + document->output_stream = output_stream; + document->refcount = 1; + document->owner = NULL; + 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; + document->pattern_id = 0; + document->filter_id = 0; + document->clip_id = 0; + document->mask_id = 0; + + document->xml_node_defs = _cairo_memory_stream_create (); + status = _cairo_output_stream_get_status (document->xml_node_defs); + if (unlikely (status)) + goto CLEANUP_NODE_DEFS; + + document->xml_node_glyphs = _cairo_memory_stream_create (); + status = _cairo_output_stream_get_status (document->xml_node_glyphs); + if (unlikely (status)) + goto CLEANUP_NODE_GLYPHS; + + document->alpha_filter = FALSE; + + document->svg_version = version; + + *document_out = document; + return CAIRO_STATUS_SUCCESS; + + CLEANUP_NODE_GLYPHS: + status_ignored = _cairo_output_stream_destroy (document->xml_node_glyphs); + CLEANUP_NODE_DEFS: + status_ignored = _cairo_output_stream_destroy (document->xml_node_defs); + _cairo_scaled_font_subsets_destroy (document->font_subsets); + CLEANUP_DOCUMENT: + free (document); + return status; +} + +static cairo_svg_document_t * +_cairo_svg_document_reference (cairo_svg_document_t *document) +{ + document->refcount++; + + return document; +} + +static unsigned int +_cairo_svg_document_allocate_mask_id (cairo_svg_document_t *document) +{ + return document->mask_id++; +} + +static cairo_status_t +_cairo_svg_document_destroy (cairo_svg_document_t *document) +{ + cairo_status_t status; + + document->refcount--; + if (document->refcount > 0) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_svg_document_finish (document); + + free (document); + + return status; +} + +static cairo_status_t +_cairo_svg_document_finish (cairo_svg_document_t *document) +{ + cairo_status_t status, status2; + cairo_output_stream_t *output = document->output_stream; + cairo_svg_page_t *page; + unsigned int i; + + if (document->finished) + return CAIRO_STATUS_SUCCESS; + + /* + * Should we add DOCTYPE? + * + * Google says no. + * + * http://tech.groups.yahoo.com/group/svg-developers/message/48562: + * There's a bunch of issues, but just to pick a few: + * - they'll give false positives. + * - they'll give false negatives. + * - they're namespace-unaware. + * - they don't wildcard. + * So when they say OK they really haven't checked anything, when + * they say NOT OK they might be on crack, and like all + * namespace-unaware things they're a dead branch of the XML tree. + * + * http://jwatt.org/svg/authoring/: + * Unfortunately the SVG DTDs are a source of so many issues that the + * SVG WG has decided not to write one for the upcoming SVG 1.2 + * standard. In fact SVG WG members are even telling people not to use + * a DOCTYPE declaration in SVG 1.0 and 1.1 documents. + */ + + _cairo_output_stream_printf (output, + "\n" + "\n", + 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]); + + status = _cairo_svg_document_emit_font_subsets (document); + + if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0 || + _cairo_memory_stream_length (document->xml_node_defs) > 0) { + _cairo_output_stream_printf (output, "\n"); + if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) { + _cairo_output_stream_printf (output, "\n"); + _cairo_memory_stream_copy (document->xml_node_glyphs, output); + _cairo_output_stream_printf (output, "\n"); + } + _cairo_memory_stream_copy (document->xml_node_defs, output); + _cairo_output_stream_printf (output, "\n"); + } + + if (document->owner != NULL) { + cairo_svg_surface_t *surface; + + surface = (cairo_svg_surface_t *) _cairo_paginated_surface_get_target (document->owner); + if (surface->xml_node != NULL && + _cairo_memory_stream_length (surface->xml_node) > 0) { + if (unlikely (_cairo_svg_surface_store_page (surface) == NULL)) { + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + if (surface->page_set.num_elements > 1 && + _cairo_svg_version_has_page_set_support (document->svg_version)) { + _cairo_output_stream_printf (output, "\n"); + for (i = 0; i < surface->page_set.num_elements; i++) { + page = _cairo_array_index (&surface->page_set, i); + _cairo_output_stream_printf (output, "\n"); + _cairo_output_stream_printf (output, + "\n", + page->surface_id); + _cairo_memory_stream_copy (page->xml_node, output); + _cairo_output_stream_printf (output, "\n\n"); + } + _cairo_output_stream_printf (output, "\n"); + } else if (surface->page_set.num_elements > 0) { + page = _cairo_array_index (&surface->page_set, surface->page_set.num_elements - 1); + _cairo_output_stream_printf (output, + "\n", + page->surface_id); + _cairo_memory_stream_copy (page->xml_node, output); + _cairo_output_stream_printf (output, "\n"); + } + } + + _cairo_output_stream_printf (output, "\n"); + + status2 = _cairo_output_stream_destroy (document->xml_node_glyphs); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (document->xml_node_defs); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (output); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + document->finished = TRUE; + + return status; +} + +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 +_cairo_svg_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + cairo_svg_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->document->svg_version >= CAIRO_SVG_VERSION_1_2) { + status = _cairo_svg_surface_analyze_operator (surface, + CAIRO_OPERATOR_SOURCE); + } + + return status == CAIRO_INT_STATUS_SUCCESS; +} + +static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = { + NULL /*_cairo_svg_surface_start_page*/, + _cairo_svg_surface_set_paginated_mode, + NULL, /* _cairo_svg_surface_set_bounding_box */ + NULL, /* _cairo_svg_surface_set_fallback_images_required */ + _cairo_svg_surface_supports_fine_grained_fallbacks, + +}; diff --git a/gfx/cairo/cairo/src/cairo-svg.h b/gfx/cairo/cairo/src/cairo-svg.h new file mode 100644 index 0000000000..4d24857bca --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-svg.h @@ -0,0 +1,131 @@ +/* cairo - a vector graphics library with display and print output + * + * cairo-svg.h + * + * Copyright © 2005 Emmanuel Pacaud + * + * 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_SVG_H +#define CAIRO_SVG_H + +#include "cairo.h" + +#if CAIRO_HAS_SVG_SURFACE + +CAIRO_BEGIN_DECLS + +/** + * cairo_svg_version_t: + * @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, + double height_in_points); + +cairo_public cairo_surface_t * +cairo_svg_surface_create_for_stream (cairo_write_func_t write_func, + void *closure, + double width_in_points, + double height_in_points); + +cairo_public void +cairo_svg_surface_restrict_to_version (cairo_surface_t *surface, + cairo_svg_version_t version); + +cairo_public void +cairo_svg_get_versions (cairo_svg_version_t const **versions, + int *num_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 */ +# error Cairo was not compiled with support for the svg backend +#endif /* CAIRO_HAS_SVG_SURFACE */ + +#endif /* CAIRO_SVG_H */ 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 0000000000..3f5fa5b64a --- /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 0000000000..6d0f63f119 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tag-attributes.c @@ -0,0 +1,728 @@ +/* -*- 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 "cairo-tag-stack-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 && end != start) + 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 *attributes, 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) + goto error; + + if (*p++ != '[') + goto error; + + while (TRUE) { + p = skip_space (p); + if (! *p) + goto error; + + if (*p == ']') { + *end = p + 1; + return CAIRO_INT_STATUS_SUCCESS; + } + + p = parse_scalar (p, type, &val); + if (!p) + goto error; + + status = _cairo_array_append (array, &val); + if (unlikely (status)) + return status; + } + + error: + return _cairo_tag_error ( + "while parsing attributes: \"%s\". Error parsing array", attributes); +} + +static cairo_int_status_t +parse_name (const char *attributes, const char *p, const char **end, char **s) +{ + const char *p2; + char *name; + int len; + + if (! _cairo_isalpha (*p)) + return _cairo_tag_error ( + "while parsing attributes: \"%s\". Error parsing name." + " \"%s\" does not start with an alphabetic character", + attributes, p); + + 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_INT_STATUS_SUCCESS; + + while (*p) { + p = skip_space (p); + if (! *p) + break; + + status = parse_name (attributes, 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_tag_error ( + "while parsing attributes: \"%s\". Unknown attribute name \"%s\"", + attributes, name); + 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_tag_error ( + "while parsing attributes: \"%s\". Expected '=' after \"%s\"", + attributes, name); + goto fail2; + } + + if (def->array_size == 0) { + const char *s = p; + p = parse_scalar (p, def->type, &attrib->scalar); + if (!p) { + status = _cairo_tag_error ( + "while parsing attributes: \"%s\". Error parsing \"%s\"", + attributes, s); + goto fail2; + } + + attrib->array_len = 0; + } else { + status = parse_array (attributes, 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_tag_error ( + "while parsing attributes: \"%s\". Expected %d elements in array. Found %d", + attributes, def->array_size, attrib->array_len); + 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); + } +} + +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_bool_t has_rect = FALSE; + cairo_bool_t invalid_combination = FALSE; + + 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)); + + cairo_list_foreach_entry (attr, attribute_t, &list, link) { + if (strcmp (attr->name, "dest") == 0) { + link_attrs->dest = strdup (attr->scalar.s); + + } else if (strcmp (attr->name, "page") == 0) { + link_attrs->page = attr->scalar.i; + if (link_attrs->page < 1) { + status = _cairo_tag_error ("Link attributes: \"%s\" page must be >= 1", attributes); + goto cleanup; + } + + } else if (strcmp (attr->name, "pos") == 0) { + _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, "uri") == 0) { + link_attrs->uri = strdup (attr->scalar.s); + + } else if (strcmp (attr->name, "file") == 0) { + link_attrs->file = strdup (attr->scalar.s); + + } 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_tag_error ("Link attributes: \"%s\" rect array size must be multiple of 4", + attributes); + 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; + } + has_rect = TRUE; + } + } + + if (link_attrs->uri) { + link_attrs->link_type = TAG_LINK_URI; + if (link_attrs->dest || link_attrs->page || link_attrs->has_pos || link_attrs->file) + invalid_combination = TRUE; + + } else if (link_attrs->file) { + link_attrs->link_type = TAG_LINK_FILE; + if (link_attrs->uri) + invalid_combination = TRUE; + else if (link_attrs->dest && (link_attrs->page || link_attrs->has_pos)) + invalid_combination = TRUE; + + } else if (link_attrs->dest) { + link_attrs->link_type = TAG_LINK_DEST; + if (link_attrs->uri || link_attrs->page || link_attrs->has_pos) + invalid_combination = TRUE; + + } else if (link_attrs->page) { + link_attrs->link_type = TAG_LINK_DEST; + if (link_attrs->uri || link_attrs->dest) + invalid_combination = TRUE; + + } else { + link_attrs->link_type = TAG_LINK_EMPTY; + if (link_attrs->has_pos) + invalid_combination = TRUE; + } + + if (invalid_combination) { + status = _cairo_tag_error ( + "Link attributes: \"%s\" invalid combination of attributes", attributes); + } + + 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_tag_error ("Destination attributes: \"%s\" missing name attribute", + attributes); + + 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 0000000000..fbb2c0e25b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tag-stack-private.h @@ -0,0 +1,110 @@ +/* -*- 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); + +cairo_private cairo_status_t +_cairo_tag_error (const char *fmt, ...) CAIRO_PRINTF_FORMAT (1, 2); + +#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 0000000000..095cd73f8d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tag-stack.c @@ -0,0 +1,298 @@ +/* -*- 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_tag_error ("Invalid tag: %s", name); + } + + 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_tag_error ("cairo_tag_end(\"%s\") no matching begin tag", name); + } + + cairo_list_del (&top->link); + stack->size--; + if (strcmp (top->name, name) != 0) { + cairo_status_t status = + _cairo_tag_error ("cairo_tag_end(\"%s\") does not matching previous begin tag \"%s\"", + name, top->name); + stack->type = TAG_TYPE_INVALID; + _cairo_tag_stack_free_elem (top); + return status; + } + + 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; +} + +cairo_status_t +_cairo_tag_error (const char *fmt, ...) +{ + va_list ap; + + if (getenv ("CAIRO_DEBUG_TAG") != NULL) { + printf ("TAG ERROR: "); + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + printf ("\n"); + } + return _cairo_error (CAIRO_STATUS_TAG_ERROR); +} diff --git a/gfx/cairo/cairo/src/cairo-tee-surface-private.h b/gfx/cairo/cairo/src/cairo-tee-surface-private.h new file mode 100644 index 0000000000..a83cfc959d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tee-surface-private.h @@ -0,0 +1,47 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Chris Wilson + */ + +#ifndef CAIRO_TEE_SURFACE_PRIVATE_H +#define CAIRO_TEE_SURFACE_PRIVATE_H + +#include "cairoint.h" + +cairo_private cairo_surface_t * +_cairo_tee_surface_find_match (void *abstract_surface, + const cairo_surface_backend_t *backend, + cairo_content_t content); + +#endif /* CAIRO_TEE_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-tee-surface.c b/gfx/cairo/cairo/src/cairo-tee-surface.c new file mode 100644 index 0000000000..7a94c9bca4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tee-surface.c @@ -0,0 +1,603 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + */ + +/* This surface supports redirecting all its input to multiple surfaces. + */ + +#include "cairoint.h" + +#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; + + cairo_surface_wrapper_t master; + cairo_array_t slaves; +} cairo_tee_surface_t; + +slim_hidden_proto (cairo_tee_surface_create); +slim_hidden_proto (cairo_tee_surface_add); + +static cairo_surface_t * +_cairo_tee_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + + cairo_tee_surface_t *other = abstract_surface; + cairo_surface_t *similar; + cairo_surface_t *surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + + similar = _cairo_surface_wrapper_create_similar (&other->master, + content, width, height); + surface = cairo_tee_surface_create (similar); + cairo_surface_destroy (similar); + if (unlikely (surface->status)) + return surface; + + num_slaves = _cairo_array_num_elements (&other->slaves); + slaves = _cairo_array_index (&other->slaves, 0); + for (n = 0; n < num_slaves; n++) { + + similar = _cairo_surface_wrapper_create_similar (&slaves[n], + content, + width, height); + cairo_tee_surface_add (surface, similar); + cairo_surface_destroy (similar); + } + + if (unlikely (surface->status)) { + cairo_status_t status = surface->status; + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + } + + return surface; +} + +static cairo_status_t +_cairo_tee_surface_finish (void *abstract_surface) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + + _cairo_surface_wrapper_fini (&surface->master); + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) + _cairo_surface_wrapper_fini (&slaves[n]); + + _cairo_array_fini (&surface->slaves); + + 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, + void **image_extra) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* we prefer to use a real image surface if available */ + if (_cairo_surface_is_image (surface->master.target)) { + return _cairo_surface_wrapper_acquire_source_image (&surface->master, + image_out, image_extra); + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (_cairo_surface_is_image (slaves[n].target)) { + return _cairo_surface_wrapper_acquire_source_image (&slaves[n], + image_out, + image_extra); + } + } + + return _cairo_surface_wrapper_acquire_source_image (&surface->master, + image_out, image_extra); +} + +static void +_cairo_tee_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_tee_surface_t *surface = abstract_surface; + + _cairo_surface_wrapper_release_source_image (&surface->master, + image, image_extra); +} + +static cairo_surface_t * +_cairo_tee_surface_snapshot (void *abstract_surface) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* we prefer to use a recording surface for our snapshots */ + if (_cairo_surface_is_recording (surface->master.target)) + return _cairo_surface_wrapper_snapshot (&surface->master); + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (_cairo_surface_is_recording (slaves[n].target)) + return _cairo_surface_wrapper_snapshot (&slaves[n]); + } + + return _cairo_surface_wrapper_snapshot (&surface->master); +} + +static cairo_bool_t +_cairo_tee_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_tee_surface_t *surface = abstract_surface; + + return _cairo_surface_wrapper_get_extents (&surface->master, rectangle); +} + +static void +_cairo_tee_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_tee_surface_t *surface = abstract_surface; + + _cairo_surface_wrapper_get_font_options (&surface->master, options); +} + +static cairo_int_status_t +_cairo_tee_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + 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++) { + status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip); + if (unlikely (status)) + return status; + } + + return _cairo_surface_wrapper_paint (&surface->master, op, source, clip); +} + +static cairo_int_status_t +_cairo_tee_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_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + cairo_int_status_t status; + int n, num_slaves; + + 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_mask (&slaves[n], + op, source, mask, clip); + if (unlikely (status)) + return status; + } + + 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, + 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_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + cairo_int_status_t status; + int n, num_slaves; + + 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_stroke (&slaves[n], + op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (unlikely (status)) + return status; + } + + 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, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + 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; + + 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_fill (&slaves[n], + op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (unlikely (status)) + return status; + } + + return _cairo_surface_wrapper_fill (&surface->master, + op, source, + path, fill_rule, + tolerance, antialias, + clip); +} + +static cairo_bool_t +_cairo_tee_surface_has_show_text_glyphs (void *abstract_surface) +{ + return TRUE; +} + +static cairo_int_status_t +_cairo_tee_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_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + cairo_int_status_t status; + int n, num_slaves; + cairo_glyph_t *glyphs_copy; + + /* 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); + + 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); + status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op, + source, + utf8, utf8_len, + glyphs_copy, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); + if (unlikely (status)) + goto 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 const cairo_surface_backend_t cairo_tee_surface_backend = { + CAIRO_SURFACE_TYPE_TEE, + _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, + _cairo_tee_surface_snapshot, + NULL, /* copy_page */ + NULL, /* show_page */ + _cairo_tee_surface_get_extents, + _cairo_tee_surface_get_font_options, + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _cairo_tee_surface_paint, + _cairo_tee_surface_mask, + _cairo_tee_surface_stroke, + _cairo_tee_surface_fill, + NULL, /* fill_stroke */ + + NULL, /* show_glyphs */ + + _cairo_tee_surface_has_show_text_glyphs, + _cairo_tee_surface_show_text_glyphs +}; + +cairo_surface_t * +cairo_tee_surface_create (cairo_surface_t *master) +{ + cairo_tee_surface_t *surface; + + if (unlikely (master->status)) + return _cairo_surface_create_in_error (master->status); + + 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, + TRUE); /* is_vector */ + + _cairo_surface_wrapper_init (&surface->master, master); + + _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t)); + + return &surface->base; +} +slim_hidden_def (cairo_tee_surface_create); + +void +cairo_tee_surface_add (cairo_surface_t *abstract_surface, + cairo_surface_t *target) +{ + cairo_tee_surface_t *surface; + cairo_surface_wrapper_t slave; + 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 (abstract_surface->backend != &cairo_tee_surface_backend) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (unlikely (target->status)) { + status = _cairo_surface_set_error (abstract_surface, target->status); + return; + } + + surface = (cairo_tee_surface_t *) abstract_surface; + + _cairo_surface_wrapper_init (&slave, target); + status = _cairo_array_append (&surface->slaves, &slave); + if (unlikely (status)) { + _cairo_surface_wrapper_fini (&slave); + status = _cairo_surface_set_error (&surface->base, status); + } +} +slim_hidden_def (cairo_tee_surface_add); + +void +cairo_tee_surface_remove (cairo_surface_t *abstract_surface, + cairo_surface_t *target) +{ + cairo_tee_surface_t *surface; + cairo_surface_wrapper_t *slaves; + int n, num_slaves; + + 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 (abstract_surface->backend != &cairo_tee_surface_backend) { + _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) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_INDEX)); + return; + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target == target) + break; + } + + if (n == num_slaves) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_INDEX)); + return; + } + + _cairo_surface_wrapper_fini (&slaves[n]); + for (n++; n < num_slaves; n++) + slaves[n-1] = slaves[n]; + surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */ +} + +cairo_surface_t * +cairo_tee_surface_index (cairo_surface_t *abstract_surface, + unsigned int index) +{ + cairo_tee_surface_t *surface; + + if (unlikely (abstract_surface->status)) + return _cairo_surface_create_in_error (abstract_surface->status); + if (unlikely (abstract_surface->finished)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + + if (abstract_surface->backend != &cairo_tee_surface_backend) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + + surface = (cairo_tee_surface_t *) abstract_surface; + if (index == 0) { + return surface->master.target; + } else { + cairo_surface_wrapper_t *slave; + + index--; + + if (index >= _cairo_array_num_elements (&surface->slaves)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX)); + + slave = _cairo_array_index (&surface->slaves, index); + return slave->target; + } +} + +cairo_surface_t * +_cairo_tee_surface_find_match (void *abstract_surface, + const cairo_surface_backend_t *backend, + cairo_content_t content) +{ + cairo_tee_surface_t *surface = abstract_surface; + cairo_surface_wrapper_t *slaves; + int num_slaves, n; + + /* exact match first */ + if (surface->master.target->backend == backend && + surface->master.target->content == content) + { + return surface->master.target; + } + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target->backend == backend && + slaves[n].target->content == content) + { + return slaves[n].target; + } + } + + /* matching backend? */ + if (surface->master.target->backend == backend) + return surface->master.target; + + num_slaves = _cairo_array_num_elements (&surface->slaves); + slaves = _cairo_array_index (&surface->slaves, 0); + for (n = 0; n < num_slaves; n++) { + if (slaves[n].target->backend == backend) + return slaves[n].target; + } + + return NULL; +} diff --git a/gfx/cairo/cairo/src/cairo-tee.h b/gfx/cairo/cairo/src/cairo-tee.h new file mode 100644 index 0000000000..9125a3a4a2 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tee.h @@ -0,0 +1,66 @@ +/* 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_TEE_H +#define CAIRO_TEE_H + +#include "cairo.h" + +#if CAIRO_HAS_TEE_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_tee_surface_create (cairo_surface_t *master); + +cairo_public void +cairo_tee_surface_add (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_public void +cairo_tee_surface_remove (cairo_surface_t *surface, + cairo_surface_t *target); + +cairo_public cairo_surface_t * +cairo_tee_surface_index (cairo_surface_t *surface, + unsigned int index); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_TEE_SURFACE*/ +# error Cairo was not compiled with support for the TEE backend +#endif /*CAIRO_HAS_TEE_SURFACE*/ + +#endif /*CAIRO_TEE_H*/ 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 0000000000..06dc912b43 --- /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 0000000000..a0003fbfc2 --- /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 new file mode 100644 index 0000000000..e8142d5bcf --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c @@ -0,0 +1,1899 @@ +/* -*- 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 + +/* 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 == 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; + int64_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 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 + * 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. 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, *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 + * 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; + + /* y2-y1 after orienting the edge downwards. */ + int64_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; +}; + +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 (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->current, 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 != (void *) 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->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 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. */ +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->data + 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 != (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 = (void *) pool->sentinel; + pool->current->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); + } +} + +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. + * + * 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) +{ + struct quorem x1, x2; + grid_scaled_x_t fx1, fx2; + int ix1, ix2; + + x1 = edge->x; + full_step (edge); + x2 = edge->x; + + /* 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; + } + } + + 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); + cell->covered_height += sign*GRID_Y; + cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; + return; + } + + /* Orient the edge left-to-right. */ + 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; + } + + /* Add coverage for all pixels [ix1,ix2] on this row crossed + * by the edge. */ + { + struct cell_pair pair; + 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 + * 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. + */ + + 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_last = y.quo; + + if (ix1+1 < ix2) { + 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 { + y.quo += dydx_full.quo; + y.rem += dydx_full.rem; + if (y.rem >= dx) { + y.quo++; + y.rem -= dx; + } + + 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); + } while (ix1 != ix2); + + pair.cell2 = cell; + } + pair.cell2->uncovered_area += sign*(GRID_Y - y_last)*fx2; + pair.cell2->covered_height += sign*(GRID_Y - y_last); + } +} + +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; +} + +static void +active_list_reset (struct active_list *active) +{ + 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 +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->cell <= head_b->cell) { + head = head_a; + } else { + head = head_b; + head_b->prev = prev; + goto start_with_b; + } + + 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; + +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); +} + +/* + * 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->cell <= head_other->cell) { + *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; + 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.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; + } + + if (active->min_height < GRID_Y) + return 0; + + /* Check for intersections as no edges end during the next row. */ + for (e = active->head.next; e != &active->tail; e = e->next) { + int cell; + + 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--; + 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; + + if (cell < prev_x) + return 0; + + prev_x = cell; + } + + return 1; +} + +/* 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 int +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; + int max_suby = 0; + + 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; + } + + active->is_vertical = is_vertical; + active->min_height = min_height; + + return max_suby; +} + +static void step (struct edge *edge) +{ + if (edge->dy == 0) + return; + + 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; + } + + 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; + } +} + +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 (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, 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__ += 1 << (GLITTER_INPUT_BITS-1); \ + tmp__ >>= GLITTER_INPUT_BITS; \ + (out) = tmp__; \ +} while (0) + +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) +{ + polygon_add_edge (converter->polygon, edge); +} + +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; + active->min_height = -1; + } + } +} + +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_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 { + 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_tor_scan_converter cairo_tor_scan_converter_t; + +static void +_cairo_tor_scan_converter_destroy (void *converter) +{ + cairo_tor_scan_converter_t *self = converter; + if (self == NULL) { + return; + } + _glitter_scan_converter_fini (self->converter); + free(self); +} + +cairo_status_t +_cairo_tor_scan_converter_add_polygon (void *converter, + const cairo_polygon_t *polygon) +{ + cairo_tor_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_tor_scan_converter_generate (void *converter, + cairo_span_renderer_t *renderer) +{ + cairo_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 ? ~0 : 1, + self->antialias != CAIRO_ANTIALIAS_NONE, + renderer); + return CAIRO_STATUS_SUCCESS; +} + +cairo_scan_converter_t * +_cairo_tor_scan_converter_create (int xmin, + int ymin, + int xmax, + int ymax, + cairo_fill_rule_t fill_rule, + cairo_antialias_t antialias) +{ + cairo_tor_scan_converter_t *self; + cairo_status_t status; + + 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.generate = _cairo_tor_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-tor22-scan-converter.c b/gfx/cairo/cairo/src/cairo-tor22-scan-converter.c new file mode 100644 index 0000000000..79c858e4e5 --- /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 new file mode 100644 index 0000000000..f51dab5ab7 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-toy-font-face.c @@ -0,0 +1,524 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005,2008 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + * Graydon Hoare + * Owen Taylor + * Behdad Esfahbod + */ + +#define _DEFAULT_SOURCE /* for strdup() */ +#include "cairoint.h" +#include "cairo-error-private.h" + + +static const cairo_font_face_t _cairo_font_face_null_pointer = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_NULL_POINTER, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_string = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_STRING, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_slant = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_SLANT, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + +static const cairo_font_face_t _cairo_font_face_invalid_weight = { + { 0 }, /* hash_entry */ + CAIRO_STATUS_INVALID_WEIGHT, /* status */ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ + { 0, 0, 0, NULL }, /* user_data */ + NULL +}; + + +static const cairo_font_face_backend_t _cairo_toy_font_face_backend; + +static int +_cairo_toy_font_face_keys_equal (const void *key_a, + const void *key_b); + +/* We maintain a hash table from family/weight/slant => + * #cairo_font_face_t for #cairo_toy_font_t. The primary purpose of + * this mapping is to provide unique #cairo_font_face_t values so that + * our cache and mapping from #cairo_font_face_t => #cairo_scaled_font_t + * works. Once the corresponding #cairo_font_face_t objects fall out of + * downstream caches, we don't need them in this hash table anymore. + * + * Modifications to this hash table are protected by + * _cairo_toy_font_face_mutex. + */ +static cairo_hash_table_t *cairo_toy_font_face_hash_table = NULL; + +static cairo_hash_table_t * +_cairo_toy_font_face_hash_table_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); + + if (cairo_toy_font_face_hash_table == NULL) + { + cairo_toy_font_face_hash_table = + _cairo_hash_table_create (_cairo_toy_font_face_keys_equal); + + if (cairo_toy_font_face_hash_table == NULL) { + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); + return NULL; + } + } + + return cairo_toy_font_face_hash_table; +} + +static void +_cairo_toy_font_face_hash_table_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); +} + +/** + * _cairo_toy_font_face_init_key: + * + * Initialize those portions of #cairo_toy_font_face_t needed to use + * it as a hash table key, including the hash code buried away in + * font_face->base.hash_entry. No memory allocation is performed here + * so that no fini call is needed. We do this to make it easier to use + * an automatic #cairo_toy_font_face_t variable as a key. + **/ +static void +_cairo_toy_font_face_init_key (cairo_toy_font_face_t *key, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + unsigned long hash; + + key->family = family; + key->owns_family = FALSE; + + key->slant = slant; + key->weight = weight; + + /* 1607 and 1451 are just a couple of arbitrary primes. */ + hash = _cairo_hash_string (family); + hash += ((unsigned long) slant) * 1607; + hash += ((unsigned long) weight) * 1451; + + key->base.hash_entry.hash = hash; +} + +static cairo_status_t +_cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face, + cairo_font_face_t **impl_font_face) +{ + const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (unlikely (font_face->base.status)) + return font_face->base.status; + + if (backend->create_for_toy != NULL && + 0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT, + strlen (CAIRO_USER_FONT_FAMILY_DEFAULT))) + { + status = backend->create_for_toy (font_face, impl_font_face); + } + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + backend = &_cairo_user_font_face_backend; + status = backend->create_for_toy (font_face, impl_font_face); + } + + return status; +} + +static cairo_status_t +_cairo_toy_font_face_init (cairo_toy_font_face_t *font_face, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + char *family_copy; + cairo_status_t status; + + family_copy = strdup (family); + if (unlikely (family_copy == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight); + font_face->owns_family = TRUE; + + _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend); + + status = _cairo_toy_font_face_create_impl_face (font_face, + &font_face->impl_face); + if (unlikely (status)) { + free (family_copy); + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face) +{ + /* We assert here that we own font_face->family before casting + * away the const qualifier. */ + assert (font_face->owns_family); + free ((char*) font_face->family); + + if (font_face->impl_face) + cairo_font_face_destroy (font_face->impl_face); +} + +static int +_cairo_toy_font_face_keys_equal (const void *key_a, + const void *key_b) +{ + const cairo_toy_font_face_t *face_a = key_a; + const cairo_toy_font_face_t *face_b = key_b; + + return (strcmp (face_a->family, face_b->family) == 0 && + face_a->slant == face_b->slant && + face_a->weight == face_b->weight); +} + +/** + * cairo_toy_font_face_create: + * @family: a font family name, encoded in UTF-8 + * @slant: the slant for the font + * @weight: the weight for the font + * + * Creates a font face from a triplet of family, slant, and weight. + * These font faces are used in implementation of the the #cairo_t "toy" + * font API. + * + * If @family is the zero-length string "", the platform-specific default + * family is assumed. The default family then can be queried using + * cairo_toy_font_face_get_family(). + * + * The cairo_select_font_face() function uses this to create font faces. + * See that function for limitations and other details of toy font faces. + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.8 + **/ +cairo_font_face_t * +cairo_toy_font_face_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) +{ + cairo_status_t status; + cairo_toy_font_face_t key, *font_face; + cairo_hash_table_t *hash_table; + + if (family == NULL) + return (cairo_font_face_t*) &_cairo_font_face_null_pointer; + + /* Make sure we've got valid UTF-8 for the family */ + status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL); + if (unlikely (status)) { + if (status == CAIRO_STATUS_INVALID_STRING) + return (cairo_font_face_t*) &_cairo_font_face_invalid_string; + + return (cairo_font_face_t*) &_cairo_font_face_nil; + } + + switch (slant) { + case CAIRO_FONT_SLANT_NORMAL: + case CAIRO_FONT_SLANT_ITALIC: + case CAIRO_FONT_SLANT_OBLIQUE: + break; + default: + return (cairo_font_face_t*) &_cairo_font_face_invalid_slant; + } + + switch (weight) { + case CAIRO_FONT_WEIGHT_NORMAL: + case CAIRO_FONT_WEIGHT_BOLD: + break; + default: + return (cairo_font_face_t*) &_cairo_font_face_invalid_weight; + } + + if (*family == '\0') + family = CAIRO_FONT_FAMILY_DEFAULT; + + hash_table = _cairo_toy_font_face_hash_table_lock (); + if (unlikely (hash_table == NULL)) + goto UNWIND; + + _cairo_toy_font_face_init_key (&key, family, slant, weight); + + /* Return existing font_face if it exists in the hash table. */ + font_face = _cairo_hash_table_lookup (hash_table, + &key.base.hash_entry); + if (font_face != NULL) { + if (font_face->base.status == CAIRO_STATUS_SUCCESS) { + 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); + } + + /* Otherwise create it and insert into hash table. */ + 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; + } + + status = _cairo_toy_font_face_init (font_face, family, slant, weight); + if (unlikely (status)) + goto UNWIND_FONT_FACE_MALLOC; + + assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); + status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry); + if (unlikely (status)) + goto UNWIND_FONT_FACE_INIT; + + _cairo_toy_font_face_hash_table_unlock (); + + return &font_face->base; + + UNWIND_FONT_FACE_INIT: + _cairo_toy_font_face_fini (font_face); + UNWIND_FONT_FACE_MALLOC: + free (font_face); + UNWIND_HASH_TABLE_LOCK: + _cairo_toy_font_face_hash_table_unlock (); + UNWIND: + return (cairo_font_face_t*) &_cairo_font_face_nil; +} +slim_hidden_def (cairo_toy_font_face_create); + +static 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; + + 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_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 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_toy_font_face_hash_table_unlock (); + + _cairo_toy_font_face_fini (font_face); + return TRUE; +} + +static cairo_status_t +_cairo_toy_font_face_scaled_font_create (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font) +{ + cairo_toy_font_face_t *font_face = (cairo_toy_font_face_t *) abstract_font_face; + + ASSERT_NOT_REACHED; + + return _cairo_font_face_set_error (&font_face->base, CAIRO_STATUS_FONT_TYPE_MISMATCH); +} + +static cairo_font_face_t * +_cairo_toy_font_face_get_implementation (void *abstract_font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options) +{ + cairo_toy_font_face_t *font_face = abstract_font_face; + + if (font_face->impl_face) { + cairo_font_face_t *impl = font_face->impl_face; + + if (impl->backend->get_implementation != NULL) { + return impl->backend->get_implementation (impl, + font_matrix, + ctm, + options); + } + + return cairo_font_face_reference (impl); + } + + return abstract_font_face; +} + +static cairo_bool_t +_cairo_font_face_is_toy (cairo_font_face_t *font_face) +{ + return font_face->backend == &_cairo_toy_font_face_backend; +} + +/** + * cairo_toy_font_face_get_family: + * @font_face: A toy font face + * + * Gets the 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). + * + * Since: 1.8 + **/ +const char * +cairo_toy_font_face_get_family (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_FAMILY_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_FAMILY_DEFAULT; + } + assert (toy_font_face->owns_family); + return toy_font_face->family; +} + +/** + * cairo_toy_font_face_get_slant: + * @font_face: A toy font face + * + * Gets the slant a toy font. + * + * Return value: The slant value + * + * Since: 1.8 + **/ +cairo_font_slant_t +cairo_toy_font_face_get_slant (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_SLANT_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_SLANT_DEFAULT; + } + return toy_font_face->slant; +} +slim_hidden_def (cairo_toy_font_face_get_slant); + +/** + * cairo_toy_font_face_get_weight: + * @font_face: A toy font face + * + * Gets the weight a toy font. + * + * Return value: The weight value + * + * Since: 1.8 + **/ +cairo_font_weight_t +cairo_toy_font_face_get_weight (cairo_font_face_t *font_face) +{ + cairo_toy_font_face_t *toy_font_face; + + if (font_face->status) + return CAIRO_FONT_WEIGHT_DEFAULT; + + toy_font_face = (cairo_toy_font_face_t *) font_face; + if (! _cairo_font_face_is_toy (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return CAIRO_FONT_WEIGHT_DEFAULT; + } + return toy_font_face->weight; +} +slim_hidden_def (cairo_toy_font_face_get_weight); + +static const cairo_font_face_backend_t _cairo_toy_font_face_backend = { + CAIRO_FONT_TYPE_TOY, + NULL, /* create_for_toy */ + _cairo_toy_font_face_destroy, + _cairo_toy_font_face_scaled_font_create, + _cairo_toy_font_face_get_implementation +}; + +void +_cairo_toy_font_face_reset_static_data (void) +{ + cairo_hash_table_t *hash_table; + + /* We manually acquire the lock rather than calling + * cairo_toy_font_face_hash_table_lock simply to avoid + * creating the table only to destroy it again. */ + CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex); + hash_table = cairo_toy_font_face_hash_table; + cairo_toy_font_face_hash_table = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex); + + if (hash_table != NULL) + _cairo_hash_table_destroy (hash_table); +} 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 0000000000..3414fc2684 --- /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 0000000000..dcaf40d186 --- /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 new file mode 100644 index 0000000000..b59b96d439 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-traps.c @@ -0,0 +1,1123 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* + * Copyright © 2002 Keith Packard + * Copyright © 2007 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 Keith Packard + * + * Contributor(s): + * Keith R. Packard + * Carl D. Worth + * + * 2002-07-15: Converted from XRenderCompositeDoublePoly to #cairo_trap_t. Carl D. Worth + */ + +#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 */ + +void +_cairo_traps_init (cairo_traps_t *traps) +{ + VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t))); + + traps->status = CAIRO_STATUS_SUCCESS; + + traps->maybe_region = 1; + traps->is_rectilinear = 0; + traps->is_rectangular = 0; + + traps->num_traps = 0; + + traps->traps_size = ARRAY_LENGTH (traps->traps_embedded); + traps->traps = traps->traps_embedded; + + traps->num_limits = 0; + traps->has_intersections = FALSE; +} + +void +_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 +_cairo_traps_clear (cairo_traps_t *traps) +{ + traps->status = CAIRO_STATUS_SUCCESS; + + traps->maybe_region = 1; + traps->is_rectilinear = 0; + traps->is_rectangular = 0; + + traps->num_traps = 0; + traps->has_intersections = FALSE; +} + +void +_cairo_traps_fini (cairo_traps_t *traps) +{ + if (traps->traps != traps->traps_embedded) + free (traps->traps); + + VG (VALGRIND_MAKE_MEM_UNDEFINED (traps, sizeof (cairo_traps_t))); +} + +/* make room for at least one more trap */ +static cairo_bool_t +_cairo_traps_grow (cairo_traps_t *traps) +{ + cairo_trapezoid_t *new_traps; + int new_size = 4 * traps->traps_size; + + if (CAIRO_INJECT_FAULT ()) { + traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + if (traps->traps == traps->traps_embedded) { + new_traps = _cairo_malloc_ab (new_size, sizeof (cairo_trapezoid_t)); + if (new_traps != NULL) + memcpy (new_traps, traps->traps, sizeof (traps->traps_embedded)); + } else { + new_traps = _cairo_realloc_ab (traps->traps, + new_size, sizeof (cairo_trapezoid_t)); + } + + if (unlikely (new_traps == NULL)) { + traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + + traps->traps = new_traps; + traps->traps_size = new_size; + return TRUE; +} + +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_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; + } + + trap = &traps->traps[traps->num_traps++]; + trap->top = top; + trap->bottom = bottom; + trap->left = *left; + 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_boxes: + * @traps: a #cairo_traps_t + * @box: an array box that will each be converted to a single trapezoid + * to store in @traps. + * + * Initializes a #cairo_traps_t to contain an array of rectangular + * trapezoids. + **/ +cairo_status_t +_cairo_traps_init_boxes (cairo_traps_t *traps, + const cairo_boxes_t *boxes) +{ + cairo_trapezoid_t *trap; + const struct _cairo_boxes_chunk *chunk; + + _cairo_traps_init (traps); + + while (traps->traps_size < boxes->num_boxes) { + if (unlikely (! _cairo_traps_grow (traps))) { + _cairo_traps_fini (traps); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + } + + traps->num_traps = boxes->num_boxes; + traps->is_rectilinear = TRUE; + traps->is_rectangular = TRUE; + traps->maybe_region = boxes->is_pixel_aligned; + + trap = &traps->traps[0]; + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + const cairo_box_t *box; + int i; + + box = chunk->base; + for (i = 0; i < chunk->count; i++) { + trap->top = box->p1.y; + trap->bottom = box->p2.y; + + trap->left.p1 = box->p1; + trap->left.p2.x = box->p1.x; + trap->left.p2.y = box->p2.y; + + trap->right.p1.x = box->p2.x; + trap->right.p1.y = box->p1.y; + trap->right.p2 = box->p2; + + box++, trap++; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +cairo_status_t +_cairo_traps_tessellate_rectangle (cairo_traps_t *traps, + const cairo_point_t *top_left, + const cairo_point_t *bottom_right) +{ + cairo_line_t left; + cairo_line_t right; + cairo_fixed_t top, bottom; + + if (top_left->y == bottom_right->y) + return CAIRO_STATUS_SUCCESS; + + if (top_left->x == bottom_right->x) + return CAIRO_STATUS_SUCCESS; + + left.p1.x = left.p2.x = top_left->x; + left.p1.y = right.p1.y = top_left->y; + right.p1.x = right.p2.x = bottom_right->x; + left.p2.y = right.p2.y = bottom_right->y; + + top = top_left->y; + bottom = bottom_right->y; + + if (traps->num_limits) { + 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) { + right.p1.x = right.p2.x = top_left->x; + 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; + cairo_fixed_t _top, _bottom; + + if (top >= limits->p2.y) + continue; + if (bottom <= limits->p1.y) + continue; + + /* Trivially reject if trapezoid is entirely to the right or + * to the left of the limits. */ + if (left.p1.x >= limits->p2.x) + continue; + if (right.p1.x <= limits->p1.x) + continue; + + /* Otherwise, clip the trapezoid to the limits. */ + _top = top; + if (_top < limits->p1.y) + _top = limits->p1.y; + + _bottom = bottom; + if (_bottom > limits->p2.y) + _bottom = limits->p2.y; + + if (_bottom <= _top) + continue; + + _left = left; + if (_left.p1.x < limits->p1.x) { + _left.p1.x = limits->p1.x; + _left.p1.y = limits->p1.y; + _left.p2.x = limits->p1.x; + _left.p2.y = limits->p2.y; + } + + _right = right; + if (_right.p1.x > limits->p2.x) { + _right.p1.x = limits->p2.x; + _right.p1.y = limits->p1.y; + _right.p2.x = limits->p2.x; + _right.p2.y = limits->p2.y; + } + + if (left.p1.x >= right.p1.x) + continue; + + if (reversed) + _cairo_traps_add_trap (traps, _top, _bottom, &_right, &_left); + else + _cairo_traps_add_trap (traps, _top, _bottom, &_left, &_right); + } + } else { + _cairo_traps_add_trap (traps, top, bottom, &left, &right); + } + + return traps->status; +} + +void +_cairo_traps_translate (cairo_traps_t *traps, int x, int y) +{ + cairo_fixed_t xoff, yoff; + cairo_trapezoid_t *t; + int i; + + /* Ugh. The cairo_composite/(Render) interface doesn't allow + an offset for the trapezoids. Need to manually shift all + the coordinates to align with the offset origin of the + intermediate surface. */ + + xoff = _cairo_fixed_from_int (x); + yoff = _cairo_fixed_from_int (y); + + for (i = 0, t = traps->traps; i < traps->num_traps; i++, t++) { + t->top += yoff; + t->bottom += yoff; + t->left.p1.x += xoff; + t->left.p1.y += yoff; + t->left.p2.x += xoff; + t->left.p2.y += yoff; + t->right.p1.x += xoff; + t->right.p1.y += yoff; + t->right.p2.x += xoff; + t->right.p2.y += yoff; + } +} + +void +_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, + cairo_trapezoid_t *src_traps, + int num_traps, + double tx, double ty, + double sx, double sy) +{ + int i; + cairo_fixed_t xoff = _cairo_fixed_from_double (tx); + cairo_fixed_t yoff = _cairo_fixed_from_double (ty); + + if (sx == 1.0 && sy == 1.0) { + for (i = 0; i < num_traps; i++) { + offset_traps[i].top = src_traps[i].top + yoff; + offset_traps[i].bottom = src_traps[i].bottom + yoff; + offset_traps[i].left.p1.x = src_traps[i].left.p1.x + xoff; + offset_traps[i].left.p1.y = src_traps[i].left.p1.y + yoff; + offset_traps[i].left.p2.x = src_traps[i].left.p2.x + xoff; + offset_traps[i].left.p2.y = src_traps[i].left.p2.y + yoff; + offset_traps[i].right.p1.x = src_traps[i].right.p1.x + xoff; + offset_traps[i].right.p1.y = src_traps[i].right.p1.y + yoff; + offset_traps[i].right.p2.x = src_traps[i].right.p2.x + xoff; + offset_traps[i].right.p2.y = src_traps[i].right.p2.y + yoff; + } + } else { + cairo_fixed_t xsc = _cairo_fixed_from_double (sx); + cairo_fixed_t ysc = _cairo_fixed_from_double (sy); + + for (i = 0; i < num_traps; i++) { + offset_traps[i].top = _cairo_fixed_mul (src_traps[i].top + yoff, ysc); + offset_traps[i].bottom = _cairo_fixed_mul (src_traps[i].bottom + yoff, ysc); + offset_traps[i].left.p1.x = _cairo_fixed_mul (src_traps[i].left.p1.x + xoff, xsc); + offset_traps[i].left.p1.y = _cairo_fixed_mul (src_traps[i].left.p1.y + yoff, ysc); + offset_traps[i].left.p2.x = _cairo_fixed_mul (src_traps[i].left.p2.x + xoff, xsc); + offset_traps[i].left.p2.y = _cairo_fixed_mul (src_traps[i].left.p2.y + yoff, ysc); + offset_traps[i].right.p1.x = _cairo_fixed_mul (src_traps[i].right.p1.x + xoff, xsc); + offset_traps[i].right.p1.y = _cairo_fixed_mul (src_traps[i].right.p1.y + yoff, ysc); + offset_traps[i].right.p2.x = _cairo_fixed_mul (src_traps[i].right.p2.x + xoff, xsc); + offset_traps[i].right.p2.y = _cairo_fixed_mul (src_traps[i].right.p2.y + yoff, ysc); + } + } +} + +static cairo_bool_t +_cairo_trap_contains (cairo_trapezoid_t *t, cairo_point_t *pt) +{ + cairo_slope_t slope_left, slope_pt, slope_right; + + if (t->top > pt->y) + return FALSE; + if (t->bottom < pt->y) + return FALSE; + + _cairo_slope_init (&slope_left, &t->left.p1, &t->left.p2); + _cairo_slope_init (&slope_pt, &t->left.p1, pt); + + if (_cairo_slope_compare (&slope_left, &slope_pt) < 0) + return FALSE; + + _cairo_slope_init (&slope_right, &t->right.p1, &t->right.p2); + _cairo_slope_init (&slope_pt, &t->right.p1, pt); + + if (_cairo_slope_compare (&slope_pt, &slope_right) < 0) + return FALSE; + + return TRUE; +} + +cairo_bool_t +_cairo_traps_contain (const cairo_traps_t *traps, + double x, double y) +{ + int i; + cairo_point_t point; + + point.x = _cairo_fixed_from_double (x); + point.y = _cairo_fixed_from_double (y); + + for (i = 0; i < traps->num_traps; i++) { + if (_cairo_trap_contains (&traps->traps[i], &point)) + return TRUE; + } + + return FALSE; +} + +static cairo_fixed_t +_line_compute_intersection_x_for_y (const cairo_line_t *line, + cairo_fixed_t y) +{ + return _cairo_edge_compute_intersection_x_for_y (&line->p1, &line->p2, y); +} + +void +_cairo_traps_extents (const cairo_traps_t *traps, + cairo_box_t *extents) +{ + int i; + + if (traps->num_traps == 0) { + extents->p1.x = extents->p1.y = 0; + extents->p2.x = extents->p2.y = 0; + return; + } + + extents->p1.x = extents->p1.y = INT32_MAX; + extents->p2.x = extents->p2.y = INT32_MIN; + + for (i = 0; i < traps->num_traps; i++) { + const cairo_trapezoid_t *trap = &traps->traps[i]; + + if (trap->top < extents->p1.y) + extents->p1.y = trap->top; + if (trap->bottom > extents->p2.y) + extents->p2.y = trap->bottom; + + if (trap->left.p1.x < extents->p1.x) { + cairo_fixed_t x = trap->left.p1.x; + if (trap->top != trap->left.p1.y) { + x = _line_compute_intersection_x_for_y (&trap->left, + trap->top); + if (x < extents->p1.x) + extents->p1.x = x; + } else + extents->p1.x = x; + } + if (trap->left.p2.x < extents->p1.x) { + cairo_fixed_t x = trap->left.p2.x; + if (trap->bottom != trap->left.p2.y) { + x = _line_compute_intersection_x_for_y (&trap->left, + trap->bottom); + if (x < extents->p1.x) + extents->p1.x = x; + } else + extents->p1.x = x; + } + + if (trap->right.p1.x > extents->p2.x) { + cairo_fixed_t x = trap->right.p1.x; + if (trap->top != trap->right.p1.y) { + x = _line_compute_intersection_x_for_y (&trap->right, + trap->top); + if (x > extents->p2.x) + extents->p2.x = x; + } else + extents->p2.x = x; + } + if (trap->right.p2.x > extents->p2.x) { + cairo_fixed_t x = trap->right.p2.x; + if (trap->bottom != trap->right.p2.y) { + x = _line_compute_intersection_x_for_y (&trap->right, + trap->bottom); + if (x > extents->p2.x) + extents->p2.x = x; + } else + extents->p2.x = x; + } + } +} + +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: + * @traps: a #cairo_traps_t + * @region: a #cairo_region_t + * + * Determines if a set of trapezoids are exactly representable as a + * cairo region. If so, the passed-in region is initialized to + * the area representing the given traps. It should be finalized + * with cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED + * is returned. + * + * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED + * or %CAIRO_STATUS_NO_MEMORY + **/ +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)]; + cairo_rectangle_int_t *rects = stack_rects; + cairo_int_status_t status; + int i, rect_count; + + /* we only treat this a hint... */ + if (antialias != CAIRO_ANTIALIAS_NONE && ! traps->maybe_region) + 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)) { + rects = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_rectangle_int_t)); + + if (unlikely (rects == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + rect_count = 0; + for (i = 0; i < traps->num_traps; i++) { + int x1, y1, x2, y2; + + 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); + } + + 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; + + if (rects != stack_rects) + free (rects); + + 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) +{ + 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); +} + +cairo_private cairo_status_t +_cairo_traps_path (const cairo_traps_t *traps, + cairo_path_fixed_t *path) +{ + int i; + + for (i = 0; i < traps->num_traps; i++) { + cairo_status_t status; + cairo_trapezoid_t trap = traps->traps[i]; + + if (trap.top == trap.bottom) + continue; + + _sanitize_trap (&trap); + + status = _cairo_path_fixed_move_to (path, trap.left.p1.x, trap.top); + if (unlikely (status)) return status; + status = _cairo_path_fixed_line_to (path, trap.right.p1.x, trap.top); + if (unlikely (status)) return status; + status = _cairo_path_fixed_line_to (path, trap.right.p2.x, trap.bottom); + if (unlikely (status)) return status; + status = _cairo_path_fixed_line_to (path, trap.left.p2.x, trap.bottom); + if (unlikely (status)) return status; + status = _cairo_path_fixed_close_path (path); + if (unlikely (status)) return status; + } + + 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 0000000000..ccd28799e8 --- /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 0000000000..bcf3b23716 --- /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 new file mode 100644 index 0000000000..d97cf91620 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-truetype-subset-private.h @@ -0,0 +1,212 @@ +/* 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 + * Adrian Johnson + */ + +#ifndef CAIRO_TRUETYPE_SUBSET_PRIVATE_H +#define CAIRO_TRUETYPE_SUBSET_PRIVATE_H + +#include "cairoint.h" + +#if CAIRO_HAS_FONT_SUBSET + +/* The structs defined here should strictly follow the TrueType + * specification and not be padded. We use only 16-bit integer + * in their definition to guarantee that. The fields of type + * "FIXED" in the TT spec are broken into two *_1 and *_2 16-bit + * parts, and 64-bit members are broken into four. + * + * The test truetype-tables in the test suite makes sure that + * these tables have the right size. Please update that test + * if you add new tables/structs that should be packed. + */ + +#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',' ') +#define TT_TAG_fpgm MAKE_TT_TAG('f','p','g','m') +#define TT_TAG_glyf MAKE_TT_TAG('g','l','y','f') +#define TT_TAG_head MAKE_TT_TAG('h','e','a','d') +#define TT_TAG_hhea MAKE_TT_TAG('h','h','e','a') +#define TT_TAG_hmtx MAKE_TT_TAG('h','m','t','x') +#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') + +/* All tt_* structs are big-endian */ +typedef struct _tt_cmap_index { + uint16_t platform; + uint16_t encoding; + uint32_t offset; +} tt_cmap_index_t; + +typedef struct _tt_cmap { + uint16_t version; + uint16_t num_tables; + tt_cmap_index_t index[1]; +} tt_cmap_t; + +typedef struct _segment_map { + uint16_t format; + uint16_t length; + uint16_t version; + uint16_t segCountX2; + uint16_t searchRange; + uint16_t entrySelector; + uint16_t rangeShift; + uint16_t endCount[1]; +} tt_segment_map_t; + +typedef struct _tt_head { + int16_t version_1; + int16_t version_2; + int16_t revision_1; + int16_t revision_2; + uint16_t checksum_1; + uint16_t checksum_2; + uint16_t magic_1; + uint16_t magic_2; + uint16_t flags; + uint16_t units_per_em; + int16_t created_1; + int16_t created_2; + int16_t created_3; + int16_t created_4; + int16_t modified_1; + int16_t modified_2; + int16_t modified_3; + int16_t modified_4; + int16_t x_min; /* FWORD */ + int16_t y_min; /* FWORD */ + int16_t x_max; /* FWORD */ + int16_t y_max; /* FWORD */ + uint16_t mac_style; + uint16_t lowest_rec_pppem; + int16_t font_direction_hint; + int16_t index_to_loc_format; + int16_t glyph_data_format; +} tt_head_t; + +typedef struct _tt_hhea { + int16_t version_1; + int16_t version_2; + int16_t ascender; /* FWORD */ + int16_t descender; /* FWORD */ + int16_t line_gap; /* FWORD */ + uint16_t advance_max_width; /* UFWORD */ + int16_t min_left_side_bearing; /* FWORD */ + int16_t min_right_side_bearing; /* FWORD */ + int16_t x_max_extent; /* FWORD */ + int16_t caret_slope_rise; + int16_t caret_slope_run; + int16_t reserved[5]; + int16_t metric_data_format; + uint16_t num_hmetrics; +} tt_hhea_t; + +typedef struct _tt_maxp { + int16_t version_1; + int16_t version_2; + uint16_t num_glyphs; + uint16_t max_points; + uint16_t max_contours; + uint16_t max_composite_points; + uint16_t max_composite_contours; + uint16_t max_zones; + uint16_t max_twilight_points; + uint16_t max_storage; + uint16_t max_function_defs; + uint16_t max_instruction_defs; + uint16_t max_stack_elements; + uint16_t max_size_of_instructions; + uint16_t max_component_elements; + uint16_t max_component_depth; +} tt_maxp_t; + +typedef struct _tt_name_record { + uint16_t platform; + uint16_t encoding; + uint16_t language; + uint16_t name; + uint16_t length; + uint16_t offset; +} tt_name_record_t; + +typedef struct _tt_name { + uint16_t format; + uint16_t num_records; + uint16_t strings_offset; + tt_name_record_t records[1]; +} 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 +#define TT_WE_HAVE_A_SCALE 0x0008 +#define TT_MORE_COMPONENTS 0x0020 +#define TT_WE_HAVE_AN_X_AND_Y_SCALE 0x0040 +#define TT_WE_HAVE_A_TWO_BY_TWO 0x0080 + +typedef struct _tt_composite_glyph { + uint16_t flags; + uint16_t index; + uint16_t args[6]; /* 1 to 6 arguments depending on value of flags */ +} tt_composite_glyph_t; + +typedef struct _tt_glyph_data { + int16_t num_contours; + int8_t data[8]; + tt_composite_glyph_t glyph; +} tt_glyph_data_t; + +#endif /* CAIRO_HAS_FONT_SUBSET */ + +#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 new file mode 100644 index 0000000000..f5f06defc9 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-truetype-subset.c @@ -0,0 +1,1681 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 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 + */ + +/* + * Useful links: + * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html + * http://www.microsoft.com/typography/specs/default.htm + */ + +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" + +#include "cairo-array-private.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-truetype-subset-private.h" + + +typedef struct subset_glyph subset_glyph_t; +struct subset_glyph { + int parent_index; + unsigned long location; +}; + +typedef struct _cairo_truetype_font cairo_truetype_font_t; + +typedef struct table table_t; +struct table { + unsigned long tag; + cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag); + int pos; /* position in the font directory */ +}; + +struct _cairo_truetype_font { + + cairo_scaled_font_subset_t *scaled_font_subset; + + table_t truetype_tables[10]; + int num_tables; + + struct { + char *font_name; + char *ps_name; + 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; /* array size: num_glyphs_in_face + 2 */ + const cairo_scaled_font_backend_t *backend; + 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; /* array size: num_glyphs_in_face + 1 */ + cairo_status_t status; + cairo_bool_t is_pdf; +}; + +/* + * Test that the structs we define for TrueType tables have the + * correct size, ie. they are not padded. + */ +#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S)) +check (tt_head_t, 54); +check (tt_hhea_t, 36); +check (tt_maxp_t, 32); +check (tt_name_record_t, 12); +check (tt_name_t, 18); +check (tt_composite_glyph_t, 16); +check (tt_glyph_data_t, 26); +#undef check + +static cairo_status_t +cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, + unsigned short glyph, + unsigned short *out); + +#define SFNT_VERSION 0x00010000 +#define SFNT_STRING_MAX_LENGTH 65535 + +static cairo_status_t +_cairo_truetype_font_set_error (cairo_truetype_font_t *font, + cairo_status_t status) +{ + if (status == CAIRO_STATUS_SUCCESS || + status == (int)CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + _cairo_status_set_error (&font->status, status); + + return _cairo_error (status); +} + +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; + tt_hhea_t hhea; + tt_maxp_t maxp; + unsigned long size; + + backend = scaled_font_subset->scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* FIXME: We should either support subsetting vertical fonts, or fail on + * vertical. Currently font_options_t doesn't have vertical flag, but + * it should be added in the future. For now, the freetype backend + * returns UNSUPPORTED in load_truetype_table if the font is vertical. + * + * if (cairo_font_options_get_vertical_layout (scaled_font_subset->scaled_font)) + * 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, + (unsigned char *) &head, + &size); + if (unlikely (status)) + return status; + + size = sizeof (tt_maxp_t); + status = backend->load_truetype_table (scaled_font_subset->scaled_font, + TT_TAG_maxp, 0, + (unsigned char *) &maxp, + &size); + if (unlikely (status)) + return status; + + size = sizeof (tt_hhea_t); + status = backend->load_truetype_table (scaled_font_subset->scaled_font, + TT_TAG_hhea, 0, + (unsigned char *) &hhea, + &size); + if (unlikely (status)) + return status; + + font = _cairo_malloc (sizeof (cairo_truetype_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->backend = backend; + font->base.num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs); + font->scaled_font_subset = scaled_font_subset; + + font->last_offset = 0; + font->last_boundary = 0; + _cairo_array_init (&font->output, sizeof (char)); + status = _cairo_array_grow_by (&font->output, 4096); + if (unlikely (status)) + goto fail1; + + /* 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; + } + + /* 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->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); + font->base.y_max = (int16_t) be16_to_cpu (head.y_max); + font->base.ascent = (int16_t) be16_to_cpu (hhea.ascender); + font->base.descent = (int16_t) be16_to_cpu (hhea.descender); + font->base.units_per_em = (int16_t) be16_to_cpu (head.units_per_em); + if (font->base.units_per_em == 0) + font->base.units_per_em = 2048; + + font->base.ps_name = NULL; + font->base.font_name = NULL; + status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font, + &font->base.ps_name, + &font->base.font_name); + if (_cairo_status_is_error (status)) + goto fail3; + + /* If the PS name is not found, create a CairoFont-x-y name. */ + if (font->base.ps_name == NULL) { + font->base.ps_name = _cairo_malloc (30); + if (unlikely (font->base.ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + + snprintf(font->base.ps_name, 30, "CairoFont-%u-%u", + scaled_font_subset->font_id, + scaled_font_subset->subset_id); + } + + /* 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; + } + + _cairo_array_init (&font->string_offsets, sizeof (unsigned long)); + status = _cairo_array_grow_by (&font->string_offsets, 10); + if (unlikely (status)) + goto fail5; + + font->status = CAIRO_STATUS_SUCCESS; + + *font_return = font; + + return CAIRO_STATUS_SUCCESS; + + fail5: + _cairo_array_fini (&font->string_offsets); + free (font->widths); + fail4: + free (font->base.ps_name); + fail3: + free (font->parent_to_subset); + free (font->base.font_name); + fail2: + free (font->glyphs); + fail1: + _cairo_array_fini (&font->output); + free (font); + + return status; +} + +static void +cairo_truetype_font_destroy (cairo_truetype_font_t *font) +{ + _cairo_array_fini (&font->string_offsets); + free (font->widths); + free (font->base.ps_name); + free (font->base.font_name); + free (font->parent_to_subset); + free (font->glyphs); + _cairo_array_fini (&font->output); + free (font); +} + +static cairo_status_t +cairo_truetype_font_allocate_write_buffer (cairo_truetype_font_t *font, + size_t length, + unsigned char **buffer) +{ + cairo_status_t status; + + if (font->status) + return font->status; + + status = _cairo_array_allocate (&font->output, length, (void **) buffer); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + return CAIRO_STATUS_SUCCESS; +} + +static void +cairo_truetype_font_write (cairo_truetype_font_t *font, + const void *data, + size_t length) +{ + cairo_status_t status; + + if (font->status) + return; + + status = _cairo_array_append_multiple (&font->output, data, length); + if (unlikely (status)) + status = _cairo_truetype_font_set_error (font, status); +} + +static void +cairo_truetype_font_write_be16 (cairo_truetype_font_t *font, + uint16_t value) +{ + uint16_t be16_value; + + if (font->status) + return; + + be16_value = cpu_to_be16 (value); + cairo_truetype_font_write (font, &be16_value, sizeof be16_value); +} + +static void +cairo_truetype_font_write_be32 (cairo_truetype_font_t *font, + uint32_t value) +{ + uint32_t be32_value; + + if (font->status) + return; + + be32_value = cpu_to_be32 (value); + cairo_truetype_font_write (font, &be32_value, sizeof be32_value); +} + +static cairo_status_t +cairo_truetype_font_align_output (cairo_truetype_font_t *font, + unsigned long *aligned) +{ + int length, pad; + unsigned char *padding; + + length = _cairo_array_num_elements (&font->output); + *aligned = (length + 3) & ~3; + pad = *aligned - length; + + if (pad) { + cairo_status_t status; + + status = cairo_truetype_font_allocate_write_buffer (font, pad, + &padding); + if (unlikely (status)) + return status; + + memset (padding, 0, pad); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_check_boundary (cairo_truetype_font_t *font, + unsigned long boundary) +{ + cairo_status_t status; + + if (font->status) + return font->status; + + if (boundary - font->last_offset > SFNT_STRING_MAX_LENGTH) + { + status = _cairo_array_append (&font->string_offsets, + &font->last_boundary); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + font->last_offset = font->last_boundary; + } + font->last_boundary = boundary; + + 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) +{ + 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, 1); /* Num tables */ + + cairo_truetype_font_write_be16 (font, 3); /* Platform */ + cairo_truetype_font_write_be16 (font, 1); /* Encoding */ + cairo_truetype_font_write_be32 (font, 12); /* Offset to start of 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, length); /* Length */ + cairo_truetype_font_write_be16 (font, 0); /* Version */ + 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 */ + + 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[] */ + + 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; +} + +static cairo_status_t +cairo_truetype_font_write_generic_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + cairo_status_t status; + unsigned char *buffer; + unsigned long size; + + if (font->status) + return font->status; + + size = 0; + status = font->backend->load_truetype_table(font->scaled_font_subset->scaled_font, + tag, 0, NULL, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, buffer, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font, + unsigned char *buffer, + unsigned long size) +{ + tt_glyph_data_t *glyph_data; + tt_composite_glyph_t *composite_glyph; + int num_args; + int has_more_components; + unsigned short flags; + unsigned short index; + cairo_status_t status; + unsigned char *end = buffer + size; + + if (font->status) + return font->status; + + glyph_data = (tt_glyph_data_t *) buffer; + if ((unsigned char *)(&glyph_data->data) >= end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if ((int16_t)be16_to_cpu (glyph_data->num_contours) >= 0) + return CAIRO_STATUS_SUCCESS; + + composite_glyph = &glyph_data->glyph; + do { + if ((unsigned char *)(&composite_glyph->args[1]) > end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + flags = be16_to_cpu (composite_glyph->flags); + has_more_components = flags & TT_MORE_COMPONENTS; + status = cairo_truetype_font_use_glyph (font, be16_to_cpu (composite_glyph->index), &index); + if (unlikely (status)) + return status; + + composite_glyph->index = cpu_to_be16 (index); + num_args = 1; + if (flags & TT_ARG_1_AND_2_ARE_WORDS) + num_args += 1; + + if (flags & TT_WE_HAVE_A_SCALE) + num_args += 1; + else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE) + num_args += 2; + else if (flags & TT_WE_HAVE_A_TWO_BY_TWO) + num_args += 4; + + composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); + } while (has_more_components); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_glyf_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned long start_offset, index, size, next; + tt_head_t header; + unsigned long begin, end; + unsigned char *buffer; + unsigned int i; + union { + unsigned char *bytes; + uint16_t *short_offsets; + uint32_t *long_offsets; + } u; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof (tt_head_t); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_head, 0, + (unsigned char*) &header, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + if (be16_to_cpu (header.index_to_loc_format) == 0) + size = sizeof (int16_t) * (font->base.num_glyphs_in_face + 1); + else + size = sizeof (int32_t) * (font->base.num_glyphs_in_face + 1); + + 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)) { + free (u.bytes); + return _cairo_truetype_font_set_error (font, status); + } + + start_offset = _cairo_array_num_elements (&font->output); + 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; + end = be16_to_cpu (u.short_offsets[index + 1]) * 2; + } + else { + begin = be32_to_cpu (u.long_offsets[index]); + end = be32_to_cpu (u.long_offsets[index + 1]); + } + + /* quick sanity check... */ + if (end < begin) { + status = CAIRO_INT_STATUS_UNSUPPORTED; + goto FAIL; + } + + size = end - begin; + status = cairo_truetype_font_align_output (font, &next); + if (unlikely (status)) + goto FAIL; + + status = cairo_truetype_font_check_boundary (font, next); + if (unlikely (status)) + goto FAIL; + + font->glyphs[i].location = next - start_offset; + + status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); + if (unlikely (status)) + goto FAIL; + + 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; + + 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); + if (unlikely (status)) + goto FAIL; + + font->glyphs[i].location = next - start_offset; + + status = font->status; +FAIL: + free (u.bytes); + + return _cairo_truetype_font_set_error (font, status); +} + +static cairo_status_t +cairo_truetype_font_write_head_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned char *buffer; + unsigned long size; + cairo_status_t status; + + if (font->status) + return font->status; + + size = 0; + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, NULL, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + font->checksum_index = _cairo_array_num_elements (&font->output) + 8; + status = cairo_truetype_font_allocate_write_buffer (font, size, &buffer); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, buffer, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + /* set checkSumAdjustment to 0 for table checksum calculation */ + *(uint32_t *)(buffer + 8) = 0; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_hhea_table (cairo_truetype_font_t *font, unsigned long tag) +{ + tt_hhea_t *hhea; + unsigned long size; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof (tt_hhea_t); + status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &hhea); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, (unsigned char *) hhea, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + hhea->num_hmetrics = cpu_to_be16 ((uint16_t)(font->num_glyphs)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_hmtx_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned long size; + unsigned long long_entry_size; + unsigned long short_entry_size; + short *p; + unsigned int i; + tt_hhea_t hhea; + int num_hmetrics; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof (tt_hhea_t); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hhea, 0, + (unsigned char*) &hhea, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + num_hmetrics = be16_to_cpu(hhea.num_hmetrics); + + 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, + long_entry_size, + (unsigned char **) &p); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + if (font->glyphs[i].parent_index < num_hmetrics) { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + font->glyphs[i].parent_index * long_entry_size, + (unsigned char *) p, &long_entry_size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + } + else + { + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + (num_hmetrics - 1) * long_entry_size, + (unsigned char *) p, &short_entry_size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_hmtx, + num_hmetrics * long_entry_size + + (font->glyphs[i].parent_index - num_hmetrics) * short_entry_size, + (unsigned char *) (p + 1), &short_entry_size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + } + font->widths[i] = be16_to_cpu (p[0]); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_loca_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + unsigned int i; + tt_head_t header; + unsigned long size; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof(tt_head_t); + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_head, 0, + (unsigned char*) &header, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + if (be16_to_cpu (header.index_to_loc_format) == 0) + { + 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->num_glyphs + 1; i++) + cairo_truetype_font_write_be32 (font, font->glyphs[i].location); + } + + return font->status; +} + +static cairo_status_t +cairo_truetype_font_write_maxp_table (cairo_truetype_font_t *font, + unsigned long tag) +{ + tt_maxp_t *maxp; + unsigned long size; + cairo_status_t status; + + if (font->status) + return font->status; + + size = sizeof (tt_maxp_t); + status = cairo_truetype_font_allocate_write_buffer (font, size, (unsigned char **) &maxp); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + tag, 0, (unsigned char *) maxp, &size); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + maxp->num_glyphs = cpu_to_be16 (font->num_glyphs); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_truetype_font_write_offset_table (cairo_truetype_font_t *font) +{ + cairo_status_t status; + unsigned char *table_buffer; + size_t table_buffer_length; + unsigned short search_range, entry_selector, range_shift; + + if (font->status) + return font->status; + + search_range = 1; + entry_selector = 0; + while (search_range * 2 <= font->num_tables) { + search_range *= 2; + entry_selector++; + } + search_range *= 16; + range_shift = font->num_tables * 16 - search_range; + + cairo_truetype_font_write_be32 (font, SFNT_VERSION); + cairo_truetype_font_write_be16 (font, font->num_tables); + cairo_truetype_font_write_be16 (font, search_range); + cairo_truetype_font_write_be16 (font, entry_selector); + cairo_truetype_font_write_be16 (font, range_shift); + + /* Allocate space for the table directory. Each directory entry + * will be filled in by cairo_truetype_font_update_entry() after + * the table is written. */ + table_buffer_length = font->num_tables * 16; + status = cairo_truetype_font_allocate_write_buffer (font, table_buffer_length, + &table_buffer); + if (unlikely (status)) + return _cairo_truetype_font_set_error (font, status); + + return CAIRO_STATUS_SUCCESS; +} + +static uint32_t +cairo_truetype_font_calculate_checksum (cairo_truetype_font_t *font, + unsigned long start, + unsigned long end) +{ + uint32_t *padded_end; + uint32_t *p; + uint32_t checksum; + char *data; + + checksum = 0; + data = _cairo_array_index (&font->output, 0); + p = (uint32_t *) (data + start); + padded_end = (uint32_t *) (data + ((end + 3) & ~3)); + while (p < padded_end) + checksum += be32_to_cpu(*p++); + + return checksum; +} + +static void +cairo_truetype_font_update_entry (cairo_truetype_font_t *font, + int index, + unsigned long tag, + unsigned long start, + unsigned long end) +{ + uint32_t *entry; + + entry = _cairo_array_index (&font->output, 12 + 16 * index); + entry[0] = cpu_to_be32 ((uint32_t)tag); + entry[1] = cpu_to_be32 (cairo_truetype_font_calculate_checksum (font, start, end)); + entry[2] = cpu_to_be32 ((uint32_t)start); + entry[3] = cpu_to_be32 ((uint32_t)(end - start)); +} + +static cairo_status_t +cairo_truetype_font_generate (cairo_truetype_font_t *font, + const char **data, + unsigned long *length, + const unsigned long **string_offsets, + unsigned long *num_strings) +{ + cairo_status_t status; + unsigned long start, end, next; + uint32_t checksum, *checksum_location; + int i; + + if (font->status) + return font->status; + + status = cairo_truetype_font_write_offset_table (font); + if (unlikely (status)) + goto FAIL; + + status = cairo_truetype_font_align_output (font, &start); + if (unlikely (status)) + goto FAIL; + + end = 0; + for (i = 0; i < font->num_tables; i++) { + status = font->truetype_tables[i].write (font, font->truetype_tables[i].tag); + if (unlikely (status)) + goto FAIL; + + end = _cairo_array_num_elements (&font->output); + status = cairo_truetype_font_align_output (font, &next); + if (unlikely (status)) + goto FAIL; + + cairo_truetype_font_update_entry (font, font->truetype_tables[i].pos, + font->truetype_tables[i].tag, start, end); + status = cairo_truetype_font_check_boundary (font, next); + if (unlikely (status)) + goto FAIL; + + start = next; + } + + checksum = + 0xb1b0afba - cairo_truetype_font_calculate_checksum (font, 0, end); + checksum_location = _cairo_array_index (&font->output, font->checksum_index); + *checksum_location = cpu_to_be32 (checksum); + + *data = _cairo_array_index (&font->output, 0); + *length = _cairo_array_num_elements (&font->output); + *num_strings = _cairo_array_num_elements (&font->string_offsets); + if (*num_strings != 0) + *string_offsets = _cairo_array_index (&font->string_offsets, 0); + else + *string_offsets = NULL; + + FAIL: + return _cairo_truetype_font_set_error (font, status); +} + +static cairo_status_t +cairo_truetype_font_use_glyph (cairo_truetype_font_t *font, + unsigned short glyph, + unsigned short *out) +{ + 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->num_glyphs; + font->glyphs[font->num_glyphs].parent_index = glyph; + font->num_glyphs++; + } + + *out = font->parent_to_subset[glyph]; + return CAIRO_STATUS_SUCCESS; +} + +static void +cairo_truetype_font_add_truetype_table (cairo_truetype_font_t *font, + unsigned long tag, + cairo_status_t (*write) (cairo_truetype_font_t *font, unsigned long tag), + int pos) +{ + font->truetype_tables[font->num_tables].tag = tag; + font->truetype_tables[font->num_tables].write = write; + font->truetype_tables[font->num_tables].pos = pos; + font->num_tables++; +} + +/* cairo_truetype_font_create_truetype_table_list() builds the list of + * truetype tables to be embedded in the subsetted font. Each call to + * cairo_truetype_font_add_truetype_table() adds a table, the callback + * for generating the table, and the position in the table directory + * to the truetype_tables array. + * + * As we write out the glyf table we remap composite glyphs. + * Remapping composite glyphs will reference the sub glyphs the + * composite glyph is made up of. The "glyf" table callback needs to + * be called first so we have all the glyphs in the subset before + * going further. + * + * The order in which tables are added to the truetype_table array + * using cairo_truetype_font_add_truetype_table() specifies the order + * in which the callback functions will be called. + * + * 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. "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) +{ + cairo_bool_t has_cvt = FALSE; + cairo_bool_t has_fpgm = FALSE; + cairo_bool_t has_prep = FALSE; + unsigned long size; + int pos; + + size = 0; + if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font, + TT_TAG_cvt, 0, NULL, + &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_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_INT_STATUS_SUCCESS) + has_prep = TRUE; + + font->num_tables = 0; + pos = 0; + if (font->is_pdf && font->scaled_font_subset->is_latin) + pos++; + if (has_cvt) + pos++; + if (has_fpgm) + pos++; + cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos); + + pos = 0; + 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) + cairo_truetype_font_add_truetype_table (font, TT_TAG_fpgm, cairo_truetype_font_write_generic_table, pos++); + pos++; + cairo_truetype_font_add_truetype_table (font, TT_TAG_head, cairo_truetype_font_write_head_table, pos++); + cairo_truetype_font_add_truetype_table (font, TT_TAG_hhea, cairo_truetype_font_write_hhea_table, pos++); + cairo_truetype_font_add_truetype_table (font, TT_TAG_hmtx, cairo_truetype_font_write_hmtx_table, pos++); + cairo_truetype_font_add_truetype_table (font, TT_TAG_loca, cairo_truetype_font_write_loca_table, pos++); + cairo_truetype_font_add_truetype_table (font, TT_TAG_maxp, cairo_truetype_font_write_maxp_table, pos++); + if (has_prep) + cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos); +} + +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; + const char *data = NULL; /* squelch bogus compiler warning */ + unsigned long length = 0; /* squelch bogus compiler warning */ + unsigned long offsets_length; + unsigned int i; + const unsigned long *string_offsets = NULL; + unsigned long num_strings = 0; + + status = _cairo_truetype_font_create (font_subset, is_pdf, &font); + if (unlikely (status)) + return status; + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + unsigned short parent_glyph = font->scaled_font_subset->glyphs[i]; + status = cairo_truetype_font_use_glyph (font, parent_glyph, &parent_glyph); + if (unlikely (status)) + goto fail1; + } + + cairo_truetype_font_create_truetype_table_list (font); + status = cairo_truetype_font_generate (font, &data, &length, + &string_offsets, &num_strings); + if (unlikely (status)) + goto fail1; + + truetype_subset->ps_name = strdup (font->base.ps_name); + if (unlikely (truetype_subset->ps_name == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + if (font->base.font_name != NULL) { + truetype_subset->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->family_name_utf8 = NULL; + } + + /* The widths array returned must contain only widths for the + * glyphs in font_subset. Any subglyphs appended after + * font_subset->num_glyphs are omitted. */ + truetype_subset->widths = calloc (sizeof (double), + font->scaled_font_subset->num_glyphs); + if (unlikely (truetype_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + 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; + truetype_subset->x_max = (double)font->base.x_max/font->base.units_per_em; + truetype_subset->y_max = (double)font->base.y_max/font->base.units_per_em; + truetype_subset->ascent = (double)font->base.ascent/font->base.units_per_em; + truetype_subset->descent = (double)font->base.descent/font->base.units_per_em; + + if (length) { + truetype_subset->data = _cairo_malloc (length); + if (unlikely (truetype_subset->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail4; + } + + memcpy (truetype_subset->data, data, length); + } else + truetype_subset->data = NULL; + truetype_subset->data_length = length; + + if (num_strings) { + offsets_length = num_strings * sizeof (unsigned long); + truetype_subset->string_offsets = _cairo_malloc (offsets_length); + if (unlikely (truetype_subset->string_offsets == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail5; + } + + memcpy (truetype_subset->string_offsets, string_offsets, offsets_length); + truetype_subset->num_string_offsets = num_strings; + } else { + truetype_subset->string_offsets = NULL; + truetype_subset->num_string_offsets = 0; + } + + cairo_truetype_font_destroy (font); + + return CAIRO_STATUS_SUCCESS; + + fail5: + free (truetype_subset->data); + fail4: + free (truetype_subset->widths); + fail3: + free (truetype_subset->family_name_utf8); + fail2: + free (truetype_subset->ps_name); + fail1: + cairo_truetype_font_destroy (font); + + 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); + free (subset->family_name_utf8); + free (subset->widths); + free (subset->data); + free (subset->string_offsets); +} + +static cairo_int_status_t +_cairo_truetype_reverse_cmap (cairo_scaled_font_t *scaled_font, + unsigned long table_offset, + unsigned long index, + uint32_t *ucs4) +{ + cairo_status_t status; + const cairo_scaled_font_backend_t *backend; + tt_segment_map_t *map; + 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 c; + + backend = scaled_font->backend; + 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 *) &map_header, + &size); + if (unlikely (status)) + return status; + + /* All table formats have the same first two words */ + if (be16_to_cpu (map_header.format) != 4) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = be16_to_cpu (map_header.length); + map = _cairo_malloc (size); + if (unlikely (map == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, + TT_TAG_cmap, table_offset, + (unsigned char *) map, + &size); + if (unlikely (status)) + goto fail; + + num_segments = be16_to_cpu (map->segCountX2)/2; + + /* A Format 4 cmap contains 8 uint16_t numbers and 4 arrays of + * uint16_t each num_segments long. */ + if (size < (8 + 4*num_segments)*sizeof(uint16_t)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + end_code = map->endCount; + start_code = &(end_code[num_segments + 1]); + delta = &(start_code[num_segments]); + range_offset = &(delta[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 >= start && c <= end) { + *ucs4 = c; + goto found; + } + } + + /* 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 = end - start + 1; + uint16_t g_id_be = cpu_to_be16 (index); + int j; + + if (range_size > 0) { + if ((char*)glyph_ids + 2*range_size > (char*)map + size) + return CAIRO_INT_STATUS_UNSUPPORTED; + + for (j = 0; j < range_size; j++) { + if (glyph_ids[j] == g_id_be) { + *ucs4 = start + j; + goto found; + } + } + } + } + } + + /* glyph not found */ + *ucs4 = -1; + +found: + status = CAIRO_STATUS_SUCCESS; + +fail: + free (map); + + return status; +} + +cairo_int_status_t +_cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font, + unsigned long index, + uint32_t *ucs4) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + const cairo_scaled_font_backend_t *backend; + tt_cmap_t *cmap; + tt_cmap_t cmap_header; + int num_tables, i; + unsigned long size; + + backend = scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 4; /* only read the header fields 'version' and 'num_tables' */ + status = backend->load_truetype_table (scaled_font, + TT_TAG_cmap, 0, + (unsigned char *) &cmap_header, + &size); + if (unlikely (status)) + return status; + + 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); + + status = backend->load_truetype_table (scaled_font, + TT_TAG_cmap, 0, + (unsigned char *) cmap, + &size); + if (unlikely (status)) + goto cleanup; + + /* Find a table with Unicode mapping */ + for (i = 0; i < num_tables; i++) { + if (be16_to_cpu (cmap->index[i].platform) == 3 && + be16_to_cpu (cmap->index[i].encoding) == 1) { + status = _cairo_truetype_reverse_cmap (scaled_font, + be32_to_cpu (cmap->index[i].offset), + index, + ucs4); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + break; + } + } + +cleanup: + free (cmap); + + 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, + char **font_name_out) +{ + cairo_status_t status; + const cairo_scaled_font_backend_t *backend; + tt_name_t *name; + unsigned long size; + char *ps_name = NULL; + char *family_name = NULL; + + backend = scaled_font->backend; + if (!backend->load_truetype_table) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, + TT_TAG_name, 0, + NULL, + &size); + if (status) + return status; + + name = _cairo_malloc (size); + if (name == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, + TT_TAG_name, 0, + (unsigned char *) name, + &size); + if (status) + goto fail; + + /* 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 (!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); + + *ps_name_out = ps_name; + *font_name_out = family_name; + + return CAIRO_STATUS_SUCCESS; + +fail: + free (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 new file mode 100644 index 0000000000..0b8e66cd0b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-type1-fallback.c @@ -0,0 +1,903 @@ +/* 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): + * Adrian Johnson + */ + +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" + +#include "cairo-array-private.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-type1-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-path-fixed-private.h" +#include "cairo-output-stream-private.h" + +typedef enum { + CAIRO_CHARSTRING_TYPE1, + CAIRO_CHARSTRING_TYPE2 +} cairo_charstring_type_t; + +typedef struct _cairo_type1_font { + int *widths; + + cairo_scaled_font_subset_t *scaled_font_subset; + cairo_scaled_font_t *type1_scaled_font; + + cairo_array_t contents; + + double x_min, y_min, x_max, y_max; + + const char *data; + unsigned long header_size; + unsigned long data_size; + unsigned long trailer_size; + int bbox_position; + int bbox_max_chars; + + cairo_output_stream_t *output; + + unsigned short eexec_key; + cairo_bool_t hex_encode; + int hex_column; +} cairo_type1_font_t; + +static cairo_status_t +cairo_type1_font_create (cairo_scaled_font_subset_t *scaled_font_subset, + cairo_type1_font_t **subset_return, + cairo_bool_t hex_encode) +{ + cairo_type1_font_t *font; + cairo_font_face_t *font_face; + cairo_matrix_t font_matrix; + cairo_matrix_t ctm; + cairo_font_options_t font_options; + cairo_status_t status; + + font = calloc (1, sizeof (cairo_type1_font_t)); + if (unlikely (font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + font->widths = calloc (scaled_font_subset->num_glyphs, sizeof (int)); + if (unlikely (font->widths == NULL)) { + free (font); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + font->scaled_font_subset = scaled_font_subset; + font->hex_encode = hex_encode; + + font_face = cairo_scaled_font_get_font_face (scaled_font_subset->scaled_font); + + cairo_matrix_init_scale (&font_matrix, 1000, -1000); + cairo_matrix_init_identity (&ctm); + + _cairo_font_options_init_default (&font_options); + cairo_font_options_set_hint_style (&font_options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (&font_options, CAIRO_HINT_METRICS_OFF); + + font->type1_scaled_font = cairo_scaled_font_create (font_face, + &font_matrix, + &ctm, + &font_options); + status = font->type1_scaled_font->status; + if (unlikely (status)) + goto fail; + + _cairo_array_init (&font->contents, sizeof (unsigned char)); + font->output = NULL; + + *subset_return = font; + + return CAIRO_STATUS_SUCCESS; + +fail: + free (font->widths); + free (font); + + return status; +} + +/* Charstring commands. If the high byte is 0 the command is encoded + * with a single byte. */ +#define CHARSTRING_sbw 0x0c07 +#define CHARSTRING_rmoveto 0x0015 +#define CHARSTRING_rlineto 0x0005 +#define CHARSTRING_rcurveto 0x0008 +#define CHARSTRING_closepath 0x0009 +#define CHARSTRING_endchar 0x000e + +/* Before calling this function, the caller must allocate sufficient + * space in data (see _cairo_array_grow_by). The maximum number of + * bytes that will be used is 2. + */ +static void +charstring_encode_command (cairo_array_t *data, int command) +{ + cairo_status_t status; + unsigned int orig_size; + unsigned char buf[5]; + unsigned char *p = buf; + + if (command & 0xff00) + *p++ = command >> 8; + *p++ = command & 0x00ff; + + /* Ensure the array doesn't grow, which allows this function to + * have no possibility of failure. */ + orig_size = _cairo_array_size (data); + status = _cairo_array_append_multiple (data, buf, p - buf); + + assert (status == CAIRO_STATUS_SUCCESS); + assert (_cairo_array_size (data) == orig_size); +} + +/* Before calling this function, the caller must allocate sufficient + * space in data (see _cairo_array_grow_by). The maximum number of + * bytes that will be used is 5. + */ +static void +charstring_encode_integer (cairo_array_t *data, + int i, + cairo_charstring_type_t type) +{ + cairo_status_t status; + unsigned int orig_size; + unsigned char buf[10]; + unsigned char *p = buf; + + if (i >= -107 && i <= 107) { + *p++ = i + 139; + } else if (i >= 108 && i <= 1131) { + i -= 108; + *p++ = (i >> 8)+ 247; + *p++ = i & 0xff; + } else if (i >= -1131 && i <= -108) { + i = -i - 108; + *p++ = (i >> 8)+ 251; + *p++ = i & 0xff; + } else { + if (type == CAIRO_CHARSTRING_TYPE1) { + *p++ = 0xff; + *p++ = i >> 24; + *p++ = (i >> 16) & 0xff; + *p++ = (i >> 8) & 0xff; + *p++ = i & 0xff; + } else { + *p++ = 0xff; + *p++ = (i >> 8) & 0xff; + *p++ = i & 0xff; + *p++ = 0; + *p++ = 0; + } + } + + /* Ensure the array doesn't grow, which allows this function to + * have no possibility of failure. */ + orig_size = _cairo_array_size (data); + status = _cairo_array_append_multiple (data, buf, p - buf); + + assert (status == CAIRO_STATUS_SUCCESS); + assert (_cairo_array_size (data) == orig_size); +} + +typedef struct _ps_path_info { + cairo_array_t *data; + int current_x, current_y; + cairo_charstring_type_t type; +} t1_path_info_t; + +static cairo_status_t +_charstring_move_to (void *closure, + const cairo_point_t *point) +{ + t1_path_info_t *path_info = (t1_path_info_t *) closure; + int dx, dy; + cairo_status_t status; + + status = _cairo_array_grow_by (path_info->data, 12); + if (unlikely (status)) + return status; + + dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; + dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; + charstring_encode_integer (path_info->data, dx, path_info->type); + charstring_encode_integer (path_info->data, dy, path_info->type); + path_info->current_x += dx; + path_info->current_y += dy; + + charstring_encode_command (path_info->data, CHARSTRING_rmoveto); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_charstring_line_to (void *closure, + const cairo_point_t *point) +{ + t1_path_info_t *path_info = (t1_path_info_t *) closure; + int dx, dy; + cairo_status_t status; + + status = _cairo_array_grow_by (path_info->data, 12); + if (unlikely (status)) + return status; + + dx = _cairo_fixed_integer_part (point->x) - path_info->current_x; + dy = _cairo_fixed_integer_part (point->y) - path_info->current_y; + charstring_encode_integer (path_info->data, dx, path_info->type); + charstring_encode_integer (path_info->data, dy, path_info->type); + path_info->current_x += dx; + path_info->current_y += dy; + + charstring_encode_command (path_info->data, CHARSTRING_rlineto); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_charstring_curve_to (void *closure, + const cairo_point_t *point1, + const cairo_point_t *point2, + const cairo_point_t *point3) +{ + t1_path_info_t *path_info = (t1_path_info_t *) closure; + int dx1, dy1, dx2, dy2, dx3, dy3; + cairo_status_t status; + + status = _cairo_array_grow_by (path_info->data, 32); + if (unlikely (status)) + return status; + + dx1 = _cairo_fixed_integer_part (point1->x) - path_info->current_x; + dy1 = _cairo_fixed_integer_part (point1->y) - path_info->current_y; + dx2 = _cairo_fixed_integer_part (point2->x) - path_info->current_x - dx1; + dy2 = _cairo_fixed_integer_part (point2->y) - path_info->current_y - dy1; + dx3 = _cairo_fixed_integer_part (point3->x) - path_info->current_x - dx1 - dx2; + dy3 = _cairo_fixed_integer_part (point3->y) - path_info->current_y - dy1 - dy2; + charstring_encode_integer (path_info->data, dx1, path_info->type); + charstring_encode_integer (path_info->data, dy1, path_info->type); + charstring_encode_integer (path_info->data, dx2, path_info->type); + charstring_encode_integer (path_info->data, dy2, path_info->type); + charstring_encode_integer (path_info->data, dx3, path_info->type); + charstring_encode_integer (path_info->data, dy3, path_info->type); + path_info->current_x += dx1 + dx2 + dx3; + path_info->current_y += dy1 + dy2 + dy3; + charstring_encode_command (path_info->data, CHARSTRING_rcurveto); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_charstring_close_path (void *closure) +{ + cairo_status_t status; + t1_path_info_t *path_info = (t1_path_info_t *) closure; + + if (path_info->type == CAIRO_CHARSTRING_TYPE2) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_array_grow_by (path_info->data, 2); + if (unlikely (status)) + return status; + + charstring_encode_command (path_info->data, CHARSTRING_closepath); + + return CAIRO_STATUS_SUCCESS; +} + +static void +charstring_encrypt (cairo_array_t *data) +{ + unsigned char *d, *end; + uint16_t c, p, r; + + r = CAIRO_TYPE1_CHARSTRING_KEY; + d = (unsigned char *) _cairo_array_index (data, 0); + end = d + _cairo_array_num_elements (data); + while (d < end) { + p = *d; + c = p ^ (r >> 8); + r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + *d++ = c; + } +} + +static cairo_int_status_t +cairo_type1_font_create_charstring (cairo_type1_font_t *font, + int subset_index, + int glyph_index, + cairo_charstring_type_t type, + cairo_array_t *data) +{ + cairo_int_status_t status; + cairo_scaled_glyph_t *scaled_glyph; + t1_path_info_t path_info; + cairo_text_extents_t *metrics; + cairo_bool_t emit_path = TRUE; + + /* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */ + status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS| + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + + /* It is ok for the .notdef glyph to not have a path available. We + * just need the metrics to emit an empty glyph. */ + if (glyph_index == 0 && status == CAIRO_INT_STATUS_UNSUPPORTED) { + emit_path = FALSE; + status = _cairo_scaled_glyph_lookup (font->type1_scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + } + if (unlikely (status)) + return status; + + metrics = &scaled_glyph->metrics; + if (subset_index == 0) { + font->x_min = metrics->x_bearing; + font->y_min = metrics->y_bearing; + font->x_max = metrics->x_bearing + metrics->width; + font->y_max = metrics->y_bearing + metrics->height; + } else { + if (metrics->x_bearing < font->x_min) + font->x_min = metrics->x_bearing; + if (metrics->y_bearing < font->y_min) + font->y_min = metrics->y_bearing; + if (metrics->x_bearing + metrics->width > font->x_max) + font->x_max = metrics->x_bearing + metrics->width; + if (metrics->y_bearing + metrics->height > font->y_max) + font->y_max = metrics->y_bearing + metrics->height; + } + font->widths[subset_index] = metrics->x_advance; + + status = _cairo_array_grow_by (data, 30); + if (unlikely (status)) + return status; + + if (type == CAIRO_CHARSTRING_TYPE1) { + charstring_encode_integer (data, (int) scaled_glyph->metrics.x_bearing, type); + charstring_encode_integer (data, (int) scaled_glyph->metrics.y_bearing, type); + charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); + charstring_encode_integer (data, (int) scaled_glyph->metrics.y_advance, type); + charstring_encode_command (data, CHARSTRING_sbw); + + path_info.current_x = (int) scaled_glyph->metrics.x_bearing; + path_info.current_y = (int) scaled_glyph->metrics.y_bearing; + } else { + charstring_encode_integer (data, (int) scaled_glyph->metrics.x_advance, type); + + path_info.current_x = 0; + path_info.current_y = 0; + } + path_info.data = data; + path_info.type = type; + if (emit_path) { + status = _cairo_path_fixed_interpret (scaled_glyph->path, + _charstring_move_to, + _charstring_line_to, + _charstring_curve_to, + _charstring_close_path, + &path_info); + if (unlikely (status)) + return status; + } + + status = _cairo_array_grow_by (data, 1); + if (unlikely (status)) + return status; + charstring_encode_command (path_info.data, CHARSTRING_endchar); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_type1_font_write_charstrings (cairo_type1_font_t *font, + cairo_output_stream_t *encrypted_output) +{ + cairo_status_t status; + unsigned char zeros[] = { 0, 0, 0, 0 }; + cairo_array_t data; + unsigned int i; + int length; + + _cairo_array_init (&data, sizeof (unsigned char)); + status = _cairo_array_grow_by (&data, 1024); + if (unlikely (status)) + goto fail; + + _cairo_output_stream_printf (encrypted_output, + "2 index /CharStrings %d dict dup begin\n", + font->scaled_font_subset->num_glyphs + 1); + + _cairo_scaled_font_freeze_cache (font->type1_scaled_font); + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + _cairo_array_truncate (&data, 0); + /* four "random" bytes required by encryption algorithm */ + status = _cairo_array_append_multiple (&data, zeros, 4); + if (unlikely (status)) + break; + + status = cairo_type1_font_create_charstring (font, i, + font->scaled_font_subset->glyphs[i], + CAIRO_CHARSTRING_TYPE1, + &data); + if (unlikely (status)) + break; + + charstring_encrypt (&data); + length = _cairo_array_num_elements (&data); + if (font->scaled_font_subset->glyph_names != NULL) { + _cairo_output_stream_printf (encrypted_output, "/%s %d RD ", + font->scaled_font_subset->glyph_names[i], + length); + } else if (i == 0) { + _cairo_output_stream_printf (encrypted_output, "/.notdef %d RD ", length); + } else { + _cairo_output_stream_printf (encrypted_output, "/g%d %d RD ", i, length); + } + _cairo_output_stream_write (encrypted_output, + _cairo_array_index (&data, 0), + length); + _cairo_output_stream_printf (encrypted_output, " ND\n"); + } + _cairo_scaled_font_thaw_cache (font->type1_scaled_font); + +fail: + _cairo_array_fini (&data); + return status; +} + +static void +cairo_type1_font_write_header (cairo_type1_font_t *font, + const char *name) +{ + unsigned int i; + const char spaces[50] = " "; + + _cairo_output_stream_printf (font->output, + "%%!FontType1-1.1 %s 1.0\n" + "11 dict begin\n" + "/FontName /%s def\n" + "/PaintType 0 def\n" + "/FontType 1 def\n" + "/FontMatrix [0.001 0 0 0.001 0 0] readonly def\n", + name, + name); + + /* We don't know the bbox values until after the charstrings have + * been generated. Reserve some space and fill in the bbox + * later. */ + + /* Worst case for four signed ints with spaces between each number */ + font->bbox_max_chars = 50; + + _cairo_output_stream_printf (font->output, "/FontBBox {"); + font->bbox_position = _cairo_output_stream_get_position (font->output); + _cairo_output_stream_write (font->output, spaces, font->bbox_max_chars); + + _cairo_output_stream_printf (font->output, + "} readonly def\n" + "/Encoding 256 array\n" + "0 1 255 {1 index exch /.notdef put} for\n"); + 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, + "readonly def\n" + "currentdict end\n" + "currentfile eexec\n"); +} + +static cairo_status_t +cairo_type1_write_stream_encrypted (void *closure, + const unsigned char *data, + unsigned int length) +{ + const unsigned char *in, *end; + uint16_t c, p; + static const char hex_digits[16] = "0123456789abcdef"; + char digits[3]; + cairo_type1_font_t *font = closure; + + in = (const unsigned char *) data; + end = (const unsigned char *) data + length; + while (in < end) { + p = *in++; + c = p ^ (font->eexec_key >> 8); + font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + + if (font->hex_encode) { + digits[0] = hex_digits[c >> 4]; + digits[1] = hex_digits[c & 0x0f]; + digits[2] = '\n'; + font->hex_column += 2; + + if (font->hex_column == 78) { + _cairo_output_stream_write (font->output, digits, 3); + font->hex_column = 0; + } else { + _cairo_output_stream_write (font->output, digits, 2); + } + } else { + digits[0] = c; + _cairo_output_stream_write (font->output, digits, 1); + } + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_type1_font_write_private_dict (cairo_type1_font_t *font, + const char *name) +{ + cairo_int_status_t status; + cairo_status_t status2; + cairo_output_stream_t *encrypted_output; + + font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY; + font->hex_column = 0; + encrypted_output = _cairo_output_stream_create ( + cairo_type1_write_stream_encrypted, + NULL, + font); + if (_cairo_output_stream_get_status (encrypted_output)) + return _cairo_output_stream_destroy (encrypted_output); + + /* Note: the first four spaces at the start of this private dict + * are the four "random" bytes of plaintext required by the + * encryption algorithm */ + _cairo_output_stream_printf (encrypted_output, + " dup /Private 9 dict dup begin\n" + "/RD {string currentfile exch readstring pop}" + " bind executeonly def\n" + "/ND {noaccess def} executeonly def\n" + "/NP {noaccess put} executeonly def\n" + "/BlueValues [] def\n" + "/MinFeature {16 16} def\n" + "/lenIV 4 def\n" + "/password 5839 def\n"); + + status = cairo_type1_font_write_charstrings (font, encrypted_output); + if (unlikely (status)) + goto fail; + + _cairo_output_stream_printf (encrypted_output, + "end\n" + "end\n" + "readonly put\n" + "noaccess put\n" + "dup /FontName get exch definefont pop\n" + "mark currentfile closefile\n"); + + fail: + status2 = _cairo_output_stream_destroy (encrypted_output); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = status2; + + return status; +} + +static void +cairo_type1_font_write_trailer(cairo_type1_font_t *font) +{ + int i; + static const char zeros[65] = + "0000000000000000000000000000000000000000000000000000000000000000\n"; + + for (i = 0; i < 8; i++) + _cairo_output_stream_write (font->output, zeros, sizeof zeros); + + _cairo_output_stream_printf (font->output, "cleartomark\n"); +} + +static cairo_status_t +cairo_type1_write_stream (void *closure, + const unsigned char *data, + unsigned int length) +{ + cairo_type1_font_t *font = closure; + + return _cairo_array_append_multiple (&font->contents, data, length); +} + +static cairo_int_status_t +cairo_type1_font_write (cairo_type1_font_t *font, + const char *name) +{ + cairo_int_status_t status; + + cairo_type1_font_write_header (font, name); + font->header_size = _cairo_output_stream_get_position (font->output); + + status = cairo_type1_font_write_private_dict (font, name); + if (unlikely (status)) + return status; + + font->data_size = _cairo_output_stream_get_position (font->output) - + font->header_size; + + cairo_type1_font_write_trailer (font); + font->trailer_size = + _cairo_output_stream_get_position (font->output) - + font->header_size - font->data_size; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +cairo_type1_font_generate (cairo_type1_font_t *font, const char *name) +{ + cairo_int_status_t status; + + status = _cairo_array_grow_by (&font->contents, 4096); + if (unlikely (status)) + return status; + + font->output = _cairo_output_stream_create (cairo_type1_write_stream, NULL, font); + if (_cairo_output_stream_get_status (font->output)) + return _cairo_output_stream_destroy (font->output); + + status = cairo_type1_font_write (font, name); + if (unlikely (status)) + return status; + + font->data = _cairo_array_index (&font->contents, 0); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_type1_font_destroy (cairo_type1_font_t *font) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + free (font->widths); + cairo_scaled_font_destroy (font->type1_scaled_font); + _cairo_array_fini (&font->contents); + if (font->output) + status = _cairo_output_stream_destroy (font->output); + free (font); + + return status; +} + +static cairo_status_t +_cairo_type1_fallback_init_internal (cairo_type1_subset_t *type1_subset, + const char *name, + cairo_scaled_font_subset_t *scaled_font_subset, + cairo_bool_t hex_encode) +{ + cairo_type1_font_t *font; + cairo_status_t status; + unsigned long length; + unsigned int i, len; + + status = cairo_type1_font_create (scaled_font_subset, &font, hex_encode); + if (unlikely (status)) + return status; + + status = cairo_type1_font_generate (font, name); + if (unlikely (status)) + goto fail1; + + type1_subset->base_font = strdup (name); + if (unlikely (type1_subset->base_font == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + type1_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs); + if (unlikely (type1_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail2; + } + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + type1_subset->widths[i] = (double)font->widths[i]/1000; + + type1_subset->x_min = (double)font->x_min/1000; + type1_subset->y_min = (double)font->y_min/1000; + type1_subset->x_max = (double)font->x_max/1000; + type1_subset->y_max = (double)font->y_max/1000; + type1_subset->ascent = (double)font->y_max/1000; + type1_subset->descent = (double)font->y_min/1000; + + length = font->header_size + font->data_size + + font->trailer_size; + type1_subset->data = _cairo_malloc (length); + if (unlikely (type1_subset->data == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail3; + } + memcpy (type1_subset->data, + _cairo_array_index (&font->contents, 0), length); + + len = snprintf(type1_subset->data + font->bbox_position, + font->bbox_max_chars, + "%d %d %d %d", + (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; + type1_subset->data_length = font->data_size; + type1_subset->trailer_length = font->trailer_size; + + return cairo_type1_font_destroy (font); + + fail3: + free (type1_subset->widths); + fail2: + free (type1_subset->base_font); + fail1: + /* status is already set, ignore further errors */ + cairo_type1_font_destroy (font); + + return status; +} + +cairo_status_t +_cairo_type1_fallback_init_binary (cairo_type1_subset_t *type1_subset, + const char *name, + cairo_scaled_font_subset_t *scaled_font_subset) +{ + return _cairo_type1_fallback_init_internal (type1_subset, + name, + scaled_font_subset, FALSE); +} + +cairo_status_t +_cairo_type1_fallback_init_hex (cairo_type1_subset_t *type1_subset, + const char *name, + cairo_scaled_font_subset_t *scaled_font_subset) +{ + return _cairo_type1_fallback_init_internal (type1_subset, + name, + scaled_font_subset, TRUE); +} + +void +_cairo_type1_fallback_fini (cairo_type1_subset_t *subset) +{ + free (subset->base_font); + free (subset->widths); + free (subset->data); +} + +cairo_status_t +_cairo_type2_charstrings_init (cairo_type2_charstrings_t *type2_subset, + cairo_scaled_font_subset_t *scaled_font_subset) +{ + cairo_type1_font_t *font; + cairo_status_t status; + unsigned int i; + cairo_array_t charstring; + + status = cairo_type1_font_create (scaled_font_subset, &font, FALSE); + if (unlikely (status)) + return status; + + _cairo_array_init (&type2_subset->charstrings, sizeof (cairo_array_t)); + + type2_subset->widths = calloc (sizeof (int), font->scaled_font_subset->num_glyphs); + if (unlikely (type2_subset->widths == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto fail1; + } + + _cairo_scaled_font_freeze_cache (font->type1_scaled_font); + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) { + _cairo_array_init (&charstring, sizeof (unsigned char)); + status = _cairo_array_grow_by (&charstring, 32); + if (unlikely (status)) + goto fail2; + + status = cairo_type1_font_create_charstring (font, i, + font->scaled_font_subset->glyphs[i], + CAIRO_CHARSTRING_TYPE2, + &charstring); + if (unlikely (status)) + goto fail2; + + status = _cairo_array_append (&type2_subset->charstrings, &charstring); + if (unlikely (status)) + goto fail2; + } + _cairo_scaled_font_thaw_cache (font->type1_scaled_font); + + for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) + type2_subset->widths[i] = font->widths[i]; + + type2_subset->x_min = (int) font->x_min; + type2_subset->y_min = (int) font->y_min; + type2_subset->x_max = (int) font->x_max; + type2_subset->y_max = (int) font->y_max; + type2_subset->ascent = (int) font->y_max; + type2_subset->descent = (int) font->y_min; + + return cairo_type1_font_destroy (font); + +fail2: + _cairo_scaled_font_thaw_cache (font->type1_scaled_font); + _cairo_array_fini (&charstring); + _cairo_type2_charstrings_fini (type2_subset); +fail1: + cairo_type1_font_destroy (font); + return status; +} + +void +_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *type2_subset) +{ + unsigned int i, num_charstrings; + cairo_array_t *charstring; + + num_charstrings = _cairo_array_num_elements (&type2_subset->charstrings); + for (i = 0; i < num_charstrings; i++) { + charstring = _cairo_array_index (&type2_subset->charstrings, i); + _cairo_array_fini (charstring); + } + _cairo_array_fini (&type2_subset->charstrings); + + free (type2_subset->widths); +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ 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 0000000000..80ccb96269 --- /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-private.h b/gfx/cairo/cairo/src/cairo-type1-private.h new file mode 100644 index 0000000000..1630397bca --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-type1-private.h @@ -0,0 +1,51 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 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): + * Adrian Johnson + */ + +#ifndef CAIRO_TYPE1_PRIVATE_H +#define CAIRO_TYPE1_PRIVATE_H + +#include "cairoint.h" + +#if CAIRO_HAS_FONT_SUBSET + +/* Magic constants for the type1 eexec encryption */ +#define CAIRO_TYPE1_ENCRYPT_C1 ((unsigned short) 52845) +#define CAIRO_TYPE1_ENCRYPT_C2 ((unsigned short) 22719) +#define CAIRO_TYPE1_PRIVATE_DICT_KEY ((unsigned short) 55665) +#define CAIRO_TYPE1_CHARSTRING_KEY ((unsigned short) 4330) + +#endif /* CAIRO_HAS_FONT_SUBSET */ + +#endif /* CAIRO_TYPE1_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-type1-subset.c b/gfx/cairo/cairo/src/cairo-type1-subset.c new file mode 100644 index 0000000000..c7ab367e84 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-type1-subset.c @@ -0,0 +1,1838 @@ +/* -*- 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 + * + * 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 + */ + +/* + * Useful links: + * http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF + */ + + +#define _DEFAULT_SOURCE /* for snprintf(), strdup() */ +#include "cairoint.h" + +#include "cairo-array-private.h" +#include "cairo-error-private.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-type1-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-output-stream-private.h" + +#include + +#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 { + 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; + unsigned long data_size; + unsigned long trailer_size; + } base; + + 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 { + 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, *np; + + int lenIV; + + char *type1_data; + unsigned int type1_length; + char *type1_end; + + char *header_segment; + unsigned int header_segment_size; + char *eexec_segment; + unsigned int eexec_segment_size; + cairo_bool_t eexec_segment_is_ascii; + + char *cleartext; + char *cleartext_end; + + 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_scaled_font_subset_t *scaled_font_subset, + cairo_bool_t hex_encode) +{ + memset (font, 0, sizeof (*font)); + font->scaled_font_subset = scaled_font_subset; + + _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; + + _cairo_array_init (&font->contents, sizeof (char)); + + return CAIRO_STATUS_SUCCESS; +} + +static void +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->subset_index_to_glyphs[font->num_glyphs] = glyph; + font->num_glyphs++; +} + +static cairo_bool_t +is_ps_delimiter(int c) +{ + static const char delimiters[] = "()[]{}<>/% \t\r\n"; + + return strchr (delimiters, c) != NULL; +} + +static const char * +find_token (const char *buffer, const char *end, const char *token) +{ + int i, length; + /* FIXME: find substring really must be find_token */ + + if (buffer == NULL) + return NULL; + + length = strlen (token); + for (i = 0; buffer + i < end - length + 1; i++) + if (memcmp (buffer + i, token, length) == 0) + if ((i == 0 || token[0] == '/' || is_ps_delimiter(buffer[i - 1])) && + (buffer + i == end - length || is_ps_delimiter(buffer[i + length]))) + return buffer + i; + + return NULL; +} + +static cairo_status_t +cairo_type1_font_subset_find_segments (cairo_type1_font_subset_t *font) +{ + unsigned char *p; + const char *eexec_token; + unsigned int size, i; + + p = (unsigned char *) font->type1_data; + font->type1_end = font->type1_data + font->type1_length; + 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) | ((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) | ((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 (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 { + eexec_token = find_token ((char *) p, font->type1_end, "eexec"); + if (eexec_token == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + font->header_segment_size = eexec_token - (char *) p + strlen ("eexec\n"); + font->header_segment = (char *) p; + font->eexec_segment_size = font->type1_length - font->header_segment_size; + font->eexec_segment = (char *) p + font->header_segment_size; + font->eexec_segment_is_ascii = TRUE; + for (i = 0; i < 4; i++) { + if (!_cairo_isxdigit (font->eexec_segment[i])) + font->eexec_segment_is_ascii = FALSE; + } + } + + return CAIRO_STATUS_SUCCESS; +} + +/* Search for the definition of key and erase it by overwriting with spaces. + * This function is looks for definitions of the form: + * + * /key1 1234 def + * /key2 [12 34 56] def + * + * ie a key defined as an integer or array of integers. + * + */ +static void +cairo_type1_font_erase_dict_key (cairo_type1_font_subset_t *font, + const char *key) +{ + const char *start, *p, *segment_end; + + segment_end = font->header_segment + font->header_segment_size; + + start = font->header_segment; + do { + start = find_token (start, segment_end, key); + if (start) { + p = start + strlen(key); + /* skip integers or array of integers */ + while (p < segment_end && + (_cairo_isspace(*p) || + _cairo_isdigit(*p) || + *p == '[' || + *p == ']')) + { + p++; + } + + if (p + 3 < segment_end && memcmp(p, "def", 3) == 0) { + /* erase definition of the key */ + memset((char *) start, ' ', p + 3 - start); + } + start += strlen(key); + } + } 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) +{ + const char *start, *end, *segment_end; + unsigned int i; + + /* FIXME: + * This function assumes that /FontName always appears + * before /Encoding. This appears to always be the case with Type1 + * fonts. + * + * The more recently added code for removing the UniqueID and XUID + * keys can not make any assumptions about the position of the + * keys in the dictionary so it is implemented by overwriting the + * key definition with spaces before we start copying the font to + * the output. + * + * This code should be rewritten to not make any assumptions about + * the order of dictionary keys. This will allow UniqueID to be + * stripped out instead of leaving a bunch of spaces in the + * output. + */ + cairo_type1_font_erase_dict_key (font, "/UniqueID"); + cairo_type1_font_erase_dict_key (font, "/XUID"); + + segment_end = font->header_segment + font->header_segment_size; + + /* Type 1 fonts created by Fontforge have some PostScript code at + * the start of the font that skips the font if the printer has a + * cached copy of the font with the same unique id. This breaks + * our subsetted font so we disable it by searching for the + * PostScript operator "known" when used to check for the + * "/UniqueID" dictionary key. We append " pop false " after it to + * pop the result of this check off the stack and replace it with + * "false" to make the PostScript code think "/UniqueID" does not + * exist. + */ + end = font->header_segment; + start = find_token (font->header_segment, segment_end, "/UniqueID"); + if (start) { + start += 9; + while (start < segment_end && _cairo_isspace (*start)) + start++; + if (start + 5 < segment_end && memcmp(start, "known", 5) == 0) { + _cairo_output_stream_write (font->output, font->header_segment, + start + 5 - font->header_segment); + _cairo_output_stream_printf (font->output, " pop false "); + end = start + 5; + } + } + + start = find_token (end, segment_end, "/FontName"); + if (start == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_output_stream_write (font->output, end, + start - end); + + _cairo_output_stream_printf (font->output, "/FontName /%s def", name); + + end = find_token (start, segment_end, "def"); + if (end == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + end += 3; + + start = find_token (end, segment_end, "/Encoding"); + if (start == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + _cairo_output_stream_write (font->output, end, start - end); + + _cairo_output_stream_printf (font->output, + "/Encoding 256 array\n" + "0 1 255 {1 index exch /.notdef put} for\n"); + 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"); + + end = find_token (start, segment_end, "def"); + if (end == NULL) + 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; +} + +static int +hex_to_int (int ch) +{ + if (ch <= '9') + return ch - '0'; + else if (ch <= 'F') + return ch - 'A' + 10; + else + return ch - 'a' + 10; +} + +static cairo_status_t +cairo_type1_font_subset_write_encrypted (cairo_type1_font_subset_t *font, + const char *data, unsigned int length) +{ + const unsigned char *in, *end; + int c, p; + static const char hex_digits[16] = "0123456789abcdef"; + char digits[3]; + + in = (const unsigned char *) data; + end = (const unsigned char *) data + length; + while (in < end) { + p = *in++; + c = p ^ (font->eexec_key >> 8); + font->eexec_key = (c + font->eexec_key) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + + if (font->hex_encode) { + digits[0] = hex_digits[c >> 4]; + digits[1] = hex_digits[c & 0x0f]; + digits[2] = '\n'; + font->hex_column += 2; + + if (font->hex_column == 78) { + _cairo_output_stream_write (font->output, digits, 3); + font->hex_column = 0; + } else { + _cairo_output_stream_write (font->output, digits, 2); + } + } else { + digits[0] = c; + _cairo_output_stream_write (font->output, digits, 1); + } + } + + return font->output->status; +} + +static cairo_status_t +cairo_type1_font_subset_decrypt_eexec_segment (cairo_type1_font_subset_t *font) +{ + unsigned short r = CAIRO_TYPE1_PRIVATE_DICT_KEY; + unsigned char *in, *end; + char *out; + int c, p; + int i; + + in = (unsigned char *) font->eexec_segment; + end = (unsigned char *) in + 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); + + out = font->cleartext; + while (in < end) { + if (font->eexec_segment_is_ascii) { + c = *in++; + if (_cairo_isspace (c)) + continue; + c = (hex_to_int (c) << 4) | hex_to_int (*in++); + } else { + c = *in++; + } + p = c ^ (r >> 8); + r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + + *out++ = p; + } + font->cleartext_end = out; + + /* Overwrite random bytes with spaces. + * + * The first 4 bytes of the cleartext are the random bytes + * required by the encryption algorithm. When encrypting the + * cleartext, the first ciphertext byte must not be a white space + * character and the first 4 bytes must not be an ASCII Hex + * character. Some fonts do not check that their randomly chosen + * bytes results in ciphertext that complies with this + * restriction. This may cause problems for some PDF consumers. By + * replacing the random bytes with spaces, the first four bytes of + * ciphertext will always be 0xf9, 0x83, 0xef, 0x00 which complies + * with this restriction. Using spaces also means we don't have to + * skip over the random bytes when parsing the cleartext. + */ + 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; +} + +static const char * +skip_token (const char *p, const char *end) +{ + while (p < end && _cairo_isspace(*p)) + p++; + + while (p < end && !_cairo_isspace(*p)) + p++; + + if (p == end) + return NULL; + + return p; +} + +static void +cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out) +{ + unsigned short r = CAIRO_TYPE1_CHARSTRING_KEY; + int c, p, i; + + for (i = 0; i < size; i++) { + c = *in++; + p = c ^ (r >> 8); + r = (c + r) * CAIRO_TYPE1_ENCRYPT_C1 + CAIRO_TYPE1_ENCRYPT_C2; + *out++ = p; + } +} + +static const unsigned char * +cairo_type1_font_subset_decode_integer (const unsigned char *p, int *integer) +{ + 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 { + *integer = ((uint32_t)p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4]; + p += 5; + } + + return p; +} + +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 = _cairo_ps_standard_encoding_to_glyphname (index); + if (glyph_name == NULL) + return CAIRO_STATUS_SUCCESS; + + 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); + + return CAIRO_STATUS_SUCCESS; + } + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + + +#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_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 command; + + charstring = _cairo_malloc (encrypted_charstring_length); + if (unlikely (charstring == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + cairo_type1_font_subset_decrypt_charstring ((const unsigned char *) + encrypted_charstring, + encrypted_charstring_length, + charstring); + end = charstring + encrypted_charstring_length; + p = charstring + font->lenIV; + status = CAIRO_STATUS_SUCCESS; + while (p < end) { + if (*p < 32) { + command = *p++; + switch (command) { + 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; + + 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 { + /* integer argument */ + 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 ", + name_length, name, charstring_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, + charstring, + charstring_length); + if (unlikely (status)) + return status; + + length = snprintf (buffer, sizeof buffer, "%s\n", font->nd); + status = cairo_type1_font_subset_write_encrypted (font, buffer, length); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +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); + +static cairo_status_t +cairo_type1_font_subset_for_each_glyph (cairo_type1_font_subset_t *font, + const char *dict_start, + const char *dict_end, + glyph_func_t func, + const char **dict_out) +{ + 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: + * + * /name 23 RD <23 binary bytes> ND + * + * or alternatively using -| and |- instead of RD and ND. + * + * We parse the glyph name and see if it is in the subset. If it + * is, we call the specified callback with the glyph name and + * glyph data, otherwise we just skip it. We need to parse + * through a glyph definition; we can't just find the next '/', + * since the binary data could contain a '/'. + */ + + p = dict_start; + glyph_count = 0; + while (*p == '/') { + name = p + 1; + p = skip_token (p, dict_end); + name_length = p - name; + + charstring_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'. */ + charstring = skip_token (end, dict_end) + 1; + + /* Skip binary data and |- or ND token. */ + p = skip_token (charstring + charstring_length, dict_end); + while (p < dict_end && _cairo_isspace(*p)) + p++; + + /* In case any of the skip_token() calls above reached EOF, p will + * be equal to dict_end. */ + if (p == dict_end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = func (font, glyph_count++, + name, name_length, + charstring, charstring_length); + if (unlikely (status)) + return status; + } + + *dict_out = p; + + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +cairo_type1_font_subset_write_private_dict (cairo_type1_font_subset_t *font, + const char *name) +{ + cairo_status_t status; + 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). + * + * 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. + * + * 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. + */ + + /* 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"); + strtol (p, &glyph_count_end, 10); + if (p == glyph_count_end) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Look for a '/' which marks the beginning of the first glyph + * definition. */ + for (p = glyph_count_end; p < font->cleartext_end; p++) + if (*p == '/') + break; + if (p == font->cleartext_end) + return CAIRO_INT_STATUS_UNSUPPORTED; + dict_start = p; + + /* Now that we have the private dictionary broken down in + * sections, do the first pass through the glyph definitions to + * 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_build_glyph_list, + &dict_end); + if (unlikely(status)) + return status; + + 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; + + /* 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.*/ + status = cairo_type1_font_subset_write_header (font, name); + if (unlikely (status)) + return status; + + 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; + + /* 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; + + /* Write out new charstring count */ + length = snprintf (buffer, sizeof buffer, + "/CharStrings %d", font->num_glyphs); + status = cairo_type1_font_subset_write_encrypted (font, buffer, length); + if (unlikely (status)) + return status; + + /* Write out text between the charstring count and the first + * charstring definition */ + status = cairo_type1_font_subset_write_encrypted (font, glyph_count_end, + dict_start - glyph_count_end); + if (unlikely (status)) + return status; + + /* Write out the charstring definitions for each of the glyphs in + * the subset. */ + status = cairo_type1_font_subset_for_each_glyph (font, + dict_start, + font->cleartext_end, + write_used_glyphs, + &p); + if (unlikely (status)) + return status; + + /* Output what's left between the end of the glyph definitions and + * the end of the private dict to the output. */ + status = cairo_type1_font_subset_write_encrypted (font, p, + closefile_token - p + strlen ("closefile") + 1); + if (unlikely (status)) + return status; + + if (font->hex_encode) + _cairo_output_stream_write (font->output, "\n", 1); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cairo_type1_font_subset_write_trailer(cairo_type1_font_subset_t *font) +{ + const char *cleartomark_token; + int i; + static const char zeros[65] = + "0000000000000000000000000000000000000000000000000000000000000000\n"; + + + for (i = 0; i < 8; i++) + _cairo_output_stream_write (font->output, zeros, sizeof zeros); + + cleartomark_token = find_token (font->type1_data, font->type1_end, "cleartomark"); + if (cleartomark_token) { + /* Some fonts have conditional save/restore around the entire + * font dict, so we need to retain whatever postscript code + * that may come after 'cleartomark'. */ + + _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\n"); + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* some fonts do not have a newline at the end of the last line */ + _cairo_output_stream_printf (font->output, "\n"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +type1_font_write (void *closure, const unsigned char *data, unsigned int length) +{ + cairo_type1_font_subset_t *font = closure; + + return _cairo_array_append_multiple (&font->contents, data, length); +} + +static cairo_status_t +cairo_type1_font_subset_write (cairo_type1_font_subset_t *font, + const char *name) +{ + cairo_status_t status; + + status = cairo_type1_font_subset_find_segments (font); + if (unlikely (status)) + return status; + + status = cairo_type1_font_subset_decrypt_eexec_segment (font); + if (unlikely (status)) + return status; + + /* Determine which glyph definition delimiters to use. */ + 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; + } + + 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; + + font->base.data_size = _cairo_output_stream_get_position (font->output) - + font->base.header_size; + + status = cairo_type1_font_subset_write_trailer (font); + if (unlikely (status)) + return status; + + font->base.trailer_size = + _cairo_output_stream_get_position (font->output) - + font->base.header_size - font->base.data_size; + + 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_scaled_font_t *scaled_font; + cairo_status_t status; + unsigned long data_length; + + 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); + + status = scaled_font->backend->load_type1_data (scaled_font, 0, + (unsigned char *) font->type1_data, + &data_length); + if (unlikely (status)) + return status; + + 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)) + return status; + + font->output = _cairo_output_stream_create (type1_font_write, NULL, font); + if (unlikely ((status = font->output->status))) + return status; + + status = cairo_type1_font_subset_write (font, name); + if (unlikely (status)) + return status; + + font->base.data = _cairo_array_index (&font->contents, 0); + + return status; +} + +static cairo_status_t +_cairo_type1_font_subset_fini (cairo_type1_font_subset_t *font) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + unsigned int i; + + /* If the subset generation failed, some of the pointers below may + * be NULL depending on at which point the error occurred. */ + + _cairo_array_fini (&font->contents); + + free (font->type1_data); + for (i = 0; i < _cairo_array_num_elements (&font->glyph_names_array); i++) { + char **s; + + 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); + + free (font->base.base_font); + + free (font->subset_index_to_glyphs); + + free (font->cleartext); + + return status; +} + +cairo_status_t +_cairo_type1_subset_init (cairo_type1_subset_t *type1_subset, + const char *name, + cairo_scaled_font_subset_t *scaled_font_subset, + cairo_bool_t hex_encode) +{ + cairo_type1_font_subset_t font; + cairo_status_t status; + cairo_bool_t is_synthetic; + unsigned long length; + unsigned int i; + char buf[30]; + + /* 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 (is_synthetic) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + status = _cairo_type1_font_subset_init (&font, scaled_font_subset, hex_encode); + if (unlikely (status)) + return status; + + status = cairo_type1_font_subset_generate (&font, name); + if (unlikely (status)) + goto fail1; + + if (font.base.base_font) { + type1_subset->base_font = strdup (font.base.base_font); + } else { + snprintf(buf, sizeof (buf), "CairoFont-%u-%u", + scaled_font_subset->font_id, scaled_font_subset->subset_id); + type1_subset->base_font = strdup (buf); + } + if (unlikely (type1_subset->base_font == NULL)) + goto fail1; + + type1_subset->widths = calloc (sizeof (double), font.num_glyphs); + if (unlikely (type1_subset->widths == NULL)) + goto fail2; + for (i = 0; i < font.base.num_glyphs; i++) { + if (font.glyphs[i].subset_index < 0) + continue; + type1_subset->widths[font.glyphs[i].subset_index] = + font.glyphs[i].width; + } + + 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 = _cairo_malloc (length); + if (unlikely (type1_subset->data == NULL)) + goto fail3; + + memcpy (type1_subset->data, + _cairo_array_index (&font.contents, 0), length); + + type1_subset->header_length = font.base.header_size; + type1_subset->data_length = font.base.data_size; + type1_subset->trailer_length = font.base.trailer_size; + + return _cairo_type1_font_subset_fini (&font); + + fail3: + free (type1_subset->widths); + fail2: + free (type1_subset->base_font); + fail1: + _cairo_type1_font_subset_fini (&font); + + return status; +} + +void +_cairo_type1_subset_fini (cairo_type1_subset_t *subset) +{ + free (subset->base_font); + free (subset->widths); + free (subset->data); +} + +cairo_bool_t +_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) +{ + cairo_status_t status; + unsigned long length; + unsigned char buf[64]; + + if (!scaled_font->backend->load_type1_data) + return FALSE; + + status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &length); + if (status) + return FALSE; + + /* We only need a few bytes to test for Type 1 */ + if (length > sizeof (buf)) + length = sizeof (buf); + + status = scaled_font->backend->load_type1_data (scaled_font, 0, buf, &length); + if (status) + return FALSE; + + return check_fontdata_is_type1 (buf, length); +} + +#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 new file mode 100644 index 0000000000..6f40f1c25b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h @@ -0,0 +1,89 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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_TYPE3_GLYPH_SURFACE_PRIVATE_H +#define CAIRO_TYPE3_GLYPH_SURFACE_PRIVATE_H + +#include "cairoint.h" + +#if CAIRO_HAS_FONT_SUBSET + +#include "cairo-surface-private.h" +#include "cairo-surface-clipper-private.h" +#include "cairo-pdf-operators-private.h" + +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; + + cairo_scaled_font_t *scaled_font; + cairo_output_stream_t *stream; + cairo_pdf_operators_t pdf_operators; + cairo_matrix_t cairo_to_pdf; + cairo_type3_glyph_surface_emit_image_t emit_image; + + cairo_surface_clipper_t clipper; +} cairo_type3_glyph_surface_t; + +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_bool_t ps_output); + +cairo_private void +_cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, + cairo_pdf_operators_use_font_subset_t use_font_subset, + void *closure); + +cairo_private cairo_status_t +_cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, + unsigned long glyph_index); + +cairo_private cairo_status_t +_cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, + cairo_output_stream_t *stream, + unsigned long glyph_index, + cairo_box_t *bbox, + double *width); + +#endif /* CAIRO_HAS_FONT_SUBSET */ + +#endif /* CAIRO_TYPE3_GLYPH_SURFACE_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c new file mode 100644 index 0000000000..6b21023190 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c @@ -0,0 +1,560 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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_FONT_SUBSET + +#include "cairo-type3-glyph-surface-private.h" +#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; + +static cairo_status_t +_cairo_type3_glyph_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_type3_glyph_surface_t *surface = cairo_container_of (clipper, + cairo_type3_glyph_surface_t, + clipper); + + if (path == NULL) { + _cairo_output_stream_printf (surface->stream, "Q q\n"); + return CAIRO_STATUS_SUCCESS; + } + + return _cairo_pdf_operators_clip (&surface->pdf_operators, + path, + fill_rule); +} + +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_bool_t ps) +{ + cairo_type3_glyph_surface_t *surface; + + if (unlikely (stream != NULL && stream->status)) + return _cairo_surface_create_in_error (stream->status); + + 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, + TRUE); /* is_vector */ + + surface->scaled_font = scaled_font; + surface->stream = stream; + surface->emit_image = emit_image; + + /* Setup the transform from the user-font device space to Type 3 + * font space. The Type 3 font space is defined by the FontMatrix + * entry in the Type 3 dictionary. In the PDF backend this is an + * identity matrix. */ + surface->cairo_to_pdf = scaled_font->scale_inverse; + + _cairo_pdf_operators_init (&surface->pdf_operators, + surface->stream, + &surface->cairo_to_pdf, + font_subsets, + ps); + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_type3_glyph_surface_clipper_intersect_clip_path); + + return &surface->base; +} + +static cairo_status_t +_cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, + cairo_image_surface_t *image, + cairo_matrix_t *image_matrix) +{ + cairo_status_t status; + + /* The only image type supported by Type 3 fonts are 1-bit masks */ + image = _cairo_image_surface_coerce_to_format (image, CAIRO_FORMAT_A1); + status = image->base.status; + if (unlikely (status)) + return status; + + _cairo_output_stream_printf (surface->stream, + "q %f %f %f %f %f %f cm\n", + image_matrix->xx, + image_matrix->xy, + image_matrix->yx, + image_matrix->yy, + image_matrix->x0, + image_matrix->y0); + + status = surface->emit_image (image, surface->stream); + cairo_surface_destroy (&image->base); + + _cairo_output_stream_printf (surface->stream, + "Q\n"); + + return status; +} + +static cairo_status_t +_cairo_type3_glyph_surface_emit_image_pattern (cairo_type3_glyph_surface_t *surface, + cairo_image_surface_t *image, + const cairo_matrix_t *pattern_matrix) +{ + cairo_matrix_t mat, upside_down; + cairo_status_t status; + + if (image->width == 0 || image->height == 0) + return CAIRO_STATUS_SUCCESS; + + mat = *pattern_matrix; + + /* Get the pattern space to user space matrix */ + status = cairo_matrix_invert (&mat); + + /* cairo_pattern_set_matrix ensures the matrix is invertible */ + assert (status == CAIRO_STATUS_SUCCESS); + + /* Make this a pattern space to Type 3 font space matrix */ + cairo_matrix_multiply (&mat, &mat, &surface->cairo_to_pdf); + + /* PDF images are in a 1 unit by 1 unit image space. Turn the 1 by + * 1 image upside down to convert to flip the Y-axis going from + * cairo to PDF. Then scale the image up to the required size. */ + cairo_matrix_scale (&mat, image->width, image->height); + cairo_matrix_init (&upside_down, 1, 0, 0, -1, 0, 1); + cairo_matrix_multiply (&mat, &upside_down, &mat); + + return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); +} + +static cairo_status_t +_cairo_type3_glyph_surface_finish (void *abstract_surface) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + + return _cairo_pdf_operators_fini (&surface->pdf_operators); +} + +static cairo_int_status_t +_cairo_type3_glyph_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + const cairo_surface_pattern_t *pattern; + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + + if (source->type != CAIRO_PATTERN_TYPE_SURFACE) + return CAIRO_INT_STATUS_IMAGE_FALLBACK; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + pattern = (const cairo_surface_pattern_t *) source; + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, &image_extra); + if (unlikely (status)) + goto fail; + + status = _cairo_type3_glyph_surface_emit_image_pattern (surface, + image, + &pattern->base.matrix); + +fail: + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_int_status_t +_cairo_type3_glyph_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + return _cairo_type3_glyph_surface_paint (abstract_surface, + op, mask, + clip); +} + +static cairo_int_status_t +_cairo_type3_glyph_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_type3_glyph_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + return _cairo_pdf_operators_stroke (&surface->pdf_operators, + path, + style, + ctm, + ctm_inverse); +} + +static cairo_int_status_t +_cairo_type3_glyph_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_type3_glyph_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + return _cairo_pdf_operators_fill (&surface->pdf_operators, + path, + fill_rule); +} + +static cairo_int_status_t +_cairo_type3_glyph_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_type3_glyph_surface_t *surface = abstract_surface; + cairo_int_status_t status; + cairo_scaled_font_t *font; + cairo_matrix_t new_ctm; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &scaled_font->ctm); + font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &new_ctm, + &scaled_font->options); + if (unlikely (font->status)) + return font->status; + + status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, + NULL, 0, + glyphs, num_glyphs, + NULL, 0, + FALSE, + font); + + cairo_scaled_font_destroy (font); + + return status; +} + +static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { + CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH, + _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, /* snapshot */ + + NULL, /* copy page */ + NULL, /* show page */ + + NULL, /* _cairo_type3_glyph_surface_get_extents */ + NULL, /* _cairo_type3_glyph_surface_get_font_options */ + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _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, +}; + +static void +_cairo_type3_glyph_surface_set_stream (cairo_type3_glyph_surface_t *surface, + cairo_output_stream_t *stream) +{ + surface->stream = stream; + _cairo_pdf_operators_set_stream (&surface->pdf_operators, stream); +} + +static cairo_status_t +_cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *surface, + unsigned long glyph_index) +{ + cairo_scaled_glyph_t *scaled_glyph; + cairo_status_t status; + cairo_image_surface_t *image; + cairo_matrix_t mat; + double x, y; + + status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph); + if (unlikely (status)) + return status; + + image = scaled_glyph->surface; + if (image->width == 0 || image->height == 0) + return CAIRO_STATUS_SUCCESS; + + x = _cairo_fixed_to_double (scaled_glyph->bbox.p1.x); + y = _cairo_fixed_to_double (scaled_glyph->bbox.p2.y); + cairo_matrix_init(&mat, image->width, 0, + 0, -image->height, + x, y); + cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse); + + return _cairo_type3_glyph_surface_emit_image (surface, image, &mat); +} + +void +_cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract_surface, + cairo_pdf_operators_use_font_subset_t use_font_subset, + void *closure) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + + if (unlikely (surface->base.status)) + return; + + _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, + use_font_subset, + closure); +} + +cairo_status_t +_cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, + unsigned long glyph_index) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + cairo_scaled_glyph_t *scaled_glyph; + cairo_int_status_t status, status2; + cairo_output_stream_t *null_stream; + + if (unlikely (surface->base.status)) + return surface->base.status; + + null_stream = _cairo_null_stream_create (); + if (unlikely (null_stream->status)) + return null_stream->status; + + _cairo_type3_glyph_surface_set_stream (surface, null_stream); + + _cairo_scaled_font_freeze_cache (surface->scaled_font); + status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + &scaled_glyph); + + if (_cairo_int_status_is_error (status)) + goto cleanup; + + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = CAIRO_INT_STATUS_SUCCESS; + goto cleanup; + } + + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); + if (unlikely (status)) + goto cleanup; + + status = _cairo_pdf_operators_flush (&surface->pdf_operators); + if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) + status = CAIRO_INT_STATUS_SUCCESS; + +cleanup: + _cairo_scaled_font_thaw_cache (surface->scaled_font); + + status2 = _cairo_output_stream_destroy (null_stream); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = status2; + + return status; +} + +cairo_status_t +_cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, + cairo_output_stream_t *stream, + unsigned long glyph_index, + cairo_box_t *bbox, + double *width) +{ + cairo_type3_glyph_surface_t *surface = abstract_surface; + cairo_scaled_glyph_t *scaled_glyph; + cairo_int_status_t status, status2; + double x_advance, y_advance; + cairo_matrix_t font_matrix_inverse; + + if (unlikely (surface->base.status)) + return surface->base.status; + + _cairo_type3_glyph_surface_set_stream (surface, stream); + + _cairo_scaled_font_freeze_cache (surface->scaled_font); + status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS | + CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE, + &scaled_glyph); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_scaled_glyph_lookup (surface->scaled_font, + glyph_index, + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = CAIRO_INT_STATUS_IMAGE_FALLBACK; + } + if (_cairo_int_status_is_error (status)) { + _cairo_scaled_font_thaw_cache (surface->scaled_font); + return status; + } + + x_advance = scaled_glyph->metrics.x_advance; + y_advance = scaled_glyph->metrics.y_advance; + font_matrix_inverse = surface->scaled_font->font_matrix; + status2 = cairo_matrix_invert (&font_matrix_inverse); + + /* The invertability of font_matrix is tested in + * pdf_operators_show_glyphs before any glyphs are mapped to the + * subset. */ + assert (status2 == CAIRO_INT_STATUS_SUCCESS); + + cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance); + *width = x_advance; + + *bbox = scaled_glyph->bbox; + _cairo_matrix_transform_bounding_box_fixed (&surface->scaled_font->scale_inverse, + bbox, NULL); + + _cairo_output_stream_printf (surface->stream, + "%f 0 %f %f %f %f d1\n", + x_advance, + _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)); + + if (status == CAIRO_INT_STATUS_SUCCESS) { + cairo_output_stream_t *mem_stream; + + mem_stream = _cairo_memory_stream_create (); + status = mem_stream->status; + if (unlikely (status)) + goto FAIL; + + _cairo_type3_glyph_surface_set_stream (surface, mem_stream); + + _cairo_output_stream_printf (surface->stream, "q\n"); + status = _cairo_recording_surface_replay (scaled_glyph->recording_surface, + &surface->base); + + status2 = _cairo_pdf_operators_flush (&surface->pdf_operators); + 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_INT_STATUS_SUCCESS) + _cairo_memory_stream_copy (mem_stream, stream); + + status2 = _cairo_output_stream_destroy (mem_stream); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = status2; + } + + if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) + status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index); + + FAIL: + _cairo_scaled_font_thaw_cache (surface->scaled_font); + + return status; +} + +#endif /* CAIRO_HAS_FONT_SUBSET */ diff --git a/gfx/cairo/cairo/src/cairo-types-private.h b/gfx/cairo/cairo/src/cairo-types-private.h new file mode 100644 index 0000000000..d47064a854 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-types-private.h @@ -0,0 +1,406 @@ +/* -*- 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. + * + * 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_TYPES_PRIVATE_H +#define CAIRO_TYPES_PRIVATE_H + +#include "cairo.h" +#include "cairo-fixed-type-private.h" +#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; +typedef struct _cairo_boxes_t cairo_boxes_t; +typedef struct _cairo_cache cairo_cache_t; +typedef struct _cairo_composite_rectangles cairo_composite_rectangles_t; +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; +typedef struct _cairo_mime_data cairo_mime_data_t; +typedef struct _cairo_observer cairo_observer_t; +typedef struct _cairo_output_stream cairo_output_stream_t; +typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t; +typedef struct _cairo_path_fixed cairo_path_fixed_t; +typedef struct _cairo_rectangle_int16 cairo_glyph_size_t; +typedef struct _cairo_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: + * + * 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 + * be type-compatible with this structure (eg. they must have an + * unsigned long as the first parameter. The easiest way to get this + * is to use: + * + * typedef _my_entry { + * cairo_hash_entry_t base; + * ... Remainder of key and value fields here .. + * } my_entry_t; + * + * which then allows a pointer to my_entry_t to be passed to any of + * the #cairo_hash_table_t functions as follows without requiring a cast: + * + * _cairo_hash_table_insert (hash_table, &my_entry->base); + * + * 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 + * code will reduce the frequency of two keys with the same code for + * which keys_equal returns %FALSE. + * + * Which parts of the entry make up the "key" and which part make up + * the value are entirely up to the caller, (as determined by the + * computation going into base.hash as well as the keys_equal + * function). A few of the #cairo_hash_table_t functions accept an entry + * which will be used exclusively as a "key", (indicated by a + * parameter name of key). In these cases, the value-related fields of + * the entry need not be initialized if so desired. + **/ +struct _cairo_hash_entry { + unsigned long hash; +}; + +struct _cairo_array { + unsigned int size; + unsigned int num_elements; + unsigned int element_size; + char *elements; +}; + +typedef enum _cairo_round_glyph_positions { + CAIRO_ROUND_GLYPH_POS_DEFAULT, + CAIRO_ROUND_GLYPH_POS_ON, + CAIRO_ROUND_GLYPH_POS_OFF +} cairo_round_glyph_positions_t; + +struct _cairo_font_options { + cairo_antialias_t antialias; + cairo_subpixel_order_t subpixel_order; + cairo_lcd_filter_t lcd_filter; + 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 + madness). I'm still working on a cleaner API, but in the meantime, + at least this does prevent precision loss in color when changing + alpha. */ +struct _cairo_color { + double red; + double green; + double blue; + double alpha; + + unsigned short red_short; + unsigned short green_short; + unsigned short blue_short; + unsigned short alpha_short; +}; + +struct _cairo_color_stop { + /* unpremultiplied */ + double red; + double green; + double blue; + double alpha; + + /* unpremultipled, for convenience */ + uint16_t red_short; + uint16_t green_short; + uint16_t blue_short; + uint16_t alpha_short; +}; + +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_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, + CAIRO_INTERNAL_SURFACE_TYPE_NULL, + 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 + +typedef struct _cairo_slope { + cairo_fixed_t dx; + cairo_fixed_t dy; +} cairo_slope_t, cairo_distance_t; + +typedef struct _cairo_point_double { + double x; + 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; +} cairo_line_t, cairo_box_t; + +typedef struct _cairo_trapezoid { + cairo_fixed_t top, bottom; + cairo_line_t left, right; +} cairo_trapezoid_t; + +typedef struct _cairo_point_int { + int x, y; +} cairo_point_int_t; + +#define CAIRO_RECT_INT_MIN (INT_MIN >> CAIRO_FIXED_FRAC_BITS) +#define CAIRO_RECT_INT_MAX (INT_MAX >> CAIRO_FIXED_FRAC_BITS) + +typedef enum _cairo_direction { + CAIRO_DIRECTION_FORWARD, + CAIRO_DIRECTION_REVERSE +} cairo_direction_t; + +typedef struct _cairo_edge { + cairo_line_t line; + int top, bottom; + int dir; +} cairo_edge_t; + +typedef struct _cairo_polygon { + cairo_status_t status; + + cairo_box_t extents; + cairo_box_t limit; + const cairo_box_t *limits; + int num_limits; + + int num_edges; + int edges_size; + cairo_edge_t *edges; + cairo_edge_t edges_embedded[32]; +} cairo_polygon_t; + +typedef cairo_warn cairo_status_t +(*cairo_spline_add_point_func_t) (void *closure, + const cairo_point_t *point, + const cairo_slope_t *tangent); + +typedef struct _cairo_spline_knots { + cairo_point_t a, b, c, d; +} cairo_spline_knots_t; + +typedef struct _cairo_spline { + cairo_spline_add_point_func_t add_point_func; + void *closure; + + cairo_spline_knots_t knots; + + cairo_slope_t initial_slope; + cairo_slope_t final_slope; + + cairo_bool_t has_point; + cairo_point_t last_point; +} cairo_spline_t; + +typedef struct _cairo_pen_vertex { + cairo_point_t point; + + cairo_slope_t slope_ccw; + cairo_slope_t slope_cw; +} cairo_pen_vertex_t; + +typedef struct _cairo_pen { + double radius; + double tolerance; + + int num_vertices; + cairo_pen_vertex_t *vertices; + cairo_pen_vertex_t vertices_embedded[32]; +} cairo_pen_t; + +typedef struct _cairo_stroke_style { + double line_width; + cairo_line_cap_t line_cap; + cairo_line_join_t line_join; + double miter_limit; + double *dash; + unsigned int num_dashes; + double dash_offset; +} cairo_stroke_style_t; + +typedef struct _cairo_format_masks { + int bpp; + unsigned long alpha_mask; + unsigned long red_mask; + unsigned long green_mask; + unsigned long blue_mask; +} cairo_format_masks_t; + +typedef enum { + CAIRO_STOCK_WHITE, + CAIRO_STOCK_BLACK, + CAIRO_STOCK_TRANSPARENT, + CAIRO_STOCK_NUM_COLORS, +} cairo_stock_t; + +typedef enum _cairo_image_transparency { + CAIRO_IMAGE_IS_OPAQUE, + CAIRO_IMAGE_HAS_BILEVEL_ALPHA, + CAIRO_IMAGE_HAS_ALPHA, + 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; + unsigned long length; + cairo_destroy_func_t destroy; + void *closure; +}; + +/* + * A #cairo_unscaled_font_t is just an opaque handle we use in the + * glyph cache. + */ +typedef struct _cairo_unscaled_font { + cairo_hash_entry_t hash_entry; + cairo_reference_count_t ref_count; + const cairo_unscaled_font_backend_t *backend; +} cairo_unscaled_font_t; +CAIRO_END_DECLS + +#endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-unicode.c b/gfx/cairo/cairo/src/cairo-unicode.c new file mode 100644 index 0000000000..966ae84b59 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-unicode.c @@ -0,0 +1,447 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * The code in this file is derived from GLib's gutf8.c and + * ultimately from libunicode. It is relicensed under the + * dual LGPL/MPL with permission of the original authors. + * + * Copyright © 1999 Tom Tromey + * 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 Tom Tromey. + * and Red Hat, Inc. + * + * Contributor(s): + * Owen Taylor + */ + +#include "cairoint.h" +#include "cairo-error-private.h" + +#define UTF8_COMPUTE(Char, Mask, Len) \ + if (Char < 128) \ + { \ + Len = 1; \ + Mask = 0x7f; \ + } \ + else if ((Char & 0xe0) == 0xc0) \ + { \ + Len = 2; \ + Mask = 0x1f; \ + } \ + else if ((Char & 0xf0) == 0xe0) \ + { \ + Len = 3; \ + Mask = 0x0f; \ + } \ + else if ((Char & 0xf8) == 0xf0) \ + { \ + Len = 4; \ + Mask = 0x07; \ + } \ + else if ((Char & 0xfc) == 0xf8) \ + { \ + Len = 5; \ + Mask = 0x03; \ + } \ + else if ((Char & 0xfe) == 0xfc) \ + { \ + Len = 6; \ + Mask = 0x01; \ + } \ + else \ + Len = -1; + +#define UTF8_LENGTH(Char) \ + ((Char) < 0x80 ? 1 : \ + ((Char) < 0x800 ? 2 : \ + ((Char) < 0x10000 ? 3 : \ + ((Char) < 0x200000 ? 4 : \ + ((Char) < 0x4000000 ? 5 : 6))))) + +#define UTF8_GET(Result, Chars, Count, Mask, Len) \ + (Result) = (Chars)[0] & (Mask); \ + for ((Count) = 1; (Count) < (Len); ++(Count)) \ + { \ + if (((Chars)[(Count)] & 0xc0) != 0x80) \ + { \ + (Result) = -1; \ + break; \ + } \ + (Result) <<= 6; \ + (Result) |= ((Chars)[(Count)] & 0x3f); \ + } + +#define UNICODE_VALID(Char) \ + ((Char) < 0x110000 && \ + (((Char) & 0xFFFFF800) != 0xD800) && \ + ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ + ((Char) & 0xFFFE) != 0xFFFE) + +static const char utf8_skip_data[256] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 +}; + +#define UTF8_NEXT_CHAR(p) ((p) + utf8_skip_data[*(unsigned char *)(p)]) + +/* Converts a sequence of bytes encoded as UTF-8 to a Unicode character. + * If @p does not point to a valid UTF-8 encoded character, results are + * undefined. + **/ +static uint32_t +_utf8_get_char (const unsigned char *p) +{ + int i, mask = 0, len; + uint32_t result; + unsigned char c = (unsigned char) *p; + + UTF8_COMPUTE (c, mask, len); + if (len == -1) + return (uint32_t)-1; + UTF8_GET (result, p, i, mask, len); + + return result; +} + +/* Like _utf8_get_char, but take a maximum length + * and return (uint32_t)-2 on incomplete trailing character + */ +static uint32_t +_utf8_get_char_extended (const unsigned char *p, + long max_len) +{ + int i, len; + uint32_t wc = (unsigned char) *p; + + if (wc < 0x80) { + return wc; + } else if (wc < 0xc0) { + return (uint32_t)-1; + } else if (wc < 0xe0) { + len = 2; + wc &= 0x1f; + } else if (wc < 0xf0) { + len = 3; + wc &= 0x0f; + } else if (wc < 0xf8) { + len = 4; + wc &= 0x07; + } else if (wc < 0xfc) { + len = 5; + wc &= 0x03; + } else if (wc < 0xfe) { + len = 6; + wc &= 0x01; + } else { + return (uint32_t)-1; + } + + if (max_len >= 0 && len > max_len) { + for (i = 1; i < max_len; i++) { + if ((((unsigned char *)p)[i] & 0xc0) != 0x80) + return (uint32_t)-1; + } + return (uint32_t)-2; + } + + for (i = 1; i < len; ++i) { + uint32_t ch = ((unsigned char *)p)[i]; + + if ((ch & 0xc0) != 0x80) { + if (ch) + return (uint32_t)-1; + else + return (uint32_t)-2; + } + + wc <<= 6; + wc |= (ch & 0x3f); + } + + if (UTF8_LENGTH(wc) != len) + return (uint32_t)-1; + + return wc; +} + +/** + * _cairo_utf8_get_char_validated: + * @p: a UTF-8 string + * @unicode: location to store one Unicode character + * + * Decodes the first character of a valid UTF-8 string, and returns + * the number of bytes consumed. + * + * Note that the string should be valid. Do not use this without + * validating the string first. + * + * Returns: the number of bytes forming the character returned. + **/ +int +_cairo_utf8_get_char_validated (const char *p, + uint32_t *unicode) +{ + int i, mask = 0, len; + uint32_t result; + unsigned char c = (unsigned char) *p; + + UTF8_COMPUTE (c, mask, len); + if (len == -1) { + if (unicode) + *unicode = (uint32_t)-1; + return 1; + } + UTF8_GET (result, p, i, mask, len); + + if (unicode) + *unicode = result; + return len; +} + +/** + * _cairo_utf8_to_ucs4: + * @str: an UTF-8 string + * @len: length of @str in bytes, or -1 if it is nul-terminated. + * If @len is supplied and the string has an embedded nul + * byte, only the portion before the nul byte is converted. + * @result: location to store a pointer to a newly allocated UTF-32 + * string (always native endian), or %NULL. Free with free(). A 0 + * word will be written after the last character. + * @items_written: location to store number of 32-bit words + * written. (Not including the trailing 0) + * + * Converts a UTF-8 string to UCS-4. UCS-4 is an encoding of Unicode + * with 1 32-bit word per character. The string is validated to + * consist entirely of valid Unicode characters. + * + * Return value: %CAIRO_STATUS_SUCCESS if the entire string was + * successfully converted. %CAIRO_STATUS_INVALID_STRING if an + * invalid sequence was found. + **/ +cairo_status_t +_cairo_utf8_to_ucs4 (const char *str, + int len, + uint32_t **result, + int *items_written) +{ + uint32_t *str32 = NULL; + int n_chars, i; + const unsigned char *in; + const unsigned char * const ustr = (const unsigned char *) str; + + in = ustr; + n_chars = 0; + while ((len < 0 || ustr + len - in > 0) && *in) + { + uint32_t wc = _utf8_get_char_extended (in, ustr + len - in); + if (wc & 0x80000000 || !UNICODE_VALID (wc)) + return _cairo_error (CAIRO_STATUS_INVALID_STRING); + + n_chars++; + if (n_chars == INT_MAX) + return _cairo_error (CAIRO_STATUS_INVALID_STRING); + + in = UTF8_NEXT_CHAR (in); + } + + if (result) { + str32 = _cairo_malloc_ab (n_chars + 1, sizeof (uint32_t)); + if (!str32) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + in = ustr; + for (i=0; i < n_chars; i++) { + str32[i] = _utf8_get_char (in); + in = UTF8_NEXT_CHAR (in); + } + str32[i] = 0; + + *result = str32; + } + + if (items_written) + *items_written = n_chars; + + return CAIRO_STATUS_SUCCESS; +} + +/** + * _cairo_ucs4_to_utf8: + * @unicode: a UCS-4 character + * @utf8: buffer to write utf8 string into. Must have at least 4 bytes + * space available. Or %NULL. + * + * This space left intentionally blank. + * + * Return value: Number of bytes in the utf8 string or 0 if an invalid + * unicode character + **/ +int +_cairo_ucs4_to_utf8 (uint32_t unicode, + char *utf8) +{ + int bytes; + char *p; + + if (unicode < 0x80) { + if (utf8) + *utf8 = unicode; + return 1; + } else if (unicode < 0x800) { + bytes = 2; + } else if (unicode < 0x10000) { + bytes = 3; + } else if (unicode < 0x200000) { + bytes = 4; + } else { + return 0; + } + + if (!utf8) + return bytes; + + p = utf8 + bytes; + while (p > utf8) { + *--p = 0x80 | (unicode & 0x3f); + unicode >>= 6; + } + *p |= 0xf0 << (4 - bytes); + + 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: + * @str: an UTF-8 string + * @len: length of @str in bytes, or -1 if it is nul-terminated. + * If @len is supplied and the string has an embedded nul + * byte, only the portion before the nul byte is converted. + * @result: location to store a pointer to a newly allocated UTF-16 + * string (always native endian). Free with free(). A 0 + * word will be written after the last character. + * @items_written: location to store number of 16-bit words + * written. (Not including the trailing 0) + * + * Converts a UTF-8 string to UTF-16. UTF-16 is an encoding of Unicode + * where characters are represented either as a single 16-bit word, or + * as a pair of 16-bit "surrogates". The string is validated to + * consist entirely of valid Unicode characters. + * + * Return value: %CAIRO_STATUS_SUCCESS if the entire string was + * successfully converted. %CAIRO_STATUS_INVALID_STRING if an + * an invalid sequence was found. + **/ +cairo_status_t +_cairo_utf8_to_utf16 (const char *str, + int len, + uint16_t **result, + int *items_written) +{ + uint16_t *str16 = NULL; + int n16, i; + const unsigned char *in; + const unsigned char * const ustr = (const unsigned char *) str; + + in = ustr; + n16 = 0; + while ((len < 0 || ustr + len - in > 0) && *in) { + uint32_t wc = _utf8_get_char_extended (in, ustr + len - in); + if (wc & 0x80000000 || !UNICODE_VALID (wc)) + return _cairo_error (CAIRO_STATUS_INVALID_STRING); + + if (wc < 0x10000) + n16 += 1; + else + n16 += 2; + + if (n16 == INT_MAX - 1 || n16 == INT_MAX) + return _cairo_error (CAIRO_STATUS_INVALID_STRING); + + in = UTF8_NEXT_CHAR (in); + } + + str16 = _cairo_malloc_ab (n16 + 1, sizeof (uint16_t)); + if (!str16) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + in = ustr; + for (i = 0; i < n16;) { + uint32_t wc = _utf8_get_char (in); + + i += _cairo_ucs4_to_utf16 (wc, str16 + i); + + in = UTF8_NEXT_CHAR (in); + } + + str16[i] = 0; + + *result = str16; + if (items_written) + *items_written = n16; + + return CAIRO_STATUS_SUCCESS; +} +#endif 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 0000000000..9dc3231ae4 --- /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-private.h b/gfx/cairo/cairo/src/cairo-user-font-private.h new file mode 100644 index 0000000000..d54ef78b44 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-user-font-private.h @@ -0,0 +1,46 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2006, 2008 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + * Behdad Esfahbod + */ + +#ifndef CAIRO_USER_FONT_PRIVATE_H +#define CAIRO_USER_FONT_PRIVATE_H + +#include "cairo.h" +#include "cairo-compiler-private.h" + +cairo_private cairo_bool_t +_cairo_font_face_is_user (cairo_font_face_t *font_face); + +#endif /* CAIRO_USER_FONT_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-user-font.c b/gfx/cairo/cairo/src/cairo-user-font.c new file mode 100644 index 0000000000..a26fb57ee1 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-user-font.c @@ -0,0 +1,831 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2006, 2008 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + * Behdad Esfahbod + */ + +#include "cairoint.h" +#include "cairo-user-font-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-analysis-surface-private.h" +#include "cairo-error-private.h" + +/** + * SECTION:cairo-user-fonts + * @Title:User Fonts + * @Short_Description: Font support with font data provided by the user + * + * The user-font feature allows the cairo user 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 application to draw "funky" fonts. + **/ + +/** + * CAIRO_HAS_USER_FONT: + * + * Defined if the user font backend is available. + * This macro can be used to conditionally compile backend-specific code. + * The user font backend is always built in versions of cairo that support + * this feature (1.8 and later). + * + * Since: 1.8 + **/ + +typedef struct _cairo_user_scaled_font_methods { + cairo_user_scaled_font_init_func_t init; + cairo_user_scaled_font_render_glyph_func_t render_glyph; + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph; + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs; +} cairo_user_scaled_font_methods_t; + +typedef struct _cairo_user_font_face { + cairo_font_face_t base; + + /* Set to true after first scaled font is created. At that point, + * the scaled_font_methods cannot change anymore. */ + cairo_bool_t immutable; + + cairo_user_scaled_font_methods_t scaled_font_methods; +} cairo_user_font_face_t; + +typedef struct _cairo_user_scaled_font { + cairo_scaled_font_t base; + + cairo_text_extents_t default_glyph_extents; + + /* space to compute extents in, and factors to convert back to user space */ + cairo_matrix_t extent_scale; + double extent_x_scale; + double extent_y_scale; + + /* multiplier for metrics hinting */ + double snap_x_scale; + double snap_y_scale; + +} cairo_user_scaled_font_t; + +/* #cairo_user_scaled_font_t */ + +static cairo_surface_t * +_cairo_user_scaled_font_create_recording_surface (const cairo_user_scaled_font_t *scaled_font) +{ + cairo_content_t content; + + content = scaled_font->base.options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ? + CAIRO_CONTENT_COLOR_ALPHA : + CAIRO_CONTENT_ALPHA; + + return cairo_recording_surface_create (content, NULL); +} + + +static cairo_t * +_cairo_user_scaled_font_create_recording_context (const cairo_user_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface) +{ + cairo_t *cr; + + cr = cairo_create (recording_surface); + + if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + cairo_matrix_t scale; + scale = scaled_font->base.scale; + scale.x0 = scale.y0 = 0.; + cairo_set_matrix (cr, &scale); + } + + cairo_set_font_size (cr, 1.0); + cairo_set_font_options (cr, &scaled_font->base.options); + cairo_set_source_rgb (cr, 1., 1., 1.); + + return cr; +} + +static cairo_int_status_t +_cairo_user_scaled_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_surface_t *recording_surface = scaled_glyph->recording_surface; + + if (!scaled_glyph->recording_surface) { + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + cairo_text_extents_t extents = scaled_font->default_glyph_extents; + cairo_t *cr; + + if (!face->scaled_font_methods.render_glyph) + return CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; + + recording_surface = _cairo_user_scaled_font_create_recording_surface (scaled_font); + + /* special case for 0 rank matrix (as in _cairo_scaled_font_init): empty surface */ + if (!_cairo_matrix_is_scale_0 (&scaled_font->base.scale)) { + cr = _cairo_user_scaled_font_create_recording_context (scaled_font, recording_surface); + status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font, + _cairo_scaled_glyph_index(scaled_glyph), + cr, &extents); + if (status == CAIRO_INT_STATUS_SUCCESS) + status = cairo_status (cr); + + cairo_destroy (cr); + + if (unlikely (status)) { + cairo_surface_destroy (recording_surface); + return status; + } + } + + _cairo_scaled_glyph_set_recording_surface (scaled_glyph, + &scaled_font->base, + recording_surface); + + + /* set metrics */ + + if (extents.width == 0.) { + cairo_box_t bbox; + double x1, y1, x2, y2; + double x_scale, y_scale; + + /* Compute extents.x/y/width/height from recording_surface, + * in font space. + */ + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, + &scaled_font->extent_scale); + if (unlikely (status)) + return status; + + _cairo_box_to_doubles (&bbox, &x1, &y1, &x2, &y2); + + x_scale = scaled_font->extent_x_scale; + y_scale = scaled_font->extent_y_scale; + extents.x_bearing = x1 * x_scale; + extents.y_bearing = y1 * y_scale; + extents.width = (x2 - x1) * x_scale; + extents.height = (y2 - y1) * y_scale; + } + + if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { + extents.x_advance = _cairo_lround (extents.x_advance / scaled_font->snap_x_scale) * scaled_font->snap_x_scale; + extents.y_advance = _cairo_lround (extents.y_advance / scaled_font->snap_y_scale) * scaled_font->snap_y_scale; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + cairo_surface_t *surface; + cairo_format_t format; + int width, height; + + /* TODO + * extend the glyph cache to support argb glyphs. + * need to figure out the semantics and interaction with subpixel + * rendering first. + */ + + width = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x) - + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + height = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y) - + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + + 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); + + cairo_surface_set_device_offset (surface, + - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x), + - _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y)); + status = _cairo_recording_surface_replay (recording_surface, surface); + + if (unlikely (status)) { + cairo_surface_destroy(surface); + return status; + } + + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) surface); + } + + if (info & CAIRO_SCALED_GLYPH_INFO_PATH) { + cairo_path_fixed_t *path = _cairo_path_fixed_create (); + if (!path) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_recording_surface_get_path (recording_surface, path); + if (unlikely (status)) { + _cairo_path_fixed_destroy (path); + return status; + } + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + } + + return status; +} + +static unsigned long +_cairo_user_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + unsigned long glyph = 0; + + if (face->scaled_font_methods.unicode_to_glyph) { + cairo_status_t status; + + status = face->scaled_font_methods.unicode_to_glyph (&scaled_font->base, + ucs4, &glyph); + + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + goto not_implemented; + + if (status != CAIRO_STATUS_SUCCESS) { + status = _cairo_scaled_font_set_error (&scaled_font->base, status); + glyph = 0; + } + + } else { +not_implemented: + glyph = ucs4; + } + + return glyph; +} + +static cairo_int_status_t +_cairo_user_text_to_glyphs (void *abstract_font, + 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_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_user_scaled_font_t *scaled_font = abstract_font; + cairo_user_font_face_t *face = + (cairo_user_font_face_t *) scaled_font->base.font_face; + + if (face->scaled_font_methods.text_to_glyphs) { + int i; + cairo_glyph_t *orig_glyphs = *glyphs; + int orig_num_glyphs = *num_glyphs; + + status = face->scaled_font_methods.text_to_glyphs (&scaled_font->base, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, cluster_flags); + + if (status != CAIRO_INT_STATUS_SUCCESS && + status != CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED) + return status; + + if (status == CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED || + *num_glyphs < 0) { + if (orig_glyphs != *glyphs) { + cairo_glyph_free (*glyphs); + *glyphs = orig_glyphs; + } + *num_glyphs = orig_num_glyphs; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Convert from font space to user space and add x,y */ + for (i = 0; i < *num_glyphs; i++) { + double gx = (*glyphs)[i].x; + double gy = (*glyphs)[i].y; + + cairo_matrix_transform_point (&scaled_font->base.font_matrix, + &gx, &gy); + + (*glyphs)[i].x = gx + x; + (*glyphs)[i].y = gy + y; + } + } + + return status; +} + +static cairo_status_t +_cairo_user_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 **scaled_font); + +static cairo_status_t +_cairo_user_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + return _cairo_font_face_twin_create_for_toy (toy_face, font_face); +} + +static const cairo_scaled_font_backend_t _cairo_user_scaled_font_backend = { + CAIRO_FONT_TYPE_USER, + NULL, /* scaled_font_fini */ + _cairo_user_scaled_glyph_init, + _cairo_user_text_to_glyphs, + _cairo_user_ucs4_to_index, + NULL, /* show_glyphs */ + NULL, /* load_truetype_table */ + NULL /* index_to_ucs4 */ +}; + +/* #cairo_user_font_face_t */ + +static cairo_status_t +_cairo_user_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 **scaled_font) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_user_font_face_t *font_face = abstract_face; + cairo_user_scaled_font_t *user_scaled_font = NULL; + cairo_font_extents_t font_extents = {1., 0., 1., 1., 0.}; + + font_face->immutable = TRUE; + + user_scaled_font = _cairo_malloc (sizeof (cairo_user_scaled_font_t)); + if (unlikely (user_scaled_font == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = _cairo_scaled_font_init (&user_scaled_font->base, + &font_face->base, + font_matrix, ctm, options, + &_cairo_user_scaled_font_backend); + + if (unlikely (status)) { + free (user_scaled_font); + return status; + } + + /* XXX metrics hinting? */ + + /* compute a normalized version of font scale matrix to compute + * extents in. This is to minimize error caused by the cairo_fixed_t + * representation. */ + { + double fixed_scale, x_scale, y_scale; + + user_scaled_font->extent_scale = user_scaled_font->base.scale_inverse; + status = _cairo_matrix_compute_basis_scale_factors (&user_scaled_font->extent_scale, + &x_scale, &y_scale, + 1); + if (status == CAIRO_STATUS_SUCCESS) { + + if (x_scale == 0) x_scale = 1.; + if (y_scale == 0) y_scale = 1.; + + user_scaled_font->snap_x_scale = x_scale; + user_scaled_font->snap_y_scale = y_scale; + + /* since glyphs are pretty much 1.0x1.0, we can reduce error by + * scaling to a larger square. say, 1024.x1024. */ + fixed_scale = 1024.; + x_scale /= fixed_scale; + y_scale /= fixed_scale; + + cairo_matrix_scale (&user_scaled_font->extent_scale, 1. / x_scale, 1. / y_scale); + + user_scaled_font->extent_x_scale = x_scale; + user_scaled_font->extent_y_scale = y_scale; + } + } + + if (status == CAIRO_STATUS_SUCCESS && + font_face->scaled_font_methods.init != NULL) + { + /* Lock the scaled_font mutex such that user doesn't accidentally try + * to use it just yet. */ + CAIRO_MUTEX_LOCK (user_scaled_font->base.mutex); + + /* Give away fontmap lock such that user-font can use other fonts */ + status = _cairo_scaled_font_register_placeholder_and_unlock_font_map (&user_scaled_font->base); + if (status == CAIRO_STATUS_SUCCESS) { + cairo_surface_t *recording_surface; + cairo_t *cr; + + recording_surface = _cairo_user_scaled_font_create_recording_surface (user_scaled_font); + cr = _cairo_user_scaled_font_create_recording_context (user_scaled_font, recording_surface); + cairo_surface_destroy (recording_surface); + + status = font_face->scaled_font_methods.init (&user_scaled_font->base, + cr, + &font_extents); + + if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED) + status = CAIRO_STATUS_SUCCESS; + + if (status == CAIRO_STATUS_SUCCESS) + status = cairo_status (cr); + + cairo_destroy (cr); + + _cairo_scaled_font_unregister_placeholder_and_lock_font_map (&user_scaled_font->base); + } + + CAIRO_MUTEX_UNLOCK (user_scaled_font->base.mutex); + } + + if (status == CAIRO_STATUS_SUCCESS) + status = _cairo_scaled_font_set_metrics (&user_scaled_font->base, &font_extents); + + if (status != CAIRO_STATUS_SUCCESS) { + _cairo_scaled_font_fini (&user_scaled_font->base); + free (user_scaled_font); + } else { + user_scaled_font->default_glyph_extents.x_bearing = 0.; + user_scaled_font->default_glyph_extents.y_bearing = -font_extents.ascent; + user_scaled_font->default_glyph_extents.width = 0.; + user_scaled_font->default_glyph_extents.height = font_extents.ascent + font_extents.descent; + user_scaled_font->default_glyph_extents.x_advance = font_extents.max_x_advance; + user_scaled_font->default_glyph_extents.y_advance = 0.; + + *scaled_font = &user_scaled_font->base; + } + + return status; +} + +const cairo_font_face_backend_t _cairo_user_font_face_backend = { + CAIRO_FONT_TYPE_USER, + _cairo_user_font_face_create_for_toy, + _cairo_font_face_destroy, + _cairo_user_font_face_scaled_font_create +}; + + +cairo_bool_t +_cairo_font_face_is_user (cairo_font_face_t *font_face) +{ + return font_face->backend == &_cairo_user_font_face_backend; +} + +/* Implement the public interface */ + +/** + * cairo_user_font_face_create: + * + * Creates a new user font-face. + * + * Use the setter functions to associate callbacks with the returned + * user font. The only mandatory callback is render_glyph. + * + * After the font-face is created, the user can attach arbitrary data + * (the actual font data) to it using cairo_font_face_set_user_data() + * and access it from the user-font callbacks by using + * cairo_scaled_font_get_font_face() followed by + * cairo_font_face_get_user_data(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + * + * Since: 1.8 + **/ +cairo_font_face_t * +cairo_user_font_face_create (void) +{ + cairo_user_font_face_t *font_face; + + 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; + } + + _cairo_font_face_init (&font_face->base, &_cairo_user_font_face_backend); + + font_face->immutable = FALSE; + memset (&font_face->scaled_font_methods, 0, sizeof (font_face->scaled_font_methods)); + + return &font_face->base; +} +slim_hidden_def(cairo_user_font_face_create); + +/* User-font method setters */ + + +/** + * cairo_user_font_face_set_init_func: + * @font_face: A user font face + * @init_func: The init callback, or %NULL + * + * Sets the scaled-font initialization function of a user-font. + * See #cairo_user_scaled_font_init_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * Since: 1.8 + **/ +void +cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_init_func_t init_func) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.init = init_func; +} +slim_hidden_def(cairo_user_font_face_set_init_func); + +/** + * cairo_user_font_face_set_render_glyph_func: + * @font_face: A user font face + * @render_glyph_func: The render_glyph callback, or %NULL + * + * Sets the glyph rendering function of a user-font. + * See #cairo_user_scaled_font_render_glyph_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * The render_glyph callback is the only mandatory callback of a user-font. + * If the callback is %NULL and a glyph is tried to be rendered using + * @font_face, a %CAIRO_STATUS_USER_FONT_ERROR will occur. + * + * Since: 1.8 + **/ +void +cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_render_glyph_func_t render_glyph_func) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.render_glyph = render_glyph_func; +} +slim_hidden_def(cairo_user_font_face_set_render_glyph_func); + +/** + * cairo_user_font_face_set_text_to_glyphs_func: + * @font_face: A user font face + * @text_to_glyphs_func: The text_to_glyphs callback, or %NULL + * + * Sets th text-to-glyphs conversion function of a user-font. + * See #cairo_user_scaled_font_text_to_glyphs_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * Since: 1.8 + **/ +void +cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.text_to_glyphs = text_to_glyphs_func; +} + +/** + * cairo_user_font_face_set_unicode_to_glyph_func: + * @font_face: A user font face + * @unicode_to_glyph_func: The unicode_to_glyph callback, or %NULL + * + * Sets the unicode-to-glyph conversion function of a user-font. + * See #cairo_user_scaled_font_unicode_to_glyph_func_t for details of how the callback + * works. + * + * The font-face should not be immutable or a %CAIRO_STATUS_USER_FONT_IMMUTABLE + * error will occur. A user font-face is immutable as soon as a scaled-font + * is created from it. + * + * Since: 1.8 + **/ +void +cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func) +{ + cairo_user_font_face_t *user_font_face; + if (font_face->status) + return; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + if (user_font_face->immutable) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE)) + return; + } + user_font_face->scaled_font_methods.unicode_to_glyph = unicode_to_glyph_func; +} +slim_hidden_def(cairo_user_font_face_set_unicode_to_glyph_func); + +/* User-font method getters */ + +/** + * cairo_user_font_face_get_init_func: + * @font_face: A user font face + * + * Gets the scaled-font initialization function of a user-font. + * + * Return value: The init callback of @font_face + * or %NULL if none set or an error has occurred. + * + * Since: 1.8 + **/ +cairo_user_scaled_font_init_func_t +cairo_user_font_face_get_init_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return NULL; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + return user_font_face->scaled_font_methods.init; +} + +/** + * cairo_user_font_face_get_render_glyph_func: + * @font_face: A user font face + * + * Gets the glyph rendering function of a user-font. + * + * Return value: The render_glyph callback of @font_face + * or %NULL if none set or an error has occurred. + * + * Since: 1.8 + **/ +cairo_user_scaled_font_render_glyph_func_t +cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return NULL; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + return user_font_face->scaled_font_methods.render_glyph; +} + +/** + * cairo_user_font_face_get_text_to_glyphs_func: + * @font_face: A user font face + * + * Gets the text-to-glyphs conversion function of a user-font. + * + * Return value: The text_to_glyphs callback of @font_face + * or %NULL if none set or an error occurred. + * + * Since: 1.8 + **/ +cairo_user_scaled_font_text_to_glyphs_func_t +cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return NULL; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + return user_font_face->scaled_font_methods.text_to_glyphs; +} + +/** + * cairo_user_font_face_get_unicode_to_glyph_func: + * @font_face: A user font face + * + * Gets the unicode-to-glyph conversion function of a user-font. + * + * Return value: The unicode_to_glyph callback of @font_face + * or %NULL if none set or an error occurred. + * + * Since: 1.8 + **/ +cairo_user_scaled_font_unicode_to_glyph_func_t +cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face) +{ + cairo_user_font_face_t *user_font_face; + + if (font_face->status) + return NULL; + + if (! _cairo_font_face_is_user (font_face)) { + if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH)) + return NULL; + } + + user_font_face = (cairo_user_font_face_t *) font_face; + return user_font_face->scaled_font_methods.unicode_to_glyph; +} diff --git a/gfx/cairo/cairo/src/cairo-version.c b/gfx/cairo/cairo/src/cairo-version.c new file mode 100644 index 0000000000..943e1cde02 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-version.c @@ -0,0 +1,255 @@ +/* -*- 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" + +/** + * SECTION:cairo-version + * @Title: Version Information + * @Short_Description: Compile-time and run-time version checks. + * + * Cairo has a three-part version number scheme. In this scheme, we use + * even vs. odd numbers to distinguish fixed points in the software + * 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 + * ask the question whether this same code can be moved "to" the API/ABI + * of another version of cairo. + * + * Moving from a release to any later version (release, snapshot, + * development) is always guaranteed to provide compatibility. + * + * Moving from a snapshot to any later version is not guaranteed to + * provide compatibility, since snapshots may introduce new API that ends + * up being removed before the next release. + * + * Moving from an in-development version (odd micro component) to any + * later version is not guaranteed to provide compatibility. In fact, + * there's not even a guarantee that the code will even continue to work + * 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 + *   + * 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: + * + * + * ##if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 0, 0) + * printf ("Compiling with suitable cairo version: %s\n", %CAIRO_VERSION_STRING); + * ##endif + * + * 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. + * + * Since: 1.0 + **/ + +/** + * CAIRO_VERSION_STRINGIZE: + * @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 string. The numbers + * 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. + * + * Since: 1.8 + **/ + +/** + * cairo_version: + * + * Returns the version of the cairo library encoded in a single + * integer as per %CAIRO_VERSION_ENCODE. The encoding ensures that + * later versions compare greater than earlier versions. + * + * A run-time comparison to check that cairo's version is greater than + * or equal to version X.Y.Z could be performed as follows: + * + * + * if (cairo_version() >= CAIRO_VERSION_ENCODE(X,Y,Z)) {...} + * + * + * See also cairo_version_string() as well as the compile-time + * equivalents %CAIRO_VERSION and %CAIRO_VERSION_STRING. + * + * Return value: the encoded version. + * + * Since: 1.0 + **/ +int +cairo_version (void) +{ + return CAIRO_VERSION; +} + +/** + * cairo_version_string: + * + * Returns the version of the cairo library as a human-readable string + * of the form "X.Y.Z". + * + * See also cairo_version() as well as the compile-time equivalents + * %CAIRO_VERSION_STRING and %CAIRO_VERSION. + * + * Return value: a string containing the version. + * + * Since: 1.0 + **/ +const char* +cairo_version_string (void) +{ + return CAIRO_VERSION_STRING; +} +slim_hidden_def (cairo_version_string); diff --git a/gfx/cairo/cairo/src/cairo-version.h b/gfx/cairo/cairo/src/cairo-version.h new file mode 100644 index 0000000000..9fd69fb45e --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-version.h @@ -0,0 +1,8 @@ +#ifndef CAIRO_VERSION_H +#define CAIRO_VERSION_H + +#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 new file mode 100644 index 0000000000..cbff748fe6 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-vg-surface.c @@ -0,0 +1,1854 @@ +/* -*- 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 Opened Hand Ltd. + * 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.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): + * Pierre Tardy + * Øyvind Kolås + * Vladimi Vukicevic (stubbed out base backend) + * Chris Wilson + */ + +#include "cairoint.h" + +#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-inline.h" +#include "cairo-surface-clipper-private.h" + +#include +#include + +//#define OPENVG_DEBUG + +/* + * Work that needs to be done: + * - Glyph cache / proper font support + * + * - First-class paths + * Paths are expensive for OpenVG, reuse paths whenever possible. + * So add a path cache, and first class paths! + */ + +typedef struct _cairo_vg_surface cairo_vg_surface_t; + +/* XXX need GL specific context control. :( */ +struct _cairo_vg_context { + cairo_status_t status; + cairo_reference_count_t ref_count; + + unsigned long target_id; + + VGPaint paint; + cairo_vg_surface_t *source; + double alpha; + + cairo_cache_t snapshot_cache; + + void *display; + void *context; + + cairo_status_t (*create_target) (cairo_vg_context_t *, + cairo_vg_surface_t *); + cairo_status_t (*set_target) (cairo_vg_context_t *, + cairo_vg_surface_t *); + void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *); +}; + +struct _cairo_vg_surface { + cairo_surface_t base; + + cairo_vg_context_t *context; + + VGImage image; + VGImageFormat format; + int width; + int height; + cairo_bool_t own_image; + + cairo_cache_entry_t snapshot_cache_entry; + + cairo_surface_clipper_t clipper; + + unsigned long target_id; +}; + +static const cairo_surface_backend_t cairo_vg_surface_backend; + +slim_hidden_proto (cairo_vg_surface_create); + +static cairo_surface_t * +_vg_surface_create_internal (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height); + +static cairo_vg_context_t * +_vg_context_reference (cairo_vg_context_t *context) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); + + _cairo_reference_count_inc (&context->ref_count); + + return context; +} + +static cairo_vg_context_t * +_vg_context_lock (cairo_vg_context_t *context) +{ + /* XXX if we need to add locking, then it has to be recursive */ + return context; +} + +static cairo_int_status_t +_vg_context_set_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + cairo_status_t status; + + if (surface->target_id == 0) { + status = context->create_target (context, surface); + if (unlikely (status)) + return status; + } + + if (context->target_id == surface->target_id) + return CAIRO_STATUS_SUCCESS; + + context->target_id = surface->target_id; + + return context->set_target (context, surface); +} + +static void +_vg_context_destroy_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + if (surface->target_id == 0) + return; + + if (context->target_id == surface->target_id) + context->set_target (context, NULL); + + context->destroy_target (context, surface); +} + +static cairo_bool_t +_vg_snapshot_cache_can_remove (const void *entry) +{ + return TRUE; +} + +static void +_vg_snapshot_cache_remove (void *cache_entry) +{ + cairo_vg_surface_t *surface = cairo_container_of (cache_entry, + cairo_vg_surface_t, + snapshot_cache_entry); + surface->snapshot_cache_entry.hash = 0; + cairo_surface_destroy (&surface->base); +} + +static cairo_status_t +_vg_context_init (cairo_vg_context_t *context) +{ + cairo_status_t status; + + context->status = CAIRO_STATUS_SUCCESS; + CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1); + + status = _cairo_cache_init (&context->snapshot_cache, + NULL, + _vg_snapshot_cache_can_remove, + _vg_snapshot_cache_remove, + 16*1024*1024); + if (unlikely (status)) + return status; + + context->target_id = 0; + context->source = NULL; + context->alpha = 1.0; + + context->paint = vgCreatePaint (); + vgLoadIdentity (); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_context_destroy (cairo_vg_context_t *context) +{ + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&context->ref_count)) + return; + + if (context->paint != VG_INVALID_HANDLE) + vgDestroyPaint (context->paint); + + _cairo_cache_fini (&context->snapshot_cache); + free (context); +} + +static void +_vg_context_unlock (cairo_vg_context_t *context) +{ +} + +#ifdef OPENVG_DEBUG +static void check_vg_errors(const char*function,int line) +{ + int err = vgGetError(); + if (err != VG_NO_ERROR){ + printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err); + assert(err == VG_NO_ERROR); + } + +} +#define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__) +#else +#define CHECK_VG_ERRORS() do{}while(0) +#endif //OPENVG_DEBUG + +static pixman_format_code_t +_vg_format_to_pixman (VGImageFormat format, + cairo_bool_t *needs_premult_fixup) +{ + *needs_premult_fixup = FALSE; + switch (format) { + /* RGB{A,X} channel ordering */ + 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; + case VG_sL_8: return PIXMAN_g8; + case VG_lRGBX_8888: return 0; + case VG_lRGBA_8888: return 0; + case VG_lRGBA_8888_PRE: return 0; + case VG_lL_8: return 0; + case VG_A_8: return PIXMAN_a8; + case VG_BW_1: return PIXMAN_a1; + case VG_A_1: return PIXMAN_a1; + case VG_A_4: return PIXMAN_a4; + + /* {A,X}RGB channel ordering */ + case VG_sXRGB_8888: return PIXMAN_x8r8g8b8; + case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8; + case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8; + case VG_sARGB_1555: return 0; + case VG_sARGB_4444: return 0; + case VG_lXRGB_8888: return 0; + case VG_lARGB_8888: return 0; + case VG_lARGB_8888_PRE: return 0; + + /* BGR{A,X} channel ordering */ + case VG_sBGRX_8888: return PIXMAN_b8g8r8x8; + case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8; + case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8; + case VG_sBGR_565: return PIXMAN_b5g6r5; + case VG_sBGRA_5551: return 0; + case VG_sBGRA_4444: return 0; + case VG_lBGRX_8888: return 0; + case VG_lBGRA_8888: return 0; + case VG_lBGRA_8888_PRE: return 0; + + /* {A,X}BGR channel ordering */ + case VG_sXBGR_8888: return PIXMAN_x8b8g8r8; + case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8; + case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8; + case VG_sABGR_1555: return 0; + case VG_sABGR_4444: return 0; + case VG_lXBGR_8888: return 0; + case VG_lABGR_8888: return 0; + case VG_lABGR_8888_PRE: return 0; + default: return 0; + } +} + +static pixman_format_code_t +_vg_format_to_content (VGImageFormat format) +{ + /* XXX could use more simple bit tests */ + switch (format) { + /* RGB{A,X} channel ordering */ + case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR; + case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGB_565: return CAIRO_CONTENT_COLOR; + case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sL_8: return CAIRO_CONTENT_ALPHA; + case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR; + case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lL_8: return CAIRO_CONTENT_ALPHA; + case VG_A_8: return CAIRO_CONTENT_ALPHA; + case VG_A_4: return CAIRO_CONTENT_ALPHA; + case VG_A_1: return CAIRO_CONTENT_ALPHA; + case VG_BW_1: return CAIRO_CONTENT_ALPHA; + + /* {A,X}RGB channel ordering */ + case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR; + case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR; + case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + + /* BGR{A,X} channel ordering */ + case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR; + case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGR_565: return CAIRO_CONTENT_COLOR; + case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR; + case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + + /* {A,X}BGR channel ordering */ + case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR; + case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR; + case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA; + case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA; + default: return 0; + } +} + +static VGImageFormat +_vg_format_from_pixman (pixman_format_code_t format) +{ + /* XXX _PRE needs fixup */ + switch ((int) format) { + case PIXMAN_r5g6b5: return VG_sRGB_565; + case PIXMAN_g8: return VG_sL_8; + case PIXMAN_a8: return VG_A_8; + case PIXMAN_a1: return VG_BW_1; + case PIXMAN_x8r8g8b8: return VG_sXRGB_8888; + case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE + case PIXMAN_b8g8r8x8: return VG_sBGRX_8888; + case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE + case PIXMAN_b5g6r5: return VG_sBGR_565; + case PIXMAN_x8b8g8r8: return VG_sXBGR_8888; + case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE + default: return 0; + } +} + +static VGImageFormat +_vg_format_for_content (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return VG_A_8; + case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888; + default: ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE + } +} + +static cairo_surface_t * +_vg_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_vg_surface_t *surface = abstract_surface; + + if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || + height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + return cairo_vg_surface_create (surface->context, content, width, height); +} + +static cairo_status_t +_vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_vg_surface_t *surface = cairo_container_of (clipper, + cairo_vg_surface_t, + clipper); + cairo_vg_surface_t *mask; + cairo_status_t status; + + if (path == NULL) { + vgMask (VG_INVALID_HANDLE, + VG_FILL_MASK, 0, 0, surface->width, surface->height); + vgSeti (VG_MASKING, VG_FALSE); + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; + } + + mask = (cairo_vg_surface_t *) + _vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA, + surface->width, surface->height); + if (unlikely (mask == NULL)) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (mask->base.status)) + return mask->base.status; + + status = _cairo_surface_fill (&mask->base, + CAIRO_OPERATOR_SOURCE, + &_cairo_pattern_white.base, + path, fill_rule, tolerance, antialias, + NULL); + if (status) { + cairo_surface_destroy (&mask->base); + return status; + } + + vgSeti (VG_MASKING, VG_TRUE); + vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height); + + cairo_surface_destroy (&mask->base); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_vg_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_vg_surface_t *surface = abstract_surface; + + extents->x = 0; + extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + + return TRUE; +} + +#define MAX_SEG 16 /* max number of knots to upload in a batch */ + +typedef struct _vg_path { + VGPath path; + const cairo_matrix_t *ctm_inverse; + + VGubyte gseg[MAX_SEG]; + VGfloat gdata[MAX_SEG*3*2]; + int dcount; + int scount; +} vg_path_t; + +static cairo_status_t +_vg_move_to (void *closure, + const cairo_point_t *point) +{ + vg_path_t *path = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (path->ctm_inverse) + cairo_matrix_transform_point (path->ctm_inverse, &x, &y); + + path->gseg[path->scount++] = VG_MOVE_TO; + path->gdata[path->dcount++] = x; + path->gdata[path->dcount++] = y; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_line_to (void *closure, + const cairo_point_t *point) +{ + vg_path_t *path = closure; + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + + if (path->ctm_inverse) + cairo_matrix_transform_point (path->ctm_inverse, &x, &y); + + path->gseg[path->scount++] = VG_LINE_TO; + path->gdata[path->dcount++] = x; + path->gdata[path->dcount++] = y; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_curve_to (void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + vg_path_t *path = closure; + double x0 = _cairo_fixed_to_double (p0->x); + double y0 = _cairo_fixed_to_double (p0->y); + double x1 = _cairo_fixed_to_double (p1->x); + double y1 = _cairo_fixed_to_double (p1->y); + double x2 = _cairo_fixed_to_double (p2->x); + double y2 = _cairo_fixed_to_double (p2->y); + + if (path->ctm_inverse) { + cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0); + cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1); + cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2); + } + + path->gseg[path->scount++] = VG_CUBIC_TO; + path->gdata[path->dcount++] = x0; + path->gdata[path->dcount++] = y0; + path->gdata[path->dcount++] = x1; + path->gdata[path->dcount++] = y1; + path->gdata[path->dcount++] = x2; + path->gdata[path->dcount++] = y2; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData(path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_close_path (void *closure) +{ + vg_path_t *path = closure; + + path->gseg[path->scount++] = VG_CLOSE_PATH; + + if (path->scount >= MAX_SEG-1) { + vgAppendPathData (path->path, path->scount, path->gseg, path->gdata); + path->scount = 0; + path->dcount = 0; + } + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_path_from_cairo (vg_path_t *vg_path, + const cairo_path_fixed_t *path) +{ + cairo_status_t status; + + vg_path->scount = 0; + vg_path->dcount = 0; + + status = _cairo_path_fixed_interpret (path, + _vg_move_to, + _vg_line_to, + _vg_curve_to, + _vg_close_path, + vg_path); + assert (status == CAIRO_STATUS_SUCCESS); + + vgAppendPathData (vg_path->path, + vg_path->scount, vg_path->gseg, vg_path->gdata); + CHECK_VG_ERRORS(); +} + +static cairo_bool_t +_vg_is_supported_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_SOURCE: + case CAIRO_OPERATOR_OVER: + case CAIRO_OPERATOR_IN: + case CAIRO_OPERATOR_DEST_OVER: + case CAIRO_OPERATOR_DEST_IN: + case CAIRO_OPERATOR_ADD: + return TRUE; + + default: + return FALSE; + } +} + +static VGBlendMode +_vg_operator (cairo_operator_t op) +{ + switch ((int) op) { + case CAIRO_OPERATOR_SOURCE: + return VG_BLEND_SRC; + case CAIRO_OPERATOR_OVER: + return VG_BLEND_SRC_OVER; + case CAIRO_OPERATOR_IN: + return VG_BLEND_SRC_IN; + case CAIRO_OPERATOR_DEST_OVER: + return VG_BLEND_DST_OVER; + case CAIRO_OPERATOR_DEST_IN: + return VG_BLEND_DST_IN; + case CAIRO_OPERATOR_ADD: + return VG_BLEND_ADDITIVE; + default: + ASSERT_NOT_REACHED; + return VG_BLEND_SRC_OVER; + } +} + +static VGFillRule +_vg_fill_rule_from_cairo (cairo_fill_rule_t rule) +{ + switch (rule) { + case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD; + case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO; + } + + ASSERT_NOT_REACHED; + return VG_NON_ZERO; +} + +static VGRenderingQuality +_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: + return VG_RENDERING_QUALITY_NONANTIALIASED; + } + + ASSERT_NOT_REACHED; + return VG_RENDERING_QUALITY_BETTER; +} + +static VGCapStyle +_vg_line_cap_from_cairo (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT; + case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND; + case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE; + } + + ASSERT_NOT_REACHED; + return VG_CAP_BUTT; +} + +static VGJoinStyle +_vg_line_join_from_cairo (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER; + case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND; + case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL; + } + + ASSERT_NOT_REACHED; + return VG_JOIN_MITER; +} + +static void +_vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src) +{ + dst[0] = /* sx */ src->xx; + dst[1] = /* shy */ src->yx; + dst[2] = /* w0 */ 0; + dst[3] = /* shx */ src->xy; + dst[4] = /* sy */ src->yy; + dst[5] = /* w1 */ 0; + dst[6] = /* tx */ src->x0; + dst[7] = /* ty */ src->y0; + dst[8] = /* w2 */ 0; +} + +static cairo_status_t +_vg_setup_gradient_stops (cairo_vg_context_t *context, + const cairo_gradient_pattern_t *pattern) +{ + VGint numstops = pattern->n_stops; + VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)]; + int i; + + if (numstops*5 < ARRAY_LENGTH (stack_stops)) { + stops = stack_stops; + } else { + stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat)); + if (unlikely (stops == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + for (i = 0; i < numstops; i++) { + stops[i*5 + 0] = pattern->stops[i].offset; + stops[i*5 + 1] = pattern->stops[i].color.red; + stops[i*5 + 2] = pattern->stops[i].color.green; + stops[i*5 + 3] = pattern->stops[i].color.blue; + stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha; + } + + vgSetParameterfv (context->paint, + VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops); + + if (stops != stack_stops) + free (stops); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static void +_vg_set_source_matrix (const cairo_pattern_t *pat) +{ + cairo_matrix_t mat; + cairo_status_t status; + VGfloat vmat[9]; + + mat = pat->matrix; + status = cairo_matrix_invert (&mat); + assert (status == CAIRO_STATUS_SUCCESS); + + _vg_matrix_from_cairo (vmat, &mat); + + vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER); + vgLoadMatrix (vmat); + vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER); + vgLoadMatrix (vmat); + vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); + + CHECK_VG_ERRORS(); +} + +static cairo_status_t +_vg_setup_linear_source (cairo_vg_context_t *context, + const cairo_linear_pattern_t *lpat) +{ + VGfloat linear[4]; + + 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, + VG_COLOR_RAMP_SPREAD_PAD); + vgSetParameteri (context->paint, + VG_PAINT_TYPE, + VG_PAINT_TYPE_LINEAR_GRADIENT); + vgSetParameterfv (context->paint, + VG_PAINT_LINEAR_GRADIENT, 4, linear); + + _vg_set_source_matrix (&lpat->base.base); + + CHECK_VG_ERRORS(); + return _vg_setup_gradient_stops (context, &lpat->base); + +} + +static cairo_status_t +_vg_setup_radial_source (cairo_vg_context_t *context, + const cairo_radial_pattern_t *rpat) +{ + VGfloat radial[5]; + + 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); + vgSetParameteri (context->paint, + VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT); + vgSetParameterfv (context->paint, + VG_PAINT_RADIAL_GRADIENT, 5, radial); + + _vg_set_source_matrix (&rpat->base.base); + + /* FIXME: copy/adapt fixes from SVG backend to add inner radius */ + + CHECK_VG_ERRORS(); + return _vg_setup_gradient_stops (context, &rpat->base); +} + +static cairo_status_t +_vg_setup_solid_source (cairo_vg_context_t *context, + const cairo_solid_pattern_t *spat) +{ + VGfloat color[] = { + spat->color.red, + spat->color.green, + spat->color.blue, + spat->color.alpha * context->alpha + }; + + vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); + vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_vg_surface_t * +_vg_clone_recording_surface (cairo_vg_context_t *context, + cairo_surface_t *surface) +{ + VGImage vg_image; + VGImageFormat format; + cairo_status_t status; + cairo_rectangle_int_t extents; + cairo_vg_surface_t *clone; + + status = _cairo_surface_get_extents (surface, &extents); + if (status) + return NULL; + + if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || + extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + format = _vg_format_for_content (surface->content); + + /* NONALIASED, FASTER, BETTER */ + vg_image = vgCreateImage (format, + extents.width, extents.height, + VG_IMAGE_QUALITY_FASTER); + clone = (cairo_vg_surface_t *) + _vg_surface_create_internal (context, vg_image, format, + extents.width, extents.height); + cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y); + + status = _cairo_recording_surface_replay (surface, &clone->base); + if (unlikely (status)) { + cairo_surface_destroy (&clone->base); + return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); + } + + return clone; +} + +static cairo_vg_surface_t * +_vg_clone_image_surface (cairo_vg_context_t *context, + cairo_surface_t *surface) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_status_t status; + VGImage vg_image; + VGImageFormat format; + cairo_rectangle_int_t extents; + cairo_vg_surface_t *clone; + + if (surface->backend->acquire_source_image == NULL) + return NULL; + + status = _cairo_surface_get_extents (surface, &extents); + if (status) + return NULL; + + if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) || + extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return NULL; + } + + status = _cairo_surface_acquire_source_image (surface, + &image, &image_extra); + if (unlikely (status)) + return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status); + + format = _vg_format_from_pixman (image->pixman_format); + if (format == 0) + format = _vg_format_for_content (image->base.content); + + /* NONALIASED, FASTER, BETTER */ + vg_image = vgCreateImage (format, + image->width, image->height, + VG_IMAGE_QUALITY_FASTER); + clone = (cairo_vg_surface_t *) + _vg_surface_create_internal (context, vg_image, format, + image->width, image->height); + if (unlikely (clone->base.status)) + return clone; + + vgImageSubData (clone->image, + image->data, image->stride, + format, 0, 0, image->width, image->height); + + _cairo_surface_release_source_image (surface, image, image_extra); + + return clone; +} + +static void +_vg_surface_remove_from_cache (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface; + + if (surface->snapshot_cache_entry.hash) { + cairo_vg_context_t *context; + + context = _vg_context_lock (surface->context); + _cairo_cache_remove (&context->snapshot_cache, + &surface->snapshot_cache_entry); + _vg_context_unlock (context); + + surface->snapshot_cache_entry.hash = 0; + } +} + +static cairo_status_t +_vg_setup_surface_source (cairo_vg_context_t *context, + const cairo_surface_pattern_t *spat) +{ + cairo_surface_t *snapshot; + cairo_vg_surface_t *clone; + cairo_status_t status; + + snapshot = _cairo_surface_has_snapshot (spat->surface, + &cairo_vg_surface_backend); + if (snapshot != NULL) { + clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot); + goto DONE; + } + + if (_cairo_surface_is_recording (spat->surface)) + clone = _vg_clone_recording_surface (context, spat->surface); + else + clone = _vg_clone_image_surface (context, spat->surface); + if (clone == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (unlikely (clone->base.status)) + return clone->base.status; + + clone->snapshot_cache_entry.hash = clone->base.unique_id; + status = _cairo_cache_insert (&context->snapshot_cache, + &clone->snapshot_cache_entry); + if (unlikely (status)) { + clone->snapshot_cache_entry.hash = 0; + cairo_surface_destroy (&clone->base); + return status; + } + + _cairo_surface_attach_snapshot (spat->surface, &clone->base, + _vg_surface_remove_from_cache); + +DONE: + cairo_surface_destroy (&context->source->base); + context->source = clone; + + vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN); + + switch (spat->base.extend) { + case CAIRO_EXTEND_PAD: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_PAD); + break; + + case CAIRO_EXTEND_NONE: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_FILL); + { + VGfloat color[] = {0,0,0,0}; + vgSetfv (VG_TILE_FILL_COLOR, 4, color); + } + break; + + case CAIRO_EXTEND_REPEAT: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_REPEAT); + break; + + default: + ASSERT_NOT_REACHED; + case CAIRO_EXTEND_REFLECT: + vgSetParameteri (context->paint, + VG_PAINT_PATTERN_TILING_MODE, + VG_TILE_REFLECT); + break; + } + vgPaintPattern (context->paint, context->source->image); + + _vg_set_source_matrix (&spat->base); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +setup_source (cairo_vg_context_t *context, + const cairo_pattern_t *source) +{ + switch (source->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return _vg_setup_solid_source (context, + (cairo_solid_pattern_t *) source); + case CAIRO_PATTERN_TYPE_LINEAR: + return _vg_setup_linear_source (context, + (cairo_linear_pattern_t *) source); + case CAIRO_PATTERN_TYPE_RADIAL: + return _vg_setup_radial_source (context, + (cairo_radial_pattern_t *) source); + case CAIRO_PATTERN_TYPE_SURFACE: + return _vg_setup_surface_source (context, + (cairo_surface_pattern_t *) source); + default: + ASSERT_NOT_REACHED; + return CAIRO_INT_STATUS_UNSUPPORTED; + } +} + +static cairo_int_status_t +_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; + cairo_status_t status; + VGfloat state[9]; + VGfloat strokeTransform[9]; + vg_path_t vg_path; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1, 0, 0, 0, + VG_PATH_CAPABILITY_ALL); + + vgGetMatrix (state); + _vg_matrix_from_cairo (strokeTransform, ctm); + vgMultMatrix (strokeTransform); + + vg_path.ctm_inverse = ctm_inverse; + + _vg_path_from_cairo (&vg_path, path); + + /* XXX DASH_PATTERN, DASH_PHASE */ + vgSetf (VG_STROKE_LINE_WIDTH, style->line_width); + vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit); + vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join)); + vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap)); + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + + vgSetPaint (context->paint, VG_STROKE_PATH); + + vgDrawPath (vg_path.path, VG_STROKE_PATH); + + vgDestroyPath (vg_path.path); + + vgLoadMatrix (state); + + CHECK_VG_ERRORS(); + _vg_context_unlock (context); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_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; + cairo_status_t status; + vg_path_t vg_path; + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1, 0, + 0, 0, + VG_PATH_CAPABILITY_ALL); + vg_path.ctm_inverse = NULL; + + _vg_path_from_cairo (&vg_path, path); + + /* XXX tolerance */ + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule)); + vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias)); + + vgSetPaint (context->paint, VG_FILL_PATH); + + vgDrawPath (vg_path.path, VG_FILL_PATH); + + vgDestroyPath (vg_path.path); + + _vg_context_unlock (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_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; + cairo_status_t status; + + if (op == CAIRO_OPERATOR_DEST) + return CAIRO_STATUS_SUCCESS; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + context = _vg_context_lock (surface->context); + status = _vg_context_set_target (context, surface); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = setup_source (context, source); + if (status) { + _vg_context_unlock (context); + return status; + } + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) { + _vg_context_unlock (context); + return status; + } + + vgSeti (VG_BLEND_MODE, _vg_operator (op)); + vgSetPaint (context->paint, VG_FILL_PATH); + + { /* creating a rectangular path that should cover the extent */ + VGubyte segs[] = { + VG_MOVE_TO_ABS, VG_LINE_TO_ABS, + VG_LINE_TO_ABS, VG_LINE_TO_ABS, + VG_CLOSE_PATH + }; + VGfloat data[] = { + 0, 0, + surface->width, 0, + surface->width, surface->height, + 0, surface->height + }; + VGPath fullext; + + fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, + 1,0,0,0, VG_PATH_CAPABILITY_ALL); + vgAppendPathData (fullext, sizeof(segs), segs, data); + + vgDrawPath (fullext, VG_FILL_PATH); + + vgDestroyPath (fullext); + } + + _vg_context_unlock (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_vg_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_vg_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (! _vg_is_supported_operator (op)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Handle paint-with-alpha to do fades cheaply */ + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask; + cairo_vg_context_t *context = _vg_context_lock (surface->context); + double alpha = context->alpha; + + context->alpha = solid->color.alpha; + status = _vg_surface_paint (abstract_surface, op, source, clip); + context->alpha = alpha; + + _vg_context_unlock (context); + + return status; + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static void +_vg_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_OFF); +} + +static cairo_int_status_t +_vg_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; + + if (num_glyphs <= 0) + return CAIRO_STATUS_SUCCESS; + + _cairo_path_fixed_init (&path); + + /* XXX Glyph cache! OpenVG font support in 1.1? */ + + status = _cairo_scaled_font_glyph_path (scaled_font, + glyphs, num_glyphs, + &path); + if (unlikely (status)) + goto BAIL; + + status = _vg_surface_fill (abstract_surface, + op, source, &path, + CAIRO_FILL_RULE_WINDING, + CAIRO_GSTATE_TOLERANCE_DEFAULT, + CAIRO_ANTIALIAS_DEFAULT, + clip); +BAIL: + _cairo_path_fixed_fini (&path); + return status; +} + +static inline int +multiply_alpha (int alpha, int color) +{ + int temp = alpha * color + 0x80; + return (temp + (temp >> 8)) >> 8; +} + +static void +premultiply_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 = 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] = ((uint32_t)alpha << 24) | (r << 16) | (g << 8) | (b << 0); + } + } + + data += stride; + } +} + +static cairo_int_status_t +_vg_get_image (cairo_vg_surface_t *surface, + int x, int y, + int width, int height, + cairo_image_surface_t **image_out) +{ + cairo_image_surface_t *image; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; + cairo_bool_t needs_premultiply; + + pixman_format = _vg_format_to_pixman (surface->format, + &needs_premultiply); + if (pixman_format == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + pixman_image = pixman_image_create_bits (pixman_format, + width, height, + NULL, 0); + if (unlikely (pixman_image == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + vgFinish (); + CHECK_VG_ERRORS(); + + vgGetImageSubData (surface->image, + pixman_image_get_data (pixman_image), + pixman_image_get_stride (pixman_image), + surface->format, + x, y, width, height); + + image = (cairo_image_surface_t *) + _cairo_image_surface_create_for_pixman_image (pixman_image, + pixman_format); + if (unlikely (image->base.status)) { + pixman_image_unref (pixman_image); + return image->base.status; + } + + if (needs_premultiply) + premultiply_argb (image->data, width, height, image->stride); + + *image_out = image; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_vg_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_vg_surface_t *surface = abstract_surface; + + CHECK_VG_ERRORS(); + *image_extra = NULL; + return _vg_get_image (surface, + 0, 0, surface->width, surface->height, + image_out); +} + +static void +_vg_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +static cairo_status_t +_vg_surface_finish (void *abstract_surface) +{ + cairo_vg_surface_t *surface = abstract_surface; + cairo_vg_context_t *context = _vg_context_lock (surface->context); + + if (surface->snapshot_cache_entry.hash) { + _cairo_cache_remove (&context->snapshot_cache, + &surface->snapshot_cache_entry); + + surface->snapshot_cache_entry.hash = 0; + } + + _cairo_surface_clipper_reset (&surface->clipper); + + if (surface->own_image) + vgDestroyImage (surface->image); + + _vg_context_destroy_target (context, surface); + + _vg_context_unlock (context); + _vg_context_destroy (context); + + CHECK_VG_ERRORS(); + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_vg_surface_backend = { + CAIRO_SURFACE_TYPE_VG, + _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, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _vg_surface_get_extents, + _vg_surface_get_font_options, /* get_font_options */ + + NULL, /* flush */ + NULL, /* mark dirty */ + + _vg_surface_paint, + _vg_surface_mask, + _vg_surface_stroke, + _vg_surface_fill, + NULL, /* fill-stroke */ + _vg_surface_show_glyphs, +}; + +static cairo_surface_t * +_vg_surface_create_internal (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height) +{ + cairo_vg_surface_t *surface; + + surface = _cairo_malloc (sizeof (cairo_vg_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->context = _vg_context_reference (context); + + surface->image = image; + surface->format = format; + + _cairo_surface_init (&surface->base, + &cairo_vg_surface_backend, + NULL, /* device */ + _vg_format_to_content (format), + FALSE); /* is_vector */ + + surface->width = width; + surface->height = height; + + _cairo_surface_clipper_init (&surface->clipper, + _vg_surface_clipper_intersect_clip_path); + + surface->snapshot_cache_entry.hash = 0; + + surface->target_id = 0; + + CHECK_VG_ERRORS(); + return &surface->base; +} + +cairo_surface_t * +cairo_vg_surface_create_for_image (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height) +{ + cairo_bool_t premult; + + if (context->status) + return _cairo_surface_create_in_error (context->status); + + if (image == VG_INVALID_HANDLE) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + if (_vg_format_to_pixman (format, &premult) == 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + return _vg_surface_create_internal (context, image, format, width, height); +} + +cairo_surface_t * +cairo_vg_surface_create (cairo_vg_context_t *context, + cairo_content_t content, + int width, + int height) +{ + VGImage image; + VGImageFormat format; + cairo_surface_t *surface; + + if (context->status) + return _cairo_surface_create_in_error (context->status); + + if (! CAIRO_CONTENT_VALID (content)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT)); + + if (width > vgGeti (VG_MAX_IMAGE_WIDTH) || + height > vgGeti (VG_MAX_IMAGE_HEIGHT)) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + + format = _vg_format_for_content (content); + image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER); + if (image == VG_INVALID_HANDLE) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = _vg_surface_create_internal (context, + image, format, width, height); + if (unlikely (surface->status)) + return surface; + + ((cairo_vg_surface_t *) surface)->own_image = TRUE; + return surface; +} +slim_hidden_def (cairo_vg_surface_create); + +VGImage +cairo_vg_surface_get_image (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return VG_INVALID_HANDLE; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->image; +} + +int +cairo_vg_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->width; +} + +int +cairo_vg_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->height; +} + +VGImageFormat +cairo_vg_surface_get_format (cairo_surface_t *abstract_surface) +{ + cairo_vg_surface_t *surface; + + if (abstract_surface->backend != &cairo_vg_surface_backend) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + surface = (cairo_vg_surface_t *) abstract_surface; + return surface->format; +} + +/* GL specific context support :-( + * + * OpenVG like cairo defers creation of surface (and the necessary + * paraphernalia to the application. + */ + +static const cairo_vg_context_t _vg_context_nil = { + CAIRO_STATUS_NO_MEMORY, + CAIRO_REFERENCE_COUNT_INVALID +}; + +static const cairo_vg_context_t _vg_context_nil_invalid_visual = { + CAIRO_STATUS_INVALID_VISUAL, + CAIRO_REFERENCE_COUNT_INVALID +}; + +#if CAIRO_HAS_GLX_FUNCTIONS +#include + +static cairo_status_t +glx_create_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + /* XXX hmm, magic required for creating an FBO points to VGImage! */ + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_status_t +glx_set_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ +#if 0 + glXMakeContextCurrent (context->display, + (GLXDrawable) surface->target_id, + (GLXDrawable) surface->target_id, + context->context); +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static void +glx_destroy_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ +} + +cairo_vg_context_t * +cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx) +{ + cairo_vg_context_t *context; + cairo_status_t status; + + context = _cairo_malloc (sizeof (*context)); + if (unlikely (context == NULL)) + return (cairo_vg_context_t *) &_vg_context_nil; + + context->display = dpy; + context->context = ctx; + + context->create_target = glx_create_target; + context->set_target = glx_set_target; + context->destroy_target = glx_destroy_target; + + status = _vg_context_init (context); + if (unlikely (status)) { + free (context); + return (cairo_vg_context_t *) &_vg_context_nil; + } + + return context; +} +#endif + +#if CAIRO_HAS_EGL_FUNCTIONS +static cairo_status_t +egl_create_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + EGLSurface *egl_surface; +#define RED 1 +#define GREEN 3 +#define BLUE 5 +#define ALPHA 7 + int attribs[] = { + EGL_RED_SIZE, 0, + EGL_GREEN_SIZE, 0, + EGL_BLUE_SIZE, 0, + EGL_ALPHA_SIZE, 0, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT, + EGL_NONE + }; + pixman_format_code_t pixman_format; + EGLConfig config; + int num_configs = 0; + cairo_bool_t needs_premultiply; + + pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply); + if (pixman_format == 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX no control over pixel ordering! */ + attribs[RED] = PIXMAN_FORMAT_R (pixman_format); + attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format); + attribs[BLUE] = PIXMAN_FORMAT_B (pixman_format); + attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format); + + if (! eglChooseConfig (context->display, + attribs, + &config, 1, &num_configs) || + num_configs != 1) + { + fprintf(stderr, "Error: eglChooseConfig() failed.\n"); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + egl_surface = + eglCreatePbufferFromClientBuffer (context->display, + EGL_OPENVG_IMAGE, + (EGLClientBuffer) surface->image, + config, + NULL); + surface->target_id = (unsigned long) egl_surface; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +egl_set_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + if (! eglMakeCurrent (context->display, + (EGLSurface *) surface->target_id, + (EGLSurface *) surface->target_id, + context->context)) + { + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + return CAIRO_STATUS_SUCCESS; +} + +static void +egl_destroy_target (cairo_vg_context_t *context, + cairo_vg_surface_t *surface) +{ + eglDestroySurface (context->display, + (EGLSurface *) surface->target_id); +} + +cairo_vg_context_t * +cairo_vg_context_create_for_egl (EGLDisplay egl_display, + EGLContext egl_context) +{ + cairo_vg_context_t *context; + cairo_status_t status; + + context = _cairo_malloc (sizeof (*context)); + if (unlikely (context == NULL)) + return (cairo_vg_context_t *) &_vg_context_nil; + + status = _vg_context_init (context); + if (unlikely (status)) { + free (context); + return (cairo_vg_context_t *) &_vg_context_nil; + } + + context->display = egl_display; + context->context = egl_context; + + context->create_target = egl_create_target; + context->set_target = egl_set_target; + context->destroy_target = egl_destroy_target; + + return context; +} +#endif + +cairo_status_t +cairo_vg_context_status (cairo_vg_context_t *context) +{ + return context->status; +} + +void +cairo_vg_context_destroy (cairo_vg_context_t *context) +{ + if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count)) + return; + + _vg_context_destroy (context); +} diff --git a/gfx/cairo/cairo/src/cairo-vg.h b/gfx/cairo/cairo/src/cairo-vg.h new file mode 100644 index 0000000000..a2701db3bd --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-vg.h @@ -0,0 +1,103 @@ +/* -*- 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 + * + * 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): + * Vladimir Vukicevic + * Chris Wilson + */ + +#ifndef CAIRO_VG_H +#define CAIRO_VG_H + +#include "cairo.h" + +#if CAIRO_HAS_VG_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +typedef struct _cairo_vg_context cairo_vg_context_t; + +#if CAIRO_HAS_GLX_FUNCTIONS +typedef struct __GLXcontextRec *GLXContext; +typedef struct _XDisplay Display; + +cairo_public cairo_vg_context_t * +cairo_vg_context_create_for_glx (Display *dpy, + GLXContext ctx); +#endif + +#if CAIRO_HAS_EGL_FUNCTIONS +#include + +cairo_public cairo_vg_context_t * +cairo_vg_context_create_for_egl (EGLDisplay egl_display, + EGLContext egl_context); +#endif + +cairo_public cairo_status_t +cairo_vg_context_status (cairo_vg_context_t *context); + +cairo_public void +cairo_vg_context_destroy (cairo_vg_context_t *context); + +cairo_public cairo_surface_t * +cairo_vg_surface_create (cairo_vg_context_t *context, + cairo_content_t content, int width, int height); + +cairo_public cairo_surface_t * +cairo_vg_surface_create_for_image (cairo_vg_context_t *context, + VGImage image, + VGImageFormat format, + int width, int height); + +cairo_public VGImage +cairo_vg_surface_get_image (cairo_surface_t *abstract_surface); + +cairo_public VGImageFormat +cairo_vg_surface_get_format (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_vg_surface_get_height (cairo_surface_t *abstract_surface); + +cairo_public int +cairo_vg_surface_get_width (cairo_surface_t *abstract_surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_VG_SURFACE*/ +# error Cairo was not compiled with support for the OpenVG backend +#endif /* CAIRO_HAS_VG_SURFACE*/ + +#endif /* CAIRO_VG_H */ 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 0000000000..4872374464 --- /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 new file mode 100644 index 0000000000..3f5491bb1c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-wideint-private.h @@ -0,0 +1,338 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Keith Packard + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Keith R. Packard + * + */ + +#ifndef CAIRO_WIDEINT_H +#define CAIRO_WIDEINT_H + +#include "cairo-wideint-type-private.h" + +#include "cairo-compiler-private.h" + +/* + * 64-bit datatypes. Two separate implementations, one using + * built-in 64-bit signed/unsigned types another implemented + * as a pair of 32-bit ints + */ + +#define I cairo_private cairo_const + +#if !HAVE_UINT64_T + +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); +cairo_uint64_t I _cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint64_t I _cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint64_t I _cairo_uint32x32_64_mul (uint32_t a, uint32_t b); +cairo_uint64_t I _cairo_uint64_lsl (cairo_uint64_t a, int shift); +cairo_uint64_t I _cairo_uint64_rsl (cairo_uint64_t a, int shift); +cairo_uint64_t I _cairo_uint64_rsa (cairo_uint64_t a, int shift); +int I _cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b); +int I _cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b); +int I _cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint64_t I _cairo_uint64_negate (cairo_uint64_t a); +#define _cairo_uint64_is_zero(a) ((a).hi == 0 && (a).lo == 0) +#define _cairo_uint64_negative(a) (((int32_t) ((a).hi)) < 0) +cairo_uint64_t I _cairo_uint64_not (cairo_uint64_t a); + +#define _cairo_uint64_to_int64(i) (i) +#define _cairo_int64_to_uint64(i) (i) + +cairo_int64_t I _cairo_int32_to_int64(int32_t i); +#define _cairo_int64_to_int32(a) ((int32_t) _cairo_uint64_to_uint32(a)) +#define _cairo_int64_add(a,b) _cairo_uint64_add (a,b) +#define _cairo_int64_sub(a,b) _cairo_uint64_sub (a,b) +#define _cairo_int64_mul(a,b) _cairo_uint64_mul (a,b) +cairo_int64_t I _cairo_int32x32_64_mul (int32_t a, int32_t b); +int I _cairo_int64_lt (cairo_int64_t a, cairo_int64_t b); +int I _cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b); +#define _cairo_int64_is_zero(a) _cairo_uint64_is_zero (a) +#define _cairo_int64_eq(a,b) _cairo_uint64_eq (a,b) +#define _cairo_int64_lsl(a,b) _cairo_uint64_lsl (a,b) +#define _cairo_int64_rsl(a,b) _cairo_uint64_rsl (a,b) +#define _cairo_int64_rsa(a,b) _cairo_uint64_rsa (a,b) +#define _cairo_int64_negate(a) _cairo_uint64_negate(a) +#define _cairo_int64_negative(a) (((int32_t) ((a).hi)) < 0) +#define _cairo_int64_not(a) _cairo_uint64_not(a) + +#else + +static inline cairo_uquorem64_t +_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) +{ + cairo_uquorem64_t qr; + + qr.quo = num / den; + qr.rem = num % 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)) +#define _cairo_uint64_sub(a,b) ((a) - (b)) +#define _cairo_uint64_mul(a,b) ((a) * (b)) +#define _cairo_uint32x32_64_mul(a,b) ((uint64_t) (a) * (b)) +#define _cairo_uint64_lsl(a,b) ((a) << (b)) +#define _cairo_uint64_rsl(a,b) ((uint64_t) (a) >> (b)) +#define _cairo_uint64_rsa(a,b) ((uint64_t) ((int64_t) (a) >> (b))) +#define _cairo_uint64_lt(a,b) ((a) < (b)) +#define _cairo_uint64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) +#define _cairo_uint64_is_zero(a) ((a) == 0) +#define _cairo_uint64_eq(a,b) ((a) == (b)) +#define _cairo_uint64_negate(a) ((uint64_t) -((int64_t) (a))) +#define _cairo_uint64_negative(a) ((int64_t) (a) < 0) +#define _cairo_uint64_not(a) (~(a)) + +#define _cairo_uint64_to_int64(i) ((int64_t) (i)) +#define _cairo_int64_to_uint64(i) ((uint64_t) (i)) + +#define _cairo_int32_to_int64(i) ((int64_t) (i)) +#define _cairo_int64_to_int32(i) ((int32_t) (i)) +#define _cairo_int64_add(a,b) ((a) + (b)) +#define _cairo_int64_sub(a,b) ((a) - (b)) +#define _cairo_int64_mul(a,b) ((a) * (b)) +#define _cairo_int32x32_64_mul(a,b) ((int64_t) (a) * (b)) +#define _cairo_int64_lt(a,b) ((a) < (b)) +#define _cairo_int64_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) +#define _cairo_int64_is_zero(a) ((a) == 0) +#define _cairo_int64_eq(a,b) ((a) == (b)) +#define _cairo_int64_lsl(a,b) ((a) << (b)) +#define _cairo_int64_rsl(a,b) ((int64_t) ((uint64_t) (a) >> (b))) +#define _cairo_int64_rsa(a,b) ((int64_t) (a) >> (b)) +#define _cairo_int64_negate(a) (-(a)) +#define _cairo_int64_negative(a) ((a) < 0) +#define _cairo_int64_not(a) (~(a)) + +#endif + +/* + * 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)) +#define _cairo_uint64_ge(a,b) (!_cairo_uint64_lt(a,b)) +#define _cairo_uint64_gt(a,b) _cairo_uint64_lt(b,a) + +#define _cairo_int64_le(a,b) (!_cairo_int64_gt(a,b)) +#define _cairo_int64_ne(a,b) (!_cairo_int64_eq(a,b)) +#define _cairo_int64_ge(a,b) (!_cairo_int64_lt(a,b)) +#define _cairo_int64_gt(a,b) _cairo_int64_lt(b,a) + +/* + * As the C implementation always computes both, create + * a function which returns both for the 'native' type as well + */ + +static inline cairo_quorem64_t +_cairo_int64_divrem (cairo_int64_t num, cairo_int64_t den) +{ + int num_neg = _cairo_int64_negative (num); + int den_neg = _cairo_int64_negative (den); + cairo_uquorem64_t uqr; + cairo_quorem64_t qr; + + if (num_neg) + num = _cairo_int64_negate (num); + if (den_neg) + den = _cairo_int64_negate (den); + uqr = _cairo_uint64_divrem (num, den); + if (num_neg) + qr.rem = _cairo_int64_negate (uqr.rem); + else + qr.rem = uqr.rem; + if (num_neg != den_neg) + qr.quo = (cairo_int64_t) _cairo_int64_negate (uqr.quo); + else + qr.quo = (cairo_int64_t) uqr.quo; + return qr; +} + +static inline int32_t +_cairo_int64_32_div (cairo_int64_t num, int32_t den) +{ +#if !HAVE_UINT64_T + return _cairo_int64_to_int32 + (_cairo_int64_divrem (num, _cairo_int32_to_int64 (den)).quo); +#else + return num / den; +#endif +} + +/* + * 128-bit datatypes. Again, provide two implementations in + * case the machine has a native 128-bit datatype. GCC supports int128_t + * on ia64 + */ + +#if !HAVE_UINT128_T + +cairo_uint128_t I _cairo_uint32_to_uint128 (uint32_t i); +cairo_uint128_t I _cairo_uint64_to_uint128 (cairo_uint64_t i); +#define _cairo_uint128_to_uint64(a) ((a).lo) +#define _cairo_uint128_to_uint32(a) _cairo_uint64_to_uint32(_cairo_uint128_to_uint64(a)) +cairo_uint128_t I _cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b); +cairo_uint128_t I _cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b); +cairo_uint128_t I _cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b); +cairo_uint128_t I _cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b); +cairo_uint128_t I _cairo_uint128_lsl (cairo_uint128_t a, int shift); +cairo_uint128_t I _cairo_uint128_rsl (cairo_uint128_t a, int shift); +cairo_uint128_t I _cairo_uint128_rsa (cairo_uint128_t a, int shift); +int I _cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b); +int I _cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b); +int I _cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b); +#define _cairo_uint128_is_zero(a) (_cairo_uint64_is_zero ((a).hi) && _cairo_uint64_is_zero ((a).lo)) +cairo_uint128_t I _cairo_uint128_negate (cairo_uint128_t a); +#define _cairo_uint128_negative(a) (_cairo_uint64_negative(a.hi)) +cairo_uint128_t I _cairo_uint128_not (cairo_uint128_t a); + +#define _cairo_uint128_to_int128(i) (i) +#define _cairo_int128_to_uint128(i) (i) + +cairo_int128_t I _cairo_int32_to_int128 (int32_t i); +cairo_int128_t I _cairo_int64_to_int128 (cairo_int64_t i); +#define _cairo_int128_to_int64(a) ((cairo_int64_t) (a).lo) +#define _cairo_int128_to_int32(a) _cairo_int64_to_int32(_cairo_int128_to_int64(a)) +#define _cairo_int128_add(a,b) _cairo_uint128_add(a,b) +#define _cairo_int128_sub(a,b) _cairo_uint128_sub(a,b) +#define _cairo_int128_mul(a,b) _cairo_uint128_mul(a,b) +cairo_int128_t I _cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b); +#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) +#define _cairo_int128_lsl(a,b) _cairo_uint128_lsl(a,b) +#define _cairo_int128_rsl(a,b) _cairo_uint128_rsl(a,b) +#define _cairo_int128_rsa(a,b) _cairo_uint128_rsa(a,b) +int I _cairo_int128_lt (cairo_int128_t a, cairo_int128_t b); +int I _cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b); +#define _cairo_int128_is_zero(a) _cairo_uint128_is_zero (a) +#define _cairo_int128_eq(a,b) _cairo_uint128_eq (a,b) +#define _cairo_int128_negate(a) _cairo_uint128_negate(a) +#define _cairo_int128_negative(a) (_cairo_uint128_negative(a)) +#define _cairo_int128_not(a) _cairo_uint128_not(a) + +#else /* !HAVE_UINT128_T */ + +#define _cairo_uint32_to_uint128(i) ((uint128_t) (i)) +#define _cairo_uint64_to_uint128(i) ((uint128_t) (i)) +#define _cairo_uint128_to_uint64(i) ((uint64_t) (i)) +#define _cairo_uint128_to_uint32(i) ((uint32_t) (i)) +#define _cairo_uint128_add(a,b) ((a) + (b)) +#define _cairo_uint128_sub(a,b) ((a) - (b)) +#define _cairo_uint128_mul(a,b) ((a) * (b)) +#define _cairo_uint64x64_128_mul(a,b) ((uint128_t) (a) * (b)) +#define _cairo_uint128_lsl(a,b) ((a) << (b)) +#define _cairo_uint128_rsl(a,b) ((uint128_t) (a) >> (b)) +#define _cairo_uint128_rsa(a,b) ((uint128_t) ((int128_t) (a) >> (b))) +#define _cairo_uint128_lt(a,b) ((a) < (b)) +#define _cairo_uint128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) +#define _cairo_uint128_is_zero(a) ((a) == 0) +#define _cairo_uint128_eq(a,b) ((a) == (b)) +#define _cairo_uint128_negate(a) ((uint128_t) -((int128_t) (a))) +#define _cairo_uint128_negative(a) ((int128_t) (a) < 0) +#define _cairo_uint128_not(a) (~(a)) + +#define _cairo_uint128_to_int128(i) ((int128_t) (i)) +#define _cairo_int128_to_uint128(i) ((uint128_t) (i)) + +#define _cairo_int32_to_int128(i) ((int128_t) (i)) +#define _cairo_int64_to_int128(i) ((int128_t) (i)) +#define _cairo_int128_to_int64(i) ((int64_t) (i)) +#define _cairo_int128_to_int32(i) ((int32_t) (i)) +#define _cairo_int128_add(a,b) ((a) + (b)) +#define _cairo_int128_sub(a,b) ((a) - (b)) +#define _cairo_int128_mul(a,b) ((a) * (b)) +#define _cairo_int64x64_128_mul(a,b) ((int128_t) (a) * (b)) +#define _cairo_int64x32_128_mul(a, b) _cairo_int64x64_128_mul(a, _cairo_int32_to_int64(b)) +#define _cairo_int128_lt(a,b) ((a) < (b)) +#define _cairo_int128_cmp(a,b) ((a) == (b) ? 0 : (a) < (b) ? -1 : 1) +#define _cairo_int128_is_zero(a) ((a) == 0) +#define _cairo_int128_eq(a,b) ((a) == (b)) +#define _cairo_int128_lsl(a,b) ((a) << (b)) +#define _cairo_int128_rsl(a,b) ((int128_t) ((uint128_t) (a) >> (b))) +#define _cairo_int128_rsa(a,b) ((int128_t) (a) >> (b)) +#define _cairo_int128_negate(a) (-(a)) +#define _cairo_int128_negative(a) ((a) < 0) +#define _cairo_int128_not(a) (~(a)) + +#endif /* HAVE_UINT128_T */ + +cairo_uquorem128_t I +_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den); + +cairo_quorem128_t I +_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den); + +cairo_uquorem64_t I +_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, + cairo_uint64_t den); + +cairo_quorem64_t I +_cairo_int_96by64_32x64_divrem (cairo_int128_t num, + cairo_int64_t den); + +#define _cairo_uint128_le(a,b) (!_cairo_uint128_gt(a,b)) +#define _cairo_uint128_ne(a,b) (!_cairo_uint128_eq(a,b)) +#define _cairo_uint128_ge(a,b) (!_cairo_uint128_lt(a,b)) +#define _cairo_uint128_gt(a,b) _cairo_uint128_lt(b,a) + +#define _cairo_int128_le(a,b) (!_cairo_int128_gt(a,b)) +#define _cairo_int128_ne(a,b) (!_cairo_int128_eq(a,b)) +#define _cairo_int128_ge(a,b) (!_cairo_int128_lt(a,b)) +#define _cairo_int128_gt(a,b) _cairo_int128_lt(b,a) + +#undef I + +#endif /* CAIRO_WIDEINT_H */ diff --git a/gfx/cairo/cairo/src/cairo-wideint-type-private.h b/gfx/cairo/cairo/src/cairo-wideint-type-private.h new file mode 100644 index 0000000000..84a3cbab0d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-wideint-type-private.h @@ -0,0 +1,158 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Keith Packard + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Keith R. Packard + * + */ + +#ifndef CAIRO_WIDEINT_TYPE_H +#define CAIRO_WIDEINT_TYPE_H + +#include "cairo.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_STDINT_H +# include +#elif HAVE_INTTYPES_H +# include +#elif HAVE_SYS_INT_TYPES_H +# include +#elif defined(_MSC_VER) + typedef __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# ifndef HAVE_UINT64_T +# define HAVE_UINT64_T 1 +# endif +#else +#error Cannot find definitions for fixed-width integral types (uint8_t, uint32_t, etc.) +#endif + +#ifndef INT16_MIN +# define INT16_MIN (-32767-1) +#endif +#ifndef INT16_MAX +# define INT16_MAX (32767) +#endif +#ifndef UINT16_MAX +# define UINT16_MAX (65535) +#endif +#ifndef INT32_MIN +# define INT32_MIN (-2147483647-1) +#endif +#ifndef INT32_MAX +# define INT32_MAX (2147483647) +#endif +#ifndef UINT32_MAX +# define UINT32_MAX (4294967295U) +#endif + +#if HAVE_BYTESWAP_H +# include +#endif +#ifndef bswap_16 +# define bswap_16(p) \ + (((((uint16_t)(p)) & 0x00ff) << 8) | \ + (((uint16_t)(p)) >> 8)); +#endif +#ifndef bswap_32 +# define bswap_32(p) \ + (((((uint32_t)(p)) & 0x000000ff) << 24) | \ + ((((uint32_t)(p)) & 0x0000ff00) << 8) | \ + ((((uint32_t)(p)) & 0x00ff0000) >> 8) | \ + ((((uint32_t)(p))) >> 24)); +#endif + + +#if !HAVE_UINT64_T + +typedef struct _cairo_uint64 { + uint32_t lo, hi; +} cairo_uint64_t, cairo_int64_t; + +#else + +typedef uint64_t cairo_uint64_t; +typedef int64_t cairo_int64_t; + +#endif + +typedef struct _cairo_uquorem64 { + cairo_uint64_t quo; + cairo_uint64_t rem; +} cairo_uquorem64_t; + +typedef struct _cairo_quorem64 { + cairo_int64_t quo; + cairo_int64_t rem; +} cairo_quorem64_t; + +/* gcc has a non-standard name. */ +#if HAVE___UINT128_T && !HAVE_UINT128_T +typedef __uint128_t uint128_t; +typedef __int128_t int128_t; +#define HAVE_UINT128_T 1 +#endif + +#if !HAVE_UINT128_T + +typedef struct cairo_uint128 { + cairo_uint64_t lo, hi; +} cairo_uint128_t, cairo_int128_t; + +#else + +typedef uint128_t cairo_uint128_t; +typedef int128_t cairo_int128_t; + +#endif + +typedef struct _cairo_uquorem128 { + cairo_uint128_t quo; + cairo_uint128_t rem; +} cairo_uquorem128_t; + +typedef struct _cairo_quorem128 { + cairo_int128_t quo; + cairo_int128_t rem; +} cairo_quorem128_t; + + +#endif /* CAIRO_WIDEINT_H */ diff --git a/gfx/cairo/cairo/src/cairo-wideint.c b/gfx/cairo/cairo/src/cairo-wideint.c new file mode 100644 index 0000000000..2e056fa36a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-wideint.c @@ -0,0 +1,852 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2004 Keith Packard + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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): + * Keith R. Packard + */ + +#include "cairoint.h" + +#if HAVE_UINT64_T + +#define uint64_lo32(i) ((i) & 0xffffffff) +#define uint64_hi32(i) ((i) >> 32) +#define uint64_lo(i) ((i) & 0xffffffff) +#define uint64_hi(i) ((i) >> 32) +#define uint64_shift32(i) ((i) << 32) +#define uint64_carry32 (((uint64_t) 1) << 32) + +#define _cairo_uint32s_to_uint64(h,l) ((uint64_t) (h) << 32 | (l)) + +#else + +#define uint64_lo32(i) ((i).lo) +#define uint64_hi32(i) ((i).hi) + +static cairo_uint64_t +uint64_lo (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = i.lo; + s.hi = 0; + return s; +} + +static cairo_uint64_t +uint64_hi (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = i.hi; + s.hi = 0; + return s; +} + +static cairo_uint64_t +uint64_shift32 (cairo_uint64_t i) +{ + cairo_uint64_t s; + + s.lo = 0; + s.hi = i.lo; + return s; +} + +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) +{ + cairo_uint64_t q; + + q.lo = i; + q.hi = 0; + return q; +} + +cairo_int64_t +_cairo_int32_to_int64 (int32_t i) +{ + cairo_uint64_t q; + + q.lo = i; + q.hi = i < 0 ? -1 : 0; + return q; +} + +static cairo_uint64_t +_cairo_uint32s_to_uint64 (uint32_t h, uint32_t l) +{ + cairo_uint64_t q; + + q.lo = l; + q.hi = h; + return q; +} + +cairo_uint64_t +_cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b) +{ + cairo_uint64_t s; + + s.hi = a.hi + b.hi; + s.lo = a.lo + b.lo; + if (s.lo < a.lo) + s.hi++; + return s; +} + +cairo_uint64_t +_cairo_uint64_sub (cairo_uint64_t a, cairo_uint64_t b) +{ + cairo_uint64_t s; + + s.hi = a.hi - b.hi; + s.lo = a.lo - b.lo; + if (s.lo > a.lo) + s.hi--; + return s; +} + +#define uint32_lo(i) ((i) & 0xffff) +#define uint32_hi(i) ((i) >> 16) +#define uint32_carry16 ((1) << 16) + +cairo_uint64_t +_cairo_uint32x32_64_mul (uint32_t a, uint32_t b) +{ + cairo_uint64_t s; + + uint16_t ah, al, bh, bl; + uint32_t r0, r1, r2, r3; + + al = uint32_lo (a); + ah = uint32_hi (a); + bl = uint32_lo (b); + bh = uint32_hi (b); + + r0 = (uint32_t) al * bl; + r1 = (uint32_t) al * bh; + r2 = (uint32_t) ah * bl; + r3 = (uint32_t) ah * bh; + + r1 += uint32_hi(r0); /* no carry possible */ + r1 += r2; /* but this can carry */ + if (r1 < r2) /* check */ + r3 += uint32_carry16; + + s.hi = r3 + uint32_hi(r1); + s.lo = (uint32_lo (r1) << 16) + uint32_lo (r0); + return s; +} + +cairo_int64_t +_cairo_int32x32_64_mul (int32_t a, int32_t b) +{ + cairo_int64_t s; + s = _cairo_uint32x32_64_mul ((uint32_t) a, (uint32_t) b); + if (a < 0) + s.hi -= b; + if (b < 0) + s.hi -= a; + return s; +} + +cairo_uint64_t +_cairo_uint64_mul (cairo_uint64_t a, cairo_uint64_t b) +{ + cairo_uint64_t s; + + s = _cairo_uint32x32_64_mul (a.lo, b.lo); + s.hi += a.lo * b.hi + a.hi * b.lo; + return s; +} + +cairo_uint64_t +_cairo_uint64_lsl (cairo_uint64_t a, int shift) +{ + if (shift >= 32) + { + a.hi = a.lo; + a.lo = 0; + shift -= 32; + } + if (shift) + { + a.hi = a.hi << shift | a.lo >> (32 - shift); + a.lo = a.lo << shift; + } + return a; +} + +cairo_uint64_t +_cairo_uint64_rsl (cairo_uint64_t a, int shift) +{ + if (shift >= 32) + { + a.lo = a.hi; + a.hi = 0; + shift -= 32; + } + if (shift) + { + a.lo = a.lo >> shift | a.hi << (32 - shift); + a.hi = a.hi >> shift; + } + return a; +} + +#define _cairo_uint32_rsa(a,n) ((uint32_t) (((int32_t) (a)) >> (n))) + +cairo_int64_t +_cairo_uint64_rsa (cairo_int64_t a, int shift) +{ + if (shift >= 32) + { + a.lo = a.hi; + a.hi = _cairo_uint32_rsa (a.hi, 31); + shift -= 32; + } + if (shift) + { + a.lo = a.lo >> shift | a.hi << (32 - shift); + a.hi = _cairo_uint32_rsa (a.hi, shift); + } + return a; +} + +int +_cairo_uint64_lt (cairo_uint64_t a, cairo_uint64_t b) +{ + return (a.hi < b.hi || + (a.hi == b.hi && a.lo < b.lo)); +} + +int +_cairo_uint64_eq (cairo_uint64_t a, cairo_uint64_t b) +{ + return a.hi == b.hi && a.lo == b.lo; +} + +int +_cairo_int64_lt (cairo_int64_t a, cairo_int64_t b) +{ + if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) + return 1; + if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) + return 0; + return _cairo_uint64_lt (a, b); +} + +int +_cairo_uint64_cmp (cairo_uint64_t a, cairo_uint64_t b) +{ + if (a.hi < b.hi) + return -1; + else if (a.hi > b.hi) + return 1; + else if (a.lo < b.lo) + return -1; + else if (a.lo > b.lo) + return 1; + else + return 0; +} + +int +_cairo_int64_cmp (cairo_int64_t a, cairo_int64_t b) +{ + if (_cairo_int64_negative (a) && !_cairo_int64_negative (b)) + return -1; + if (!_cairo_int64_negative (a) && _cairo_int64_negative (b)) + return 1; + + return _cairo_uint64_cmp (a, b); +} + +cairo_uint64_t +_cairo_uint64_not (cairo_uint64_t a) +{ + a.lo = ~a.lo; + a.hi = ~a.hi; + return a; +} + +cairo_uint64_t +_cairo_uint64_negate (cairo_uint64_t a) +{ + a.lo = ~a.lo; + a.hi = ~a.hi; + if (++a.lo == 0) + ++a.hi; + return a; +} + +/* + * Simple bit-at-a-time divide. + */ +cairo_uquorem64_t +_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) +{ + cairo_uquorem64_t qr; + cairo_uint64_t bit; + cairo_uint64_t quo; + + bit = _cairo_uint32_to_uint64 (1); + + /* normalize to make den >= num, but not overflow */ + while (_cairo_uint64_lt (den, num) && (den.hi & 0x80000000) == 0) + { + bit = _cairo_uint64_lsl (bit, 1); + den = _cairo_uint64_lsl (den, 1); + } + quo = _cairo_uint32_to_uint64 (0); + + /* generate quotient, one bit at a time */ + while (bit.hi | bit.lo) + { + if (_cairo_uint64_le (den, num)) + { + num = _cairo_uint64_sub (num, den); + quo = _cairo_uint64_add (quo, bit); + } + bit = _cairo_uint64_rsl (bit, 1); + den = _cairo_uint64_rsl (den, 1); + } + qr.quo = quo; + qr.rem = num; + return qr; +} + +#endif /* !HAVE_UINT64_T */ + +#if HAVE_UINT128_T +cairo_uquorem128_t +_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) +{ + cairo_uquorem128_t qr; + + qr.quo = num / den; + qr.rem = num % den; + return qr; +} + +#else + +cairo_uint128_t +_cairo_uint32_to_uint128 (uint32_t i) +{ + cairo_uint128_t q; + + q.lo = _cairo_uint32_to_uint64 (i); + q.hi = _cairo_uint32_to_uint64 (0); + return q; +} + +cairo_int128_t +_cairo_int32_to_int128 (int32_t i) +{ + cairo_int128_t q; + + q.lo = _cairo_int32_to_int64 (i); + q.hi = _cairo_int32_to_int64 (i < 0 ? -1 : 0); + return q; +} + +cairo_uint128_t +_cairo_uint64_to_uint128 (cairo_uint64_t i) +{ + cairo_uint128_t q; + + q.lo = i; + q.hi = _cairo_uint32_to_uint64 (0); + return q; +} + +cairo_int128_t +_cairo_int64_to_int128 (cairo_int64_t i) +{ + cairo_int128_t q; + + q.lo = i; + q.hi = _cairo_int32_to_int64 (_cairo_int64_negative(i) ? -1 : 0); + return q; +} + +cairo_uint128_t +_cairo_uint128_add (cairo_uint128_t a, cairo_uint128_t b) +{ + cairo_uint128_t s; + + s.hi = _cairo_uint64_add (a.hi, b.hi); + s.lo = _cairo_uint64_add (a.lo, b.lo); + if (_cairo_uint64_lt (s.lo, a.lo)) + s.hi = _cairo_uint64_add (s.hi, _cairo_uint32_to_uint64 (1)); + return s; +} + +cairo_uint128_t +_cairo_uint128_sub (cairo_uint128_t a, cairo_uint128_t b) +{ + cairo_uint128_t s; + + s.hi = _cairo_uint64_sub (a.hi, b.hi); + s.lo = _cairo_uint64_sub (a.lo, b.lo); + if (_cairo_uint64_gt (s.lo, a.lo)) + s.hi = _cairo_uint64_sub (s.hi, _cairo_uint32_to_uint64(1)); + return s; +} + +cairo_uint128_t +_cairo_uint64x64_128_mul (cairo_uint64_t a, cairo_uint64_t b) +{ + cairo_uint128_t s; + uint32_t ah, al, bh, bl; + cairo_uint64_t r0, r1, r2, r3; + + al = uint64_lo32 (a); + ah = uint64_hi32 (a); + bl = uint64_lo32 (b); + bh = uint64_hi32 (b); + + r0 = _cairo_uint32x32_64_mul (al, bl); + r1 = _cairo_uint32x32_64_mul (al, bh); + r2 = _cairo_uint32x32_64_mul (ah, bl); + r3 = _cairo_uint32x32_64_mul (ah, bh); + + r1 = _cairo_uint64_add (r1, uint64_hi (r0)); /* no carry possible */ + r1 = _cairo_uint64_add (r1, r2); /* but this can carry */ + if (_cairo_uint64_lt (r1, r2)) /* check */ + r3 = _cairo_uint64_add (r3, uint64_carry32); + + s.hi = _cairo_uint64_add (r3, uint64_hi(r1)); + s.lo = _cairo_uint64_add (uint64_shift32 (r1), + uint64_lo (r0)); + return s; +} + +cairo_int128_t +_cairo_int64x64_128_mul (cairo_int64_t a, cairo_int64_t b) +{ + cairo_int128_t s; + s = _cairo_uint64x64_128_mul (_cairo_int64_to_uint64(a), + _cairo_int64_to_uint64(b)); + if (_cairo_int64_negative (a)) + s.hi = _cairo_uint64_sub (s.hi, + _cairo_int64_to_uint64 (b)); + if (_cairo_int64_negative (b)) + s.hi = _cairo_uint64_sub (s.hi, + _cairo_int64_to_uint64 (a)); + return s; +} + +cairo_uint128_t +_cairo_uint128_mul (cairo_uint128_t a, cairo_uint128_t b) +{ + cairo_uint128_t s; + + s = _cairo_uint64x64_128_mul (a.lo, b.lo); + s.hi = _cairo_uint64_add (s.hi, + _cairo_uint64_mul (a.lo, b.hi)); + s.hi = _cairo_uint64_add (s.hi, + _cairo_uint64_mul (a.hi, b.lo)); + return s; +} + +cairo_uint128_t +_cairo_uint128_lsl (cairo_uint128_t a, int shift) +{ + if (shift >= 64) + { + a.hi = a.lo; + a.lo = _cairo_uint32_to_uint64 (0); + shift -= 64; + } + if (shift) + { + a.hi = _cairo_uint64_add (_cairo_uint64_lsl (a.hi, shift), + _cairo_uint64_rsl (a.lo, (64 - shift))); + a.lo = _cairo_uint64_lsl (a.lo, shift); + } + return a; +} + +cairo_uint128_t +_cairo_uint128_rsl (cairo_uint128_t a, int shift) +{ + if (shift >= 64) + { + a.lo = a.hi; + a.hi = _cairo_uint32_to_uint64 (0); + shift -= 64; + } + if (shift) + { + a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), + _cairo_uint64_lsl (a.hi, (64 - shift))); + a.hi = _cairo_uint64_rsl (a.hi, shift); + } + return a; +} + +cairo_uint128_t +_cairo_uint128_rsa (cairo_int128_t a, int shift) +{ + if (shift >= 64) + { + a.lo = a.hi; + a.hi = _cairo_uint64_rsa (a.hi, 64-1); + shift -= 64; + } + if (shift) + { + a.lo = _cairo_uint64_add (_cairo_uint64_rsl (a.lo, shift), + _cairo_uint64_lsl (a.hi, (64 - shift))); + a.hi = _cairo_uint64_rsa (a.hi, shift); + } + return a; +} + +int +_cairo_uint128_lt (cairo_uint128_t a, cairo_uint128_t b) +{ + return (_cairo_uint64_lt (a.hi, b.hi) || + (_cairo_uint64_eq (a.hi, b.hi) && + _cairo_uint64_lt (a.lo, b.lo))); +} + +int +_cairo_int128_lt (cairo_int128_t a, cairo_int128_t b) +{ + if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) + return 1; + if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) + return 0; + return _cairo_uint128_lt (a, b); +} + +int +_cairo_uint128_cmp (cairo_uint128_t a, cairo_uint128_t b) +{ + int cmp; + + cmp = _cairo_uint64_cmp (a.hi, b.hi); + if (cmp) + return cmp; + return _cairo_uint64_cmp (a.lo, b.lo); +} + +int +_cairo_int128_cmp (cairo_int128_t a, cairo_int128_t b) +{ + if (_cairo_int128_negative (a) && !_cairo_int128_negative (b)) + return -1; + if (!_cairo_int128_negative (a) && _cairo_int128_negative (b)) + return 1; + + return _cairo_uint128_cmp (a, b); +} + +int +_cairo_uint128_eq (cairo_uint128_t a, cairo_uint128_t b) +{ + return (_cairo_uint64_eq (a.hi, b.hi) && + _cairo_uint64_eq (a.lo, b.lo)); +} + +#if HAVE_UINT64_T +#define _cairo_msbset64(q) (q & ((uint64_t) 1 << 63)) +#else +#define _cairo_msbset64(q) (q.hi & ((uint32_t) 1 << 31)) +#endif + +cairo_uquorem128_t +_cairo_uint128_divrem (cairo_uint128_t num, cairo_uint128_t den) +{ + cairo_uquorem128_t qr; + cairo_uint128_t bit; + cairo_uint128_t quo; + + bit = _cairo_uint32_to_uint128 (1); + + /* normalize to make den >= num, but not overflow */ + while (_cairo_uint128_lt (den, num) && !_cairo_msbset64(den.hi)) + { + bit = _cairo_uint128_lsl (bit, 1); + den = _cairo_uint128_lsl (den, 1); + } + quo = _cairo_uint32_to_uint128 (0); + + /* generate quotient, one bit at a time */ + while (_cairo_uint128_ne (bit, _cairo_uint32_to_uint128(0))) + { + if (_cairo_uint128_le (den, num)) + { + num = _cairo_uint128_sub (num, den); + quo = _cairo_uint128_add (quo, bit); + } + bit = _cairo_uint128_rsl (bit, 1); + den = _cairo_uint128_rsl (den, 1); + } + qr.quo = quo; + qr.rem = num; + return qr; +} + +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_uint128_t +_cairo_uint128_not (cairo_uint128_t a) +{ + a.lo = _cairo_uint64_not (a.lo); + a.hi = _cairo_uint64_not (a.hi); + return a; +} + +#endif /* !HAVE_UINT128_T */ + +cairo_quorem128_t +_cairo_int128_divrem (cairo_int128_t num, cairo_int128_t den) +{ + int num_neg = _cairo_int128_negative (num); + int den_neg = _cairo_int128_negative (den); + cairo_uquorem128_t uqr; + cairo_quorem128_t qr; + + if (num_neg) + num = _cairo_int128_negate (num); + if (den_neg) + den = _cairo_int128_negate (den); + uqr = _cairo_uint128_divrem (num, den); + if (num_neg) + qr.rem = _cairo_int128_negate (uqr.rem); + else + qr.rem = uqr.rem; + if (num_neg != den_neg) + qr.quo = _cairo_int128_negate (uqr.quo); + else + qr.quo = uqr.quo; + return qr; +} + +/** + * _cairo_uint_96by64_32x64_divrem: + * + * Compute a 32 bit quotient and 64 bit remainder of a 96 bit unsigned + * dividend and 64 bit divisor. If the quotient doesn't fit into 32 + * 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. + **/ +cairo_uquorem64_t +_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num, + cairo_uint64_t den) +{ + cairo_uquorem64_t result; + cairo_uint64_t B = _cairo_uint32s_to_uint64 (1, 0); + + /* These are the high 64 bits of the *96* bit numerator. We're + * going to represent the numerator as xB + y, where x is a 64, + * and y is a 32 bit number. */ + cairo_uint64_t x = _cairo_uint128_to_uint64 (_cairo_uint128_rsl(num, 32)); + + /* Initialise the result to indicate overflow. */ + result.quo = _cairo_uint32s_to_uint64 (-1U, -1U); + result.rem = den; + + /* Don't bother if the quotient is going to overflow. */ + if (_cairo_uint64_ge (x, den)) { + return /* overflow */ result; + } + + if (_cairo_uint64_lt (x, B)) { + /* When the final quotient is known to fit in 32 bits, then + * num < 2^64 if and only if den < 2^32. */ + return _cairo_uint64_divrem (_cairo_uint128_to_uint64 (num), den); + } + else { + /* Denominator is >= 2^32. the numerator is >= 2^64, and the + * division won't overflow: need two divrems. Write the + * numerator and denominator as + * + * num = xB + y x : 64 bits, y : 32 bits + * den = uB + v u, v : 32 bits + */ + uint32_t y = _cairo_uint128_to_uint32 (num); + uint32_t u = uint64_hi32 (den); + uint32_t v = _cairo_uint64_to_uint32 (den); + + /* Compute a lower bound approximate quotient of num/den + * from x/(u+1). Then we have + * + * x = q(u+1) + r ; q : 32 bits, r <= u : 32 bits. + * + * xB + y = q(u+1)B + (rB+y) + * = q(uB + B + v - v) + (rB+y) + * = q(uB + v) + qB - qv + (rB+y) + * = q(uB + v) + q(B-v) + (rB+y) + * + * The true quotient of num/den then is q plus the + * contribution of q(B-v) + (rB+y). The main contribution + * comes from the term q(B-v), with the term (rB+y) only + * contributing at most one part. + * + * The term q(B-v) must fit into 64 bits, since q fits into 32 + * bits on account of being a lower bound to the true + * quotient, and as B-v <= 2^32, we may safely use a single + * 64/64 bit division to find its contribution. */ + + cairo_uquorem64_t quorem; + cairo_uint64_t remainder; /* will contain final remainder */ + uint32_t quotient; /* will contain final quotient. */ + uint32_t q; + uint32_t r; + + /* Approximate quotient by dividing the high 64 bits of num by + * u+1. Watch out for overflow of u+1. */ + if (u+1) { + quorem = _cairo_uint64_divrem (x, _cairo_uint32_to_uint64 (u+1)); + q = _cairo_uint64_to_uint32 (quorem.quo); + r = _cairo_uint64_to_uint32 (quorem.rem); + } + else { + q = uint64_hi32 (x); + r = _cairo_uint64_to_uint32 (x); + } + quotient = q; + + /* Add the main term's contribution to quotient. Note B-v = + * -v as an uint32 (unless v = 0) */ + if (v) + quorem = _cairo_uint64_divrem (_cairo_uint32x32_64_mul (q, -v), den); + else + quorem = _cairo_uint64_divrem (_cairo_uint32s_to_uint64 (q, 0), den); + quotient += _cairo_uint64_to_uint32 (quorem.quo); + + /* Add the contribution of the subterm and start computing the + * true remainder. */ + remainder = _cairo_uint32s_to_uint64 (r, y); + if (_cairo_uint64_ge (remainder, den)) { + remainder = _cairo_uint64_sub (remainder, den); + quotient++; + } + + /* Add the contribution of the main term's remainder. The + * funky test here checks that remainder + main_rem >= den, + * taking into account overflow of the addition. */ + remainder = _cairo_uint64_add (remainder, quorem.rem); + if (_cairo_uint64_ge (remainder, den) || + _cairo_uint64_lt (remainder, quorem.rem)) + { + remainder = _cairo_uint64_sub (remainder, den); + quotient++; + } + + result.quo = _cairo_uint32_to_uint64 (quotient); + result.rem = remainder; + } + return result; +} + +cairo_quorem64_t +_cairo_int_96by64_32x64_divrem (cairo_int128_t num, cairo_int64_t den) +{ + int num_neg = _cairo_int128_negative (num); + int den_neg = _cairo_int64_negative (den); + cairo_uint64_t nonneg_den; + cairo_uquorem64_t uqr; + cairo_quorem64_t qr; + + if (num_neg) + num = _cairo_int128_negate (num); + if (den_neg) + nonneg_den = _cairo_int64_negate (den); + else + nonneg_den = den; + + uqr = _cairo_uint_96by64_32x64_divrem (num, nonneg_den); + if (_cairo_uint64_eq (uqr.rem, nonneg_den)) { + /* bail on overflow. */ + qr.quo = _cairo_uint32s_to_uint64 (0x7FFFFFFF, -1U); + qr.rem = den; + return qr; + } + + if (num_neg) + qr.rem = _cairo_int64_negate (uqr.rem); + else + qr.rem = uqr.rem; + if (num_neg != den_neg) + qr.quo = _cairo_int64_negate (uqr.quo); + else + qr.quo = uqr.quo; + return qr; +} diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h new file mode 100644 index 0000000000..d11f9dec0d --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-win32.h @@ -0,0 +1,147 @@ +/* -*- 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 + */ + +#ifndef _CAIRO_WIN32_H_ +#define _CAIRO_WIN32_H_ + +#include "cairo.h" + +#if CAIRO_HAS_WIN32_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_win32_surface_create (HDC hdc); + +cairo_public cairo_surface_t * +cairo_win32_surface_create_with_format (HDC hdc, + cairo_format_t format); + +cairo_public cairo_surface_t * +cairo_win32_printing_surface_create (HDC hdc); + +cairo_public cairo_surface_t * +cairo_win32_surface_create_with_ddb (HDC hdc, + cairo_format_t format, + int width, + int height); + +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_get_size (const cairo_surface_t *surface, int *width, int *height); + +#if CAIRO_HAS_WIN32_FONT + +/* + * Win32 font support + */ + +cairo_public cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont); + +cairo_public cairo_font_face_t * +cairo_win32_font_face_create_for_hfont (HFONT font); + +cairo_public cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font); + +cairo_public cairo_status_t +cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, + HDC hdc); + +cairo_public void +cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font); + +cairo_public double +cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font); + +cairo_public void +cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *logical_to_device); + +cairo_public void +cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *device_to_logical); + +cairo_public BYTE +cairo_win32_get_system_text_quality (void); + +#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_set_force_GDI_classic (cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed); + +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 */ + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_WIN32_SURFACE */ +# error Cairo was not compiled with support for the win32 backend +#endif /* CAIRO_HAS_WIN32_SURFACE */ + +#endif /* _CAIRO_WIN32_H_ */ 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 0000000000..e01dc1a836 --- /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 0000000000..61119653e9 --- /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 0000000000..7720bbbd25 --- /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 0000000000..51f5ee323c --- /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 0000000000..bdb82617b0 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-private.h @@ -0,0 +1,802 @@ +/* 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_bool_t permit_subpixel_antialiasing); +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 0000000000..1877758c25 --- /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 0000000000..9ee7306bb8 --- /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 0000000000..763778ab2c --- /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 0000000000..91c0ff995e --- /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 0000000000..2d6d110a6a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-surface-render.c @@ -0,0 +1,4880 @@ +/* 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_bool_t permit_subpixel_antialiasing) +{ + 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 new file mode 100644 index 0000000000..1b55ec8dea --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb-surface.c @@ -0,0 +1,1532 @@ +/* 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): + * Behdad Esfahbod + * Carl D. Worth + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ + +#include "cairoint.h" + +#include "cairo-xcb.h" +#include "cairo-xcb-private.h" + +#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); +slim_hidden_proto (cairo_xcb_surface_create_for_bitmap); +slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format); +#endif + +/** + * SECTION:cairo-xcb + * @Title: XCB Surfaces + * @Short_Description: X Window System rendering using the XCB library + * @See_Also: #cairo_surface_t + * + * The XCB surface is used to render cairo graphics to X Window System + * windows and pixmaps using the XCB library. + * + * Note that the XCB surface automatically takes advantage of the X render + * extension if it is available. + **/ + +/** + * 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, + cairo_content_t content, + int width, + int height) +{ + cairo_xcb_surface_t *other = abstract_other; + cairo_xcb_surface_t *surface; + cairo_xcb_connection_t *connection; + xcb_pixmap_t pixmap; + cairo_status_t status; + + 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 ((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); + + if (content == other->base.content) { + pixmap = _cairo_xcb_connection_create_pixmap (connection, + other->depth, + other->drawable, + width, height); + + surface = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_internal (other->screen, + pixmap, TRUE, + other->pixman_format, + other->xrender_format, + width, height); + } else { + cairo_format_t format; + pixman_format_code_t pixman_format; + + /* XXX find a compatible xrender format */ + switch (content) { + case CAIRO_CONTENT_ALPHA: + pixman_format = PIXMAN_a8; + format = CAIRO_FORMAT_A8; + break; + case CAIRO_CONTENT_COLOR: + pixman_format = PIXMAN_x8r8g8b8; + format = CAIRO_FORMAT_RGB24; + break; + default: + ASSERT_NOT_REACHED; + case CAIRO_CONTENT_COLOR_ALPHA: + pixman_format = PIXMAN_a8r8g8b8; + format = CAIRO_FORMAT_ARGB32; + break; + } + + pixmap = _cairo_xcb_connection_create_pixmap (connection, + PIXMAN_FORMAT_DEPTH (pixman_format), + other->drawable, + width, height); + + surface = (cairo_xcb_surface_t *) + _cairo_xcb_surface_create_internal (other->screen, + pixmap, TRUE, + pixman_format, + connection->standard_formats[format], + width, height); + } + + if (unlikely (surface->base.status)) + _cairo_xcb_connection_free_pixmap (connection, pixmap); + + _cairo_xcb_connection_release (connection); + + 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) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_status_t status; + + if (surface->fallback != NULL) { + cairo_surface_finish (&surface->fallback->base); + cairo_surface_destroy (&surface->fallback->base); + } + _cairo_boxes_fini (&surface->fallback_damage); + + cairo_list_del (&surface->link); + + status = _cairo_xcb_connection_acquire (surface->connection); + if (status == 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); + _cairo_xcb_connection_release (surface->connection); + } + + _cairo_xcb_connection_destroy (surface->connection); + + return status; +} + +static void +_destroy_image (pixman_image_t *image, void *data) +{ + free (data); +} + +#if CAIRO_HAS_XCB_SHM_FUNCTIONS +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_surface_t *image; + cairo_xcb_shm_info_t *shm_info; + cairo_int_status_t status; + size_t stride; + + *shm_info_out = NULL; + + 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)) { + if (status == CAIRO_INT_STATUS_UNSUPPORTED) + return NULL; + + return _cairo_surface_create_in_error (status); + } + + 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); + _cairo_xcb_shm_info_destroy (shm_info); + return _cairo_surface_create_in_error (status); + } + + *shm_info_out = shm_info; + return image; +} +#endif + +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_xcb_shm_info_t *shm_info; + cairo_surface_t *image; + cairo_status_t status; + + if ((surface->connection->flags & CAIRO_XCB_HAS_SHM) == 0) + return NULL; + + 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); + } + +done: + return image; +#else + return NULL; +#endif +} + +static cairo_surface_t * +_get_image (cairo_xcb_surface_t *surface, + cairo_bool_t use_shm, + int x, int y, + int width, int height) +{ + cairo_surface_t *image; + cairo_xcb_connection_t *connection; + xcb_get_image_reply_t *reply; + cairo_int_status_t status; + + 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, + width, height, + 0); + 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 _cairo_surface_create_in_error (status); + + if (use_shm) { + 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); + } + } + + 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 + * produce errors if the window is unmapped or partially + * 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; + + gc = _cairo_xcb_screen_get_gc (surface->screen, + surface->drawable, + surface->depth); + pixmap = _cairo_xcb_connection_create_pixmap (connection, + surface->depth, + surface->drawable, + width, height); + + /* XXX IncludeInferiors? */ + _cairo_xcb_connection_copy_area (connection, + surface->drawable, + pixmap, gc, + x, y, + 0, 0, + width, height); + + _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); + + reply = _cairo_xcb_connection_get_image (connection, + pixmap, + 0, 0, + width, height); + _cairo_xcb_connection_free_pixmap (connection, pixmap); + } + + if (unlikely (reply == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + /* XXX byte swap */ + /* XXX format conversion */ + assert (reply->depth == surface->depth); + + image = _cairo_image_surface_create_with_pixman_format + (xcb_get_image_data (reply), + surface->pixman_format, + width, height, + CAIRO_STRIDE_FOR_WIDTH_BPP (width, + PIXMAN_FORMAT_BPP (surface->pixman_format))); + status = image->status; + if (unlikely (status)) { + free (reply); + goto FAIL; + } + + /* XXX */ + pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply); + + _cairo_xcb_connection_release (connection); + + return image; + +FAIL: + _cairo_xcb_connection_release (connection); + 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 +_cairo_xcb_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_xcb_surface_t *surface = abstract_surface; + cairo_surface_t *image; + + if (surface->fallback != NULL) { + image = cairo_surface_reference (&surface->fallback->base); + goto DONE; + } + + image = _cairo_surface_has_snapshot (&surface->base, + &_cairo_image_surface_backend); + if (image != NULL) { + image = cairo_surface_reference (image); + goto DONE; + } + + 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, NULL); + +DONE: + *image_out = (cairo_image_surface_t *) image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xcb_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_surface_destroy (&image->base); +} + +cairo_bool_t +_cairo_xcb_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + extents->x = extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + return TRUE; +} + +static void +_cairo_xcb_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_xcb_surface_t *surface = abstract_surface; + + *options = *_cairo_xcb_screen_get_font_options (surface->screen); +} + +static cairo_status_t +_put_shm_image (cairo_xcb_surface_t *surface, + xcb_gcontext_t gc, + cairo_image_surface_t *image) +{ +#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) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _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 + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +static cairo_status_t +_put_image (cairo_xcb_surface_t *surface, + cairo_image_surface_t *image) +{ + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + /* XXX track damaged region? */ + + status = _cairo_xcb_connection_acquire (surface->connection); + if (unlikely (status)) + return status; + + if (image->pixman_format == surface->pixman_format) { + xcb_gcontext_t gc; + + 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 (surface, gc, image); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + _cairo_xcb_connection_put_image (surface->connection, + surface->drawable, gc, + image->width, image->height, + image->base.device_transform_inverse.x0, + image->base.device_transform_inverse.y0, + image->depth, + image->stride, + image->data); + status = CAIRO_STATUS_SUCCESS; + } + + _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc); + } else { + ASSERT_NOT_REACHED; + } + + _cairo_xcb_connection_release (surface->connection); + 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 +_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 (flags) + return CAIRO_STATUS_SUCCESS; + + if (likely (surface->fallback == NULL)) { + status = CAIRO_STATUS_SUCCESS; + if (! surface->base.finished && surface->deferred_clear) + status = _cairo_xcb_surface_clear (surface); + + return status; + } + + status = surface->base.status; + if (status == CAIRO_STATUS_SUCCESS && + (! surface->base._finishing || ! surface->owns_pixmap)) { + status = cairo_surface_status (&surface->fallback->base); + + 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) + 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_boxes_clear (&surface->fallback_damage); + cairo_surface_destroy (&surface->fallback->base); + surface->fallback = NULL; + + return status; +} + +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; + 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_fallback (cairo_xcb_surface_t *surface, + cairo_composite_rectangles_t *composite) +{ + cairo_image_surface_t *image; + cairo_status_t status; + + status = _cairo_composite_rectangles_add_to_damage (composite, + &surface->fallback_damage); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + 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_bool_t permit_subpixel_antialiasing) +{ + 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, + const cairo_clip_t *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 +_cairo_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_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, + 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_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, + 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_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 +_cairo_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_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_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, /* snapshot */ + + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_xcb_surface_get_extents, + _cairo_xcb_surface_get_font_options, + + _cairo_xcb_surface_flush, + NULL, + + _cairo_xcb_surface_paint, + _cairo_xcb_surface_mask, + _cairo_xcb_surface_stroke, + _cairo_xcb_surface_fill, + NULL, /* fill-stroke */ + _cairo_xcb_surface_glyphs, +}; + +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_xcb_surface_t *surface; + + 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), + FALSE); /* is_vector */ + + surface->connection = _cairo_xcb_connection_reference (screen->connection); + surface->screen = screen; + cairo_list_add (&surface->link, &screen->surfaces); + + surface->drawable = drawable; + surface->owns_pixmap = owns_pixmap; + + 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->fallback = NULL; + _cairo_boxes_init (&surface->fallback_damage); + + return &surface->base; +} + +static xcb_screen_t * +_cairo_xcb_screen_from_visual (xcb_connection_t *connection, + xcb_visualtype_t *visual, + int *depth) +{ + xcb_depth_iterator_t d; + 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_visual == visual->visual_id) { + *depth = s.data->root_depth; + return s.data; + } + + d = xcb_screen_allowed_depths_iterator(s.data); + for (; d.rem; xcb_depth_next (&d)) { + xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data); + + for (; v.rem; xcb_visualtype_next (&v)) { + if (v.data->visual_id == visual->visual_id) { + *depth = d.data->depth; + return s.data; + } + } + } + } + + 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 *connection, + xcb_drawable_t drawable, + xcb_visualtype_t *visual, + int width, + int height) +{ + cairo_xcb_screen_t *screen; + xcb_screen_t *xcb_screen; + cairo_format_masks_t image_masks; + pixman_format_code_t pixman_format; + xcb_render_pictformat_t xrender_format; + int depth; + + 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_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 (connection, visual, &depth); + if (unlikely (xcb_screen == NULL)) + 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) + image_masks.bpp = 16; + else if (depth > 1) + image_masks.bpp = 8; + else + image_masks.bpp = 1; + + 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 (connection, xcb_screen); + if (unlikely (screen == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + xrender_format = + _cairo_xcb_connection_get_xrender_format_for_visual (screen->connection, + visual->visual_id); + + return _cairo_xcb_surface_create_internal (screen, drawable, FALSE, + pixman_format, + xrender_format, + width, height); +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +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 *connection, + xcb_screen_t *screen, + xcb_pixmap_t bitmap, + int width, + int height) +{ + cairo_xcb_screen_t *cairo_xcb_screen; + + 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_error (CAIRO_STATUS_INVALID_SIZE)); + if (unlikely (width <= 0 || height <= 0)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + 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 (cairo_xcb_screen, bitmap, FALSE, + PIXMAN_a1, + cairo_xcb_screen->connection->standard_formats[CAIRO_FORMAT_A1], + width, height); +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +slim_hidden_def (cairo_xcb_surface_create_for_bitmap); +#endif + +/** + * cairo_xcb_surface_create_with_xrender_format: + * @connection: an XCB connection + * @drawable: an XCB drawable + * @screen: the XCB screen associated with @drawable + * @format: the picture format to use for drawing to @drawable. The + * depth of @format mush match the depth of the drawable. + * @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 picture format. + * + * 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_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 *cairo_xcb_screen; + cairo_format_masks_t image_masks; + pixman_format_code_t pixman_format; + + 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_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; + image_masks.red_mask = + (unsigned long) format->direct.red_mask << format->direct.red_shift; + image_masks.green_mask = + (unsigned long) format->direct.green_mask << format->direct.green_shift; + image_masks.blue_mask = + (unsigned long) format->direct.blue_mask << format->direct.blue_shift; +#if 0 + image_masks.bpp = format->depth; +#else + if (format->depth > 16) + image_masks.bpp = 32; + else if (format->depth > 8) + image_masks.bpp = 16; + else if (format->depth > 1) + image_masks.bpp = 8; + else + image_masks.bpp = 1; +#endif + + if (! _pixman_format_from_masks (&image_masks, &pixman_format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + 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 (cairo_xcb_screen, + drawable, + FALSE, + pixman_format, + format->id, + width, height); +} +#if CAIRO_HAS_XLIB_XCB_FUNCTIONS +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 + * @width: the new width of the surface + * @height: the new height of the surface + * + * Informs cairo of the new size of the XCB drawable underlying the + * surface. For a surface created for a window (rather than a pixmap), + * this function must be called each time the size of the window + * changes. (For a subwindow, you are normally resizing the window + * yourself, but for a toplevel window, it is necessary to listen for + * ConfigureNotify events.) + * + * 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, + 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; + + _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 new file mode 100644 index 0000000000..e321d8482c --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xcb.h @@ -0,0 +1,116 @@ +/* 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 + */ + +#ifndef CAIRO_XCB_H +#define CAIRO_XCB_H + +#include "cairo.h" + +#if CAIRO_HAS_XCB_SURFACE + +#include +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_xcb_surface_create (xcb_connection_t *connection, + xcb_drawable_t drawable, + xcb_visualtype_t *visual, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection, + xcb_screen_t *screen, + xcb_pixmap_t bitmap, + int width, + int height); + +cairo_public cairo_surface_t * +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_public void +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 +cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device, + int major_version, + int minor_version); + +cairo_public void +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 */ +# error Cairo was not compiled with support for the xcb backend +#endif /* CAIRO_HAS_XCB_SURFACE */ + +#endif /* CAIRO_XCB_H */ 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 0000000000..1392999617 --- /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 new file mode 100644 index 0000000000..108897e92a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-display.c @@ -0,0 +1,667 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 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): + * Karl Tomlinson , Mozilla Corporation + */ + +#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); + +static cairo_xlib_display_t *_cairo_xlib_display_list; + +static int +_noop_error_handler (Display *display, + XErrorEvent *event) +{ + return False; /* return value is ignored */ +} + +static void +_cairo_xlib_display_finish (void *abstract_display) +{ + cairo_xlib_display_t *display = abstract_display; + Display *dpy = display->display; + + _cairo_xlib_display_fini_shm (display); + + if (! cairo_device_acquire (&display->base)) { + cairo_xlib_error_func_t old_handler; + + /* protect the notifies from triggering XErrors */ + XSync (dpy, False); + old_handler = XSetErrorHandler (_noop_error_handler); + + while (! cairo_list_is_empty (&display->fonts)) { + _cairo_xlib_font_close (cairo_list_first_entry (&display->fonts, + cairo_xlib_font_t, + link)); + } + + while (! cairo_list_is_empty (&display->screens)) { + _cairo_xlib_screen_destroy (display, + cairo_list_first_entry (&display->screens, + cairo_xlib_screen_t, + link)); + } + + XSync (dpy, False); + XSetErrorHandler (old_handler); + + 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_MUTEX_LOCK (_cairo_xlib_display_mutex); + for (display = _cairo_xlib_display_list; display; display = display->next) + if (display->display == dpy) + break; + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + if (display == NULL) + return 0; + + cairo_device_finish (&display->base); + + /* + * Unhook from the global list + */ + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + prev = &_cairo_xlib_display_list; + for (display = _cairo_xlib_display_list; display; display = next) { + next = display->next; + if (display->display == dpy) { + *prev = next; + break; + } else + prev = &display->next; + } + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + + display->display = NULL; /* catch any later invalid access */ + cairo_device_destroy (&display->base); + + /* Return value in accordance with requirements of + * XESetCloseDisplay */ + return 0; +} + +static const cairo_device_backend_t _cairo_xlib_device_backend = { + CAIRO_DEVICE_TYPE_XLIB, + + NULL, + NULL, + + NULL, /* flush */ + _cairo_xlib_display_finish, + _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: + * @dpy: the display to create the device for + * + * Gets the device belonging to @dpy, or creates it if it doesn't exist yet. + * + * Returns: the device belonging to @dpy + **/ +cairo_device_t * +_cairo_xlib_device_create (Display *dpy) +{ + cairo_xlib_display_t *display; + cairo_xlib_display_t **prev; + cairo_device_t *device; + XExtCodes *codes; + const char *env; + + CAIRO_MUTEX_INITIALIZE (); + + /* There is an apparent deadlock between this mutex and the + * mutex for the display, but it's actually safe. For the + * app to call XCloseDisplay() while any other thread is + * inside this function would be an error in the logic + * app, and the CloseDisplay hook is the only other place we + * acquire this mutex. + */ + CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex); + + for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next) + { + if (display->display == dpy) { + /* + * MRU the list + */ + if (prev != &_cairo_xlib_display_list) { + *prev = display->next; + display->next = _cairo_xlib_display_list; + _cairo_xlib_display_list = display; + } + device = cairo_device_reference (&display->base); + goto UNLOCK; + } + } + + 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 + * add our hook. For now, that means Render, so we call into its + * QueryVersion function to ensure it gets initialized. + */ + display->render_major = display->render_minor = -1; + XRenderQueryVersion (dpy, &display->render_major, &display->render_minor); + env = getenv ("CAIRO_DEBUG"); + if (env != NULL && (env = strstr (env, "xrender-version=")) != 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 < display->render_major || + (max_render_major == display->render_major && + max_render_minor < display->render_minor)) + { + display->render_major = max_render_major; + display->render_minor = max_render_minor; + } + } + + _cairo_xlib_display_select_compositor (display); + + 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. + */ +#if RENDER_MAJOR == 0 && RENDER_MINOR < 10 + display->buggy_gradients = TRUE; +#else + display->buggy_gradients = FALSE; +#endif + display->buggy_pad_reflect = FALSE; + display->buggy_repeat = FALSE; + + /* This buggy_repeat condition is very complicated because there + * are multiple X server code bases (with multiple versioning + * schemes within a code base), and multiple bugs. + * + * The X servers: + * + * 1. The Vendor=="XFree86" code base with release numbers such + * as 4.7.0 (VendorRelease==40700000). + * + * 2. The Vendor=="X.Org" code base (a descendant of the + * XFree86 code base). It originally had things like + * VendorRelease==60700000 for release 6.7.0 but then changed + * its versioning scheme so that, for example, + * VendorRelease==10400000 for the 1.4.0 X server within the + * X.Org 7.3 release. + * + * The bugs: + * + * 1. The original bug that led to the buggy_repeat + * workaround. This was a bug that Owen Taylor investigated, + * understood well, and characterized against various X + * servers. Confirmed X servers with this bug include: + * + * "XFree86" <= 40500000 + * "X.Org" <= 60802000 (only with old numbering >= 60700000) + * + * 2. A separate bug resulting in a crash of the X server when + * using cairo's extend-reflect test case, (which, surprisingly + * enough was not passing RepeatReflect to the X server, but + * instead using RepeatNormal in a workaround). Nobody to date + * has understood the bug well, but it appears to be gone as of + * the X.Org 1.4.0 server. This bug is coincidentally avoided + * by using the same buggy_repeat workaround. Confirmed X + * servers with this bug include: + * + * "X.org" == 60900000 (old versioning scheme) + * "X.org" < 10400000 (new numbering scheme) + * + * For the old-versioning-scheme X servers we don't know + * exactly when second the bug started, but since bug 1 is + * present through 6.8.2 and bug 2 is present in 6.9.0 it seems + * safest to just blacklist all old-versioning-scheme X servers, + * (just using VendorRelease < 70000000), as buggy_repeat=TRUE. + */ + if (_cairo_xlib_vendor_is_xorg (dpy)) { + if (VendorRelease (dpy) >= 60700000) { + if (VendorRelease (dpy) < 70000000) + display->buggy_repeat = TRUE; + + /* We know that gradients simply do not work in early Xorg servers */ + if (VendorRelease (dpy) < 70200000) + display->buggy_gradients = TRUE; + + /* And the extended repeat modes were not fixed until much later */ + display->buggy_pad_reflect = TRUE; + } else { + if (VendorRelease (dpy) < 10400000) + display->buggy_repeat = TRUE; + + /* Too many bugs in the early drivers */ + if (VendorRelease (dpy) < 10699000) + display->buggy_pad_reflect = TRUE; + } + } else if (strstr (ServerVendor (dpy), "XFree86") != NULL) { + if (VendorRelease (dpy) <= 40500000) + display->buggy_repeat = TRUE; + + display->buggy_gradients = TRUE; + display->buggy_pad_reflect = TRUE; + } + + codes = XAddExtension (dpy); + if (unlikely (codes == NULL)) { + device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + free (display); + goto UNLOCK; + } + + 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; + + device = &display->base; + +UNLOCK: + CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex); + return device; +} + +cairo_status_t +_cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display) +{ + cairo_status_t status; + + status = cairo_device_acquire (device); + if (status) + return status; + + *display = (cairo_xlib_display_t *) device; + 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 * +_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display, + cairo_format_t format) +{ + XRenderPictFormat *xrender_format; + + xrender_format = display->cached_xrender_formats[format]; + if (xrender_format == NULL) { + int pict_format = PictStandardNUM; + + switch (format) { + case CAIRO_FORMAT_A1: + pict_format = PictStandardA1; break; + case CAIRO_FORMAT_A8: + pict_format = PictStandardA8; break; + case CAIRO_FORMAT_RGB24: + pict_format = PictStandardRGB24; break; + 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 (pict_format != PictStandardNUM) + xrender_format = + XRenderFindStandardFormat (display->display, pict_format); + display->cached_xrender_formats[format] = xrender_format; + } + + return xrender_format; +} + +cairo_xlib_screen_t * +_cairo_xlib_display_get_screen (cairo_xlib_display_t *display, + Screen *screen) +{ + cairo_xlib_screen_t *info; + + cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) { + if (info->screen == screen) { + if (display->screens.next != &info->link) + cairo_list_move (&info->link, &display->screens); + return info; + } + } + + return NULL; +} + +cairo_bool_t +_cairo_xlib_display_has_repeat (cairo_device_t *device) +{ + return ! ((cairo_xlib_display_t *) device)->buggy_repeat; +} + +cairo_bool_t +_cairo_xlib_display_has_reflect (cairo_device_t *device) +{ + return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect; +} + +cairo_bool_t +_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 0000000000..ed2845db57 --- /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 new file mode 100644 index 0000000000..8e338aea6f --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-private.h @@ -0,0 +1,472 @@ +/* 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. + * + * Contributors(s): + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ + +#ifndef CAIRO_XLIB_PRIVATE_H +#define CAIRO_XLIB_PRIVATE_H + +#include "cairo-xlib-xrender-private.h" +#include "cairo-xlib.h" + +#include "cairo-compiler-private.h" +#include "cairo-device-private.h" +#include "cairo-freelist-type-private.h" +#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_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; + + cairo_xlib_display_t *next; + + 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_RGB30 + 1]; + + int force_precision; + + 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; +}; + +typedef struct _cairo_xlib_visual_info { + cairo_list_t link; + VisualID visualid; + struct { uint8_t a, r, g, b; } colors[256]; + uint8_t cube_to_pseudocolor[CUBE_SIZE][CUBE_SIZE][CUBE_SIZE]; + uint8_t field8_to_cube[256]; + int8_t dither8_to_cube[256]; + uint8_t gray8_to_pseudocolor[256]; +} cairo_xlib_visual_info_t; + +struct _cairo_xlib_screen { + cairo_list_t link; + + cairo_device_t *device; + Screen *screen; + + cairo_list_t surfaces; + + cairo_bool_t has_font_options; + cairo_font_options_t font_options; + + 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 cairo_status_t +_cairo_xlib_display_acquire (cairo_device_t *device, + cairo_xlib_display_t **display); + +cairo_private cairo_bool_t +_cairo_xlib_display_has_repeat (cairo_device_t *device); + +cairo_private cairo_bool_t +_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_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, + int depth, + GC gc); + +cairo_private cairo_font_options_t * +_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info); + +cairo_private cairo_status_t +_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + Visual *visual, + cairo_xlib_visual_info_t **out); + +cairo_private cairo_status_t +_cairo_xlib_visual_info_create (Display *dpy, + int screen, + VisualID visualid, + cairo_xlib_visual_info_t **out); + +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 0000000000..bf8d20546b --- /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 new file mode 100644 index 0000000000..379ec7bdc8 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-screen.c @@ -0,0 +1,467 @@ +/* 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. + * + * 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" + +#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" + +static int +parse_boolean (const char *v) +{ + char c0, c1; + + c0 = *v; + if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1') + return 1; + if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0') + return 0; + if (c0 == 'o') + { + c1 = v[1]; + if (c1 == 'n' || c1 == 'N') + return 1; + if (c1 == 'f' || c1 == 'F') + return 0; + } + + return -1; +} + +static cairo_bool_t +get_boolean_default (Display *dpy, + const char *option, + cairo_bool_t *value) +{ + char *v; + int i; + + v = XGetDefault (dpy, "Xft", option); + if (v) { + i = parse_boolean (v); + if (i >= 0) { + *value = i; + return TRUE; + } + } + + return FALSE; +} + +static cairo_bool_t +get_integer_default (Display *dpy, + const char *option, + int *value) +{ + char *v, *e; + + v = XGetDefault (dpy, "Xft", option); + if (v) { +#if CAIRO_HAS_FC_FONT + if (FcNameConstant ((FcChar8 *) v, value)) + return TRUE; +#endif + + *value = strtol (v, &e, 0); + if (e != v) + return TRUE; + } + + return FALSE; +} + +static void +_cairo_xlib_init_screen_font_options (Display *dpy, + cairo_xlib_screen_t *info) +{ + cairo_bool_t xft_hinting; + cairo_bool_t xft_antialias; + int xft_hintstyle; + int xft_rgba; + int xft_lcdfilter; + cairo_antialias_t antialias; + cairo_subpixel_order_t subpixel_order; + cairo_lcd_filter_t lcd_filter; + cairo_hint_style_t hint_style; + + if (!get_boolean_default (dpy, "antialias", &xft_antialias)) + xft_antialias = TRUE; + + if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) { + /* -1 is an non-existant Fontconfig constant used to differentiate + * the case when no lcdfilter property is available. + */ + xft_lcdfilter = -1; + } + + if (!get_boolean_default (dpy, "hinting", &xft_hinting)) + xft_hinting = TRUE; + + if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle)) + xft_hintstyle = FC_HINT_FULL; + + if (!get_integer_default (dpy, "rgba", &xft_rgba)) + { + cairo_xlib_display_t *display = (cairo_xlib_display_t *) info->device; + + xft_rgba = FC_RGBA_UNKNOWN; + +#if RENDER_MAJOR > 0 || RENDER_MINOR >= 6 + if (display->render_major > 0 || display->render_minor >= 6) { + int render_order = XRenderQuerySubpixelOrder (dpy, + XScreenNumberOfScreen (info->screen)); + + switch (render_order) { + default: + case SubPixelUnknown: + xft_rgba = FC_RGBA_UNKNOWN; + break; + case SubPixelHorizontalRGB: + xft_rgba = FC_RGBA_RGB; + break; + case SubPixelHorizontalBGR: + xft_rgba = FC_RGBA_BGR; + break; + case SubPixelVerticalRGB: + xft_rgba = FC_RGBA_VRGB; + break; + case SubPixelVerticalBGR: + xft_rgba = FC_RGBA_VBGR; + break; + case SubPixelNone: + xft_rgba = FC_RGBA_NONE; + break; + } + } +#endif + } + + if (xft_hinting) { + switch (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 (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 (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 (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 (&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_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON); +} + +void +_cairo_xlib_screen_destroy (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info) +{ + Display *dpy; + int i; + + dpy = display->display; + + 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; + } + } + + while (! cairo_list_is_empty (&info->visuals)) { + _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals, + cairo_xlib_visual_info_t, + link)); + } + + cairo_list_del (&info->link); + + free (info); +} + +cairo_status_t +_cairo_xlib_screen_get (Display *dpy, + Screen *screen, + cairo_xlib_screen_t **out) +{ + cairo_xlib_display_t *display; + cairo_device_t *device; + cairo_xlib_screen_t *info; + cairo_status_t status; + + device = _cairo_xlib_device_create (dpy); + status = device->status; + if (unlikely (status)) + goto CLEANUP_DEVICE; + + status = _cairo_xlib_display_acquire (device, &display); + if (unlikely (status)) + goto CLEANUP_DEVICE; + + info = _cairo_xlib_display_get_screen (display, screen); + if (info != NULL) { + *out = info; + goto CLEANUP_DISPLAY; + } + + info = _cairo_malloc (sizeof (cairo_xlib_screen_t)); + if (unlikely (info == NULL)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_DISPLAY; + } + + info->device = device; + info->screen = screen; + info->has_font_options = FALSE; + 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); + + *out = info; + + CLEANUP_DISPLAY: + cairo_device_release (&display->base); + + CLEANUP_DEVICE: + cairo_device_destroy (device); + return status; +} + +GC +_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + int depth, + Drawable drawable) +{ + GC gc = NULL; + int i; + + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (info->gc_depths[i] == depth) { + info->gc_depths[i] = 0; + gc = info->gc[i]; + break; + } + } + + if (gc == NULL) { + XGCValues gcv; + + gcv.graphics_exposures = False; + gcv.fill_style = FillTiled; + gc = XCreateGC (display->display, + drawable, + GCGraphicsExposures | GCFillStyle, &gcv); + } + + return gc; +} + +void +_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + int depth, + GC gc) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH (info->gc); i++) { + if (info->gc_depths[i] == 0) + break; + } + + if (i == ARRAY_LENGTH (info->gc)) { + /* perform random substitution to ensure fair caching over depths */ + i = rand () % ARRAY_LENGTH (info->gc); + XFreeGC(display->display, info->gc[i]); + } + + info->gc[i] = gc; + info->gc_depths[i] = depth; +} + +cairo_status_t +_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info, + Visual *v, + cairo_xlib_visual_info_t **out) +{ + cairo_xlib_visual_info_t *visual; + cairo_status_t status; + + cairo_list_foreach_entry (visual, + cairo_xlib_visual_info_t, + &info->visuals, + link) + { + if (visual->visualid == v->visualid) { + *out = visual; + return CAIRO_STATUS_SUCCESS; + } + } + + status = _cairo_xlib_visual_info_create (display->display, + XScreenNumberOfScreen (info->screen), + v->visualid, + &visual); + if (unlikely (status)) + return status; + + cairo_list_add (&visual->link, &info->visuals); + *out = visual; + return CAIRO_STATUS_SUCCESS; +} + +cairo_font_options_t * +_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info) +{ + if (! info->has_font_options) { + _cairo_font_options_init_default (&info->font_options); + _cairo_font_options_set_round_glyph_positions (&info->font_options, CAIRO_ROUND_GLYPH_POS_ON); + + if (info->screen != NULL) { + cairo_xlib_display_t *display; + + if (! _cairo_xlib_display_acquire (info->device, &display)) { + _cairo_xlib_init_screen_font_options (display->display, + info); + cairo_device_release (&display->base); + } + } + + info->has_font_options = TRUE; + } + + 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 0000000000..4c3b99d9e7 --- /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 new file mode 100644 index 0000000000..83d9b63cc4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-surface-private.h @@ -0,0 +1,44 @@ +/* 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. + */ + +#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-surface-private.h" +#include "cairo-surface-backend-private.h" + + +#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 0000000000..10f947d9cf --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-surface-shm.c @@ -0,0 +1,1466 @@ +/* -*- 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) +{ + display->shm = NULL; +} + +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 new file mode 100644 index 0000000000..b37b21badb --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c @@ -0,0 +1,2380 @@ +/* -*- 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 + */ + +/* Heed well the words of Owen Taylor: + * "Any patch that works around a render bug, or claims to, without a + * specific reference to the bug filed in bugzilla.freedesktop.org will + * never pass approval." + */ + +#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 /* for XDestroyImage */ + +#include +#include +#include + +#define DEBUG 0 + +#if DEBUG +#define UNSUPPORTED(reason) \ + fprintf (stderr, \ + "cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \ + __FUNCTION__, __LINE__, reason), \ + CAIRO_INT_STATUS_UNSUPPORTED +#else +#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED +#endif + +#if DEBUG +#include +static void CAIRO_PRINTF_FORMAT (2, 3) +_x_bread_crumb (Display *dpy, + const char *fmt, + ...) +{ + xReq *req; + char buf[2048]; + unsigned int len, len_dwords; + va_list ap; + + va_start (ap, fmt); + len = vsnprintf (buf, sizeof (buf), fmt, ap); + va_end (ap); + + buf[len++] = '\0'; + while (len & 3) + buf[len++] = '\0'; + + LockDisplay (dpy); + GetEmptyReq (NoOperation, req); + + len_dwords = len >> 2; + SetReqLen (req, len_dwords, len_dwords); + Data (dpy, buf, len); + + UnlockDisplay (dpy); + SyncHandle (); +} +#define X_DEBUG(x) _x_bread_crumb x +#else +#define X_DEBUG(x) +#endif + +/** + * SECTION:cairo-xlib + * @Title: XLib Surfaces + * @Short_Description: X Window System rendering using XLib + * @See_Also: #cairo_surface_t + * + * The XLib surface is used to render cairo graphics to X Window System + * windows and pixmaps using the XLib library. + * + * 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 + * @Short_Description: X Window System rendering using XLib and the X Render extension + * @See_Also: #cairo_surface_t + * + * The XLib surface is used to render cairo graphics to X Window System + * windows and pixmaps using the XLib and Xrender libraries. + * + * 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, + XErrorEvent *event); + +static cairo_surface_t * +_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, + Drawable drawable, + Visual *visual, + XRenderPictFormat *xrender_format, + int width, + int height, + int depth); + +static cairo_bool_t +_cairo_surface_is_xlib (cairo_surface_t *surface); + +/* + * Instead of taking two round trips for each blending request, + * assume that if a particular drawable fails GetImage that it will + * fail for a "while"; use temporary pixmaps to avoid the errors + */ + +#define CAIRO_ASSUME_PIXMAP 20 + +static Visual * +_visual_for_xrender_format(Screen *screen, + XRenderPictFormat *xrender_format) +{ + int d, v; + + /* XXX Consider searching through the list of known cairo_visual_t for + * the reverse mapping. + */ + + for (d = 0; d < screen->ndepths; d++) { + Depth *d_info = &screen->depths[d]; + + if (d_info->depth != xrender_format->depth) + continue; + + for (v = 0; v < d_info->nvisuals; v++) { + Visual *visual = &d_info->visuals[v]; + + switch (visual->class) { + case TrueColor: + if (xrender_format->type != PictTypeDirect) + continue; + break; + + case DirectColor: + /* Prefer TrueColor to DirectColor. + * (XRenderFindVisualFormat considers both TrueColor and DirectColor + * Visuals to match the same PictFormat.) + */ + continue; + + case StaticGray: + case GrayScale: + case StaticColor: + case PseudoColor: + if (xrender_format->type != PictTypeIndexed) + continue; + break; + } + + if (xrender_format == + XRenderFindVisualFormat (DisplayOfScreen(screen), visual)) + return visual; + } + } + + return NULL; +} + +static cairo_content_t +_xrender_format_to_content (XRenderPictFormat *xrender_format) +{ + 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; + + 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; + + return content; +} + +static cairo_surface_t * +_cairo_xlib_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + cairo_xlib_surface_t *src = abstract_src; + XRenderPictFormat *xrender_format; + cairo_xlib_surface_t *surface; + cairo_xlib_display_t *display; + Pixmap pix; + + if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) + return NULL; + + if (width == 0 || height == 0) + return NULL; + + if (_cairo_xlib_display_acquire (src->base.device, &display)) + return NULL; + + /* If we never found an XRenderFormat or if it isn't compatible + * with the content being requested, then we fallback to just + * constructing a cairo_format_t instead, (which will fairly + * arbitrarily pick a visual/depth for the similar surface. + */ + 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, height, xrender_format->depth); + + if (xrender_format == src->xrender_format) + visual = src->visual; + else + visual = _visual_for_xrender_format(src->screen->screen, + xrender_format); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (src->screen, pix, visual, + xrender_format, + width, height, + xrender_format->depth); + } + else + { + Screen *screen = src->screen->screen; + int depth; + + /* 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); + return NULL; + } + + depth = DefaultDepthOfScreen (screen); + + pix = XCreatePixmap (display->display, RootWindowOfScreen (screen), + width <= 0 ? 1 : width, height <= 0 ? 1 : height, + depth); + + surface = (cairo_xlib_surface_t *) + _cairo_xlib_surface_create_internal (src->screen, pix, + DefaultVisualOfScreen (screen), + NULL, + width, height, depth); + } + + 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; +} + +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) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_status_t status; + cairo_xlib_display_t *display; + + cairo_list_del (&surface->link); + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return status; + + X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable)); + + if (surface->embedded_source.picture) + XRenderFreePicture (display->display, surface->embedded_source.picture); + if (surface->picture) + XRenderFreePicture (display->display, surface->picture); + + _cairo_xlib_surface_discard_shm (surface); + + if (surface->owns_pixmap) + XFreePixmap (display->display, surface->drawable); + + cairo_device_release (&display->base); + + return status; +} + +cairo_status_t +_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + GC *gc) +{ + *gc = _cairo_xlib_screen_get_gc (display, + surface->screen, + surface->depth, + surface->drawable); + if (unlikely (*gc == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + return CAIRO_STATUS_SUCCESS; +} + +static int +_noop_error_handler (Display *display, + XErrorEvent *event) +{ + return False; /* return value is ignored */ +} + +static void +_swap_ximage_2bytes (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + + for (j = ximage->height; j; j--) { + uint16_t *p = (uint16_t *) line; + for (i = ximage->width; i; i--) { + *p = bswap_16 (*p); + p++; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_3bytes (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + + for (j = ximage->height; j; j--) { + uint8_t *p = (uint8_t *) line; + for (i = ximage->width; i; i--) { + uint8_t tmp; + tmp = p[2]; + p[2] = p[0]; + p[0] = tmp; + p += 3; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_4bytes (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + + for (j = ximage->height; j; j--) { + uint32_t *p = (uint32_t *) line; + for (i = ximage->width; i; i--) { + *p = bswap_32 (*p); + p++; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_nibbles (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + + for (j = ximage->height; j; j--) { + uint8_t *p = (uint8_t *) line; + for (i = (ximage->width + 1) / 2; i; i--) { + *p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf); + p++; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_bits (XImage *ximage) +{ + int i, j; + char *line = ximage->data; + int unit = ximage->bitmap_unit; + int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8; + + for (j = ximage->height; j; j--) { + char *p = line; + + for (i = line_bytes; i; i--) { + char b = *p; + b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55); + b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33); + b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f); + *p = b; + + p++; + } + + line += ximage->bytes_per_line; + } +} + +static void +_swap_ximage_to_native (XImage *ximage) +{ + int unit_bytes = 0; + int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst; + + if (ximage->bits_per_pixel == 1 && + ximage->bitmap_bit_order != native_byte_order) + { + _swap_ximage_bits (ximage); + if (ximage->bitmap_bit_order == ximage->byte_order) + return; + } + + if (ximage->byte_order == native_byte_order) + return; + + switch (ximage->bits_per_pixel) { + case 1: + unit_bytes = ximage->bitmap_unit / 8; + break; + case 4: + _swap_ximage_nibbles (ximage); + /* fall-through */ + case 8: + case 16: + case 20: + case 24: + case 28: + case 30: + case 32: + unit_bytes = (ximage->bits_per_pixel + 7) / 8; + break; + default: + /* This could be hit on some rare but possible cases. */ + ASSERT_NOT_REACHED; + } + + switch (unit_bytes) { + case 1: + break; + case 2: + _swap_ximage_2bytes (ximage); + break; + case 3: + _swap_ximage_3bytes (ximage); + break; + case 4: + _swap_ximage_4bytes (ximage); + break; + default: + ASSERT_NOT_REACHED; + } +} + + +/* Given a mask, (with a single sequence of contiguous 1 bits), return + * the number of 1 bits in 'width' and the number of 0 bits to its + * right in 'shift'. */ +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; +} + +/* Convert a field of 'width' bits to 'new_width' bits with correct + * rounding. */ +static inline uint32_t +_resize_field (uint32_t field, int width, int new_width) +{ + if (width == 0) + return 0; + + if (width >= new_width) { + return field >> (width - new_width); + } else { + uint32_t result = field << (new_width - width); + + while (width < new_width) { + result |= result >> width; + width <<= 1; + } + return result; + } +} + +static inline uint32_t +_adjust_field (uint32_t field, int adjustment) +{ + return MIN (255, MAX(0, (int)field + adjustment)); +} + +/* Given a shifted field value, (described by 'width' and 'shift), + * resize it 8-bits and return that value. + * + * Note that the original field value must not have any non-field bits + * set. + */ +static inline uint32_t +_field_to_8 (uint32_t field, int width, int shift) +{ + return _resize_field (field >> shift, width, 8); +} + +static inline uint32_t +_field_to_8_undither (uint32_t field, int width, int shift, + int dither_adjustment) +{ + return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width); +} + +/* Given an 8-bit value, convert it to a field of 'width', shift it up + * to 'shift, and return it. */ +static inline uint32_t +_field_from_8 (uint32_t field, int width, int shift) +{ + return _resize_field (field, 8, width) << shift; +} + +static inline uint32_t +_field_from_8_dither (uint32_t field, int width, int shift, + int8_t dither_adjustment) +{ + return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift); +} + +static inline uint32_t +_pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info, + uint32_t r, uint32_t g, uint32_t b, + int8_t dither_adjustment) +{ + if (r == g && g == b) { + dither_adjustment /= RAMP_SIZE; + return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)]; + } else { + dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128]; + return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]] + [visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]] + [visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]]; + } +} + +static inline uint32_t +_pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info, + uint32_t pixel) +{ + uint32_t r, g, b; + pixel &= 0xff; + r = visual_info->colors[pixel].r; + g = visual_info->colors[pixel].g; + b = visual_info->colors[pixel].b; + return (r << 16) | + (g << 8) | + (b ); +} + +/* should range from -128 to 127 */ +#define X 16 +static const int8_t dither_pattern[4][4] = { + {-8*X, +0*X, -6*X, +2*X}, + {+4*X, -4*X, +6*X, -2*X}, + {-5*X, +4*X, -7*X, +1*X}, + {+7*X, -1*X, +5*X, -3*X} +}; +#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; +} + +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, + const cairo_rectangle_int_t *extents, + int try_shm) +{ + cairo_int_status_t status; + cairo_image_surface_t *image = NULL; + XImage *ximage; + pixman_format_code_t pixman_format; + cairo_xlib_display_t *display; + + assert (extents->x >= 0); + assert (extents->y >= 0); + assert (extents->x + extents->width <= surface->width); + assert (extents->y + extents->height <= surface->height); + + 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 _cairo_surface_create_in_error (status); + + 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; + + _cairo_xlib_shm_surface_get_ximage (&image->base, &shm_image); + + 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, + AllPlanes, ZPixmap); + + XSetErrorHandler (old_handler); + + /* If we get an error, the surface must have been a window, + * so retry with the safe code path. + */ + if (!ximage) + surface->use_pixmap = CAIRO_ASSUME_PIXMAP; + } else { + surface->use_pixmap--; + ximage = NULL; + } + + if (ximage == NULL) { + /* XGetImage from a window is dangerous because it can + * produce errors if the window is unmapped or partially + * outside the screen. We could check for errors and + * retry, but to keep things simple, we just create a + * temporary pixmap + */ + Pixmap pixmap; + GC gc; + + status = _cairo_xlib_surface_get_gc (display, surface, &gc); + if (unlikely (status)) + goto BAIL; + + pixmap = XCreatePixmap (display->display, + surface->drawable, + 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, + 0, 0); + + gcv.subwindow_mode = ClipByChildren; + XChangeGC (display->display, gc, GCSubwindowMode, &gcv); + + ximage = XGetImage (display->display, + pixmap, + 0, 0, + extents->width, extents->height, + AllPlanes, ZPixmap); + + XFreePixmap (display->display, pixmap); + } + + _cairo_xlib_surface_put_gc (display, surface, gc); + + if (ximage == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + } + + _swap_ximage_to_native (ximage); + + /* 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 (pixman_format && + ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 && + (surface->visual == NULL || surface->visual->class == TrueColor)) + { + image = (cairo_image_surface_t*) + _cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data, + pixman_format, + ximage->width, + ximage->height, + ximage->bytes_per_line); + status = image->base.status; + if (unlikely (status)) + goto BAIL; + + /* Let the surface take ownership of the data */ + _cairo_image_surface_assume_ownership_of_data (image); + ximage->data = NULL; + } else { + /* The visual we are dealing with is not supported by the + * standard pixman formats. So we must first convert the data + * to a supported format. */ + + cairo_format_t format; + unsigned char *data; + uint32_t *row; + uint32_t in_pixel, out_pixel; + unsigned int rowstride; + uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0; + int a_width=0, r_width=0, g_width=0, b_width=0; + int a_shift=0, r_shift=0, g_shift=0, b_shift=0; + int x, y, x0, y0, x_off, y_off; + cairo_xlib_visual_info_t *visual_info = NULL; + + if (surface->visual == NULL || surface->visual->class == TrueColor) { + cairo_bool_t has_alpha; + cairo_bool_t has_color; + + has_alpha = surface->a_mask; + has_color = (surface->r_mask || + surface->g_mask || + surface->b_mask); + + if (has_color) { + if (has_alpha) { + format = CAIRO_FORMAT_ARGB32; + } else { + format = CAIRO_FORMAT_RGB24; + } + } else { + /* XXX: Using CAIRO_FORMAT_A8 here would be more + * efficient, but would require slightly different code in + * the image conversion to put the alpha channel values + * into the right place. */ + format = CAIRO_FORMAT_ARGB32; + } + + a_mask = surface->a_mask; + r_mask = surface->r_mask; + g_mask = surface->g_mask; + b_mask = surface->b_mask; + + _characterize_field (a_mask, &a_width, &a_shift); + _characterize_field (r_mask, &r_width, &r_shift); + _characterize_field (g_mask, &g_width, &g_shift); + _characterize_field (b_mask, &b_width, &b_shift); + + } else { + format = CAIRO_FORMAT_RGB24; + + status = _cairo_xlib_screen_get_visual_info (display, + surface->screen, + surface->visual, + &visual_info); + if (unlikely (status)) + goto BAIL; + } + + image = (cairo_image_surface_t *) cairo_image_surface_create + (format, ximage->width, ximage->height); + status = image->base.status; + if (unlikely (status)) + goto BAIL; + + 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; + for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); + y < ximage->height; + y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) { + const int8_t *dither_row = dither_pattern[y_off]; + for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); + x < ximage->width; + x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) { + int dither_adjustment = dither_row[x_off]; + + in_pixel = XGetPixel (ximage, x, y); + if (visual_info == NULL) { + out_pixel = ( + (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)); + } else { + /* Undithering pseudocolor does not look better */ + out_pixel = _pseudocolor_to_rgb888 (visual_info, in_pixel); + } + row[x] = out_pixel; + } + row += rowstride; + } + cairo_surface_mark_dirty (&image->base); + } + + BAIL: + if (ximage) + XDestroyImage (ximage); + + cairo_device_release (&display->base); + + if (unlikely (status)) { + if (image) + cairo_surface_destroy (&image->base); + return _cairo_surface_create_in_error (status); + } + + return &image->base; +} + +void +_cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface, + cairo_antialias_t antialias) +{ + cairo_xlib_display_t *display = surface->display; + int precision; + + 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_BEST: + case CAIRO_ANTIALIAS_SUBPIXEL: + precision = PolyModePrecise; + break; + } + + if (surface->precision != precision) { + XRenderPictureAttributes pa; + + pa.poly_mode = precision; + XRenderChangePicture (display->display, surface->picture, + CPPolyMode, &pa); + + surface->precision = precision; + } +} + +void +_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface) +{ + cairo_xlib_display_t *display = surface->display; + XRenderPictureAttributes pa; + int mask = 0; + + 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); +} + +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 = _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 = FALSE; + cairo_bool_t is_rgb_image; + GC gc; + + ximage.width = image->width; + ximage.height = image->height; + ximage.format = ZPixmap; + 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 = surface->depth; + ximage.red_mask = surface->r_mask; + 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; + + 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; + + image_masks.alpha_mask = surface->a_mask; + image_masks.red_mask = surface->r_mask; + image_masks.green_mask = surface->g_mask; + image_masks.blue_mask = surface->b_mask; + image_masks.bpp = bits_per_pixel (surface); + ret = _pixman_format_from_masks (&image_masks, &intermediate_format); + assert (ret); + + 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_composite32 (PIXMAN_OP_SRC, + image->pixman_image, + NULL, + clone->pixman_image, + src_x, src_y, + 0, 0, + 0, 0, + width, 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; + + ret = XInitImage (&ximage); + assert (ret != 0); + + src_x = src_y = 0; + } + else + { + unsigned int stride, rowstride; + int x, y, x0, y0, x_off, y_off; + uint32_t in_pixel, out_pixel, *row; + int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0; + int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0; + int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0; + int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0; + cairo_xlib_visual_info_t *visual_info = NULL; + cairo_bool_t true_color; + int ret; + + 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)) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto BAIL; + } + + own_data = TRUE; + + ret = XInitImage (&ximage); + assert (ret != 0); + + _characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift); + _characterize_field (image_masks.red_mask , &i_r_width, &i_r_shift); + _characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift); + _characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift); + + true_color = surface->visual == NULL || + surface->visual->class == TrueColor; + if (true_color) { + _characterize_field (surface->a_mask, &o_a_width, &o_a_shift); + _characterize_field (surface->r_mask, &o_r_width, &o_r_shift); + _characterize_field (surface->g_mask, &o_g_width, &o_g_shift); + _characterize_field (surface->b_mask, &o_b_width, &o_b_shift); + } else { + status = _cairo_xlib_screen_get_visual_info (display, + surface->screen, + surface->visual, + &visual_info); + if (unlikely (status)) + goto BAIL; + } + + rowstride = image->stride >> 2; + row = (uint32_t *) image->data; + x0 = dst_x + surface->base.device_transform.x0; + y0 = dst_y + surface->base.device_transform.y0; + for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern); + y < ximage.height; + y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) + { + const int8_t *dither_row = dither_pattern[y_off]; + + for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]); + x < ximage.width; + x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) + { + int dither_adjustment = dither_row[x_off]; + int a, r, g, b; + + if (image_masks.bpp == 1) + in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7))); + else if (image_masks.bpp <= 8) + in_pixel = ((uint8_t*)row)[x]; + else if (image_masks.bpp <= 16) + in_pixel = ((uint16_t*)row)[x]; + else if (image_masks.bpp <= 24) +#ifdef WORDS_BIGENDIAN + in_pixel = ((uint8_t*)row)[3 * x] << 16 | + ((uint8_t*)row)[3 * x + 1] << 8 | + ((uint8_t*)row)[3 * x + 2]; +#else + in_pixel = ((uint8_t*)row)[3 * x] | + ((uint8_t*)row)[3 * x + 1] << 8 | + ((uint8_t*)row)[3 * x + 2] << 16; +#endif + else + in_pixel = row[x]; + + /* If the incoming image has no alpha channel, then the input + * is opaque and the output should have the maximum alpha value. + * For all other channels, their absence implies 0. + */ + if (image_masks.alpha_mask == 0x0) + a = 0xff; + else + a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift); + r = _field_to_8 (in_pixel & image_masks.red_mask , i_r_width, i_r_shift); + g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift); + b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift); + + if (true_color) { + out_pixel = _field_from_8 (a, o_a_width, o_a_shift) | + _field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) | + _field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) | + _field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment); + } else { + out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment); + } + + XPutPixel (&ximage, x, y, out_pixel); + } + + row += rowstride; + } + } + + status = _cairo_xlib_surface_get_gc (display, surface, &gc); + if (unlikely (status)) + goto BAIL; + + 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_rectangle_int_t extents; + + *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; + + 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_rectangle_int_t extents; + + extents.x = extents.y = 0; + extents.width = surface->width; + extents.height = surface->height; + + 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_xlib_surface_t *surface = abstract_surface; + + if (&image->base == surface->shm) + return; + + cairo_surface_destroy (&image->base); +} + +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_surface_t *image; + + 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); + } + + image = _get_image_surface (abstract_surface, extents, TRUE); + cairo_surface_set_device_offset (image, -extents->x, -extents->y); + + return (cairo_image_surface_t *) image; +} + +static cairo_int_status_t +_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; + + if (surface->shm) { + cairo_rectangle_int_t r; + + assert (surface->fallback); + assert (surface->base.damage); + + 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->shm->damage = + _cairo_damage_add_rectangle (surface->shm->damage, &r); + + return _cairo_image_surface_unmap_image (surface->shm, image); + } + + status = _cairo_xlib_surface_draw_image (abstract_surface, image, + 0, 0, + image->width, image->height, + image->base.device_transform_inverse.x0, + image->base.device_transform_inverse.y0); + + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return status; +} + +static cairo_status_t +_cairo_xlib_surface_flush (void *abstract_surface, + unsigned flags) +{ + cairo_xlib_surface_t *surface = abstract_surface; + cairo_int_status_t status; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_xlib_surface_put_shm (surface); + if (unlikely (status)) + return status; + + surface->fallback >>= 1; + if (surface->shm && _cairo_xlib_shm_surface_is_idle (surface->shm)) + _cairo_xlib_surface_discard_shm (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_xlib_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_xlib_surface_t *surface = abstract_surface; + + rectangle->x = 0; + rectangle->y = 0; + + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static void +_cairo_xlib_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + cairo_xlib_surface_t *surface = abstract_surface; + + *options = *_cairo_xlib_screen_get_font_options (surface->screen); +} + +static inline cairo_int_status_t +get_compositor (cairo_xlib_surface_t **surface, + const cairo_compositor_t **compositor) +{ + 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_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_snapshot, + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_xlib_surface_get_extents, + _cairo_xlib_surface_get_font_options, + + _cairo_xlib_surface_flush, + NULL, /* mark_dirty_rectangle */ + + _cairo_xlib_surface_paint, + _cairo_xlib_surface_mask, + _cairo_xlib_surface_stroke, + _cairo_xlib_surface_fill, + NULL, /* fill-stroke */ + _cairo_xlib_surface_glyphs, +}; + +/** + * _cairo_surface_is_xlib: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_xlib_surface_t + * + * Return value: True if the surface is an xlib surface + **/ +static cairo_bool_t +_cairo_surface_is_xlib (cairo_surface_t *surface) +{ + return surface->backend == &cairo_xlib_surface_backend; +} + +static cairo_surface_t * +_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, + Drawable drawable, + Visual *visual, + XRenderPictFormat *xrender_format, + int width, + int height, + int depth) +{ + cairo_xlib_surface_t *surface; + cairo_xlib_display_t *display; + cairo_status_t status; + + if (depth == 0) { + if (xrender_format) { + depth = xrender_format->depth; + + /* XXX find matching visual for core/dithering fallbacks? */ + } else if (visual) { + Screen *scr = screen->screen; + + if (visual == DefaultVisualOfScreen (scr)) { + depth = DefaultDepthOfScreen (scr); + } else { + int j, k; + + /* This is ugly, but we have to walk over all visuals + * for the display to find the correct depth. + */ + depth = 0; + for (j = 0; j < scr->ndepths; j++) { + Depth *d = &scr->depths[j]; + for (k = 0; k < d->nvisuals; k++) { + if (&d->visuals[k] == visual) { + depth = d->depth; + goto found; + } + } + } + } + } + + if (depth == 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); + +found: + ; + } + + surface = _cairo_malloc (sizeof (cairo_xlib_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + status = _cairo_xlib_display_acquire (screen->device, &display); + if (unlikely (status)) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (status)); + } + + surface->display = display; + if (CAIRO_RENDER_HAS_CREATE_PICTURE (display)) { + if (!xrender_format) { + if (visual) { + xrender_format = XRenderFindVisualFormat (display->display, visual); + } else if (depth == 1) { + xrender_format = + _cairo_xlib_display_get_xrender_format (display, + CAIRO_FORMAT_A1); + } + } + } + + cairo_device_release (&display->base); + + _cairo_surface_init (&surface->base, + &cairo_xlib_surface_backend, + screen->device, + _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; + surface->use_pixmap = 0; + surface->width = width; + surface->height = height; + + surface->picture = None; + surface->precision = PolyModePrecise; + + surface->embedded_source.picture = None; + + surface->visual = visual; + surface->xrender_format = xrender_format; + surface->depth = depth; + + /* + * Compute the pixel format masks from either a XrenderFormat or + * else from a visual; failing that we assume the drawable is an + * alpha-only pixmap as it could only have been created that way + * through the cairo_xlib_surface_create_for_bitmap function. + */ + if (xrender_format) { + surface->a_mask = (unsigned long) + surface->xrender_format->direct.alphaMask + << surface->xrender_format->direct.alpha; + surface->r_mask = (unsigned long) + surface->xrender_format->direct.redMask + << surface->xrender_format->direct.red; + surface->g_mask = (unsigned long) + surface->xrender_format->direct.greenMask + << surface->xrender_format->direct.green; + surface->b_mask = (unsigned long) + surface->xrender_format->direct.blueMask + << surface->xrender_format->direct.blue; + } else if (visual) { + surface->a_mask = 0; + surface->r_mask = visual->red_mask; + surface->g_mask = visual->green_mask; + surface->b_mask = visual->blue_mask; + } else { + if (depth < 32) + surface->a_mask = (1 << depth) - 1; + else + surface->a_mask = 0xffffffff; + surface->r_mask = 0; + surface->g_mask = 0; + surface->b_mask = 0; + } + + cairo_list_add (&surface->link, &screen->surfaces); + + 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; +} + +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 + * @drawable: an X Drawable, (a Pixmap or a Window) + * @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 Xlib 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_xlib_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: the newly created surface + * + * Since: 1.0 + **/ +cairo_surface_t * +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height) +{ + Screen *scr; + cairo_xlib_screen_t *screen; + cairo_status_t status; + + if (! valid_size (width, height)) { + /* you're lying, and you know it! */ + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + } + + scr = _cairo_xlib_screen_from_visual (dpy, visual); + if (scr == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); + + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable)); + + return _cairo_xlib_surface_create_internal (screen, drawable, + visual, NULL, + width, height, 0); +} + +/** + * cairo_xlib_surface_create_for_bitmap: + * @dpy: an X Display + * @bitmap: an X Drawable, (a depth-1 Pixmap) + * @screen: the X Screen associated with @bitmap + * @width: the current width of @bitmap. + * @height: the current height of @bitmap. + * + * Creates an Xlib surface that draws to the given bitmap. + * 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, + Pixmap bitmap, + Screen *scr, + int width, + int height) +{ + cairo_xlib_screen_t *screen; + cairo_status_t status; + + 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)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap)); + + return _cairo_xlib_surface_create_internal (screen, bitmap, + NULL, NULL, + width, height, 1); +} + +#if CAIRO_HAS_XLIB_XRENDER_SURFACE +/** + * cairo_xlib_surface_create_with_xrender_format: + * @dpy: an X Display + * @drawable: an X Drawable, (a Pixmap or a Window) + * @screen: the X Screen associated with @drawable + * @format: the picture format to use for drawing to @drawable. The depth + * of @format must match the depth of the drawable. + * @width: the current width of @drawable. + * @height: the current height of @drawable. + * + * Creates an Xlib surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided picture format. + * + * Note: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size() must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface + * + * Since: 1.0 + **/ +cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + Screen *scr, + XRenderPictFormat *format, + int width, + int height) +{ + cairo_xlib_screen_t *screen; + cairo_status_t status; + + 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)) + return _cairo_surface_create_in_error (status); + + X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable)); + + return _cairo_xlib_surface_create_internal (screen, drawable, + _visual_for_xrender_format (scr, format), + format, width, height, 0); +} + +/** + * cairo_xlib_surface_get_xrender_format: + * @surface: an xlib surface + * + * Gets the X Render picture format that @surface uses for rendering with the + * X Render extension. If the surface was created by + * cairo_xlib_surface_create_with_xrender_format() originally, the return + * value is the format passed to that constructor. + * + * Return value: the XRenderPictFormat* associated with @surface, + * or %NULL if the surface is not an xlib surface + * or if the X Render extension is not available. + * + * Since: 1.6 + **/ +XRenderPictFormat * +cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) +{ + cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; + + /* Throw an error for a non-xlib surface */ + if (! _cairo_surface_is_xlib (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return xlib_surface->xrender_format; +} +#endif + +/** + * cairo_xlib_surface_set_size: + * @surface: a #cairo_surface_t for the XLib backend + * @width: the new width of the surface + * @height: the new height of the surface + * + * Informs cairo of the new size of the X Drawable underlying the + * surface. For a surface created for a Window (rather than a Pixmap), + * this function must be called each time the size of the window + * changes. (For a subwindow, you are normally resizing the window + * yourself, but for a toplevel window, it is necessary to listen for + * ConfigureNotify events.) + * + * 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, + int width, + int height) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + cairo_status_t status; + + 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_xlib (abstract_surface)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + 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 + * @drawable: the new drawable for the surface + * @width: the width of the new drawable + * @height: the height of the new drawable + * + * Informs cairo of a new X Drawable underlying the + * surface. The drawable must match the display, screen + * and format of the existing drawable or the application + * 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, + Drawable drawable, + int width, + int height) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_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_xlib (abstract_surface)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; + } + + if (! valid_size (width, height)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + return; + } + + /* XXX: and what about this case? */ + 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; + + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return; + + X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable)); + + if (surface->picture != None) { + XRenderFreePicture (display->display, surface->picture); + if (unlikely (status)) { + status = _cairo_surface_set_error (&surface->base, status); + return; + } + + surface->picture = None; + } + + cairo_device_release (&display->base); + + surface->drawable = drawable; + } + + if (surface->width != width || surface->height != height) { + _cairo_xlib_surface_discard_shm (surface); + + surface->width = width; + surface->height = height; + } +} + +/** + * cairo_xlib_surface_get_display: + * @surface: a #cairo_xlib_surface_t + * + * Get the X Display for the underlying X Drawable. + * + * Return value: the display. + * + * Since: 1.2 + **/ +Display * +cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) +{ + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return ((cairo_xlib_display_t *) abstract_surface->device)->display; +} + +/** + * cairo_xlib_surface_get_drawable: + * @surface: a #cairo_xlib_surface_t + * + * Get the underlying X Drawable used for the surface. + * + * Return value: the drawable. + * + * Since: 1.2 + **/ +Drawable +cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->drawable; +} + +/** + * cairo_xlib_surface_get_screen: + * @surface: a #cairo_xlib_surface_t + * + * Get the X Screen for the underlying X Drawable. + * + * Return value: the screen. + * + * Since: 1.2 + **/ +Screen * +cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return surface->screen->screen; +} + +/** + * cairo_xlib_surface_get_visual: + * @surface: a #cairo_xlib_surface_t + * + * Gets the X Visual associated with @surface, suitable for use with the + * underlying X Drawable. If @surface was created by + * cairo_xlib_surface_create(), the return value is the Visual passed to that + * constructor. + * + * Return value: the Visual or %NULL if there is no appropriate Visual for + * @surface. + * + * Since: 1.2 + **/ +Visual * +cairo_xlib_surface_get_visual (cairo_surface_t *surface) +{ + cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; + + if (! _cairo_surface_is_xlib (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return xlib_surface->visual; +} + +/** + * cairo_xlib_surface_get_depth: + * @surface: a #cairo_xlib_surface_t + * + * Get the number of bits used to represent each pixel value. + * + * Return value: the depth of the surface in bits. + * + * Since: 1.2 + **/ +int +cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->depth; +} + +/** + * cairo_xlib_surface_get_width: + * @surface: a #cairo_xlib_surface_t + * + * Get the width of the X Drawable underlying the surface in pixels. + * + * Return value: the width of the surface in pixels. + * + * Since: 1.2 + **/ +int +cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->width; +} + +/** + * cairo_xlib_surface_get_height: + * @surface: a #cairo_xlib_surface_t + * + * Get the height of the X Drawable underlying the surface in pixels. + * + * Return value: the height of the surface in pixels. + * + * Since: 1.2 + **/ +int +cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) +{ + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return surface->height; +} + +#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 new file mode 100644 index 0000000000..979cd5b36b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-visual.c @@ -0,0 +1,194 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + */ + +#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. */ + +/* XXX: This is currently using linear distance in RGB space which is + * decidedly not perceptually linear. If someone cared a lot about the + * quality, they might choose something else here. Then again, they + * might also choose not to use a PseudoColor visual... */ +static inline int +_color_distance (unsigned short r1, unsigned short g1, unsigned short b1, + unsigned short r2, unsigned short g2, unsigned short b2) +{ + r1 >>= 8; g1 >>= 8; b1 >>= 8; + r2 >>= 8; g2 >>= 8; b2 >>= 8; + + return ((r2 - r1) * (r2 - r1) + + (g2 - g1) * (g2 - g1) + + (b2 - b1) * (b2 - b1)); +} + +cairo_status_t +_cairo_xlib_visual_info_create (Display *dpy, + int screen, + VisualID visualid, + cairo_xlib_visual_info_t **out) +{ + cairo_xlib_visual_info_t *info; + Colormap colormap = DefaultColormap (dpy, screen); + XColor color; + int gray, red, green, blue; + int i, j, distance, min_distance = 0; + XColor colors[256]; + unsigned short cube_index_to_short[CUBE_SIZE]; + unsigned short ramp_index_to_short[RAMP_SIZE]; + unsigned char gray_to_pseudocolor[RAMP_SIZE]; + + for (i = 0; i < CUBE_SIZE; i++) + cube_index_to_short[i] = (0xffff * i + ((CUBE_SIZE-1)>>1)) / (CUBE_SIZE-1); + for (i = 0; i < RAMP_SIZE; i++) + ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1); + + 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. + * Give up as soon as failures start. */ + + for (gray = 0; gray < RAMP_SIZE; gray++) { + color.red = color.green = color.blue = ramp_index_to_short[gray]; + if (! XAllocColor (dpy, colormap, &color)) + goto DONE_ALLOCATE; + } + + /* XXX: Could do this in a more clever order to have the best + * possible results from early failure. Could also choose a cube + * uniformly distributed in a better space than RGB. */ + for (red = 0; red < CUBE_SIZE; red++) { + for (green = 0; green < CUBE_SIZE; green++) { + for (blue = 0; blue < CUBE_SIZE; blue++) { + color.red = cube_index_to_short[red]; + color.green = cube_index_to_short[green]; + color.blue = cube_index_to_short[blue]; + color.pixel = 0; + color.flags = 0; + color.pad = 0; + if (! XAllocColor (dpy, colormap, &color)) + goto DONE_ALLOCATE; + } + } + } + DONE_ALLOCATE: + + for (i = 0; i < ARRAY_LENGTH (colors); i++) + colors[i].pixel = i; + XQueryColors (dpy, colormap, colors, ARRAY_LENGTH (colors)); + + /* Search for nearest colors within allocated colormap. */ + for (gray = 0; gray < RAMP_SIZE; gray++) { + for (i = 0; i < 256; i++) { + distance = _color_distance (ramp_index_to_short[gray], + ramp_index_to_short[gray], + ramp_index_to_short[gray], + colors[i].red, + colors[i].green, + colors[i].blue); + if (i == 0 || distance < min_distance) { + gray_to_pseudocolor[gray] = colors[i].pixel; + min_distance = distance; + if (!min_distance) + break; + } + } + } + for (red = 0; red < CUBE_SIZE; red++) { + for (green = 0; green < CUBE_SIZE; green++) { + for (blue = 0; blue < CUBE_SIZE; blue++) { + for (i = 0; i < 256; i++) { + distance = _color_distance (cube_index_to_short[red], + cube_index_to_short[green], + cube_index_to_short[blue], + colors[i].red, + colors[i].green, + colors[i].blue); + if (i == 0 || distance < min_distance) { + info->cube_to_pseudocolor[red][green][blue] = colors[i].pixel; + min_distance = distance; + if (!min_distance) + break; + } + } + } + } + } + + for (i = 0, j = 0; i < 256; i++) { + if (j < CUBE_SIZE - 1 && (((i<<8)+i) - (int)cube_index_to_short[j]) > ((int)cube_index_to_short[j+1] - ((i<<8)+i))) + j++; + info->field8_to_cube[i] = j; + + info->dither8_to_cube[i] = ((int)i - 128) / (CUBE_SIZE - 1); + } + for (i = 0, j = 0; i < 256; i++) { + if (j < RAMP_SIZE - 1 && (((i<<8)+i) - (int)ramp_index_to_short[j]) > ((int)ramp_index_to_short[j+1] - ((i<<8)+i))) + j++; + info->gray8_to_pseudocolor[i] = gray_to_pseudocolor[j]; + } + + for (i = 0; i < 256; i++) { + info->colors[i].a = 0xff; + info->colors[i].r = colors[i].red >> 8; + info->colors[i].g = colors[i].green >> 8; + info->colors[i].b = colors[i].blue >> 8; + } + + *out = info; + return CAIRO_STATUS_SUCCESS; +} + +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 0000000000..4fa9f28832 --- /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 new file mode 100644 index 0000000000..c622b6268b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h @@ -0,0 +1,1178 @@ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007 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. + */ + +#ifndef CAIRO_XLIB_XRENDER_PRIVATE_H +#define CAIRO_XLIB_XRENDER_PRIVATE_H + +#include "cairo-features.h" +#include "cairo-compiler-private.h" + +#include + +/* These prototypes are used when defining interfaces missing from the + * render headers. As it happens, it is the case that all libxrender + * functions take a pointer as first argument. */ + +__attribute__((__unused__)) static void _void_consume (void *p, ...) { } +__attribute__((__unused__)) static void * _voidp_consume (void *p, ...) { return (void *)0; } +__attribute__((__unused__)) static int _int_consume (void *p, ...) { return 0; } +__attribute__((__unused__)) static void _void_consume_free (Display *p, XID n) { } + + +#if CAIRO_HAS_XLIB_XRENDER_SURFACE + +#include "cairo-xlib-xrender.h" + +#include +#include + +/* We require Render >= 0.6. The following defines were only added in + * 0.10. Make sure they are defined. + */ + +/* Filters included in 0.10 */ +#ifndef FilterConvolution +#define FilterConvolution "convolution" +#endif + +/* Extended repeat attributes included in 0.10 */ +#ifndef RepeatNone +#define RepeatNone 0 +#define RepeatNormal 1 +#define RepeatPad 2 +#define RepeatReflect 3 +#endif + + +#ifndef PictOptBlendMinimum +/* + * Operators only available in version 0.11 + */ +#define PictOpBlendMinimum 0x30 +#define PictOpMultiply 0x30 +#define PictOpScreen 0x31 +#define PictOpOverlay 0x32 +#define PictOpDarken 0x33 +#define PictOpLighten 0x34 +#define PictOpColorDodge 0x35 +#define PictOpColorBurn 0x36 +#define PictOpHardLight 0x37 +#define PictOpSoftLight 0x38 +#define PictOpDifference 0x39 +#define PictOpExclusion 0x3a +#define PictOpHSLHue 0x3b +#define PictOpHSLSaturation 0x3c +#define PictOpHSLColor 0x3d +#define PictOpHSLLuminosity 0x3e +#define PictOpBlendMaximum 0x3e +#endif + +/* The mozilla build doesn't set up all the following HAVE_* symbols, + so we cheat by just checking the version number for now. */ +#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 + +typedef struct _XCircle { + XFixed x; + XFixed y; + XFixed radius; +} XCircle; +typedef struct _XRadialGradient { + XCircle inner; + XCircle outer; +} XRadialGradient; +#endif + +#if !HAVE_XRENDERCREATECONICALGRADIENT +#define XRenderCreateConicalGradient _int_consume + +typedef struct _XConicalGradient { + XPointFixed center; + XFixed angle; /* in degrees */ +} XConicalGradient; +#endif + +#endif /* RENDER_MAJOR == 0 && RENDER_MINOR < 10 */ + +#else /* !CAIRO_HAS_XLIB_XRENDER_SURFACE */ + +/* Provide dummy symbols and macros to get it compile and take the fallback + * route, just like as if Xrender is not available in the server at run-time. */ + + +/* Functions */ + +#define XRenderQueryExtension _int_consume +#define XRenderQueryVersion _int_consume +#define XRenderQueryFormats _int_consume +#define XRenderQuerySubpixelOrder _int_consume +#define XRenderSetSubpixelOrder _int_consume +#define XRenderFindVisualFormat _voidp_consume +#define XRenderFindFormat _voidp_consume +#define XRenderFindStandardFormat _voidp_consume +#define XRenderQueryPictIndexValues _voidp_consume +#define XRenderCreatePicture _int_consume +#define XRenderChangePicture _void_consume +#define XRenderSetPictureClipRectangles _void_consume +#define XRenderSetPictureClipRegion _void_consume +#define XRenderSetPictureTransform _void_consume +#define XRenderFreePicture _void_consume_free +#define XRenderComposite _void_consume +#define XRenderCreateGlyphSet _int_consume +#define XRenderReferenceGlyphSet _int_consume +#define XRenderFreeGlyphSet _void_consume_free +#define XRenderAddGlyphs _void_consume +#define XRenderFreeGlyphs _void_consume +#define XRenderCompositeString8 _void_consume +#define XRenderCompositeString16 _void_consume +#define XRenderCompositeString32 _void_consume +#define XRenderCompositeText8 (cairo_xrender_composite_text_func_t) _void_consume +#define XRenderCompositeText16 _void_consume +#define XRenderCompositeText32 _void_consume +#define XRenderFillRectangle _void_consume +#define XRenderFillRectangles _void_consume +#define XRenderCompositeTrapezoids _void_consume +#define XRenderCompositeTriangles _void_consume +#define XRenderCompositeTriStrip _void_consume +#define XRenderCompositeTriFan _void_consume +#define XRenderCompositeDoublePoly _void_consume +#define XRenderParseColor _int_consume +#define XRenderCreateCursor _int_consume +#define XRenderQueryFilters _voidp_consume +#define XRenderSetPictureFilter _void_consume +#define XRenderCreateAnimCursor _int_consume +#define XRenderAddTraps _void_consume +#define XRenderCreateSolidFill _int_consume +#define XRenderCreateLinearGradient _int_consume +#define XRenderCreateRadialGradient _int_consume +#define XRenderCreateConicalGradient _int_consume + +#define cairo_xlib_surface_create_with_xrender_format _voidp_consume + + + +/* The rest of this file is copied from various Xrender header files, with + * the following copyright/license information: + * + * Copyright © 2000 SuSE, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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: Keith Packard, SuSE, Inc. + */ + + +/* Copied from X11/extensions/render.h */ + +typedef unsigned long Glyph; +typedef unsigned long GlyphSet; +typedef unsigned long Picture; +typedef unsigned long PictFormat; + +#define BadPictFormat 0 +#define BadPicture 1 +#define BadPictOp 2 +#define BadGlyphSet 3 +#define BadGlyph 4 +#define RenderNumberErrors (BadGlyph+1) + +#define PictTypeIndexed 0 +#define PictTypeDirect 1 + +#define PictOpMinimum 0 +#define PictOpClear 0 +#define PictOpSrc 1 +#define PictOpDst 2 +#define PictOpOver 3 +#define PictOpOverReverse 4 +#define PictOpIn 5 +#define PictOpInReverse 6 +#define PictOpOut 7 +#define PictOpOutReverse 8 +#define PictOpAtop 9 +#define PictOpAtopReverse 10 +#define PictOpXor 11 +#define PictOpAdd 12 +#define PictOpSaturate 13 +#define PictOpMaximum 13 + +/* + * Operators only available in version 0.2 + */ +#define PictOpDisjointMinimum 0x10 +#define PictOpDisjointClear 0x10 +#define PictOpDisjointSrc 0x11 +#define PictOpDisjointDst 0x12 +#define PictOpDisjointOver 0x13 +#define PictOpDisjointOverReverse 0x14 +#define PictOpDisjointIn 0x15 +#define PictOpDisjointInReverse 0x16 +#define PictOpDisjointOut 0x17 +#define PictOpDisjointOutReverse 0x18 +#define PictOpDisjointAtop 0x19 +#define PictOpDisjointAtopReverse 0x1a +#define PictOpDisjointXor 0x1b +#define PictOpDisjointMaximum 0x1b + +#define PictOpConjointMinimum 0x20 +#define PictOpConjointClear 0x20 +#define PictOpConjointSrc 0x21 +#define PictOpConjointDst 0x22 +#define PictOpConjointOver 0x23 +#define PictOpConjointOverReverse 0x24 +#define PictOpConjointIn 0x25 +#define PictOpConjointInReverse 0x26 +#define PictOpConjointOut 0x27 +#define PictOpConjointOutReverse 0x28 +#define PictOpConjointAtop 0x29 +#define PictOpConjointAtopReverse 0x2a +#define PictOpConjointXor 0x2b +#define PictOpConjointMaximum 0x2b + +/* + * Operators only available in version 0.11 + */ +#define PictOpBlendMinimum 0x30 +#define PictOpMultiply 0x30 +#define PictOpScreen 0x31 +#define PictOpOverlay 0x32 +#define PictOpDarken 0x33 +#define PictOpLighten 0x34 +#define PictOpColorDodge 0x35 +#define PictOpColorBurn 0x36 +#define PictOpHardLight 0x37 +#define PictOpSoftLight 0x38 +#define PictOpDifference 0x39 +#define PictOpExclusion 0x3a +#define PictOpHSLHue 0x3b +#define PictOpHSLSaturation 0x3c +#define PictOpHSLColor 0x3d +#define PictOpHSLLuminosity 0x3e +#define PictOpBlendMaximum 0x3e + +#define PolyEdgeSharp 0 +#define PolyEdgeSmooth 1 + +#define PolyModePrecise 0 +#define PolyModeImprecise 1 + +#define CPRepeat (1 << 0) +#define CPAlphaMap (1 << 1) +#define CPAlphaXOrigin (1 << 2) +#define CPAlphaYOrigin (1 << 3) +#define CPClipXOrigin (1 << 4) +#define CPClipYOrigin (1 << 5) +#define CPClipMask (1 << 6) +#define CPGraphicsExposure (1 << 7) +#define CPSubwindowMode (1 << 8) +#define CPPolyEdge (1 << 9) +#define CPPolyMode (1 << 10) +#define CPDither (1 << 11) +#define CPComponentAlpha (1 << 12) +#define CPLastBit 12 + +/* Filters included in 0.6 */ +#define FilterNearest "nearest" +#define FilterBilinear "bilinear" +/* Filters included in 0.10 */ +#define FilterConvolution "convolution" + +#define FilterFast "fast" +#define FilterGood "good" +#define FilterBest "best" + +#define FilterAliasNone -1 + +/* Subpixel orders included in 0.6 */ +#define SubPixelUnknown 0 +#define SubPixelHorizontalRGB 1 +#define SubPixelHorizontalBGR 2 +#define SubPixelVerticalRGB 3 +#define SubPixelVerticalBGR 4 +#define SubPixelNone 5 + +/* Extended repeat attributes included in 0.10 */ +#define RepeatNone 0 +#define RepeatNormal 1 +#define RepeatPad 2 +#define RepeatReflect 3 + + + +/* Copied from X11/extensions/Xrender.h */ + +typedef struct { + short red; + short redMask; + short green; + short greenMask; + short blue; + short blueMask; + short alpha; + short alphaMask; +} XRenderDirectFormat; + +typedef struct { + PictFormat id; + int type; + int depth; + XRenderDirectFormat direct; + Colormap colormap; +} XRenderPictFormat; + +#define PictFormatID (1 << 0) +#define PictFormatType (1 << 1) +#define PictFormatDepth (1 << 2) +#define PictFormatRed (1 << 3) +#define PictFormatRedMask (1 << 4) +#define PictFormatGreen (1 << 5) +#define PictFormatGreenMask (1 << 6) +#define PictFormatBlue (1 << 7) +#define PictFormatBlueMask (1 << 8) +#define PictFormatAlpha (1 << 9) +#define PictFormatAlphaMask (1 << 10) +#define PictFormatColormap (1 << 11) + +typedef struct _XRenderPictureAttributes { + int repeat; + Picture alpha_map; + int alpha_x_origin; + int alpha_y_origin; + int clip_x_origin; + int clip_y_origin; + Pixmap clip_mask; + Bool graphics_exposures; + int subwindow_mode; + int poly_edge; + int poly_mode; + Atom dither; + Bool component_alpha; +} XRenderPictureAttributes; + +typedef struct { + unsigned short red; + unsigned short green; + unsigned short blue; + unsigned short alpha; +} XRenderColor; + +typedef struct _XGlyphInfo { + unsigned short width; + unsigned short height; + short x; + short y; + short xOff; + short yOff; +} XGlyphInfo; + +typedef struct _XGlyphElt8 { + GlyphSet glyphset; + _Xconst char *chars; + int nchars; + int xOff; + int yOff; +} XGlyphElt8; + +typedef struct _XGlyphElt16 { + GlyphSet glyphset; + _Xconst unsigned short *chars; + int nchars; + int xOff; + int yOff; +} XGlyphElt16; + +typedef struct _XGlyphElt32 { + GlyphSet glyphset; + _Xconst unsigned int *chars; + int nchars; + int xOff; + int yOff; +} XGlyphElt32; + +typedef double XDouble; + +typedef struct _XPointDouble { + XDouble x, y; +} XPointDouble; + +#define XDoubleToFixed(f) ((XFixed) ((f) * 65536)) +#define XFixedToDouble(f) (((XDouble) (f)) / 65536) + +typedef int XFixed; + +typedef struct _XPointFixed { + XFixed x, y; +} XPointFixed; + +typedef struct _XLineFixed { + XPointFixed p1, p2; +} XLineFixed; + +typedef struct _XTriangle { + XPointFixed p1, p2, p3; +} XTriangle; + +typedef struct _XCircle { + XFixed x; + XFixed y; + XFixed radius; +} XCircle; + +typedef struct _XTrapezoid { + XFixed top, bottom; + XLineFixed left, right; +} XTrapezoid; + +typedef struct _XTransform { + XFixed matrix[3][3]; +} XTransform; + +typedef struct _XFilters { + int nfilter; + char **filter; + int nalias; + short *alias; +} XFilters; + +typedef struct _XIndexValue { + unsigned long pixel; + unsigned short red, green, blue, alpha; +} XIndexValue; + +typedef struct _XAnimCursor { + Cursor cursor; + unsigned long delay; +} XAnimCursor; + +typedef struct _XSpanFix { + XFixed left, right, y; +} XSpanFix; + +typedef struct _XTrap { + XSpanFix top, bottom; +} XTrap; + +typedef struct _XLinearGradient { + XPointFixed p1; + XPointFixed p2; +} XLinearGradient; + +typedef struct _XRadialGradient { + XCircle inner; + XCircle outer; +} XRadialGradient; + +typedef struct _XConicalGradient { + XPointFixed center; + XFixed angle; /* in degrees */ +} XConicalGradient; + +#define PictStandardARGB32 0 +#define PictStandardRGB24 1 +#define PictStandardA8 2 +#define PictStandardA4 3 +#define PictStandardA1 4 +#define PictStandardNUM 5 + + + +/* Copied from X11/extensions/renderproto.h */ + +#include + +#define Window CARD32 +#define Drawable CARD32 +#define Font CARD32 +#define Pixmap CARD32 +#define Cursor CARD32 +#define Colormap CARD32 +#define GContext CARD32 +#define Atom CARD32 +#define VisualID CARD32 +#define Time CARD32 +#define KeyCode CARD8 +#define KeySym CARD32 + +#define Picture CARD32 +#define PictFormat CARD32 +#define Fixed INT32 +#define Glyphset CARD32 +#define Glyph CARD32 + +/* + * data structures + */ + +typedef struct { + CARD16 red B16; + CARD16 redMask B16; + CARD16 green B16; + CARD16 greenMask B16; + CARD16 blue B16; + CARD16 blueMask B16; + CARD16 alpha B16; + CARD16 alphaMask B16; +} xDirectFormat; + +#define sz_xDirectFormat 16 + +typedef struct { + PictFormat id B32; + CARD8 type; + CARD8 depth; + CARD16 pad1 B16; + xDirectFormat direct; + Colormap colormap; +} xPictFormInfo; + +#define sz_xPictFormInfo 28 + +typedef struct { + VisualID visual; + PictFormat format; +} xPictVisual; + +#define sz_xPictVisual 8 + +typedef struct { + CARD8 depth; + CARD8 pad1; + CARD16 nPictVisuals B16; + CARD32 pad2 B32; +} xPictDepth; + +#define sz_xPictDepth 8 + +typedef struct { + CARD32 nDepth B32; + PictFormat fallback B32; +} xPictScreen; + +#define sz_xPictScreen 8 + +typedef struct { + CARD32 pixel B32; + CARD16 red B16; + CARD16 green B16; + CARD16 blue B16; + CARD16 alpha B16; +} xIndexValue; + +#define sz_xIndexValue 12 + +typedef struct { + CARD16 red B16; + CARD16 green B16; + CARD16 blue B16; + CARD16 alpha B16; +} xRenderColor; + +#define sz_xRenderColor 8 + +typedef struct { + Fixed x B32; + Fixed y B32; +} xPointFixed; + +#define sz_xPointFixed 8 + +typedef struct { + xPointFixed p1; + xPointFixed p2; +} xLineFixed; + +#define sz_xLineFixed 16 + +typedef struct { + xPointFixed p1, p2, p3; +} xTriangle; + +#define sz_xTriangle 24 + +typedef struct { + Fixed top B32; + Fixed bottom B32; + xLineFixed left; + xLineFixed right; +} xTrapezoid; + +#define sz_xTrapezoid 40 + +typedef struct { + CARD16 width B16; + CARD16 height B16; + INT16 x B16; + INT16 y B16; + INT16 xOff B16; + INT16 yOff B16; +} xGlyphInfo; + +#define sz_xGlyphInfo 12 + +typedef struct { + CARD8 len; + CARD8 pad1; + CARD16 pad2; + INT16 deltax; + INT16 deltay; +} xGlyphElt; + +#define sz_xGlyphElt 8 + +typedef struct { + Fixed l, r, y; +} xSpanFix; + +#define sz_xSpanFix 12 + +typedef struct { + xSpanFix top, bot; +} xTrap; + +#define sz_xTrap 24 + +/* + * requests and replies + */ +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD32 majorVersion B32; + CARD32 minorVersion B32; +} xRenderQueryVersionReq; + +#define sz_xRenderQueryVersionReq 12 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 majorVersion B32; + CARD32 minorVersion B32; + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; +} xRenderQueryVersionReply; + +#define sz_xRenderQueryVersionReply 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; +} xRenderQueryPictFormatsReq; + +#define sz_xRenderQueryPictFormatsReq 4 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 numFormats B32; + CARD32 numScreens B32; + CARD32 numDepths B32; + CARD32 numVisuals B32; + CARD32 numSubpixel B32; /* Version 0.6 */ + CARD32 pad5 B32; +} xRenderQueryPictFormatsReply; + +#define sz_xRenderQueryPictFormatsReply 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + PictFormat format B32; +} xRenderQueryPictIndexValuesReq; + +#define sz_xRenderQueryPictIndexValuesReq 8 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 numIndexValues; + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; + CARD32 pad6 B32; +} xRenderQueryPictIndexValuesReply; + +#define sz_xRenderQueryPictIndexValuesReply 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + Drawable drawable B32; + PictFormat format B32; + CARD32 mask B32; +} xRenderCreatePictureReq; + +#define sz_xRenderCreatePictureReq 20 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; + CARD32 mask B32; +} xRenderChangePictureReq; + +#define sz_xRenderChangePictureReq 12 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; + INT16 xOrigin B16; + INT16 yOrigin B16; +} xRenderSetPictureClipRectanglesReq; + +#define sz_xRenderSetPictureClipRectanglesReq 12 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; +} xRenderFreePictureReq; + +#define sz_xRenderFreePictureReq 8 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture mask B32; + Picture dst B32; + INT16 xSrc B16; + INT16 ySrc B16; + INT16 xMask B16; + INT16 yMask B16; + INT16 xDst B16; + INT16 yDst B16; + CARD16 width B16; + CARD16 height B16; +} xRenderCompositeReq; + +#define sz_xRenderCompositeReq 36 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture src B32; + Picture dst B32; + CARD32 colorScale B32; + CARD32 alphaScale B32; + INT16 xSrc B16; + INT16 ySrc B16; + INT16 xDst B16; + INT16 yDst B16; + CARD16 width B16; + CARD16 height B16; +} xRenderScaleReq; + +#define sz_xRenderScaleReq 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderTrapezoidsReq; + +#define sz_xRenderTrapezoidsReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderTrianglesReq; + +#define sz_xRenderTrianglesReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderTriStripReq; + +#define sz_xRenderTriStripReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderTriFanReq; + +#define sz_xRenderTriFanReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset gsid B32; + PictFormat format B32; +} xRenderCreateGlyphSetReq; + +#define sz_xRenderCreateGlyphSetReq 12 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset gsid B32; + Glyphset existing B32; +} xRenderReferenceGlyphSetReq; + +#define sz_xRenderReferenceGlyphSetReq 24 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset glyphset B32; +} xRenderFreeGlyphSetReq; + +#define sz_xRenderFreeGlyphSetReq 8 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset glyphset B32; + CARD32 nglyphs; +} xRenderAddGlyphsReq; + +#define sz_xRenderAddGlyphsReq 12 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Glyphset glyphset B32; +} xRenderFreeGlyphsReq; + +#define sz_xRenderFreeGlyphsReq 8 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture src B32; + Picture dst B32; + PictFormat maskFormat B32; + Glyphset glyphset B32; + INT16 xSrc B16; + INT16 ySrc B16; +} xRenderCompositeGlyphsReq, xRenderCompositeGlyphs8Req, +xRenderCompositeGlyphs16Req, xRenderCompositeGlyphs32Req; + +#define sz_xRenderCompositeGlyphs8Req 28 +#define sz_xRenderCompositeGlyphs16Req 28 +#define sz_xRenderCompositeGlyphs32Req 28 + +/* 0.1 and higher */ + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + CARD8 op; + CARD8 pad1; + CARD16 pad2 B16; + Picture dst B32; + xRenderColor color; +} xRenderFillRectanglesReq; + +#define sz_xRenderFillRectanglesReq 20 + +/* 0.5 and higher */ + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Cursor cid B32; + Picture src B32; + CARD16 x B16; + CARD16 y B16; +} xRenderCreateCursorReq; + +#define sz_xRenderCreateCursorReq 16 + +/* 0.6 and higher */ + +/* + * This can't use an array because 32-bit values may be in bitfields + */ +typedef struct { + Fixed matrix11 B32; + Fixed matrix12 B32; + Fixed matrix13 B32; + Fixed matrix21 B32; + Fixed matrix22 B32; + Fixed matrix23 B32; + Fixed matrix31 B32; + Fixed matrix32 B32; + Fixed matrix33 B32; +} xRenderTransform; + +#define sz_xRenderTransform 36 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; + xRenderTransform transform; +} xRenderSetPictureTransformReq; + +#define sz_xRenderSetPictureTransformReq 44 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Drawable drawable B32; +} xRenderQueryFiltersReq; + +#define sz_xRenderQueryFiltersReq 8 + +typedef struct { + BYTE type; /* X_Reply */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; + CARD32 numAliases B32; /* LISTofCARD16 */ + CARD32 numFilters B32; /* LISTofSTRING8 */ + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; +} xRenderQueryFiltersReply; + +#define sz_xRenderQueryFiltersReply 32 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture B32; + CARD16 nbytes B16; /* number of bytes in name */ + CARD16 pad B16; +} xRenderSetPictureFilterReq; + +#define sz_xRenderSetPictureFilterReq 12 + +/* 0.8 and higher */ + +typedef struct { + Cursor cursor B32; + CARD32 delay B32; +} xAnimCursorElt; + +#define sz_xAnimCursorElt 8 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Cursor cid B32; +} xRenderCreateAnimCursorReq; + +#define sz_xRenderCreateAnimCursorReq 8 + +/* 0.9 and higher */ + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture picture; + INT16 xOff B16; + INT16 yOff B16; +} xRenderAddTrapsReq; + +#define sz_xRenderAddTrapsReq 12 + +/* 0.10 and higher */ + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + xRenderColor color; +} xRenderCreateSolidFillReq; + +#define sz_xRenderCreateSolidFillReq 16 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + xPointFixed p1; + xPointFixed p2; + CARD32 nStops; +} xRenderCreateLinearGradientReq; + +#define sz_xRenderCreateLinearGradientReq 28 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + xPointFixed inner; + xPointFixed outer; + Fixed inner_radius; + Fixed outer_radius; + CARD32 nStops; +} xRenderCreateRadialGradientReq; + +#define sz_xRenderCreateRadialGradientReq 36 + +typedef struct { + CARD8 reqType; + CARD8 renderReqType; + CARD16 length B16; + Picture pid B32; + xPointFixed center; + Fixed angle; /* in degrees */ + CARD32 nStops; +} xRenderCreateConicalGradientReq; + +#define sz_xRenderCreateConicalGradientReq 24 + +#undef Window +#undef Drawable +#undef Font +#undef Pixmap +#undef Cursor +#undef Colormap +#undef GContext +#undef Atom +#undef VisualID +#undef Time +#undef KeyCode +#undef KeySym + +#undef Picture +#undef PictFormat +#undef Fixed +#undef Glyphset +#undef Glyph + + +#endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ + +#endif /* CAIRO_XLIB_XRENDER_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-xlib-xrender.h b/gfx/cairo/cairo/src/cairo-xlib-xrender.h new file mode 100644 index 0000000000..b34b057de4 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib-xrender.h @@ -0,0 +1,66 @@ +/* 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 + */ + +#ifndef CAIRO_XLIB_XRENDER_H +#define CAIRO_XLIB_XRENDER_H + +#include "cairo.h" + +#if CAIRO_HAS_XLIB_XRENDER_SURFACE + +#include +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + Screen *screen, + XRenderPictFormat *format, + int width, + int height); + +cairo_public XRenderPictFormat * +cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ +# error Cairo was not compiled with support for the xlib XRender backend +#endif /* CAIRO_HAS_XLIB_XRENDER_SURFACE */ + +#endif /* CAIRO_XLIB_XRENDER_H */ diff --git a/gfx/cairo/cairo/src/cairo-xlib.h b/gfx/cairo/cairo/src/cairo-xlib.h new file mode 100644 index 0000000000..ecf8d6c86b --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xlib.h @@ -0,0 +1,118 @@ +/* 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 + */ + +#ifndef CAIRO_XLIB_H +#define CAIRO_XLIB_H + +#include "cairo.h" + +#if CAIRO_HAS_XLIB_SURFACE + +#include + +CAIRO_BEGIN_DECLS + +cairo_public cairo_surface_t * +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height); + +cairo_public cairo_surface_t * +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + Screen *screen, + int width, + int height); + +cairo_public void +cairo_xlib_surface_set_size (cairo_surface_t *surface, + int width, + int height); + +cairo_public void +cairo_xlib_surface_set_drawable (cairo_surface_t *surface, + Drawable drawable, + int width, + int height); + +cairo_public Display * +cairo_xlib_surface_get_display (cairo_surface_t *surface); + +cairo_public Drawable +cairo_xlib_surface_get_drawable (cairo_surface_t *surface); + +cairo_public Screen * +cairo_xlib_surface_get_screen (cairo_surface_t *surface); + +cairo_public Visual * +cairo_xlib_surface_get_visual (cairo_surface_t *surface); + +cairo_public int +cairo_xlib_surface_get_depth (cairo_surface_t *surface); + +cairo_public int +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 */ +# error Cairo was not compiled with support for the xlib backend +#endif /* CAIRO_HAS_XLIB_SURFACE */ + +#endif /* CAIRO_XLIB_H */ diff --git a/gfx/cairo/cairo/src/cairo-xml-surface.c b/gfx/cairo/cairo/src/cairo-xml-surface.c new file mode 100644 index 0000000000..43cb6dddfa --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xml-surface.c @@ -0,0 +1,1210 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Chris Wilson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Chris Wilson. + * + * Contributor(s): + * Chris Wilson + */ + +/* This surface is intended to produce a verbose, hierarchical, DAG XML file + * representing a single surface. It is intended to be used by debuggers, + * such as cairo-sphinx, or by application test-suites that want a log of + * operations. + */ + +#include "cairoint.h" + +#include "cairo-xml.h" + +#include "cairo-clip-private.h" +#include "cairo-device-private.h" +#include "cairo-default-context-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-error-private.h" +#include "cairo-output-stream-private.h" +#include "cairo-recording-surface-inline.h" + +#define static cairo_warn static + +typedef struct _cairo_xml_surface cairo_xml_surface_t; + +typedef struct _cairo_xml { + cairo_device_t base; + + cairo_output_stream_t *stream; + int indent; +} cairo_xml_t; + +struct _cairo_xml_surface { + cairo_surface_t base; + + double width, height; +}; + +slim_hidden_proto (cairo_xml_for_recording_surface); + +static const cairo_surface_backend_t _cairo_xml_surface_backend; + +static const char * +_operator_to_string (cairo_operator_t op) +{ + static const char *names[] = { + "CLEAR", /* CAIRO_OPERATOR_CLEAR */ + + "SOURCE", /* CAIRO_OPERATOR_SOURCE */ + "OVER", /* CAIRO_OPERATOR_OVER */ + "IN", /* CAIRO_OPERATOR_IN */ + "OUT", /* CAIRO_OPERATOR_OUT */ + "ATOP", /* CAIRO_OPERATOR_ATOP */ + + "DEST", /* CAIRO_OPERATOR_DEST */ + "DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */ + "DEST_IN", /* CAIRO_OPERATOR_DEST_IN */ + "DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */ + "DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */ + + "XOR", /* CAIRO_OPERATOR_XOR */ + "ADD", /* CAIRO_OPERATOR_ADD */ + "SATURATE", /* CAIRO_OPERATOR_SATURATE */ + + "MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */ + "SCREEN", /* CAIRO_OPERATOR_SCREEN */ + "OVERLAY", /* CAIRO_OPERATOR_OVERLAY */ + "DARKEN", /* CAIRO_OPERATOR_DARKEN */ + "LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */ + "DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */ + "BURN", /* CAIRO_OPERATOR_COLOR_BURN */ + "HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */ + "SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */ + "DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */ + "EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */ + "HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */ + "HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */ + "HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */ + "HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */ + }; + assert (op < ARRAY_LENGTH (names)); + return names[op]; +} + +static const char * +_extend_to_string (cairo_extend_t extend) +{ + static const char *names[] = { + "EXTEND_NONE", /* CAIRO_EXTEND_NONE */ + "EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */ + "EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */ + "EXTEND_PAD" /* CAIRO_EXTEND_PAD */ + }; + assert (extend < ARRAY_LENGTH (names)); + return names[extend]; +} + +static const char * +_filter_to_string (cairo_filter_t filter) +{ + static const char *names[] = { + "FILTER_FAST", /* CAIRO_FILTER_FAST */ + "FILTER_GOOD", /* CAIRO_FILTER_GOOD */ + "FILTER_BEST", /* CAIRO_FILTER_BEST */ + "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */ + "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */ + "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */ + }; + assert (filter < ARRAY_LENGTH (names)); + return names[filter]; +} + +static const char * +_fill_rule_to_string (cairo_fill_rule_t rule) +{ + static const char *names[] = { + "WINDING", /* CAIRO_FILL_RULE_WINDING */ + "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */ + }; + assert (rule < ARRAY_LENGTH (names)); + return names[rule]; +} + +static const char * +_antialias_to_string (cairo_antialias_t antialias) +{ + static const char *names[] = { + "DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */ + "NONE", /* CAIRO_ANTIALIAS_NONE */ + "GRAY", /* CAIRO_ANTIALIAS_GRAY */ + "SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */ + "FAST", /* CAIRO_ANTIALIAS_FAST */ + "GOOD", /* CAIRO_ANTIALIAS_GOOD */ + "BEST", /* CAIRO_ANTIALIAS_BEST */ + }; + assert (antialias < ARRAY_LENGTH (names)); + return names[antialias]; +} + +static const char * +_line_cap_to_string (cairo_line_cap_t line_cap) +{ + static const char *names[] = { + "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */ + "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */ + "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */ + }; + assert (line_cap < ARRAY_LENGTH (names)); + return names[line_cap]; +} + +static const char * +_line_join_to_string (cairo_line_join_t line_join) +{ + static const char *names[] = { + "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */ + "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */ + "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */ + }; + assert (line_join < ARRAY_LENGTH (names)); + return names[line_join]; +} + +static const char * +_content_to_string (cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_ALPHA: return "ALPHA"; + case CAIRO_CONTENT_COLOR: return "COLOR"; + default: + case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA"; + } +} + +static const char * +_format_to_string (cairo_format_t format) +{ + switch (format) { + case CAIRO_FORMAT_ARGB32: return "ARGB32"; + case CAIRO_FORMAT_RGB30: return "RGB30"; + case CAIRO_FORMAT_RGB24: return "RGB24"; + case CAIRO_FORMAT_RGB16_565: return "RGB16_565"; + case CAIRO_FORMAT_A8: return "A8"; + case CAIRO_FORMAT_A1: return "A1"; + case CAIRO_FORMAT_INVALID: return "INVALID"; + } + ASSERT_NOT_REACHED; + return "INVALID"; +} + +static cairo_status_t +_device_flush (void *abstract_device) +{ + cairo_xml_t *xml = abstract_device; + cairo_status_t status; + + status = _cairo_output_stream_flush (xml->stream); + + return status; +} + +static void +_device_destroy (void *abstract_device) +{ + cairo_xml_t *xml = abstract_device; + cairo_status_t status; + + status = _cairo_output_stream_destroy (xml->stream); + + free (xml); +} + +static const cairo_device_backend_t _cairo_xml_device_backend = { + CAIRO_DEVICE_TYPE_XML, + + NULL, NULL, /* lock, unlock */ + + _device_flush, + NULL, /* finish */ + _device_destroy +}; + +static cairo_device_t * +_cairo_xml_create_internal (cairo_output_stream_t *stream) +{ + cairo_xml_t *xml; + + xml = _cairo_malloc (sizeof (cairo_xml_t)); + if (unlikely (xml == NULL)) + return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY); + + memset (xml, 0, sizeof (cairo_xml_t)); + + _cairo_device_init (&xml->base, &_cairo_xml_device_backend); + + xml->indent = 0; + xml->stream = stream; + + return &xml->base; +} + +static void +_cairo_xml_indent (cairo_xml_t *xml, int indent) +{ + xml->indent += indent; + assert (xml->indent >= 0); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...) +{ + va_list ap; + char indent[80]; + int len; + + len = MIN (xml->indent, ARRAY_LENGTH (indent)); + memset (indent, ' ', len); + _cairo_output_stream_write (xml->stream, indent, len); + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + + _cairo_output_stream_write (xml->stream, "\n", 1); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...) +{ + char indent[80]; + int len; + + len = MIN (xml->indent, ARRAY_LENGTH (indent)); + memset (indent, ' ', len); + _cairo_output_stream_write (xml->stream, indent, len); + + if (fmt != NULL) { + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + } +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); +} + +static void CAIRO_PRINTF_FORMAT (2, 3) +_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...) +{ + if (fmt != NULL) { + va_list ap; + + va_start (ap, fmt); + _cairo_output_stream_vprintf (xml->stream, fmt, ap); + va_end (ap); + } + + _cairo_output_stream_write (xml->stream, "\n", 1); +} + +static cairo_surface_t * +_cairo_xml_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t extents; + + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + + return cairo_recording_surface_create (content, &extents); +} + +static cairo_bool_t +_cairo_xml_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_xml_surface_t *surface = abstract_surface; + + if (surface->width < 0 || surface->height < 0) + return FALSE; + + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; + + return TRUE; +} + +static cairo_status_t +_cairo_xml_move_to (void *closure, + const cairo_point_t *p1) +{ + _cairo_xml_printf_continue (closure, " %f %f m", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_line_to (void *closure, + const cairo_point_t *p1) +{ + _cairo_xml_printf_continue (closure, " %f %f l", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_curve_to (void *closure, + const cairo_point_t *p1, + const cairo_point_t *p2, + const cairo_point_t *p3) +{ + _cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c", + _cairo_fixed_to_double (p1->x), + _cairo_fixed_to_double (p1->y), + _cairo_fixed_to_double (p2->x), + _cairo_fixed_to_double (p2->y), + _cairo_fixed_to_double (p3->x), + _cairo_fixed_to_double (p3->y)); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_close_path (void *closure) +{ + _cairo_xml_printf_continue (closure, " h"); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xml_emit_path (cairo_xml_t *xml, + const cairo_path_fixed_t *path) +{ + cairo_status_t status; + + _cairo_xml_printf_start (xml, ""); + status = _cairo_path_fixed_interpret (path, + _cairo_xml_move_to, + _cairo_xml_line_to, + _cairo_xml_curve_to, + _cairo_xml_close_path, + xml); + assert (status == CAIRO_STATUS_SUCCESS); + _cairo_xml_printf_end (xml, ""); +} + +static void +_cairo_xml_emit_string (cairo_xml_t *xml, + const char *node, + const char *data) +{ + _cairo_xml_printf (xml, "<%s>%s", node, data, node); +} + +static void +_cairo_xml_emit_double (cairo_xml_t *xml, + const char *node, + double data) +{ + _cairo_xml_printf (xml, "<%s>%f", node, data, node); +} + +static cairo_xml_t * +to_xml (cairo_xml_surface_t *surface) +{ + return (cairo_xml_t *) surface->base.device; +} + +static cairo_status_t +_cairo_xml_surface_emit_clip_boxes (cairo_xml_surface_t *surface, + const cairo_clip_t *clip) +{ + cairo_box_t *box; + cairo_xml_t *xml; + int n; + + if (clip->num_boxes == 0) + return CAIRO_STATUS_SUCCESS; + + /* skip the trivial clip covering the surface extents */ + if (surface->width >= 0 && surface->height >= 0 && clip->num_boxes == 1) { + box = &clip->boxes[0]; + if (box->p1.x <= 0 && box->p1.y <= 0 && + box->p2.x - box->p1.x >= _cairo_fixed_from_double (surface->width) && + box->p2.y - box->p1.y >= _cairo_fixed_from_double (surface->height)) + { + return CAIRO_STATUS_SUCCESS; + } + } + + xml = to_xml (surface); + + _cairo_xml_printf (xml, ""); + _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, + const cairo_clip_path_t *clip_path) +{ + cairo_box_t box; + cairo_status_t status; + cairo_xml_t *xml; + + if (clip_path == NULL) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev); + if (unlikely (status)) + return status; + + /* skip the trivial clip covering the surface extents */ + if (surface->width >= 0 && surface->height >= 0 && + _cairo_path_fixed_is_box (&clip_path->path, &box)) + { + if (box.p1.x <= 0 && box.p1.y <= 0 && + box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) && + box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height)) + { + return CAIRO_STATUS_SUCCESS; + } + } + + xml = to_xml (surface); + + _cairo_xml_printf_start (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_path (xml, &clip_path->path); + _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance); + _cairo_xml_emit_string (xml, "antialias", + _antialias_to_string (clip_path->antialias)); + _cairo_xml_emit_string (xml, "fill-rule", + _fill_rule_to_string (clip_path->fill_rule)); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf_end (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface, + const cairo_clip_t *clip) +{ + cairo_status_t status; + + if (clip == NULL) + return CAIRO_STATUS_SUCCESS; + + status = _cairo_xml_surface_emit_clip_boxes (surface, clip); + if (unlikely (status)) + return status; + + return _cairo_xml_surface_emit_clip_path (surface, clip->path); +} + +static cairo_status_t +_cairo_xml_emit_solid (cairo_xml_t *xml, + const cairo_solid_pattern_t *solid) +{ + _cairo_xml_printf (xml, "%f %f %f %f", + solid->color.red, + solid->color.green, + solid->color.blue, + solid->color.alpha); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_xml_emit_matrix (cairo_xml_t *xml, + const cairo_matrix_t *matrix) +{ + if (! _cairo_matrix_is_identity (matrix)) { + _cairo_xml_printf (xml, "%f %f %f %f %f %f", + matrix->xx, matrix->yx, + matrix->xy, matrix->yy, + matrix->x0, matrix->y0); + } +} + +static void +_cairo_xml_emit_gradient (cairo_xml_t *xml, + const cairo_gradient_pattern_t *gradient) +{ + unsigned int i; + + for (i = 0; i < gradient->n_stops; i++) { + _cairo_xml_printf (xml, + "%f %f %f %f %f", + gradient->stops[i].offset, + gradient->stops[i].color.red, + gradient->stops[i].color.green, + gradient->stops[i].color.blue, + gradient->stops[i].color.alpha); + } +} + +static cairo_status_t +_cairo_xml_emit_linear (cairo_xml_t *xml, + const cairo_linear_pattern_t *linear) +{ + _cairo_xml_printf (xml, + "", + linear->pd1.x, linear->pd1.y, + linear->pd2.x, linear->pd2.y); + _cairo_xml_indent (xml, 2); + _cairo_xml_emit_gradient (xml, &linear->base); + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_radial (cairo_xml_t *xml, + const cairo_radial_pattern_t *radial) +{ + _cairo_xml_printf (xml, + "", + radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius, + radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius); + _cairo_xml_indent (xml, 2); + _cairo_xml_emit_gradient (xml, &radial->base); + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_write_func (void *closure, const unsigned char *data, unsigned len) +{ + _cairo_output_stream_write (closure, data, len); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_image (cairo_xml_t *xml, + cairo_image_surface_t *image) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + _cairo_xml_printf_start (xml, + "", + image->width, image->height, + _format_to_string (image->format)); + + stream = _cairo_base64_stream_create (xml->stream); + status = cairo_surface_write_to_png_stream (&image->base, + _write_func, stream); + assert (status == CAIRO_STATUS_SUCCESS); + status = _cairo_output_stream_destroy (stream); + if (unlikely (status)) + return status; + + _cairo_xml_printf_end (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_surface (cairo_xml_t *xml, + const cairo_surface_pattern_t *pattern) +{ + cairo_surface_t *source = pattern->surface; + cairo_status_t status; + + if (_cairo_surface_is_recording (source)) { + status = cairo_xml_for_recording_surface (&xml->base, source); + } else { + cairo_image_surface_t *image; + void *image_extra; + + status = _cairo_surface_acquire_source_image (source, + &image, &image_extra); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_image (xml, image); + + _cairo_surface_release_source_image (source, image, image_extra); + } + + return status; +} + +static cairo_status_t +_cairo_xml_emit_pattern (cairo_xml_t *xml, + const char *source_or_mask, + const cairo_pattern_t *pattern) +{ + cairo_status_t status; + + _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask); + _cairo_xml_indent (xml, 2); + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_LINEAR: + status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern); + break; + case CAIRO_PATTERN_TYPE_SURFACE: + status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern); + break; + default: + ASSERT_NOT_REACHED; + status = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { + _cairo_xml_emit_matrix (xml, &pattern->matrix); + _cairo_xml_printf (xml, + "%s", + _extend_to_string (pattern->extend)); + _cairo_xml_printf (xml, + "%s", + _filter_to_string (pattern->filter)); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, "", source_or_mask); + + return status; +} + +static cairo_int_status_t +_cairo_xml_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "mask", mask); + if (unlikely (status)) + return status; + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + _cairo_xml_emit_double (xml, "line-width", style->line_width); + _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit); + _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap)); + _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + if (style->num_dashes) { + unsigned int i; + + _cairo_xml_printf_start (xml, "", + style->dash_offset); + for (i = 0; i < style->num_dashes; i++) + _cairo_xml_printf_continue (xml, "%f ", style->dash[i]); + + _cairo_xml_printf_end (xml, ""); + } + + _cairo_xml_emit_path (xml, path); + _cairo_xml_emit_double (xml, "tolerance", tolerance); + _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); + + _cairo_xml_emit_matrix (xml, ctm); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_xml_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t*path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + _cairo_xml_emit_path (xml, path); + _cairo_xml_emit_double (xml, "tolerance", tolerance); + _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias)); + _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule)); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +#if CAIRO_HAS_FT_FONT +#include "cairo-ft-private.h" +static cairo_status_t +_cairo_xml_emit_type42_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font) +{ + const cairo_scaled_font_backend_t *backend; + cairo_output_stream_t *base64_stream; + cairo_output_stream_t *zlib_stream; + cairo_status_t status, status2; + unsigned long size; + uint32_t len; + uint8_t *buf; + + backend = scaled_font->backend; + if (backend->load_truetype_table == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + size = 0; + status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size); + if (unlikely (status)) + return status; + + buf = _cairo_malloc (size); + if (unlikely (buf == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size); + if (unlikely (status)) { + free (buf); + return status; + } + + _cairo_xml_printf_start (xml, "", + _cairo_ft_scaled_font_get_load_flags (scaled_font)); + + + base64_stream = _cairo_base64_stream_create (xml->stream); + len = size; + _cairo_output_stream_write (base64_stream, &len, sizeof (len)); + + zlib_stream = _cairo_deflate_stream_create (base64_stream); + + _cairo_output_stream_write (zlib_stream, buf, size); + free (buf); + + status2 = _cairo_output_stream_destroy (zlib_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + status2 = _cairo_output_stream_destroy (base64_stream); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + _cairo_xml_printf_end (xml, ""); + + return status; +} +#else +static cairo_status_t +_cairo_xml_emit_type42_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font) +{ + return CAIRO_INT_STATUS_UNSUPPORTED; +} +#endif + +static cairo_status_t +_cairo_xml_emit_type3_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs) +{ + _cairo_xml_printf_start (xml, ""); + _cairo_xml_printf_end (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_xml_emit_scaled_font (cairo_xml_t *xml, + cairo_scaled_font_t *scaled_font, + cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_int_status_t status; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + status = _cairo_xml_emit_type42_font (xml, scaled_font); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_xml_emit_type3_font (xml, scaled_font, + glyphs, num_glyphs); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return status; +} + +static cairo_int_status_t +_cairo_xml_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) +{ + cairo_xml_surface_t *surface = abstract_surface; + cairo_xml_t *xml = to_xml (surface); + cairo_status_t status; + int i; + + _cairo_xml_printf (xml, ""); + _cairo_xml_indent (xml, 2); + + _cairo_xml_emit_string (xml, "operator", _operator_to_string (op)); + + status = _cairo_xml_surface_emit_clip (surface, clip); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_pattern (xml, "source", source); + if (unlikely (status)) + return status; + + status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs); + if (unlikely (status)) + return status; + + for (i = 0; i < num_glyphs; i++) { + _cairo_xml_printf (xml, "%f %f", + glyphs[i].index, + glyphs[i].x, + glyphs[i].y); + } + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t +_cairo_xml_surface_backend = { + CAIRO_SURFACE_TYPE_XML, + NULL, + + _cairo_default_context_create, + + _cairo_xml_surface_create_similar, + NULL, /* create_similar_image */ + NULL, /* map_to_image */ + NULL, /* unmap_image */ + + _cairo_surface_default_source, + NULL, /* acquire source image */ + NULL, /* release source image */ + NULL, /* snapshot */ + + NULL, /* copy page */ + NULL, /* show page */ + + _cairo_xml_surface_get_extents, + NULL, /* get_font_options */ + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _cairo_xml_surface_paint, + _cairo_xml_surface_mask, + _cairo_xml_surface_stroke, + _cairo_xml_surface_fill, + NULL, /* fill_stroke */ + _cairo_xml_surface_glyphs, +}; + +static cairo_surface_t * +_cairo_xml_surface_create_internal (cairo_device_t *device, + cairo_content_t content, + double width, + double height) +{ + cairo_xml_surface_t *surface; + + surface = _cairo_malloc (sizeof (cairo_xml_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->base, + &_cairo_xml_surface_backend, + device, + content, + TRUE); /* is_vector */ + + surface->width = width; + surface->height = height; + + return &surface->base; +} + +cairo_device_t * +cairo_xml_create (const char *filename) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create_for_filename (filename); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_xml_create_internal (stream); +} + +cairo_device_t * +cairo_xml_create_for_stream (cairo_write_func_t write_func, + void *closure) +{ + cairo_output_stream_t *stream; + cairo_status_t status; + + stream = _cairo_output_stream_create (write_func, NULL, closure); + if ((status = _cairo_output_stream_get_status (stream))) + return _cairo_device_create_in_error (status); + + return _cairo_xml_create_internal (stream); +} + +cairo_surface_t * +cairo_xml_surface_create (cairo_device_t *device, + cairo_content_t content, + double width, double height) +{ + if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) + return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (device->status)) + return _cairo_surface_create_in_error (device->status); + + return _cairo_xml_surface_create_internal (device, content, width, height); +} + +cairo_status_t +cairo_xml_for_recording_surface (cairo_device_t *device, + cairo_surface_t *recording_surface) +{ + cairo_box_t bbox; + cairo_rectangle_int_t extents; + cairo_surface_t *surface; + cairo_xml_t *xml; + cairo_status_t status; + + if (unlikely (device->status)) + return device->status; + + if (unlikely (recording_surface->status)) + return recording_surface->status; + + if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML)) + return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH); + + if (unlikely (! _cairo_surface_is_recording (recording_surface))) + return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + + status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface, + &bbox, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&bbox, &extents); + surface = _cairo_xml_surface_create_internal (device, + recording_surface->content, + extents.width, + extents.height); + if (unlikely (surface->status)) + return surface->status; + + xml = (cairo_xml_t *) device; + + _cairo_xml_printf (xml, + "", + _content_to_string (recording_surface->content), + extents.width, extents.height); + _cairo_xml_indent (xml, 2); + + cairo_surface_set_device_offset (surface, -extents.x, -extents.y); + status = _cairo_recording_surface_replay (recording_surface, surface); + cairo_surface_destroy (surface); + + _cairo_xml_indent (xml, -2); + _cairo_xml_printf (xml, ""); + + return status; +} +slim_hidden_def (cairo_xml_for_recording_surface); diff --git a/gfx/cairo/cairo/src/cairo-xml.h b/gfx/cairo/cairo/src/cairo-xml.h new file mode 100644 index 0000000000..9ae76e90aa --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-xml.h @@ -0,0 +1,67 @@ +/* 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_XML_H +#define CAIRO_XML_H + +#include "cairo.h" + +#if CAIRO_HAS_XML_SURFACE + +CAIRO_BEGIN_DECLS + +cairo_public cairo_device_t * +cairo_xml_create (const char *filename); + +cairo_public cairo_device_t * +cairo_xml_create_for_stream (cairo_write_func_t write_func, + void *closure); + +cairo_public cairo_surface_t * +cairo_xml_surface_create (cairo_device_t *xml, + cairo_content_t content, + double width, double height); + +cairo_public cairo_status_t +cairo_xml_for_recording_surface (cairo_device_t *xml, + cairo_surface_t *surface); + +CAIRO_END_DECLS + +#else /*CAIRO_HAS_XML_SURFACE*/ +# error Cairo was not compiled with support for the XML backend +#endif /*CAIRO_HAS_XML_SURFACE*/ + +#endif /*CAIRO_XML_H*/ diff --git a/gfx/cairo/cairo/src/cairo.c b/gfx/cairo/cairo/src/cairo.c new file mode 100644 index 0000000000..b2bda657d2 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo.c @@ -0,0 +1,4341 @@ +/* -*- 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-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 + * @Title: cairo_t + * @Short_Description: The cairo drawing context + * @See_Also: #cairo_surface_t + * + * #cairo_t is the main object used when drawing with cairo. To + * draw with cairo, you create a #cairo_t, set the target surface, + * and drawing options for the #cairo_t, create shapes with + * functions like cairo_move_to() and cairo_line_to(), and then + * 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 losing the current state. + * Use cairo_restore() to restore to the saved state. + **/ + +/** + * SECTION:cairo-text + * @Title: text + * @Short_Description: Rendering text and glyphs + * @See_Also: #cairo_font_face_t, #cairo_scaled_font_t, cairo_text_path(), + * cairo_glyph_path() + * + * The functions with text in their name form cairo's + * toy text API. The toy API takes UTF-8 encoded + * text and is limited in its functionality to rendering simple + * left-to-right text with no advanced features. That means for example + * that most complex scripts like Hebrew, Arabic, and Indic scripts are + * out of question. No kerning or correct positioning of diacritical marks + * either. The font selection is pretty limited too and doesn't handle the + * case that the selected font does not cover the characters in the text. + * This set of functions are really that, a toy text API, for testing and + * demonstration purposes. Any serious application should avoid them. + * + * The functions with glyphs in their name form cairo's + * low-level text API. The low-level API relies on + * the user to convert text to a set of glyph indexes and positions. This + * is a very hard problem and is best handled by external libraries, like + * the pangocairo that is part of the Pango text layout and rendering library. + * Pango is available from http://www.pango.org/. + **/ + +/** + * SECTION:cairo-transforms + * @Title: Transformations + * @Short_Description: Manipulating the current transformation matrix + * @See_Also: #cairo_matrix_t + * + * The current transformation matrix, ctm, is a + * two-dimensional affine transformation that maps all coordinates and other + * drawing instruments from the user space into the + * surface's canonical coordinate system, also known as the device + * space. + **/ + +/** + * SECTION:cairo-tag + * @Title: Tags and Links + * @Short_Description: Hyperlinks and document structure + * @See_Also: #cairo_pdf_surface_t + * + * 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 + * + * # Link Tags # {#link} + * A hyperlink is specified by enclosing the hyperlink text with the %CAIRO_TAG_LINK tag. + * + * 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"); + * + * + **/ + +#define DEFINE_NIL_CONTEXT(status) \ + { \ + CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ + status, /* status */ \ + { 0, 0, 0, NULL }, /* user_data */ \ + NULL \ + } + +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: + * @cr: a cairo context + * @status: a status value indicating an error + * + * Atomically sets cr->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS. + * + * All assignments of an error status to cr->status should happen + * through _cairo_set_error(). Note that due to the nature of the atomic + * operation, it is not safe to call this function on the nil objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + **/ +static void +_cairo_set_error (cairo_t *cr, cairo_status_t status) +{ + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (&cr->status, _cairo_error (status)); +} + +cairo_t * +_cairo_create_in_error (cairo_status_t status) +{ + cairo_t *cr; + + assert (status != CAIRO_STATUS_SUCCESS); + + cr = (cairo_t *) &_cairo_nil[status - CAIRO_STATUS_NO_MEMORY]; + assert (status == cr->status); + + return cr; +} + +/** + * cairo_create: + * @target: target surface for the context + * + * Creates a new #cairo_t with all graphics state parameters set to + * 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). + * + * This function references @target, so you can immediately + * call cairo_surface_destroy() on it if you don't need to + * maintain a separate reference to it. + * + * Return value: a newly allocated #cairo_t with a reference + * count of 1. The initial reference count should be released + * 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. 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) +{ + 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)); + + if (target->backend->create_context == NULL) + return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR)); + + return target->backend->create_context (target); + +} +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 + * + * Increases the reference count on @cr by one. This prevents + * @cr from being destroyed until a matching call to cairo_destroy() + * is made. + * + * 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) +{ + if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) + return cr; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count)); + + _cairo_reference_count_inc (&cr->ref_count); + + return cr; +} + +void +_cairo_fini (cairo_t *cr) +{ + _cairo_user_data_array_fini (&cr->user_data); +} + +/** + * cairo_destroy: + * @cr: a #cairo_t + * + * 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) +{ + if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) + return; + + assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count)); + + if (! _cairo_reference_count_dec_and_test (&cr->ref_count)) + return; + + cr->backend->destroy (cr); +} +slim_hidden_def (cairo_destroy); + +/** + * cairo_get_user_data: + * @cr: a #cairo_t + * @key: the address of the #cairo_user_data_key_t the user data was + * attached to + * + * Return user data previously attached to @cr using the specified + * key. If no user data has been attached with the given key this + * function returns %NULL. + * + * Return value: the user data previously attached or %NULL. + * + * Since: 1.4 + **/ +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); +} + +/** + * cairo_set_user_data: + * @cr: a #cairo_t + * @key: the address of a #cairo_user_data_key_t to attach the user data to + * @user_data: the user data to attach to the #cairo_t + * @destroy: a #cairo_destroy_func_t which will be called when the + * #cairo_t is destroyed or when new user data is attached using the + * same key. + * + * Attach user data to @cr. To remove user data from a surface, + * call this function with the key that was used to set it and %NULL + * for @data. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a + * slot could not be allocated for the user data. + * + * Since: 1.4 + **/ +cairo_status_t +cairo_set_user_data (cairo_t *cr, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy) +{ + if (CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) + return cr->status; + + return _cairo_user_data_array_set_data (&cr->user_data, + key, user_data, destroy); +} + +/** + * cairo_get_reference_count: + * @cr: a #cairo_t + * + * Returns the current reference count of @cr. + * + * Return value: the current reference count of @cr. If the + * object is a nil object, 0 will be returned. + * + * Since: 1.4 + **/ +unsigned int +cairo_get_reference_count (cairo_t *cr) +{ + if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count)) + return 0; + + return CAIRO_REFERENCE_COUNT_GET_VALUE (&cr->ref_count); +} + +/** + * cairo_save: + * @cr: a #cairo_t + * + * Makes a copy of the current state of @cr and saves it + * on an internal stack of saved states for @cr. When + * cairo_restore() is called, @cr will be restored to + * the saved state. Multiple calls to cairo_save() and + * cairo_restore() can be nested; each call to cairo_restore() + * restores the state from the matching paired cairo_save(). + * + * It isn't necessary to clear all saved states before + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->save (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_save); + +/** + * cairo_restore: + * @cr: a #cairo_t + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->restore (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_restore); + +/** + * cairo_push_group: + * @cr: a cairo context + * + * Temporarily redirects drawing to an intermediate surface known as a + * group. The redirection lasts until the group is completed by a call + * to cairo_pop_group() or cairo_pop_group_to_source(). These calls + * provide the result of any drawing to the group as a pattern, + * (either as an explicit object, or set as the source pattern). + * + * This group functionality can be convenient for performing + * intermediate compositing. One common use of a group is to render + * objects as opaque within the group, (so that they occlude each + * other), and then blend the result with translucence onto the + * destination. + * + * Groups can be nested arbitrarily deep by making balanced calls to + * cairo_push_group()/cairo_pop_group(). Each call pushes/pops the new + * target group onto/from a stack. + * + * The cairo_push_group() function calls cairo_save() so that any + * changes to the graphics state will not be visible outside the + * group, (the pop_group functions call cairo_restore()). + * + * By default the intermediate group will have a content type of + * %CAIRO_CONTENT_COLOR_ALPHA. Other content types can be chosen for + * the group by using cairo_push_group_with_content() instead. + * + * As an example, here is how one might fill and stroke a path with + * translucence, but without any portion of the fill being visible + * under the stroke: + * + * + * cairo_push_group (cr); + * cairo_set_source (cr, fill_pattern); + * cairo_fill_preserve (cr); + * cairo_set_source (cr, stroke_pattern); + * cairo_stroke (cr); + * cairo_pop_group_to_source (cr); + * cairo_paint_with_alpha (cr, alpha); + * + * + * Since: 1.2 + **/ +void +cairo_push_group (cairo_t *cr) +{ + cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); +} + +/** + * cairo_push_group_with_content: + * @cr: a cairo context + * @content: a #cairo_content_t indicating the type of group that + * will be created + * + * Temporarily redirects drawing to an intermediate surface known as a + * group. The redirection lasts until the group is completed by a call + * to cairo_pop_group() or cairo_pop_group_to_source(). These calls + * provide the result of any drawing to the group as a pattern, + * (either as an explicit object, or set as the source pattern). + * + * The group will have a content type of @content. The ability to + * control this content type is the only distinction between this + * function and cairo_push_group() which you should see for a more + * detailed description of group rendering. + * + * Since: 1.2 + **/ +void +cairo_push_group_with_content (cairo_t *cr, cairo_content_t content) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->push_group (cr, content); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_push_group_with_content); + +/** + * cairo_pop_group: + * @cr: a cairo context + * + * Terminates the redirection begun by a call to cairo_push_group() or + * cairo_push_group_with_content() and returns a new pattern + * containing the results of all drawing operations performed to the + * group. + * + * The cairo_pop_group() function calls cairo_restore(), (balancing a + * call to cairo_save() by the push_group function), so that any + * changes to the graphics state will not be visible outside the + * group. + * + * Return value: a newly created (surface) pattern containing the + * results of all drawing operations performed to the group. The + * caller owns the returned object and should call + * cairo_pattern_destroy() when finished with it. + * + * Since: 1.2 + **/ +cairo_pattern_t * +cairo_pop_group (cairo_t *cr) +{ + cairo_pattern_t *group_pattern; + + if (unlikely (cr->status)) + return _cairo_pattern_create_in_error (cr->status); + + group_pattern = cr->backend->pop_group (cr); + if (unlikely (group_pattern->status)) + _cairo_set_error (cr, group_pattern->status); + + return group_pattern; +} +slim_hidden_def(cairo_pop_group); + +/** + * cairo_pop_group_to_source: + * @cr: a cairo context + * + * Terminates the redirection begun by a call to cairo_push_group() or + * cairo_push_group_with_content() and installs the resulting pattern + * as the source pattern in the given cairo context. + * + * The behavior of this function is equivalent to the sequence of + * operations: + * + * + * cairo_pattern_t *group = cairo_pop_group (cr); + * cairo_set_source (cr, group); + * cairo_pattern_destroy (group); + * + * + * but is more convenient as their is no need for a variable to store + * the short-lived pointer to the pattern. + * + * The cairo_pop_group() function calls cairo_restore(), (balancing a + * call to cairo_save() by the push_group function), so that any + * changes to the graphics state will not be visible outside the + * group. + * + * Since: 1.2 + **/ +void +cairo_pop_group_to_source (cairo_t *cr) +{ + cairo_pattern_t *group_pattern; + + group_pattern = cairo_pop_group (cr); + cairo_set_source (cr, group_pattern); + cairo_pattern_destroy (group_pattern); +} + +/** + * cairo_set_operator: + * @cr: a #cairo_t + * @op: a compositing operator, specified as a #cairo_operator_t + * + * Sets the compositing operator to be used for all drawing + * operations. See #cairo_operator_t for details on the semantics of + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_operator (cr, op); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_operator); + + +#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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_opacity (cr, opacity); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +#endif + +/** + * cairo_set_source_rgb: + * @cr: a cairo context + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * + * Sets the source pattern within @cr to an opaque color. This opaque + * color will then be used for any subsequent drawing operation until + * a new source pattern is set. + * + * The color components are floating point numbers in the range 0 to + * 1. If the values passed in are outside that range, they will be + * clamped. + * + * 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_status_t status; + + if (unlikely (cr->status)) + return; + + 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); + +/** + * cairo_set_source_rgba: + * @cr: a cairo context + * @red: red component of color + * @green: green component of color + * @blue: blue component of color + * @alpha: alpha component of color + * + * Sets the source pattern within @cr to a translucent color. This + * color will then be used for any subsequent drawing operation until + * a new source pattern is set. + * + * The color and alpha components are floating point numbers in the + * range 0 to 1. If the values passed in are outside that range, they + * will be clamped. + * + * 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_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_source_rgba (cr, red, green, blue, alpha); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_set_source_surface: + * @cr: a cairo context + * @surface: a surface to be used to set the source pattern + * @x: User-space X coordinate for surface origin + * @y: User-space Y coordinate for surface origin + * + * This is a convenience function for creating a pattern from @surface + * and setting it as the source in @cr with cairo_set_source(). + * + * The @x and @y parameters give the user-space coordinate at which + * the surface origin should appear. (The surface origin is its + * upper-left corner before any transformation has been applied.) The + * @x and @y parameters are negated and then set as translation values + * in the pattern matrix. + * + * Other than the initial translation pattern matrix, as described + * above, all other pattern attributes, (such as its extend mode), are + * set to the default values as in cairo_pattern_create_for_surface(). + * 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, + cairo_surface_t *surface, + double x, + double y) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (unlikely (surface == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + 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: + * @cr: a cairo context + * @source: a #cairo_pattern_t to be used as the source for + * subsequent drawing operations. + * + * Sets the source pattern within @cr to @source. This pattern + * will then be used for any subsequent drawing operation until a new + * source pattern is set. + * + * Note: The pattern's transformation matrix will be locked to the + * user space in effect at the time of cairo_set_source(). This means + * that further modifications of the current transformation matrix + * will not affect the source pattern. See cairo_pattern_set_matrix(). + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (unlikely (source == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + if (unlikely (source->status)) { + _cairo_set_error (cr, source->status); + return; + } + + status = cr->backend->set_source (cr, source); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_source); + +/** + * cairo_get_source: + * @cr: a cairo context + * + * Gets the current source pattern for @cr. + * + * 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) +{ + if (unlikely (cr->status)) + return _cairo_pattern_create_in_error (cr->status); + + return cr->backend->get_source (cr); +} + +/** + * cairo_set_tolerance: + * @cr: a #cairo_t + * @tolerance: the tolerance, in device units (typically pixels) + * + * Sets the tolerance used when converting paths into trapezoids. + * Curved segments of the path will be subdivided until the maximum + * deviation between the original path and the polygonal approximation + * is less than @tolerance. The default value is 0.1. A larger + * value will give better performance, a smaller value, better + * appearance. (Reducing the value from the default value of 0.1 + * is unlikely to improve appearance significantly.) The accuracy of paths + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_tolerance (cr, tolerance); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_tolerance); + +/** + * cairo_set_antialias: + * @cr: a #cairo_t + * @antialias: the new antialiasing mode + * + * Set the antialiasing mode of the rasterizer used for drawing shapes. + * This value is a hint, and a particular backend may or may not support + * a particular value. At the current time, no backend supports + * %CAIRO_ANTIALIAS_SUBPIXEL when drawing shapes. + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_antialias (cr, antialias); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_set_fill_rule: + * @cr: a #cairo_t + * @fill_rule: a fill rule, specified as a #cairo_fill_rule_t + * + * Set the current fill rule within the cairo context. The fill rule + * is used to determine which regions are inside or outside a complex + * (potentially self-intersecting) path. The current fill rule affects + * both cairo_fill() and cairo_clip(). See #cairo_fill_rule_t for details + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_fill_rule (cr, fill_rule); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_set_line_width: + * @cr: a #cairo_t + * @width: a line width + * + * Sets the current line width within the cairo context. The line + * width value specifies the diameter of a pen that is circular in + * user space, (though device-space pen may be an ellipse in general + * due to scaling/shear/rotation of the CTM). + * + * Note: When the description above refers to user space and CTM it + * refers to the user space and CTM in effect at the time of the + * stroking operation, not the user space and CTM in effect at the + * time of the call to cairo_set_line_width(). The simplest usage + * makes both of these spaces identical. That is, if there is no + * change to the CTM between a call to cairo_set_line_width() and the + * stroking operation, then one can just pass user-space values to + * cairo_set_line_width() and ignore this note. + * + * As with the other stroke parameters, the current line width is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + * + * The default line width value is 2.0. + * + * Since: 1.0 + **/ +void +cairo_set_line_width (cairo_t *cr, double width) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (width < 0.) + width = 0.; + + status = cr->backend->set_line_width (cr, width); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_line_width); + +/** + * cairo_set_line_cap: + * @cr: a cairo context + * @line_cap: a line cap style + * + * Sets the current line cap style within the cairo context. See + * #cairo_line_cap_t for details about how the available line cap + * styles are drawn. + * + * As with the other stroke parameters, the current line cap style is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_line_cap (cr, line_cap); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_line_cap); + +/** + * cairo_set_line_join: + * @cr: a cairo context + * @line_join: a line join style + * + * Sets the current line join style within the cairo context. See + * #cairo_line_join_t for details about how the available line join + * styles are drawn. + * + * As with the other stroke parameters, the current line join style is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_line_join (cr, line_join); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_line_join); + +/** + * cairo_set_dash: + * @cr: a cairo context + * @dashes: an array specifying alternate lengths of on and off stroke portions + * @num_dashes: the length of the dashes array + * @offset: an offset into the dash pattern at which the stroke should start + * + * Sets the dash pattern to be used by cairo_stroke(). A dash pattern + * is specified by @dashes, an array of positive values. Each value + * provides the length of alternate "on" and "off" portions of the + * stroke. The @offset specifies an offset into the pattern at which + * the stroke begins. + * + * Each "on" segment will have caps applied as if the segment were a + * separate sub-path. In particular, it is valid to use an "on" length + * of 0.0 with %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE in order + * to distributed dots or squares along a path. + * + * Note: The length values are in user-space units as evaluated at the + * time of stroking. This is not necessarily the same as the user + * space at the time of cairo_set_dash(). + * + * If @num_dashes is 0 dashing is disabled. + * + * If @num_dashes is 1 a symmetric pattern is assumed with alternating + * on and off portions of the size specified by the single value in + * @dashes. + * + * 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, + const double *dashes, + int num_dashes, + double offset) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_dash (cr, dashes, num_dashes, offset); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_get_dash_count: + * @cr: a #cairo_t + * + * This function returns the length of the dash array in @cr (0 if dashing + * is not currently in effect). + * + * See also cairo_set_dash() and cairo_get_dash(). + * + * 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) +{ + int num_dashes; + + if (unlikely (cr->status)) + return 0; + + cr->backend->get_dash (cr, NULL, &num_dashes, NULL); + + return num_dashes; +} + +/** + * cairo_get_dash: + * @cr: a #cairo_t + * @dashes: return value for the dash array, or %NULL + * @offset: return value for the current dash offset, or %NULL + * + * Gets the current dash array. If not %NULL, @dashes should be big + * enough to hold at least the number of values returned by + * cairo_get_dash_count(). + * + * Since: 1.4 + **/ +void +cairo_get_dash (cairo_t *cr, + double *dashes, + double *offset) +{ + if (unlikely (cr->status)) + return; + + cr->backend->get_dash (cr, dashes, NULL, offset); +} + +/** + * cairo_set_miter_limit: + * @cr: a cairo context + * @limit: miter limit to set + * + * Sets the current miter limit within the cairo context. + * + * If the current line join style is set to %CAIRO_LINE_JOIN_MITER + * (see cairo_set_line_join()), the miter limit is used to determine + * whether the lines should be joined with a bevel instead of a miter. + * Cairo divides the length of the miter by the line width. + * If the result is greater than the miter limit, the style is + * converted to a bevel. + * + * As with the other stroke parameters, the current line miter limit is + * examined by cairo_stroke(), cairo_stroke_extents(), and + * cairo_stroke_to_path(), but does not have any effect during path + * construction. + * + * The default miter limit value is 10.0, which will convert joins + * with interior angles less than 11 degrees to bevels instead of + * miters. For reference, a miter limit of 2.0 makes the miter cutoff + * at 60 degrees, and a miter limit of 1.414 makes the cutoff at 90 + * degrees. + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_miter_limit (cr, limit); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_translate: + * @cr: a cairo context + * @tx: amount to translate in the X direction + * @ty: amount to translate in the Y direction + * + * Modifies the current transformation matrix (CTM) by translating the + * user-space origin by (@tx, @ty). This offset is interpreted as a + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->translate (cr, tx, ty); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_translate); + +/** + * cairo_scale: + * @cr: a cairo context + * @sx: scale factor for the X dimension + * @sy: scale factor for the Y dimension + * + * Modifies the current transformation matrix (CTM) by scaling the X + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->scale (cr, sx, sy); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_scale); + +/** + * cairo_rotate: + * @cr: a cairo context + * @angle: angle (in radians) by which the user-space axes will be + * rotated + * + * Modifies the current transformation matrix (CTM) by rotating the + * user-space axes by @angle radians. The rotation of the axes takes + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->rotate (cr, angle); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_transform: + * @cr: a cairo context + * @matrix: a transformation to be applied to the user-space axes + * + * 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, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->transform (cr, matrix); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_transform); + +/** + * cairo_set_matrix: + * @cr: a cairo context + * @matrix: a transformation matrix from user space to device space + * + * Modifies the current transformation matrix (CTM) by setting it + * equal to @matrix. + * + * Since: 1.0 + **/ +void +cairo_set_matrix (cairo_t *cr, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_matrix (cr, matrix); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_matrix); + +/** + * cairo_identity_matrix: + * @cr: a cairo context + * + * Resets the current transformation matrix (CTM) by setting it equal + * 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; + + status = cr->backend->set_identity_matrix (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_user_to_device: + * @cr: a cairo context + * @x: X value of coordinate (in/out parameter) + * @y: Y value of coordinate (in/out parameter) + * + * 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) +{ + if (unlikely (cr->status)) + return; + + cr->backend->user_to_device (cr, x, y); +} +slim_hidden_def (cairo_user_to_device); + +/** + * cairo_user_to_device_distance: + * @cr: a cairo context + * @dx: X component of a distance vector (in/out parameter) + * @dy: Y component of a distance vector (in/out parameter) + * + * Transform a distance vector from user space to device space. This + * 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) +{ + if (unlikely (cr->status)) + return; + + cr->backend->user_to_device_distance (cr, dx, dy); +} +slim_hidden_def (cairo_user_to_device_distance); + +/** + * cairo_device_to_user: + * @cr: a cairo + * @x: X value of coordinate (in/out parameter) + * @y: Y value of coordinate (in/out parameter) + * + * 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) +{ + if (unlikely (cr->status)) + return; + + cr->backend->device_to_user (cr, x, y); +} +slim_hidden_def (cairo_device_to_user); + +/** + * cairo_device_to_user_distance: + * @cr: a cairo context + * @dx: X component of a distance vector (in/out parameter) + * @dy: Y component of a distance vector (in/out parameter) + * + * Transform a distance vector from device space to user space. This + * 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) +{ + if (unlikely (cr->status)) + return; + + cr->backend->device_to_user_distance (cr, dx, dy); +} + +/** + * cairo_new_path: + * @cr: a cairo context + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->new_path (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_new_path); + +/** + * cairo_new_sub_path: + * @cr: a cairo context + * + * Begin a new sub-path. Note that the existing path is not + * affected. After this call there will be no current point. + * + * In many cases, this call is not needed since new sub-paths are + * frequently started with cairo_move_to(). + * + * A call to cairo_new_sub_path() is particularly useful when + * beginning a new sub-path with one of the cairo_arc() calls. This + * makes things easier as it is no longer necessary to manually + * compute the arc's initial coordinates for a call to + * cairo_move_to(). + * + * Since: 1.2 + **/ +void +cairo_new_sub_path (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + 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 + * @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 path from the current point to position (@x, @y) + * in user-space coordinates. After this call the current point + * will be (@x, @y). + * + * 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; + + if (unlikely (cr->status)) + return; + + status = cr->backend->line_to (cr, x, y); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_line_to); + +/** + * cairo_curve_to: + * @cr: a cairo context + * @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 path from the current point to + * position (@x3, @y3) in user-space coordinates, using (@x1, @y1) and + * (@x2, @y2) as the control points. After this call the current point + * will be (@x3, @y3). + * + * 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, + double x1, double y1, + double x2, double y2, + double x3, double y3) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->curve_to (cr, + x1, y1, + x2, y2, + x3, y3); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_curve_to); + +/** + * cairo_arc: + * @cr: a cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * 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. + * + * 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 + * arc. If this initial line is undesired, it can be avoided by + * 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. + * + * (To convert from degrees to radians, use degrees * (M_PI / + * 180.).) + * + * This function gives the arc in the direction of increasing angles; + * see cairo_arc_negative() to get the arc in the direction of + * decreasing angles. + * + * The arc is circular in user space. To achieve an elliptical arc, + * you can scale the current transformation matrix by different + * amounts in the X and Y directions. For example, to draw an ellipse + * in the box given by @x, @y, @width, @height: + * + * + * cairo_save (cr); + * cairo_translate (cr, x + width / 2., y + height / 2.); + * cairo_scale (cr, width / 2., height / 2.); + * cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI); + * cairo_restore (cr); + * + * + * Since: 1.0 + **/ +void +cairo_arc (cairo_t *cr, + double xc, double yc, + double radius, + double angle1, double angle2) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + 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; + } + + status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, TRUE); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_arc_negative: + * @cr: a cairo context + * @xc: X position of the center of the arc + * @yc: Y position of the center of the arc + * @radius: the radius of the arc + * @angle1: the start angle, in radians + * @angle2: the end angle, in radians + * + * 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. + * + * 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, + double xc, double yc, + double radius, + double angle1, double angle2) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + 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; + } + + status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, FALSE); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/* XXX: NYI +void +cairo_arc_to (cairo_t *cr, + double x1, double y1, + double x2, double y2, + double radius) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + 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); +} +*/ + +/** + * cairo_rel_move_to: + * @cr: a cairo context + * @dx: the X offset + * @dy: the Y offset + * + * Begin a new sub-path. After this call the current point will offset + * by (@x, @y). + * + * Given a current point of (x, y), cairo_rel_move_to(@cr, @dx, @dy) + * is logically equivalent to cairo_move_to(@cr, x + @dx, y + @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_move_to (cairo_t *cr, double dx, double dy) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->rel_move_to (cr, dx, dy); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_rel_line_to: + * @cr: a cairo context + * @dx: the X offset to the end of the new line + * @dy: the Y offset to the end of the new line + * + * Relative-coordinate version of cairo_line_to(). Adds a line to the + * path from the current point to a point that is offset from the + * current point by (@dx, @dy) in user space. After this call the + * current point will be offset by (@dx, @dy). + * + * Given a current point of (x, y), cairo_rel_line_to(@cr, @dx, @dy) + * is logically equivalent to cairo_line_to(@cr, x + @dx, y + @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_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->rel_line_to (cr, dx, dy); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_rel_line_to); + +/** + * cairo_rel_curve_to: + * @cr: a cairo context + * @dx1: the X offset to the first control point + * @dy1: the Y offset to the first control point + * @dx2: the X offset to the second control point + * @dy2: the Y offset to the second control point + * @dx3: the X offset to the end of the curve + * @dy3: the Y offset to the end of the curve + * + * Relative-coordinate version of cairo_curve_to(). All offsets are + * relative to the current point. Adds a cubic Bézier spline to the + * path from the current point to a point offset from the current + * point by (@dx3, @dy3), using points offset by (@dx1, @dy1) and + * (@dx2, @dy2) as the control points. After this call the current + * point will be offset by (@dx3, @dy3). + * + * Given a current point of (x, y), cairo_rel_curve_to(@cr, @dx1, + * @dy1, @dx2, @dy2, @dx3, @dy3) is logically equivalent to + * cairo_curve_to(@cr, x+@dx1, y+@dy1, x+@dx2, y+@dy2, x+@dx3, y+@dy3). + * + * 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, + double dx1, double dy1, + double dx2, double dy2, + double dx3, double dy3) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->rel_curve_to (cr, + dx1, dy1, + dx2, dy2, + dx3, dy3); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_rectangle: + * @cr: a cairo context + * @x: the X coordinate of the top left corner of the rectangle + * @y: the Y coordinate to the top left corner of the rectangle + * @width: the width of the rectangle + * @height: the height of the rectangle + * + * Adds a closed sub-path rectangle of the given size to the current + * path at position (@x, @y) in user-space coordinates. + * + * This function is logically equivalent to: + * + * 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); + * + * + * 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; + + status = cr->backend->rectangle (cr, x, y, width, height); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +#if 0 +/* XXX: NYI */ +void +cairo_stroke_to_path (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + /* The code in _cairo_recording_surface_get_path has a poorman's stroke_to_path */ + + status = _cairo_gstate_stroke_path (cr->gstate); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +#endif + +/** + * cairo_close_path: + * @cr: a cairo context + * + * Adds a line segment to the path from the current point to the + * beginning of the current sub-path, (the most recent point passed to + * cairo_move_to()), and closes this sub-path. After this call the + * current point will be at the joined endpoint of the sub-path. + * + * The behavior of cairo_close_path() is distinct from simply calling + * cairo_line_to() with the equivalent coordinate in the case of + * stroking. When a closed sub-path is stroked, there are no caps on + * the ends of the sub-path. Instead, there is a line join connecting + * the final and initial segments of the sub-path. + * + * If there is no current point before the call to cairo_close_path(), + * this function will have no effect. + * + * Note: As of cairo version 1.2.4 any call to cairo_close_path() will + * place an explicit MOVE_TO element into the path immediately after + * the CLOSE_PATH element, (which can be seen in cairo_copy_path() for + * example). This can simplify path processing in some cases as it may + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->close_path (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_close_path); + +/** + * cairo_path_extents: + * @cr: a cairo context + * @x1: left of the resulting extents + * @y1: top of the resulting extents + * @x2: right of the resulting extents + * @y2: bottom of the resulting extents + * + * Computes a bounding box in user-space coordinates covering the + * points on the current path. If the current path is empty, returns + * an empty rectangle ((0,0), (0,0)). Stroke parameters, fill rule, + * surface dimensions and clipping are not taken into account. + * + * Contrast with cairo_fill_extents() and cairo_stroke_extents() which + * return the extents of only the area that would be "inked" by + * the corresponding drawing operations. + * + * The result of cairo_path_extents() is defined as equivalent to the + * limit of cairo_stroke_extents() with %CAIRO_LINE_CAP_ROUND as the + * line width approaches 0.0, (but never reaching the empty-rectangle + * returned by cairo_stroke_extents() for a line width of 0.0). + * + * Specifically, this means that zero-area sub-paths such as + * cairo_move_to();cairo_line_to() segments, (even degenerate cases + * where the coordinates to both calls are identical), will be + * considered as contributing to the extents. However, a lone + * cairo_move_to() will not contribute to the results of + * cairo_path_extents(). + * + * Since: 1.6 + **/ +void +cairo_path_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; + + return; + } + + cr->backend->path_extents (cr, x1, y1, x2, y2); +} + +/** + * cairo_paint: + * @cr: a cairo context + * + * A drawing operator that paints the current source everywhere within + * the current clip region. + * + * Since: 1.0 + **/ +void +cairo_paint (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->paint (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_paint); + +/** + * cairo_paint_with_alpha: + * @cr: a cairo context + * @alpha: alpha value, between 0 (transparent) and 1 (opaque) + * + * A drawing operator that paints the current source everywhere within + * 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; + + if (unlikely (cr->status)) + return; + + status = cr->backend->paint_with_alpha (cr, alpha); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_mask: + * @cr: a cairo context + * @pattern: a #cairo_pattern_t + * + * A drawing operator that paints the current source + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (unlikely (pattern == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + if (unlikely (pattern->status)) { + _cairo_set_error (cr, pattern->status); + return; + } + + status = cr->backend->mask (cr, pattern); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_mask); + +/** + * cairo_mask_surface: + * @cr: a cairo context + * @surface: a #cairo_surface_t + * @surface_x: X coordinate at which to place the origin of @surface + * @surface_y: Y coordinate at which to place the origin of @surface + * + * A drawing operator that paints the current source + * 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, + double surface_x, + double surface_y) +{ + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + + if (unlikely (cr->status)) + return; + + pattern = cairo_pattern_create_for_surface (surface); + + cairo_matrix_init_translate (&matrix, - surface_x, - surface_y); + cairo_pattern_set_matrix (pattern, &matrix); + + cairo_mask (cr, pattern); + + cairo_pattern_destroy (pattern); +} + +/** + * cairo_stroke: + * @cr: a cairo context + * + * A drawing operator that strokes the current path according to the + * current line width, line join, line cap, and dash settings. After + * cairo_stroke(), the current path will be cleared from the cairo + * context. See cairo_set_line_width(), cairo_set_line_join(), + * cairo_set_line_cap(), cairo_set_dash(), and + * cairo_stroke_preserve(). + * + * Note: Degenerate segments and sub-paths are treated specially and + * provide a useful result. These can result in two different + * situations: + * + * 1. Zero-length "on" segments set in cairo_set_dash(). If the cap + * style is %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE then these + * segments will be drawn as circular dots or squares respectively. In + * the case of %CAIRO_LINE_CAP_SQUARE, the orientation of the squares + * is determined by the direction of the underlying path. + * + * 2. A sub-path created by cairo_move_to() followed by either a + * cairo_close_path() or one or more calls to cairo_line_to() to the + * same coordinate as the cairo_move_to(). If the cap style is + * %CAIRO_LINE_CAP_ROUND then these sub-paths will be drawn as circular + * dots. Note that in the case of %CAIRO_LINE_CAP_SQUARE a degenerate + * sub-path will not be drawn at all, (since the correct orientation + * is indeterminate). + * + * 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_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->stroke (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_stroke); + +/** + * cairo_stroke_preserve: + * @cr: a cairo context + * + * A drawing operator that strokes the current path according to the + * current line width, line join, line cap, and dash settings. Unlike + * cairo_stroke(), cairo_stroke_preserve() preserves the path within the + * cairo context. + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->stroke_preserve (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_stroke_preserve); + +/** + * cairo_fill: + * @cr: a cairo context + * + * A drawing operator that fills the current path according to the + * current fill rule, (each sub-path is implicitly closed before being + * 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_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->fill (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_fill_preserve: + * @cr: a cairo context + * + * A drawing operator that fills the current path according to the + * current fill rule, (each sub-path is implicitly closed before being + * filled). Unlike cairo_fill(), cairo_fill_preserve() preserves the + * path within the cairo context. + * + * See cairo_set_fill_rule() and cairo_fill(). + * + * Since: 1.0 + **/ +void +cairo_fill_preserve (cairo_t *cr) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->fill_preserve (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_fill_preserve); + +/** + * cairo_copy_page: + * @cr: a cairo context + * + * Emits the current page for backends that support multiple pages, but + * doesn't clear it, so, the contents of the current page will be retained + * for the next page too. Use cairo_show_page() if you want to get an + * empty page after the emission. + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->copy_page (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_show_page: + * @cr: a cairo context + * + * Emits and clears the current page for backends that support multiple + * pages. Use cairo_copy_page() if you don't want to clear the page. + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->show_page (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_in_stroke: + * @cr: a cairo context + * @x: X coordinate of the point to test + * @y: Y coordinate of the point to test + * + * Tests whether the given point is inside the area that would be + * affected by a cairo_stroke() operation given the current path and + * stroking parameters. Surface dimensions and clipping are not taken + * into account. + * + * See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(), + * cairo_set_line_cap(), cairo_set_dash(), and + * cairo_stroke_preserve(). + * + * 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) +{ + cairo_status_t status; + cairo_bool_t inside = FALSE; + + if (unlikely (cr->status)) + return FALSE; + + status = cr->backend->in_stroke (cr, x, y, &inside); + if (unlikely (status)) + _cairo_set_error (cr, status); + + return inside; +} + +/** + * cairo_in_fill: + * @cr: a cairo context + * @x: X coordinate of the point to test + * @y: Y coordinate of the point to test + * + * Tests whether the given point is inside the area that would be + * affected by a cairo_fill() operation given the current path and + * filling parameters. Surface dimensions and clipping are not taken + * into account. + * + * See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve(). + * + * 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; + + status = cr->backend->in_fill (cr, x, y, &inside); + if (unlikely (status)) + _cairo_set_error (cr, status); + + return inside; +} + +/** + * cairo_stroke_extents: + * @cr: a cairo context + * @x1: left of the resulting extents + * @y1: top of the resulting extents + * @x2: right of the resulting extents + * @y2: bottom of the resulting extents + * + * Computes a bounding box in user coordinates covering the area that + * would be affected, (the "inked" area), by a cairo_stroke() + * operation given the current path and stroke parameters. + * If the current path is empty, returns an empty rectangle ((0,0), (0,0)). + * Surface dimensions and clipping are not taken into account. + * + * Note that if the line width is set to exactly zero, then + * cairo_stroke_extents() will return an empty rectangle. Contrast with + * cairo_path_extents() which can be used to compute the non-empty + * bounds as the line width approaches zero. + * + * Note that cairo_stroke_extents() must necessarily do more work to + * compute the precise inked areas in light of the stroke parameters, + * so cairo_path_extents() may be more desirable for sake of + * performance if non-inked path extents are desired. + * + * 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, + double *x1, double *y1, double *x2, double *y2) +{ + cairo_status_t status; + + if (unlikely (cr->status)) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + return; + } + + status = cr->backend->stroke_extents (cr, x1, y1, x2, y2); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_fill_extents: + * @cr: a cairo context + * @x1: left of the resulting extents + * @y1: top of the resulting extents + * @x2: right of the resulting extents + * @y2: bottom of the resulting extents + * + * Computes a bounding box in user coordinates covering the area that + * would be affected, (the "inked" area), by a cairo_fill() operation + * given the current path and fill parameters. If the current path is + * empty, returns an empty rectangle ((0,0), (0,0)). Surface + * dimensions and clipping are not taken into account. + * + * Contrast with cairo_path_extents(), which is similar, but returns + * non-zero extents for some paths with no inked area, (such as a + * simple line segment). + * + * Note that cairo_fill_extents() must necessarily do more work to + * compute the precise inked areas in light of the fill rule, so + * cairo_path_extents() may be more desirable for sake of performance + * 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, + double *x1, double *y1, double *x2, double *y2) +{ + cairo_status_t status; + + if (unlikely (cr->status)) { + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + return; + } + + status = cr->backend->fill_extents (cr, x1, y1, x2, y2); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_clip: + * @cr: a cairo context + * + * Establishes a new clip region by intersecting the current clip + * region with the current path as it would be filled by cairo_fill() + * and according to the current fill rule (see cairo_set_fill_rule()). + * + * After cairo_clip(), the current path will be cleared from the cairo + * context. + * + * The current clip region affects all drawing operations by + * effectively masking out any changes to the surface that are outside + * the current clip region. + * + * Calling cairo_clip() can only make the clip region smaller, never + * larger. But the current clip is part of the graphics state, so a + * temporary restriction of the clip region can be achieved by + * 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_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->clip (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_clip_preserve: + * @cr: a cairo context + * + * Establishes a new clip region by intersecting the current clip + * region with the current path as it would be filled by cairo_fill() + * and according to the current fill rule (see cairo_set_fill_rule()). + * + * Unlike cairo_clip(), cairo_clip_preserve() preserves the path within + * the cairo context. + * + * The current clip region affects all drawing operations by + * effectively masking out any changes to the surface that are outside + * the current clip region. + * + * Calling cairo_clip_preserve() can only make the clip region smaller, never + * larger. But the current clip is part of the graphics state, so a + * temporary restriction of the clip region can be achieved by + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->clip_preserve (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def(cairo_clip_preserve); + +/** + * cairo_reset_clip: + * @cr: a cairo context + * + * Reset the current clip region to its original, unrestricted + * state. That is, set the clip region to an infinitely large shape + * containing the target surface. Equivalently, if infinity is too + * hard to grasp, one can imagine the clip region being reset to the + * exact bounds of the target surface. + * + * Note that code meant to be reusable should not call + * cairo_reset_clip() as it will cause results unexpected by + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->reset_clip (cr); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_clip_extents: + * @cr: a cairo context + * @x1: left of the resulting extents + * @y1: top of the resulting extents + * @x2: right of the resulting extents + * @y2: bottom of the resulting extents + * + * Computes a bounding box in user coordinates covering the area inside the + * current clip. + * + * Since: 1.4 + **/ +void +cairo_clip_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2) +{ + 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; + + status = cr->backend->clip_extents (cr, x1, y1, x2, y2); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_in_clip: + * @cr: a cairo context + * @x: X coordinate of the point to test + * @y: Y coordinate of the point to test + * + * Tests whether the given point is inside the area that would be + * visible through the current clip, i.e. the area that would be filled by + * a cairo_paint() operation. + * + * See cairo_clip(), and cairo_clip_preserve(). + * + * Return value: A non-zero value if the point is inside, or zero if + * outside. + * + * Since: 1.10 + **/ +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; + + status = cr->backend->in_clip (cr, x, y, &inside); + if (unlikely (status)) + _cairo_set_error (cr, status); + + return inside; +} + +/** + * cairo_copy_clip_rectangle_list: + * @cr: a cairo context + * + * Gets the current clip region as a list of rectangles in user coordinates. + * Never returns %NULL. + * + * The status in the list may be %CAIRO_STATUS_CLIP_NOT_REPRESENTABLE to + * indicate that the clip region cannot be represented as a list of + * user-space rectangles. The status may have other values to indicate + * other errors. + * + * Returns: the current clip region as a list of rectangles in user coordinates, + * which should be destroyed using cairo_rectangle_list_destroy(). + * + * Since: 1.4 + **/ +cairo_rectangle_list_t * +cairo_copy_clip_rectangle_list (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_rectangle_list_create_in_error (cr->status); + + 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); +} + +/** + * cairo_select_font_face: + * @cr: a #cairo_t + * @family: a font family name, encoded in UTF-8 + * @slant: the slant for the font + * @weight: the weight for the font + * + * Note: The cairo_select_font_face() function call is part of what + * the cairo designers call the "toy" text API. It is convenient for + * short demos and simple programs, but it is not expected to be + * adequate for serious text-using applications. + * + * Selects a family and style of font from a simplified description as + * a family name, slant and weight. Cairo provides no operation to + * list available family names on the system (this is a "toy", + * remember), but the standard CSS2 generic family names, ("serif", + * "sans-serif", "cursive", "fantasy", "monospace"), are likely to + * work as expected. + * + * If @family starts with the string "@cairo:", or if no native font + * backends are compiled in, cairo will use an internal font family. + * The internal font family recognizes many modifiers in the @family + * string, most notably, it recognizes the string "monospace". That is, + * the family name "@cairo:monospace" will use the monospace version of + * the internal font family. + * + * For "real" font selection, see the font-backend-specific + * font_face_create functions for the font backend you are using. (For + * example, if you are using the freetype-based cairo-ft font backend, + * see cairo_ft_font_face_create_for_ft_face() or + * cairo_ft_font_face_create_for_pattern().) The resulting font face + * could then be used with cairo_scaled_font_create() and + * cairo_set_scaled_font(). + * + * Similarly, when using the "real" font support, you can call + * directly into the underlying font system, (such as fontconfig or + * freetype), for operations such as listing available fonts, etc. + * + * It is expected that most applications will need to use a more + * comprehensive font handling and text layout library, (for example, + * pango), in conjunction with cairo. + * + * If text is drawn without a call to cairo_select_font_face(), (nor + * cairo_set_font_face() nor cairo_set_scaled_font()), the default + * family is platform-specific, but is essentially "sans-serif". + * Default slant is %CAIRO_FONT_SLANT_NORMAL, and default weight is + * %CAIRO_FONT_WEIGHT_NORMAL. + * + * 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, + const char *family, + 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; + + 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); +} + +/** + * cairo_font_extents: + * @cr: a #cairo_t + * @extents: a #cairo_font_extents_t object into which the results + * will be stored. + * + * Gets the font extents for the currently selected font. + * + * Since: 1.0 + **/ +void +cairo_font_extents (cairo_t *cr, + cairo_font_extents_t *extents) +{ + cairo_status_t status; + + extents->ascent = 0.0; + extents->descent = 0.0; + extents->height = 0.0; + extents->max_x_advance = 0.0; + extents->max_y_advance = 0.0; + + if (unlikely (cr->status)) + return; + + status = cr->backend->font_extents (cr, extents); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_set_font_face: + * @cr: a #cairo_t + * @font_face: a #cairo_font_face_t, or %NULL to restore to the default font + * + * 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, + cairo_font_face_t *font_face) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cr->backend->set_font_face (cr, font_face); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_get_font_face: + * @cr: a #cairo_t + * + * Gets the current font face for a #cairo_t. + * + * Return value: the current font face. This object is owned by + * cairo. To keep a reference to it, you must call + * cairo_font_face_reference(). + * + * This function never returns %NULL. If memory cannot be allocated, a + * special "nil" #cairo_font_face_t object will be returned on which + * cairo_font_face_status() returns %CAIRO_STATUS_NO_MEMORY. Using + * this nil object will cause its error state to propagate to other + * 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) +{ + if (unlikely (cr->status)) + return (cairo_font_face_t*) &_cairo_font_face_nil; + + return cr->backend->get_font_face (cr); +} + +/** + * cairo_set_font_size: + * @cr: a #cairo_t + * @size: the new font size, in user space units + * + * Sets the current font matrix to a scale by a factor of @size, replacing + * any font matrix previously set with cairo_set_font_size() or + * cairo_set_font_matrix(). This results in a font size of @size user space + * units. (More precisely, this matrix will result in the font's + * em-square being a @size by @size square in user space.) + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + 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: + * @cr: a #cairo_t + * @matrix: a #cairo_matrix_t describing a transform to be applied to + * the current font. + * + * Sets the current font matrix to @matrix. The font matrix gives a + * transformation from the design space of the font (in this space, + * the em-square is 1 unit by 1 unit) to user space. Normally, a + * 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, + const cairo_matrix_t *matrix) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + 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: + * @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) +{ + if (unlikely (cr->status)) { + cairo_matrix_init_identity (matrix); + return; + } + + cr->backend->get_font_matrix (cr, matrix); +} + +/** + * cairo_set_font_options: + * @cr: a #cairo_t + * @options: font options to use + * + * Sets a set of custom font rendering options for the #cairo_t. + * Rendering options are derived by merging these options with the + * 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, + const cairo_font_options_t *options) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + status = cairo_font_options_status ((cairo_font_options_t *) options); + if (unlikely (status)) { + _cairo_set_error (cr, status); + return; + } + + status = cr->backend->set_font_options (cr, options); + if (unlikely (status)) + _cairo_set_error (cr, status); +} +slim_hidden_def (cairo_set_font_options); + +/** + * cairo_get_font_options: + * @cr: a #cairo_t + * @options: a #cairo_font_options_t object into which to store + * the retrieved options. All existing values are overwritten + * + * Retrieves font rendering options set via #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, + cairo_font_options_t *options) +{ + /* check that we aren't trying to overwrite the nil object */ + if (cairo_font_options_status (options)) + return; + + if (unlikely (cr->status)) { + _cairo_font_options_init_default (options); + return; + } + + cr->backend->get_font_options (cr, options); +} + +/** + * cairo_set_scaled_font: + * @cr: a #cairo_t + * @scaled_font: a #cairo_scaled_font_t + * + * Replaces the current font face, font matrix, and font options in + * the #cairo_t with those of the #cairo_scaled_font_t. Except for + * some translation, the current CTM of the #cairo_t should be the + * same as that of the #cairo_scaled_font_t, which can be accessed + * using cairo_scaled_font_get_ctm(). + * + * Since: 1.2 + **/ +void +cairo_set_scaled_font (cairo_t *cr, + const cairo_scaled_font_t *scaled_font) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if ((scaled_font == NULL)) { + _cairo_set_error (cr, _cairo_error (CAIRO_STATUS_NULL_POINTER)); + return; + } + + status = scaled_font->status; + if (unlikely (status)) { + _cairo_set_error (cr, status); + return; + } + + status = cr->backend->set_scaled_font (cr, (cairo_scaled_font_t *) scaled_font); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_get_scaled_font: + * @cr: a #cairo_t + * + * Gets the current scaled font for a #cairo_t. + * + * Return value: the current scaled font. This object is owned by + * cairo. To keep a reference to it, you must call + * cairo_scaled_font_reference(). + * + * This function never returns %NULL. If memory cannot be allocated, a + * special "nil" #cairo_scaled_font_t object will be returned on which + * cairo_scaled_font_status() returns %CAIRO_STATUS_NO_MEMORY. Using + * this nil object will cause its error state to propagate to other + * objects it is passed to, (for example, calling + * cairo_set_scaled_font() with a nil font will trigger an error that + * will shutdown the #cairo_t object). + * + * Since: 1.4 + **/ +cairo_scaled_font_t * +cairo_get_scaled_font (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_scaled_font_create_in_error (cr->status); + + return cr->backend->get_scaled_font (cr); +} +slim_hidden_def (cairo_get_scaled_font); + +/** + * cairo_text_extents: + * @cr: a #cairo_t + * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL + * @extents: a #cairo_text_extents_t object into which the results + * will be stored + * + * Gets the extents for a string of text. The extents describe a + * user-space rectangle that encloses the "inked" portion of the text, + * (as it would be drawn by cairo_show_text()). Additionally, the + * x_advance and y_advance values indicate the amount by which the + * current point would be advanced by cairo_show_text(). + * + * Note that whitespace characters do not directly contribute to the + * size of the rectangle (extents.width and extents.height). They do + * contribute indirectly by changing the position of non-whitespace + * 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, + const char *utf8, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + cairo_scaled_font_t *scaled_font; + cairo_glyph_t *glyphs = NULL; + int num_glyphs = 0; + double x, y; + + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + if (unlikely (cr->status)) + return; + + 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); + + if (likely (status == CAIRO_STATUS_SUCCESS)) { + status = cr->backend->glyph_extents (cr, + glyphs, num_glyphs, + extents); + } + cairo_glyph_free (glyphs); + + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_glyph_extents: + * @cr: a #cairo_t + * @glyphs: an array of #cairo_glyph_t objects + * @num_glyphs: the number of elements in @glyphs + * @extents: a #cairo_text_extents_t object into which the results + * will be stored + * + * Gets the extents for an array of glyphs. The extents describe a + * user-space rectangle that encloses the "inked" portion of the + * glyphs, (as they would be drawn by cairo_show_glyphs()). + * Additionally, the x_advance and y_advance values indicate the + * amount by which the current point would be advanced by + * cairo_show_glyphs(). + * + * 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, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + cairo_status_t status; + + extents->x_bearing = 0.0; + extents->y_bearing = 0.0; + extents->width = 0.0; + extents->height = 0.0; + extents->x_advance = 0.0; + extents->y_advance = 0.0; + + if (unlikely (cr->status)) + return; + + if (num_glyphs == 0) + return; + + if (unlikely (num_glyphs < 0)) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + return; + } + + if (unlikely (glyphs == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + status = cr->backend->glyph_extents (cr, glyphs, num_glyphs, extents); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_show_text: + * @cr: a cairo context + * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL + * + * A drawing operator that generates the shape from a string of UTF-8 + * characters, rendered according to the current font_face, font_size + * (font_matrix), and font_options. + * + * This function first computes a set of glyphs for the string of + * text. The first glyph is placed so that its origin is at the + * current point. The origin of each subsequent glyph is offset from + * that of the previous glyph by the advance values of the previous + * glyph. + * + * After this call the current point is moved to the origin of where + * the next glyph would be placed in this same progression. That is, + * the current point will be at the origin of the final glyph offset + * by its advance values. This allows for easy display of a single + * logical string with multiple calls to cairo_show_text(). + * + * Note: The cairo_show_text() function call is part of what the cairo + * designers call the "toy" text API. It is convenient for short demos + * 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) +{ + cairo_text_extents_t extents; + cairo_status_t status; + cairo_glyph_t *glyphs, *last_glyph; + cairo_text_cluster_t *clusters; + int utf8_len, num_glyphs, num_clusters; + cairo_text_cluster_flags_t cluster_flags; + double x, y; + cairo_bool_t has_show_text_glyphs; + cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)]; + cairo_scaled_font_t *scaled_font; + cairo_glyph_text_info_t info, *i; + + if (unlikely (cr->status)) + return; + + if (utf8 == NULL) + return; + + scaled_font = cairo_get_scaled_font (cr); + if (unlikely (scaled_font->status)) { + _cairo_set_error (cr, scaled_font->status); + return; + } + + utf8_len = strlen (utf8); + + has_show_text_glyphs = + cairo_surface_has_show_text_glyphs (cairo_get_target (cr)); + + glyphs = stack_glyphs; + num_glyphs = ARRAY_LENGTH (stack_glyphs); + + if (has_show_text_glyphs) { + clusters = stack_clusters; + num_clusters = ARRAY_LENGTH (stack_clusters); + } else { + clusters = NULL; + num_clusters = 0; + } + + 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; + + 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 = 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; + cr->backend->move_to (cr, x, y); + + BAIL: + if (glyphs != stack_glyphs) + cairo_glyph_free (glyphs); + if (clusters != stack_clusters) + cairo_text_cluster_free (clusters); + + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_show_glyphs: + * @cr: a cairo context + * @glyphs: array of glyphs to show + * @num_glyphs: number of glyphs to show + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (num_glyphs == 0) + return; + + if (num_glyphs < 0) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + return; + } + + if (glyphs == NULL) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_show_text_glyphs: + * @cr: a cairo context + * @utf8: a string of text encoded in UTF-8 + * @utf8_len: length of @utf8 in bytes, or -1 if it is NUL-terminated + * @glyphs: array of glyphs to show + * @num_glyphs: number of glyphs to show + * @clusters: array of cluster mapping information + * @num_clusters: number of clusters in the mapping + * @cluster_flags: cluster mapping flags + * + * This operation has rendering effects similar to cairo_show_glyphs() + * but, if the target surface supports it, uses the provided text and + * cluster mapping to embed the text for the glyphs shown in the output. + * If the target does not support the extended attributes, this function + * acts like the basic cairo_show_glyphs() as if it had been passed + * @glyphs and @num_glyphs. + * + * The mapping between @utf8 and @glyphs is provided by an array of + * clusters. Each cluster covers a number of + * text bytes and glyphs, and neighboring clusters cover neighboring + * areas of @utf8 and @glyphs. The clusters should collectively cover @utf8 + * and @glyphs in entirety. + * + * The first cluster always covers bytes from the beginning of @utf8. + * If @cluster_flags do not have the %CAIRO_TEXT_CLUSTER_FLAG_BACKWARD + * set, the first cluster also covers the beginning + * of @glyphs, otherwise it covers the end of the @glyphs array and + * following clusters move backward. + * + * See #cairo_text_cluster_t for constraints on valid clusters. + * + * Since: 1.8 + **/ +void +cairo_show_text_glyphs (cairo_t *cr, + 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_status_t status; + + if (unlikely (cr->status)) + return; + + /* A slew of sanity checks */ + + /* Special case for NULL and -1 */ + if (utf8 == NULL && utf8_len == -1) + utf8_len = 0; + + /* No NULLs for non-zeros */ + if ((num_glyphs && glyphs == NULL) || + (utf8_len && utf8 == NULL) || + (num_clusters && clusters == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + /* A -1 for utf8_len means NUL-terminated */ + if (utf8_len == -1) + utf8_len = strlen (utf8); + + /* Apart from that, no negatives */ + if (num_glyphs < 0 || utf8_len < 0 || num_clusters < 0) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + return; + } + + if (num_glyphs == 0 && utf8_len == 0) + return; + + 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); +} + +/** + * cairo_text_path: + * @cr: a cairo context + * @utf8: a NUL-terminated string of text encoded in UTF-8, or %NULL + * + * Adds closed paths for text to the current path. The generated + * path if filled, achieves an effect similar to that of + * cairo_show_text(). + * + * Text conversion and positioning is done similar to cairo_show_text(). + * + * Like cairo_show_text(), After this call the current point is + * moved to the origin of where the next glyph would be placed in + * this same progression. That is, the current point will be at + * the origin of the final glyph offset by its advance values. + * This allows for chaining multiple calls to to cairo_text_path() + * without having to set current point in between. + * + * Note: The cairo_text_path() function call is part of what the cairo + * designers call the "toy" text API. It is convenient for short demos + * 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_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; + + if (unlikely (cr->status)) + return; + + if (utf8 == NULL) + return; + + + glyphs = stack_glyphs; + num_glyphs = ARRAY_LENGTH (stack_glyphs); + + 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); + + if (num_glyphs == 0) + return; + + status = cr->backend->glyph_path (cr, glyphs, num_glyphs); + + if (unlikely (status)) + goto BAIL; + + last_glyph = &glyphs[num_glyphs - 1]; + 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; + cr->backend->move_to (cr, x, y); + + BAIL: + if (glyphs != stack_glyphs) + cairo_glyph_free (glyphs); + + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_glyph_path: + * @cr: a cairo context + * @glyphs: array of glyphs to show + * @num_glyphs: number of glyphs to show + * + * 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) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (num_glyphs == 0) + return; + + if (unlikely (num_glyphs < 0)) { + _cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT); + return; + } + + if (unlikely (glyphs == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + status = cr->backend->glyph_path (cr, glyphs, num_glyphs); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_get_operator: + * @cr: a cairo context + * + * 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) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_OPERATOR_DEFAULT; + + 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 + * + * 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) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_TOLERANCE_DEFAULT; + + return cr->backend->get_tolerance (cr); +} +slim_hidden_def (cairo_get_tolerance); + +/** + * cairo_get_antialias: + * @cr: a cairo context + * + * Gets the current shape antialiasing mode, as set by + * cairo_set_antialias(). + * + * Return value: the current shape antialiasing mode. + * + * Since: 1.0 + **/ +cairo_antialias_t +cairo_get_antialias (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_ANTIALIAS_DEFAULT; + + return cr->backend->get_antialias (cr); +} + +/** + * cairo_has_current_point: + * @cr: a cairo context + * + * Returns whether a current point is defined on the current path. + * See cairo_get_current_point() for details on the current point. + * + * Return value: whether a current point is defined. + * + * Since: 1.6 + **/ +cairo_bool_t +cairo_has_current_point (cairo_t *cr) +{ + if (unlikely (cr->status)) + return FALSE; + + return cr->backend->has_current_point (cr); +} + +/** + * cairo_get_current_point: + * @cr: a cairo context + * @x: return value for X coordinate of the current point + * @y: return value for Y coordinate of the current point + * + * Gets the current point of the current path, which is + * conceptually the final point reached by the path so far. + * + * The current point is returned in the user-space coordinate + * system. If there is no defined current point or if @cr is in an + * error status, @x and @y will both be set to 0.0. It is possible to + * check this in advance with cairo_has_current_point(). + * + * Most path construction functions alter the current point. See the + * following for details on how they affect the current point: + * cairo_new_path(), cairo_new_sub_path(), + * cairo_append_path(), cairo_close_path(), + * cairo_move_to(), cairo_line_to(), cairo_curve_to(), + * cairo_rel_move_to(), cairo_rel_line_to(), cairo_rel_curve_to(), + * cairo_arc(), cairo_arc_negative(), cairo_rectangle(), + * cairo_text_path(), cairo_glyph_path(), cairo_stroke_to_path(). + * + * Some functions use and alter the current point but do not + * otherwise change current path: + * cairo_show_text(). + * + * 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) +{ + double x, y; + + x = y = 0; + if (cr->status == CAIRO_STATUS_SUCCESS && + cr->backend->has_current_point (cr)) + { + cr->backend->get_current_point (cr, &x, &y); + } + + if (x_ret) + *x_ret = x; + if (y_ret) + *y_ret = y; +} +slim_hidden_def(cairo_get_current_point); + +/** + * cairo_get_fill_rule: + * @cr: a cairo context + * + * 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) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_FILL_RULE_DEFAULT; + + return cr->backend->get_fill_rule (cr); +} + +/** + * cairo_get_line_width: + * @cr: a cairo context + * + * This function returns the current line width value exactly as set by + * cairo_set_line_width(). Note that the value is unchanged even if + * the CTM has changed between the calls to cairo_set_line_width() and + * cairo_get_line_width(). + * + * Return value: the current line width. + * + * Since: 1.0 + **/ +double +cairo_get_line_width (cairo_t *cr) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_LINE_WIDTH_DEFAULT; + + return cr->backend->get_line_width (cr); +} +slim_hidden_def (cairo_get_line_width); + +/** + * cairo_get_line_cap: + * @cr: a cairo context + * + * 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) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_LINE_CAP_DEFAULT; + + return cr->backend->get_line_cap (cr); +} + +/** + * cairo_get_line_join: + * @cr: a cairo context + * + * 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) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_LINE_JOIN_DEFAULT; + + return cr->backend->get_line_join (cr); +} + +/** + * cairo_get_miter_limit: + * @cr: a cairo context + * + * 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) +{ + if (unlikely (cr->status)) + return CAIRO_GSTATE_MITER_LIMIT_DEFAULT; + + return cr->backend->get_miter_limit (cr); +} + +/** + * cairo_get_matrix: + * @cr: a cairo context + * @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) +{ + if (unlikely (cr->status)) { + cairo_matrix_init_identity (matrix); + return; + } + + cr->backend->get_matrix (cr, matrix); +} +slim_hidden_def (cairo_get_matrix); + +/** + * cairo_get_target: + * @cr: a cairo context + * + * Gets the target surface for the cairo context as passed to + * cairo_create(). + * + * This function will always return a valid pointer, but the result + * can be a "nil" surface if @cr is already in an error state, + * (ie. cairo_status() != %CAIRO_STATUS_SUCCESS). + * A nil surface is indicated by cairo_surface_status() + * != %CAIRO_STATUS_SUCCESS. + * + * 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) +{ + if (unlikely (cr->status)) + return _cairo_surface_create_in_error (cr->status); + + return cr->backend->get_original_target (cr); +} +slim_hidden_def (cairo_get_target); + +/** + * cairo_get_group_target: + * @cr: a cairo context + * + * Gets the current destination surface for the context. This is either + * the original target surface as passed to cairo_create() or the target + * surface for the current group as started by the most recent call to + * cairo_push_group() or cairo_push_group_with_content(). + * + * This function will always return a valid pointer, but the result + * can be a "nil" surface if @cr is already in an error state, + * (ie. cairo_status() != %CAIRO_STATUS_SUCCESS). + * A nil surface is indicated by cairo_surface_status() + * != %CAIRO_STATUS_SUCCESS. + * + * 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.2 + **/ +cairo_surface_t * +cairo_get_group_target (cairo_t *cr) +{ + if (unlikely (cr->status)) + return _cairo_surface_create_in_error (cr->status); + + return cr->backend->get_current_target (cr); +} + +/** + * cairo_copy_path: + * @cr: a cairo context + * + * Creates a copy of the current path and returns it to the user as a + * #cairo_path_t. See #cairo_path_data_t for hints on how to iterate + * over the returned data structure. + * + * This function will always return a valid pointer, but the result + * will have no data (data==%NULL and + * num_data==0), if either of the following + * conditions hold: + * + * + * If there is insufficient memory to copy the path. In this + * case path->status will be set to + * %CAIRO_STATUS_NO_MEMORY. + * If @cr is already in an error state. In this case + * path->status will contain the same status that + * would be returned by cairo_status(). + * + * + * 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) +{ + if (unlikely (cr->status)) + return _cairo_path_create_in_error (cr->status); + + return cr->backend->copy_path (cr); +} + +/** + * cairo_copy_path_flat: + * @cr: a cairo context + * + * Gets a flattened copy of the current path and returns it to the + * user as a #cairo_path_t. See #cairo_path_data_t for hints on + * how to iterate over the returned data structure. + * + * This function is like cairo_copy_path() except that any curves + * in the path will be approximated with piecewise-linear + * approximations, (accurate to within the current tolerance + * value). That is, the result is guaranteed to not have any elements + * of type %CAIRO_PATH_CURVE_TO which will instead be replaced by a + * series of %CAIRO_PATH_LINE_TO elements. + * + * This function will always return a valid pointer, but the result + * will have no data (data==%NULL and + * num_data==0), if either of the following + * conditions hold: + * + * + * If there is insufficient memory to copy the path. In this + * case path->status will be set to + * %CAIRO_STATUS_NO_MEMORY. + * If @cr is already in an error state. In this case + * path->status will contain the same status that + * would be returned by cairo_status(). + * + * + * 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) +{ + if (unlikely (cr->status)) + return _cairo_path_create_in_error (cr->status); + + return cr->backend->copy_path_flat (cr); +} + +/** + * cairo_append_path: + * @cr: a cairo context + * @path: path to be appended + * + * Append the @path onto the current path. The @path may be either the + * return value from one of cairo_copy_path() or + * cairo_copy_path_flat() or it may be constructed manually. See + * #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, + const cairo_path_t *path) +{ + cairo_status_t status; + + if (unlikely (cr->status)) + return; + + if (unlikely (path == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + if (unlikely (path->status)) { + if (path->status > CAIRO_STATUS_SUCCESS && + path->status <= CAIRO_STATUS_LAST_STATUS) + _cairo_set_error (cr, path->status); + else + _cairo_set_error (cr, CAIRO_STATUS_INVALID_STATUS); + return; + } + + if (path->num_data == 0) + return; + + if (unlikely (path->data == NULL)) { + _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER); + return; + } + + status = cr->backend->append_path (cr, path); + if (unlikely (status)) + _cairo_set_error (cr, status); +} + +/** + * cairo_status: + * @cr: a cairo context + * + * 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) +{ + return cr->status; +} +slim_hidden_def (cairo_status); diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h new file mode 100644 index 0000000000..a2c54d4083 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo.h @@ -0,0 +1,3238 @@ +/* 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_H +#define CAIRO_H + +#include "cairo-version.h" +#include "cairo-features.h" +#include "cairo-deprecated.h" + +#ifdef __cplusplus +# define CAIRO_BEGIN_DECLS extern "C" { +# define CAIRO_END_DECLS } +#else +# define CAIRO_BEGIN_DECLS +# define CAIRO_END_DECLS +#endif + +#ifndef cairo_public +# if defined (_MSC_VER) && ! defined (CAIRO_WIN32_STATIC_BUILD) +# define cairo_public __declspec(dllimport) +# else +# define cairo_public +# endif +#endif + +CAIRO_BEGIN_DECLS + +#define CAIRO_VERSION_ENCODE(major, minor, micro) ( \ + ((major) * 10000) \ + + ((minor) * 100) \ + + ((micro) * 1)) + +#define CAIRO_VERSION CAIRO_VERSION_ENCODE( \ + CAIRO_VERSION_MAJOR, \ + CAIRO_VERSION_MINOR, \ + CAIRO_VERSION_MICRO) + + +#define CAIRO_VERSION_STRINGIZE_(major, minor, micro) \ + #major"."#minor"."#micro +#define CAIRO_VERSION_STRINGIZE(major, minor, micro) \ + CAIRO_VERSION_STRINGIZE_(major, minor, micro) + +#define CAIRO_VERSION_STRING CAIRO_VERSION_STRINGIZE( \ + CAIRO_VERSION_MAJOR, \ + CAIRO_VERSION_MINOR, \ + CAIRO_VERSION_MICRO) + + +cairo_public int +cairo_version (void); + +cairo_public const char* +cairo_version_string (void); + +/** + * cairo_bool_t: + * + * #cairo_bool_t is used for boolean values. Returns of type + * #cairo_bool_t will always be either 0 or 1, but testing against + * these values explicitly is not encouraged; just use the + * value as a boolean condition. + * + * + * if (cairo_in_stroke (cr, x, y)) { + * /* do something */ + * } + * + * + * Since: 1.0 + **/ +typedef int cairo_bool_t; + +/** + * cairo_t: + * + * A #cairo_t contains the current state of the rendering device, + * including coordinates of yet to be drawn shapes. + * + * Cairo contexts, as #cairo_t objects are named, are central to + * cairo and all drawing with cairo is always done to a #cairo_t + * object. + * + * Memory management of #cairo_t is done with + * cairo_reference() and cairo_destroy(). + * + * Since: 1.0 + **/ +typedef struct _cairo cairo_t; + +/** + * cairo_surface_t: + * + * A #cairo_surface_t represents an image, either as the destination + * of a drawing operation or as source when drawing onto another + * surface. To draw to a #cairo_surface_t, create a cairo context + * with the surface as the target, using cairo_create(). + * + * There are different subtypes of #cairo_surface_t for + * different drawing backends; for example, cairo_image_surface_create() + * creates a bitmap image in memory. + * The type of a surface can be queried with cairo_surface_get_type(). + * + * The initial contents of a surface after creation depend upon the manner + * of its creation. If cairo creates the surface and backing storage for + * the user, it will be initially cleared; for example, + * cairo_image_surface_create() and cairo_surface_create_similar(). + * Alternatively, if the user passes in a reference to some backing storage + * and asks cairo to wrap that in a #cairo_surface_t, then the contents are + * not modified; for example, cairo_image_surface_create_for_data() and + * cairo_xlib_surface_create(). + * + * 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; + +/** + * cairo_device_t: + * + * A #cairo_device_t represents the driver interface for drawing + * operations to a #cairo_surface_t. There are different subtypes of + * #cairo_device_t for different drawing backends; for example, + * cairo_egl_device_create() creates a device that wraps an EGL display and + * context. + * + * The type of a device can be queried with cairo_device_get_type(). + * + * Memory management of #cairo_device_t is done with + * cairo_device_reference() and cairo_device_destroy(). + * + * Since: 1.10 + **/ +typedef struct _cairo_device cairo_device_t; + +/** + * cairo_matrix_t: + * @xx: xx component of the affine transformation + * @yx: yx component of the affine transformation + * @xy: xy component of the affine transformation + * @yy: yy component of the affine transformation + * @x0: X translation component of the affine transformation + * @y0: Y translation component of the affine transformation + * + * A #cairo_matrix_t holds an affine transformation, such as a scale, + * rotation, shear, or a combination of those. The transformation of + * a point (x, y) is given by: + * + * 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; + double xy; double yy; + double x0; double y0; +} cairo_matrix_t; + +/** + * cairo_pattern_t: + * + * A #cairo_pattern_t represents a source when drawing onto a + * surface. There are different subtypes of #cairo_pattern_t, + * for different types of sources; for example, + * 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; + * 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; + +/** + * cairo_destroy_func_t: + * @data: The data element being destroyed. + * + * #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_user_data_key_t: + * @unused: not used; ignore. + * + * #cairo_user_data_key_t is used for attaching user data to cairo + * data structures. The actual contents of the struct is never used, + * 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; +} cairo_user_data_key_t; + +/** + * cairo_status_t: + * @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) + * @CAIRO_STATUS_TEMP_FILE_ERROR: error creating or writing to a temporary file (Since 1.6) + * @CAIRO_STATUS_INVALID_STRIDE: invalid value for stride (Since 1.6) + * @CAIRO_STATUS_FONT_TYPE_MISMATCH: the font type is not appropriate for the operation (Since 1.8) + * @CAIRO_STATUS_USER_FONT_IMMUTABLE: the user-font is immutable (Since 1.8) + * @CAIRO_STATUS_USER_FONT_ERROR: error occurred in a user-font callback function (Since 1.8) + * @CAIRO_STATUS_NEGATIVE_COUNT: negative number used where it is not allowed (Since 1.8) + * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8) + * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8) + * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8) + * @CAIRO_STATUS_INVALID_SIZE: invalid value (typically too big) for the size of the input (surface, pattern, etc.) (Since 1.10) + * @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10) + * @CAIRO_STATUS_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 + * defined than the value of this symbol at compile-time. (Since 1.10) + * + * #cairo_status_t is used to indicate errors that can occur when + * using Cairo. In some cases it is returned directly by functions. + * but when using #cairo_t, the last error, if any, is stored in + * the context and can be retrieved with cairo_status(). + * + * 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, + + CAIRO_STATUS_NO_MEMORY, + CAIRO_STATUS_INVALID_RESTORE, + CAIRO_STATUS_INVALID_POP_GROUP, + CAIRO_STATUS_NO_CURRENT_POINT, + CAIRO_STATUS_INVALID_MATRIX, + CAIRO_STATUS_INVALID_STATUS, + CAIRO_STATUS_NULL_POINTER, + CAIRO_STATUS_INVALID_STRING, + CAIRO_STATUS_INVALID_PATH_DATA, + CAIRO_STATUS_READ_ERROR, + CAIRO_STATUS_WRITE_ERROR, + CAIRO_STATUS_SURFACE_FINISHED, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH, + CAIRO_STATUS_PATTERN_TYPE_MISMATCH, + CAIRO_STATUS_INVALID_CONTENT, + CAIRO_STATUS_INVALID_FORMAT, + CAIRO_STATUS_INVALID_VISUAL, + CAIRO_STATUS_FILE_NOT_FOUND, + CAIRO_STATUS_INVALID_DASH, + CAIRO_STATUS_INVALID_DSC_COMMENT, + CAIRO_STATUS_INVALID_INDEX, + CAIRO_STATUS_CLIP_NOT_REPRESENTABLE, + CAIRO_STATUS_TEMP_FILE_ERROR, + CAIRO_STATUS_INVALID_STRIDE, + CAIRO_STATUS_FONT_TYPE_MISMATCH, + CAIRO_STATUS_USER_FONT_IMMUTABLE, + CAIRO_STATUS_USER_FONT_ERROR, + CAIRO_STATUS_NEGATIVE_COUNT, + CAIRO_STATUS_INVALID_CLUSTERS, + CAIRO_STATUS_INVALID_SLANT, + CAIRO_STATUS_INVALID_WEIGHT, + CAIRO_STATUS_INVALID_SIZE, + CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, + CAIRO_STATUS_DEVICE_TYPE_MISMATCH, + CAIRO_STATUS_DEVICE_ERROR, + 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. (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 + * vs. opacity), or both. + * + * 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, + CAIRO_CONTENT_ALPHA = 0x2000, + 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 + * @data: the buffer containing the data to write + * @length: the amount of data to write + * + * #cairo_write_func_t is the type of function which is called when a + * backend needs to write data to an output stream. It is passed the + * closure which was specified by the user at the time the write + * function was registered, the data to write and the length of the + * data in bytes. The write function should return + * %CAIRO_STATUS_SUCCESS if all the data was successfully written, + * %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, + unsigned int length); + +/** + * cairo_read_func_t: + * @closure: the input closure + * @data: the buffer into which to read the data + * @length: the amount of data to read + * + * #cairo_read_func_t is the type of function which is called when a + * backend needs to read data from an input stream. It is passed the + * closure which was specified by the user at the time the read + * function was registered, the buffer to read the data into and the + * length of the data in bytes. The read function should return + * %CAIRO_STATUS_SUCCESS if all the data was successfully read, + * %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); + +cairo_public cairo_t * +cairo_reference (cairo_t *cr); + +cairo_public void +cairo_destroy (cairo_t *cr); + +cairo_public unsigned int +cairo_get_reference_count (cairo_t *cr); + +cairo_public void * +cairo_get_user_data (cairo_t *cr, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_set_user_data (cairo_t *cr, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +cairo_public void +cairo_save (cairo_t *cr); + +cairo_public void +cairo_restore (cairo_t *cr); + +cairo_public void +cairo_push_group (cairo_t *cr); + +cairo_public void +cairo_push_group_with_content (cairo_t *cr, cairo_content_t content); + +cairo_public cairo_pattern_t * +cairo_pop_group (cairo_t *cr); + +cairo_public void +cairo_pop_group_to_source (cairo_t *cr); + +/* Modify state */ + +/** + * cairo_operator_t: + * @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) (Since 1.0) + * @CAIRO_OPERATOR_IN: draw source where there was destination content + * (unbounded) (Since 1.0) + * @CAIRO_OPERATOR_OUT: draw source where there was no destination + * content (unbounded) (Since 1.0) + * @CAIRO_OPERATOR_ATOP: draw source on top of destination content and + * 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) (Since 1.0) + * @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no + * source content (Since 1.0) + * @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content + * and only there (unbounded) (Since 1.0) + * @CAIRO_OPERATOR_XOR: source and destination are shown where there is only + * 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 (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. (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. (Since 1.10) + * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the + * 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. (Since 1.10) + * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it + * is lighter, otherwise keeps the source. (Since 1.10) + * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect + * the source color. (Since 1.10) + * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect + * 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. (Since 1.10) + * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but + * 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. (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 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. (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. (Since 1.10) + * + * #cairo_operator_t is used to set the compositing operator for all cairo + * drawing operations. + * + * The default operator is %CAIRO_OPERATOR_OVER. + * + * The operators marked as unbounded modify their + * destination even outside of the mask layer (that is, their effect is not + * bound by the mask layer). However, their effect can still be limited by + * way of clipping. + * + * To keep things simple, the operator descriptions here + * document the behavior for when both source and destination are either fully + * transparent or fully opaque. The actual implementation works for + * translucent layers too. + * For a more detailed explanation of the effects of each operator, including + * the mathematical definitions, see + * https://cairographics.org/operators/. + * + * Since: 1.0 + **/ +typedef enum _cairo_operator { + CAIRO_OPERATOR_CLEAR, + + CAIRO_OPERATOR_SOURCE, + CAIRO_OPERATOR_OVER, + CAIRO_OPERATOR_IN, + CAIRO_OPERATOR_OUT, + CAIRO_OPERATOR_ATOP, + + CAIRO_OPERATOR_DEST, + CAIRO_OPERATOR_DEST_OVER, + CAIRO_OPERATOR_DEST_IN, + CAIRO_OPERATOR_DEST_OUT, + CAIRO_OPERATOR_DEST_ATOP, + + CAIRO_OPERATOR_XOR, + CAIRO_OPERATOR_ADD, + CAIRO_OPERATOR_SATURATE, + + CAIRO_OPERATOR_MULTIPLY, + CAIRO_OPERATOR_SCREEN, + CAIRO_OPERATOR_OVERLAY, + CAIRO_OPERATOR_DARKEN, + CAIRO_OPERATOR_LIGHTEN, + CAIRO_OPERATOR_COLOR_DODGE, + CAIRO_OPERATOR_COLOR_BURN, + CAIRO_OPERATOR_HARD_LIGHT, + CAIRO_OPERATOR_SOFT_LIGHT, + CAIRO_OPERATOR_DIFFERENCE, + CAIRO_OPERATOR_EXCLUSION, + CAIRO_OPERATOR_HSL_HUE, + CAIRO_OPERATOR_HSL_SATURATION, + CAIRO_OPERATOR_HSL_COLOR, + CAIRO_OPERATOR_HSL_LUMINOSITY +} cairo_operator_t; + +cairo_public void +cairo_set_operator (cairo_t *cr, cairo_operator_t op); + +cairo_public void +cairo_set_source (cairo_t *cr, cairo_pattern_t *source); + +cairo_public void +cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue); + +cairo_public void +cairo_set_source_rgba (cairo_t *cr, + double red, double green, double blue, + double alpha); + +cairo_public void +cairo_set_source_surface (cairo_t *cr, + cairo_surface_t *surface, + double x, + double y); + +cairo_public void +cairo_set_tolerance (cairo_t *cr, double tolerance); + +/** + * cairo_antialias_t: + * @CAIRO_ANTIALIAS_DEFAULT: Use the default antialiasing for + * 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), since 1.0 + * @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking + * advantage of the order of subpixel elements on devices + * 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, + + /* hints */ + CAIRO_ANTIALIAS_FAST, + CAIRO_ANTIALIAS_GOOD, + CAIRO_ANTIALIAS_BEST +} cairo_antialias_t; + +cairo_public void +cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias); + +/** + * cairo_fill_rule_t: + * @CAIRO_FILL_RULE_WINDING: If the path crosses the ray from + * 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. (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. (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 + * determined by taking a ray from that point to infinity and looking + * at intersections with the path. The ray can be in any direction, + * as long as it doesn't pass through the end point of a segment + * or have a tricky intersection such as intersecting tangent to the path. + * (Note that filling is not actually implemented in this way. This + * is just a description of the rule that is applied.) + * + * 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, + CAIRO_FILL_RULE_EVEN_ODD +} cairo_fill_rule_t; + +cairo_public void +cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule); + +cairo_public void +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 (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, + CAIRO_LINE_CAP_ROUND, + CAIRO_LINE_CAP_SQUARE +} cairo_line_cap_t; + +cairo_public void +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() (Since 1.0) + * @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the + * 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 (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, + CAIRO_LINE_JOIN_ROUND, + CAIRO_LINE_JOIN_BEVEL +} cairo_line_join_t; + +cairo_public void +cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join); + +cairo_public void +cairo_set_dash (cairo_t *cr, + const double *dashes, + int num_dashes, + double offset); + +cairo_public void +cairo_set_miter_limit (cairo_t *cr, double limit); + +cairo_public void +cairo_translate (cairo_t *cr, double tx, double ty); + +cairo_public void +cairo_scale (cairo_t *cr, double sx, double sy); + +cairo_public void +cairo_rotate (cairo_t *cr, double angle); + +cairo_public void +cairo_transform (cairo_t *cr, + const cairo_matrix_t *matrix); + +cairo_public void +cairo_set_matrix (cairo_t *cr, + const cairo_matrix_t *matrix); + +cairo_public void +cairo_identity_matrix (cairo_t *cr); + +cairo_public void +cairo_user_to_device (cairo_t *cr, double *x, double *y); + +cairo_public void +cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy); + +cairo_public void +cairo_device_to_user (cairo_t *cr, double *x, double *y); + +cairo_public void +cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy); + +/* Path creation functions */ +cairo_public void +cairo_new_path (cairo_t *cr); + +cairo_public void +cairo_move_to (cairo_t *cr, double x, double y); + +cairo_public void +cairo_new_sub_path (cairo_t *cr); + +cairo_public void +cairo_line_to (cairo_t *cr, double x, double y); + +cairo_public void +cairo_curve_to (cairo_t *cr, + double x1, double y1, + double x2, double y2, + double x3, double y3); + +cairo_public void +cairo_arc (cairo_t *cr, + double xc, double yc, + double radius, + double angle1, double angle2); + +cairo_public void +cairo_arc_negative (cairo_t *cr, + double xc, double yc, + double radius, + double angle1, double angle2); + +/* XXX: NYI +cairo_public void +cairo_arc_to (cairo_t *cr, + double x1, double y1, + double x2, double y2, + double radius); +*/ + +cairo_public void +cairo_rel_move_to (cairo_t *cr, double dx, double dy); + +cairo_public void +cairo_rel_line_to (cairo_t *cr, double dx, double dy); + +cairo_public void +cairo_rel_curve_to (cairo_t *cr, + double dx1, double dy1, + double dx2, double dy2, + double dx3, double dy3); + +cairo_public void +cairo_rectangle (cairo_t *cr, + double x, double y, + double width, double height); + +/* XXX: NYI +cairo_public void +cairo_stroke_to_path (cairo_t *cr); +*/ + +cairo_public void +cairo_close_path (cairo_t *cr); + +cairo_public void +cairo_path_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +/* Painting functions */ +cairo_public void +cairo_paint (cairo_t *cr); + +cairo_public void +cairo_paint_with_alpha (cairo_t *cr, + double alpha); + +cairo_public void +cairo_mask (cairo_t *cr, + cairo_pattern_t *pattern); + +cairo_public void +cairo_mask_surface (cairo_t *cr, + cairo_surface_t *surface, + double surface_x, + double surface_y); + +cairo_public void +cairo_stroke (cairo_t *cr); + +cairo_public void +cairo_stroke_preserve (cairo_t *cr); + +cairo_public void +cairo_fill (cairo_t *cr); + +cairo_public void +cairo_fill_preserve (cairo_t *cr); + +cairo_public void +cairo_copy_page (cairo_t *cr); + +cairo_public void +cairo_show_page (cairo_t *cr); + +/* Insideness testing */ +cairo_public cairo_bool_t +cairo_in_stroke (cairo_t *cr, double x, double y); + +cairo_public cairo_bool_t +cairo_in_fill (cairo_t *cr, double x, double y); + +cairo_public cairo_bool_t +cairo_in_clip (cairo_t *cr, double x, double y); + +/* Rectangular extents */ +cairo_public void +cairo_stroke_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +cairo_public void +cairo_fill_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +/* Clipping */ +cairo_public void +cairo_reset_clip (cairo_t *cr); + +cairo_public void +cairo_clip (cairo_t *cr); + +cairo_public void +cairo_clip_preserve (cairo_t *cr); + +cairo_public void +cairo_clip_extents (cairo_t *cr, + double *x1, double *y1, + double *x2, double *y2); + +/** + * cairo_rectangle_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. + * + * Since: 1.4 + **/ +typedef struct _cairo_rectangle { + double x, y, width, height; +} cairo_rectangle_t; + +/** + * cairo_rectangle_list_t: + * @status: Error status of the rectangle list + * @rectangles: Array containing the rectangles + * @num_rectangles: Number of rectangles in this list + * + * A data structure for holding a dynamically allocated + * array of rectangles. + * + * Since: 1.4 + **/ +typedef struct _cairo_rectangle_list { + cairo_status_t status; + cairo_rectangle_t *rectangles; + int num_rectangles; +} cairo_rectangle_list_t; + +cairo_public cairo_rectangle_list_t * +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 */ + +/** + * cairo_scaled_font_t: + * + * A #cairo_scaled_font_t is a font scaled to a particular size and device + * resolution. A #cairo_scaled_font_t is most useful for low-level font + * usage where a library or application wants to cache a reference + * to a scaled font to speed up the computation of metrics. + * + * There are various types of scaled fonts, depending on the + * font backend they use. The type of a + * scaled font can be queried using cairo_scaled_font_get_type(). + * + * 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; + +/** + * cairo_font_face_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 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(). + * + * There are various types of font faces, depending on the + * font backend they use. The type of a + * font face can be queried using cairo_font_face_get_type(). + * + * 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; + +/** + * cairo_glyph_t: + * @index: glyph index in the font. The exact interpretation of the + * glyph index depends on the font technology being used. + * @x: the offset in the X direction between the origin used for + * drawing or measuring the string and the origin of this glyph. + * @y: the offset in the Y direction between the origin used for + * drawing or measuring the string and the origin of this glyph. + * + * The #cairo_glyph_t structure holds information about a single glyph + * when drawing or measuring text. A font is (in simple terms) a + * collection of shapes used to draw text. A glyph is one of these + * shapes. There can be multiple glyphs for a single character + * (alternates to be used in different contexts, for example), or a + * glyph can be a ligature of multiple + * characters. Cairo doesn't expose any way of converting input text + * into glyphs, so in order to use the Cairo interfaces that take + * arrays of glyphs, you must directly access the appropriate + * underlying font system. + * + * 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; + double x; + double y; +} cairo_glyph_t; + +cairo_public cairo_glyph_t * +cairo_glyph_allocate (int num_glyphs); + +cairo_public void +cairo_glyph_free (cairo_glyph_t *glyphs); + +/** + * cairo_text_cluster_t: + * @num_bytes: the number of bytes of UTF-8 text covered by cluster + * @num_glyphs: the number of glyphs covered by cluster + * + * The #cairo_text_cluster_t structure holds information about a single + * text cluster. A text cluster is a minimal + * mapping of some glyphs corresponding to some UTF-8 text. + * + * For a cluster to be valid, both @num_bytes and @num_glyphs should + * be non-negative, and at least one should be non-zero. + * Note that clusters with zero glyphs are not as well supported as + * normal clusters. For example, PDF rendering applications typically + * ignore those clusters when PDF text is being selected. + * + * See cairo_show_text_glyphs() for how clusters are used in advanced + * text operations. + * + * Since: 1.8 + **/ +typedef struct { + int num_bytes; + int num_glyphs; +} cairo_text_cluster_t; + +cairo_public cairo_text_cluster_t * +cairo_text_cluster_allocate (int num_clusters); + +cairo_public void +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. (Since 1.8) + * + * Specifies properties of a text cluster mapping. + * + * Since: 1.8 + **/ +typedef enum _cairo_text_cluster_flags { + CAIRO_TEXT_CLUSTER_FLAG_BACKWARD = 0x00000001 +} cairo_text_cluster_flags_t; + +/** + * cairo_text_extents_t: + * @x_bearing: the horizontal distance from the origin to the + * leftmost part of the glyphs as drawn. Positive if the + * glyphs lie entirely to the right of the origin. + * @y_bearing: the vertical distance from the origin to the + * topmost part of the glyphs as drawn. Positive only if the + * glyphs lie completely below the origin; will usually be + * negative. + * @width: width of the glyphs as drawn + * @height: height of the glyphs as drawn + * @x_advance:distance to advance in the X direction + * after drawing these glyphs + * @y_advance: distance to advance in the Y direction + * after drawing these glyphs. Will typically be zero except + * for vertical text layout as found in East-Asian languages. + * + * The #cairo_text_extents_t structure stores the extents of a single + * glyph or a string of glyphs in user-space coordinates. Because text + * extents are in user-space coordinates, they are mostly, but not + * entirely, independent of the current transformation matrix. If you call + * cairo_scale(cr, 2.0, 2.0), text will + * be drawn twice as big, but the reported text extents will 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 x_bearing; + double y_bearing; + double width; + double height; + double x_advance; + double y_advance; +} cairo_text_extents_t; + +/** + * cairo_font_extents_t: + * @ascent: the distance that the font extends above 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 font should align with elements above it. + * @descent: the distance that the font extends below the baseline. + * This value is positive for typical fonts that include + * 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 font should + * align with elements below it. + * @height: the recommended vertical distance between baselines when + * setting consecutive lines of text with the font. This + * is greater than @ascent+@descent by a + * quantity known as the line spacing + * or external leading. When space + * 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 origin is advanced for any glyph in the font. + * @max_y_advance: the maximum distance in the Y direction that + * 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.) + * + * The #cairo_font_extents_t structure stores metric information for + * a font. Values are given in the current user-space coordinate + * system. + * + * Because font metrics are in user-space coordinates, they are + * mostly, but not entirely, independent of the current transformation + * matrix. If you call cairo_scale(cr, 2.0, 2.0), + * text will be drawn twice as big, but the reported text extents will + * 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; + double descent; + double height; + double max_x_advance; + double max_y_advance; +} cairo_font_extents_t; + +/** + * cairo_font_slant_t: + * @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, + CAIRO_FONT_SLANT_ITALIC, + CAIRO_FONT_SLANT_OBLIQUE +} cairo_font_slant_t; + +/** + * cairo_font_weight_t: + * @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, + CAIRO_FONT_WEIGHT_BOLD +} cairo_font_weight_t; + +/** + * cairo_subpixel_order_t: + * @CAIRO_SUBPIXEL_ORDER_DEFAULT: Use the default subpixel order for + * for the target device, since 1.0 + * @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally + * with red at the left, since 1.0 + * @CAIRO_SUBPIXEL_ORDER_BGR: Subpixel elements are arranged horizontally + * with blue at the left, since 1.0 + * @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically + * with red at the top, since 1.0 + * @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically + * 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, + CAIRO_SUBPIXEL_ORDER_RGB, + CAIRO_SUBPIXEL_ORDER_BGR, + CAIRO_SUBPIXEL_ORDER_VRGB, + CAIRO_SUBPIXEL_ORDER_VBGR +} cairo_subpixel_order_t; + +/** + * cairo_hint_style_t: + * @CAIRO_HINT_STYLE_DEFAULT: Use the default hint style for + * 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, since 1.0 + * @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength + * giving a compromise between fidelity to the original shapes + * 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 + * to improve the appearance of the result. Since hinting outlines + * involves distorting them, it also reduces the faithfulness + * to the original outline shapes. Not all of the outline hinting + * 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, + CAIRO_HINT_STYLE_NONE, + CAIRO_HINT_STYLE_SLIGHT, + CAIRO_HINT_STYLE_MEDIUM, + CAIRO_HINT_STYLE_FULL +} cairo_hint_style_t; + +/** + * cairo_hint_metrics_t: + * @CAIRO_HINT_METRICS_DEFAULT: Hint metrics in the default + * 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, + CAIRO_HINT_METRICS_OFF, + CAIRO_HINT_METRICS_ON +} cairo_hint_metrics_t; + +/** + * _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; + +/** + * cairo_font_options_t: + * + * An opaque structure holding all options that are used when + * rendering fonts. + * + * 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_antialias() and + * cairo_font_options_get_antialias(). + * + * New features may be added to a #cairo_font_options_t in the + * future. For this reason, cairo_font_options_copy(), + * cairo_font_options_equal(), cairo_font_options_merge(), and + * 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; + +cairo_public cairo_font_options_t * +cairo_font_options_create (void); + +cairo_public cairo_font_options_t * +cairo_font_options_copy (const cairo_font_options_t *original); + +cairo_public void +cairo_font_options_destroy (cairo_font_options_t *options); + +cairo_public cairo_status_t +cairo_font_options_status (cairo_font_options_t *options); + +cairo_public void +cairo_font_options_merge (cairo_font_options_t *options, + const cairo_font_options_t *other); +cairo_public cairo_bool_t +cairo_font_options_equal (const cairo_font_options_t *options, + const cairo_font_options_t *other); + +cairo_public unsigned long +cairo_font_options_hash (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_antialias (cairo_font_options_t *options, + cairo_antialias_t antialias); +cairo_public cairo_antialias_t +cairo_font_options_get_antialias (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_subpixel_order (cairo_font_options_t *options, + cairo_subpixel_order_t subpixel_order); +cairo_public cairo_subpixel_order_t +cairo_font_options_get_subpixel_order (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_hint_style (cairo_font_options_t *options, + cairo_hint_style_t hint_style); +cairo_public cairo_hint_style_t +cairo_font_options_get_hint_style (const cairo_font_options_t *options); + +cairo_public void +cairo_font_options_set_hint_metrics (cairo_font_options_t *options, + cairo_hint_metrics_t hint_metrics); +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_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. */ + +cairo_public void +cairo_select_font_face (cairo_t *cr, + const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + +cairo_public void +cairo_set_font_size (cairo_t *cr, double size); + +cairo_public void +cairo_set_font_matrix (cairo_t *cr, + const cairo_matrix_t *matrix); + +cairo_public void +cairo_get_font_matrix (cairo_t *cr, + cairo_matrix_t *matrix); + +cairo_public void +cairo_set_font_options (cairo_t *cr, + const cairo_font_options_t *options); + +cairo_public void +cairo_get_font_options (cairo_t *cr, + 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_public cairo_lcd_filter_t +cairo_font_options_get_lcd_filter (const cairo_font_options_t *options); + +cairo_public void +cairo_set_font_face (cairo_t *cr, cairo_font_face_t *font_face); + +cairo_public cairo_font_face_t * +cairo_get_font_face (cairo_t *cr); + +cairo_public void +cairo_set_scaled_font (cairo_t *cr, + const cairo_scaled_font_t *scaled_font); + +cairo_public cairo_scaled_font_t * +cairo_get_scaled_font (cairo_t *cr); + +cairo_public void +cairo_show_text (cairo_t *cr, const char *utf8); + +cairo_public void +cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); + +cairo_public void +cairo_show_text_glyphs (cairo_t *cr, + 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_public void +cairo_text_path (cairo_t *cr, const char *utf8); + +cairo_public void +cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs); + +cairo_public void +cairo_text_extents (cairo_t *cr, + const char *utf8, + cairo_text_extents_t *extents); + +cairo_public void +cairo_glyph_extents (cairo_t *cr, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); + +cairo_public void +cairo_font_extents (cairo_t *cr, + cairo_font_extents_t *extents); + +/* Generic identifier for a font style */ + +cairo_public cairo_font_face_t * +cairo_font_face_reference (cairo_font_face_t *font_face); + +cairo_public void +cairo_font_face_destroy (cairo_font_face_t *font_face); + +cairo_public unsigned int +cairo_font_face_get_reference_count (cairo_font_face_t *font_face); + +cairo_public cairo_status_t +cairo_font_face_status (cairo_font_face_t *font_face); + + +/** + * cairo_font_type_t: + * @CAIRO_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 + * face or scaled font. The font types are also known as "font + * backends" within cairo. + * + * The type of a font face is determined by the function used to + * create it, which will generally be of the form + * cairo_type_font_face_create(). + * The font face type can be queried with cairo_font_face_get_type() + * + * The various #cairo_font_face_t functions can be used with a font face + * of any type. + * + * The type of a scaled font is determined by the type of the font + * face passed to cairo_scaled_font_create(). The scaled font type can + * be queried with cairo_scaled_font_get_type() + * + * The various #cairo_scaled_font_t functions can be used with scaled + * fonts of any type, but some font backends also provide + * type-specific functions that must only be called with a scaled font + * of the appropriate type. These functions have names that begin with + * cairo_type_scaled_font() + * such as cairo_ft_scaled_font_lock_face(). + * + * The behavior of calling a type-specific function with a scaled font + * of the wrong type is undefined. + * + * New entries may be added in future versions. + * + * Since: 1.2 + **/ +typedef enum _cairo_font_type { + CAIRO_FONT_TYPE_TOY, + CAIRO_FONT_TYPE_FT, + CAIRO_FONT_TYPE_WIN32, + CAIRO_FONT_TYPE_QUARTZ, + CAIRO_FONT_TYPE_USER, + CAIRO_FONT_TYPE_DWRITE +} cairo_font_type_t; + +cairo_public cairo_font_type_t +cairo_font_face_get_type (cairo_font_face_t *font_face); + +cairo_public void * +cairo_font_face_get_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_font_face_set_user_data (cairo_font_face_t *font_face, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +/* Portable interface to general font features. */ + +cairo_public cairo_scaled_font_t * +cairo_scaled_font_create (cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options); + +cairo_public cairo_scaled_font_t * +cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font); + +cairo_public void +cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font); + +cairo_public unsigned int +cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font); + +cairo_public cairo_status_t +cairo_scaled_font_status (cairo_scaled_font_t *scaled_font); + +cairo_public cairo_font_type_t +cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font); + +cairo_public void * +cairo_scaled_font_get_user_data (cairo_scaled_font_t *scaled_font, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_scaled_font_set_user_data (cairo_scaled_font_t *scaled_font, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +cairo_public void +cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents); + +cairo_public void +cairo_scaled_font_text_extents (cairo_scaled_font_t *scaled_font, + const char *utf8, + cairo_text_extents_t *extents); + +cairo_public void +cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents); + +cairo_public cairo_status_t +cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t *scaled_font, + 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_public cairo_font_face_t * +cairo_scaled_font_get_font_face (cairo_scaled_font_t *scaled_font); + +cairo_public void +cairo_scaled_font_get_font_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *font_matrix); + +cairo_public void +cairo_scaled_font_get_ctm (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *ctm); + +cairo_public void +cairo_scaled_font_get_scale_matrix (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *scale_matrix); + +cairo_public void +cairo_scaled_font_get_font_options (cairo_scaled_font_t *scaled_font, + cairo_font_options_t *options); + + +/* Toy fonts */ + +cairo_public cairo_font_face_t * +cairo_toy_font_face_create (const char *family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + +cairo_public const char * +cairo_toy_font_face_get_family (cairo_font_face_t *font_face); + +cairo_public cairo_font_slant_t +cairo_toy_font_face_get_slant (cairo_font_face_t *font_face); + +cairo_public cairo_font_weight_t +cairo_toy_font_face_get_weight (cairo_font_face_t *font_face); + + +/* User fonts */ + +cairo_public cairo_font_face_t * +cairo_user_font_face_create (void); + +/* User-font method signatures */ + +/** + * cairo_user_scaled_font_init_func_t: + * @scaled_font: the scaled-font being created + * @cr: a cairo context, in font space + * @extents: font extents to fill in, in font space + * + * #cairo_user_scaled_font_init_func_t is the type of function which is + * called when a scaled-font needs to be created for a user font-face. + * + * The cairo context @cr is not used by the caller, but is prepared in font + * space, similar to what the cairo contexts passed to the render_glyph + * method will look like. The callback can use this context for extents + * computation for example. After the callback is called, @cr is checked + * for any error status. + * + * The @extents argument is where the user font sets the font extents for + * @scaled_font. It is in font space, which means that for most cases its + * ascent and descent members should add to 1.0. @extents is preset to + * hold a value of 1.0 for ascent, height, and max_x_advance, and 0.0 for + * descent and max_y_advance members. + * + * The callback is optional. If not set, default font extents as described + * in the previous paragraph will be used. + * + * Note that @scaled_font is not fully initialized at this + * point and trying to use it for text operations in the callback will result + * in deadlock. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, or an error status on error. + * + * Since: 1.8 + **/ +typedef cairo_status_t (*cairo_user_scaled_font_init_func_t) (cairo_scaled_font_t *scaled_font, + cairo_t *cr, + cairo_font_extents_t *extents); + +/** + * cairo_user_scaled_font_render_glyph_func_t: + * @scaled_font: user scaled-font + * @glyph: glyph code to render + * @cr: cairo context to draw to, in font space + * @extents: glyph extents to fill in, in font space + * + * #cairo_user_scaled_font_render_glyph_func_t is the type of function which + * is called when a user scaled-font needs to render a glyph. + * + * The callback is mandatory, and expected to draw the glyph with code @glyph to + * the cairo context @cr. @cr is prepared such that the glyph drawing is done in + * font space. That is, the matrix set on @cr is the scale matrix of @scaled_font, + * The @extents argument is where the user font sets the font extents for + * @scaled_font. However, if user prefers to draw in user space, they can + * achieve that by changing the matrix on @cr. All cairo rendering operations + * to @cr are permitted, however, the result is undefined if any source other + * than the default source on @cr is used. That means, glyph bitmaps should + * be rendered using cairo_mask() instead of cairo_paint(). + * + * Other non-default settings on @cr include a font size of 1.0 (given that + * it is set up to be in font space), and font options corresponding to + * @scaled_font. + * + * The @extents argument is preset to have x_bearing, + * width, and y_advance of zero, + * y_bearing set to -font_extents.ascent, + * height to font_extents.ascent+font_extents.descent, + * and x_advance to font_extents.max_x_advance. + * The only field user needs to set in majority of cases is + * x_advance. + * If the width field is zero upon the callback returning + * (which is its preset value), the glyph extents are automatically computed + * based on the drawings done to @cr. This is in most cases exactly what the + * desired behavior is. However, if for any reason the callback sets the + * extents, it must be ink extents, and include the extents of all drawing + * done to @cr in the callback. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, or + * %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * + * Since: 1.8 + **/ +typedef cairo_status_t (*cairo_user_scaled_font_render_glyph_func_t) (cairo_scaled_font_t *scaled_font, + unsigned long glyph, + cairo_t *cr, + cairo_text_extents_t *extents); + +/** + * cairo_user_scaled_font_text_to_glyphs_func_t: + * @scaled_font: the scaled-font being created + * @utf8: a string of text encoded in UTF-8 + * @utf8_len: length of @utf8 in bytes + * @glyphs: pointer to array of glyphs to fill, in font space + * @num_glyphs: pointer to number of glyphs + * @clusters: pointer to array of cluster mapping information to fill, or %NULL + * @num_clusters: pointer to number of clusters + * @cluster_flags: pointer to location to store cluster flags corresponding to the + * output @clusters + * + * #cairo_user_scaled_font_text_to_glyphs_func_t is the type of function which + * is called to convert input text to an array of glyphs. This is used by the + * cairo_show_text() operation. + * + * Using this callback the user-font has full control on glyphs and their + * positions. That means, it allows for features like ligatures and kerning, + * as well as complex shaping required for scripts like + * Arabic and Indic. + * + * The @num_glyphs argument is preset to the number of glyph entries available + * in the @glyphs buffer. If the @glyphs buffer is %NULL, the value of + * @num_glyphs will be zero. If the provided glyph array is too short for + * the conversion (or for convenience), a new glyph array may be allocated + * 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(). 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. + * + * If @clusters is not %NULL, @num_clusters and @cluster_flags are also + * non-%NULL, and cluster mapping should be computed. The semantics of how + * cluster array allocation works is similar to the glyph array. That is, + * if @clusters initially points to a non-%NULL value, that array may be used + * 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. 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(). + * + * The callback is optional. If @num_glyphs is negative upon + * the callback returning or if the return value + * is %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, the unicode_to_glyph callback + * is tried. See #cairo_user_scaled_font_unicode_to_glyph_func_t. + * + * Note: While cairo does not impose any limitation on glyph indices, + * some applications may assume that a glyph index fits in a 16-bit + * unsigned integer. As such, it is advised that user-fonts keep their + * glyphs in the 0 to 65535 range. Furthermore, some applications may + * assume that glyph 0 is a special glyph-not-found glyph. User-fonts + * are advised to use glyph 0 for such purposes and do not use that + * glyph value for other purposes. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, + * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * + * Since: 1.8 + **/ +typedef cairo_status_t (*cairo_user_scaled_font_text_to_glyphs_func_t) (cairo_scaled_font_t *scaled_font, + 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_user_scaled_font_unicode_to_glyph_func_t: + * @scaled_font: the scaled-font being created + * @unicode: input unicode character code-point + * @glyph_index: output glyph index + * + * #cairo_user_scaled_font_unicode_to_glyph_func_t is the type of function which + * is called to convert an input Unicode character to a single glyph. + * This is used by the cairo_show_text() operation. + * + * This callback is used to provide the same functionality as the + * text_to_glyphs callback does (see #cairo_user_scaled_font_text_to_glyphs_func_t) + * but has much less control on the output, + * in exchange for increased ease of use. The inherent assumption to using + * this callback is that each character maps to one glyph, and that the + * mapping is context independent. It also assumes that glyphs are positioned + * according to their advance width. These mean no ligatures, kerning, or + * complex scripts can be implemented using this callback. + * + * The callback is optional, and only used if text_to_glyphs callback is not + * set or fails to return glyphs. If this callback is not set or if it returns + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED, an identity mapping from Unicode + * code-points to glyph indices is assumed. + * + * Note: While cairo does not impose any limitation on glyph indices, + * some applications may assume that a glyph index fits in a 16-bit + * unsigned integer. As such, it is advised that user-fonts keep their + * glyphs in the 0 to 65535 range. Furthermore, some applications may + * assume that glyph 0 is a special glyph-not-found glyph. User-fonts + * are advised to use glyph 0 for such purposes and do not use that + * glyph value for other purposes. + * + * Returns: %CAIRO_STATUS_SUCCESS upon success, + * %CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED if fallback options should be tried, + * or %CAIRO_STATUS_USER_FONT_ERROR or any other error status on error. + * + * Since: 1.8 + **/ +typedef cairo_status_t (*cairo_user_scaled_font_unicode_to_glyph_func_t) (cairo_scaled_font_t *scaled_font, + unsigned long unicode, + unsigned long *glyph_index); + +/* User-font method setters */ + +cairo_public void +cairo_user_font_face_set_init_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_init_func_t init_func); + +cairo_public void +cairo_user_font_face_set_render_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_render_glyph_func_t render_glyph_func); + +cairo_public void +cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_text_to_glyphs_func_t text_to_glyphs_func); + +cairo_public void +cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t *font_face, + cairo_user_scaled_font_unicode_to_glyph_func_t unicode_to_glyph_func); + +/* User-font method getters */ + +cairo_public cairo_user_scaled_font_init_func_t +cairo_user_font_face_get_init_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_render_glyph_func_t +cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_text_to_glyphs_func_t +cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face); + +cairo_public cairo_user_scaled_font_unicode_to_glyph_func_t +cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face); + + +/* Query functions */ + +cairo_public cairo_operator_t +cairo_get_operator (cairo_t *cr); + +cairo_public cairo_pattern_t * +cairo_get_source (cairo_t *cr); + +cairo_public double +cairo_get_tolerance (cairo_t *cr); + +cairo_public cairo_antialias_t +cairo_get_antialias (cairo_t *cr); + +cairo_public cairo_bool_t +cairo_has_current_point (cairo_t *cr); + +cairo_public void +cairo_get_current_point (cairo_t *cr, double *x, double *y); + +cairo_public cairo_fill_rule_t +cairo_get_fill_rule (cairo_t *cr); + +cairo_public double +cairo_get_line_width (cairo_t *cr); + +cairo_public cairo_line_cap_t +cairo_get_line_cap (cairo_t *cr); + +cairo_public cairo_line_join_t +cairo_get_line_join (cairo_t *cr); + +cairo_public double +cairo_get_miter_limit (cairo_t *cr); + +cairo_public int +cairo_get_dash_count (cairo_t *cr); + +cairo_public void +cairo_get_dash (cairo_t *cr, double *dashes, double *offset); + +cairo_public void +cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix); + +cairo_public cairo_surface_t * +cairo_get_target (cairo_t *cr); + +cairo_public cairo_surface_t * +cairo_get_group_target (cairo_t *cr); + +/** + * cairo_path_data_type_t: + * @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, + CAIRO_PATH_LINE_TO, + CAIRO_PATH_CURVE_TO, + CAIRO_PATH_CLOSE_PATH +} cairo_path_data_type_t; + +/** + * cairo_path_data_t: + * + * #cairo_path_data_t is used to represent the path data inside a + * #cairo_path_t. + * + * The data structure is designed to try to balance the demands of + * efficiency and ease-of-use. A path is represented as an array of + * #cairo_path_data_t, which is a union of headers and points. + * + * Each portion of the path is represented by one or more elements in + * the array, (one header followed by 0 or more points). The length + * value of the header is the number of array elements for the current + * portion including the header, (ie. length == 1 + # of points), and + * where the number of points for each element type is as follows: + * + * + * %CAIRO_PATH_MOVE_TO: 1 point + * %CAIRO_PATH_LINE_TO: 1 point + * %CAIRO_PATH_CURVE_TO: 3 points + * %CAIRO_PATH_CLOSE_PATH: 0 points + * + * + * The semantics and ordering of the coordinate values are consistent + * with cairo_move_to(), cairo_line_to(), cairo_curve_to(), and + * cairo_close_path(). + * + * Here is sample code for iterating through a #cairo_path_t: + * + * + * int i; + * cairo_path_t *path; + * cairo_path_data_t *data; + *   + * path = cairo_copy_path (cr); + *   + * for (i=0; i < path->num_data; i += path->data[i].header.length) { + * data = &path->data[i]; + * switch (data->header.type) { + * case CAIRO_PATH_MOVE_TO: + * do_move_to_things (data[1].point.x, data[1].point.y); + * break; + * case CAIRO_PATH_LINE_TO: + * do_line_to_things (data[1].point.x, data[1].point.y); + * break; + * case CAIRO_PATH_CURVE_TO: + * do_curve_to_things (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: + * do_close_path_things (); + * break; + * } + * } + * cairo_path_destroy (path); + * + * + * As of cairo 1.4, cairo does not mind if there are more elements in + * a portion of the path than needed. Such elements can be used by + * users of the cairo API to hold extra values in the path data + * structure. For this reason, it is recommended that applications + * 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 { + struct { + cairo_path_data_type_t type; + int length; + } header; + struct { + double x, y; + } point; +}; + +/** + * cairo_path_t: + * @status: the current error status + * @data: the elements in the path + * @num_data: the number of elements in the data array + * + * A data structure for holding a path. This data structure serves as + * the return value for cairo_copy_path() and + * cairo_copy_path_flat() as well the input value for + * cairo_append_path(). + * + * See #cairo_path_data_t for hints on how to iterate over the + * actual data within the path. + * + * The num_data member gives the number of elements in the data + * 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; + cairo_path_data_t *data; + int num_data; +} cairo_path_t; + +cairo_public cairo_path_t * +cairo_copy_path (cairo_t *cr); + +cairo_public cairo_path_t * +cairo_copy_path_flat (cairo_t *cr); + +cairo_public void +cairo_append_path (cairo_t *cr, + const cairo_path_t *path); + +cairo_public void +cairo_path_destroy (cairo_path_t *path); + +/* Error status queries */ + +cairo_public cairo_status_t +cairo_status (cairo_t *cr); + +cairo_public const char * +cairo_status_to_string (cairo_status_t status); + +/* Backend device manipulation */ + +cairo_public cairo_device_t * +cairo_device_reference (cairo_device_t *device); + +/** + * cairo_device_type_t: + * @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 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_cap_xrender_version(). + * + * 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. + * + * Since: 1.10 + **/ +typedef enum _cairo_device_type { + CAIRO_DEVICE_TYPE_DRM, + CAIRO_DEVICE_TYPE_GL, + CAIRO_DEVICE_TYPE_SCRIPT, + CAIRO_DEVICE_TYPE_XCB, + CAIRO_DEVICE_TYPE_XLIB, + 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 +cairo_device_get_type (cairo_device_t *device); + +cairo_public cairo_status_t +cairo_device_status (cairo_device_t *device); + +cairo_public cairo_status_t +cairo_device_acquire (cairo_device_t *device); + +cairo_public void +cairo_device_release (cairo_device_t *device); + +cairo_public void +cairo_device_flush (cairo_device_t *device); + +cairo_public void +cairo_device_finish (cairo_device_t *device); + +cairo_public void +cairo_device_destroy (cairo_device_t *device); + +cairo_public unsigned int +cairo_device_get_reference_count (cairo_device_t *device); + +cairo_public void * +cairo_device_get_user_data (cairo_device_t *device, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_device_set_user_data (cairo_device_t *device, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + + +/* Surface manipulation */ + +cairo_public cairo_surface_t * +cairo_surface_create_similar (cairo_surface_t *other, + cairo_content_t content, + 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, + double y, + 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); + +cairo_public void +cairo_surface_finish (cairo_surface_t *surface); + +cairo_public void +cairo_surface_destroy (cairo_surface_t *surface); + +cairo_public cairo_device_t * +cairo_surface_get_device (cairo_surface_t *surface); + +cairo_public unsigned int +cairo_surface_get_reference_count (cairo_surface_t *surface); + +cairo_public cairo_status_t +cairo_surface_status (cairo_surface_t *surface); + +/** + * cairo_surface_type_t: + * @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 + * @CAIRO_SURFACE_TYPE_VG: The surface is a OpenVG surface, since 1.10 + * @CAIRO_SURFACE_TYPE_GL: The surface is of type OpenGL, since 1.10 + * @CAIRO_SURFACE_TYPE_DRM: The surface is of type Direct Render Manager, since 1.10 + * @CAIRO_SURFACE_TYPE_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_SUBSURFACE: The surface is a subsurface created with + * cairo_surface_create_for_rectangle(), since 1.10 + * @CAIRO_SURFACE_TYPE_COGL: This surface is of type Cogl, since 1.12 + * + * #cairo_surface_type_t is used to describe the type of a given + * surface. The surface types are also known as "backends" or "surface + * backends" within cairo. + * + * The type of a surface is determined by the function used to create + * it, which will generally be of the form + * cairo_type_surface_create(), + * (though see cairo_surface_create_similar() as well). + * + * The surface type can be queried with cairo_surface_get_type() + * + * The various #cairo_surface_t functions can be used with surfaces of + * any type, but some backends also provide type-specific functions + * that must only be called with a surface of the appropriate + * type. These functions have names that begin with + * cairo_type_surface such as cairo_image_surface_get_width(). + * + * The behavior of calling a type-specific function with a surface of + * the wrong type is undefined. + * + * New entries may be added in future versions. + * + * Since: 1.2 + **/ +typedef enum _cairo_surface_type { + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_SURFACE_TYPE_PDF, + CAIRO_SURFACE_TYPE_PS, + CAIRO_SURFACE_TYPE_XLIB, + CAIRO_SURFACE_TYPE_XCB, + CAIRO_SURFACE_TYPE_GLITZ, + CAIRO_SURFACE_TYPE_QUARTZ, + CAIRO_SURFACE_TYPE_WIN32, + CAIRO_SURFACE_TYPE_BEOS, + CAIRO_SURFACE_TYPE_DIRECTFB, + CAIRO_SURFACE_TYPE_SVG, + CAIRO_SURFACE_TYPE_OS2, + CAIRO_SURFACE_TYPE_WIN32_PRINTING, + CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, + CAIRO_SURFACE_TYPE_SCRIPT, + CAIRO_SURFACE_TYPE_QT, + CAIRO_SURFACE_TYPE_RECORDING, + CAIRO_SURFACE_TYPE_VG, + CAIRO_SURFACE_TYPE_GL, + CAIRO_SURFACE_TYPE_DRM, + CAIRO_SURFACE_TYPE_TEE, + CAIRO_SURFACE_TYPE_XML, + CAIRO_SURFACE_TYPE_SKIA, + CAIRO_SURFACE_TYPE_SUBSURFACE, + CAIRO_SURFACE_TYPE_COGL +} cairo_surface_type_t; + +cairo_public cairo_surface_type_t +cairo_surface_get_type (cairo_surface_t *surface); + +cairo_public cairo_content_t +cairo_surface_get_content (cairo_surface_t *surface); + +#if CAIRO_HAS_PNG_FUNCTIONS + +cairo_public cairo_status_t +cairo_surface_write_to_png (cairo_surface_t *surface, + const char *filename); + +cairo_public cairo_status_t +cairo_surface_write_to_png_stream (cairo_surface_t *surface, + cairo_write_func_t write_func, + void *closure); + +#endif + +cairo_public void * +cairo_surface_get_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_surface_set_user_data (cairo_surface_t *surface, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +#define CAIRO_MIME_TYPE_JPEG "image/jpeg" +#define CAIRO_MIME_TYPE_PNG "image/png" +#define CAIRO_MIME_TYPE_JP2 "image/jp2" +#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, + const char *mime_type, + const unsigned char **data, + unsigned long *length); + +cairo_public cairo_status_t +cairo_surface_set_mime_data (cairo_surface_t *surface, + const char *mime_type, + const unsigned char *data, + unsigned long length, + 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); + +cairo_public void +cairo_surface_flush (cairo_surface_t *surface); + +cairo_public void +cairo_surface_mark_dirty (cairo_surface_t *surface); + +cairo_public void +cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, + int x, + int y, + 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, + double y_offset); + +cairo_public void +cairo_surface_get_device_offset (cairo_surface_t *surface, + double *x_offset, + double *y_offset); + +cairo_public void +cairo_surface_set_fallback_resolution (cairo_surface_t *surface, + double x_pixels_per_inch, + double y_pixels_per_inch); + +cairo_public void +cairo_surface_get_fallback_resolution (cairo_surface_t *surface, + double *x_pixels_per_inch, + double *y_pixels_per_inch); + +cairo_public void +cairo_surface_copy_page (cairo_surface_t *surface); + +cairo_public void +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_public cairo_surface_t * +cairo_image_surface_create (cairo_format_t format, + int width, + int height); + +cairo_public int +cairo_format_stride_for_width (cairo_format_t format, + int width); + +cairo_public cairo_surface_t * +cairo_image_surface_create_for_data (unsigned char *data, + cairo_format_t format, + int width, + int height, + int stride); + +cairo_public unsigned char * +cairo_image_surface_get_data (cairo_surface_t *surface); + +cairo_public cairo_format_t +cairo_image_surface_get_format (cairo_surface_t *surface); + +cairo_public int +cairo_image_surface_get_width (cairo_surface_t *surface); + +cairo_public int +cairo_image_surface_get_height (cairo_surface_t *surface); + +cairo_public int +cairo_image_surface_get_stride (cairo_surface_t *surface); + +#if CAIRO_HAS_PNG_FUNCTIONS + +cairo_public cairo_surface_t * +cairo_image_surface_create_from_png (const char *filename); + +cairo_public cairo_surface_t * +cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func, + void *closure); + +#endif + +/* Recording-surface functions */ + +cairo_public cairo_surface_t * +cairo_recording_surface_create (cairo_content_t content, + const cairo_rectangle_t *extents); + +cairo_public void +cairo_recording_surface_ink_extents (cairo_surface_t *surface, + double *x0, + double *y0, + double *width, + double *height); + +cairo_public cairo_bool_t +cairo_recording_surface_get_extents (cairo_surface_t *surface, + cairo_rectangle_t *extents); + +/* 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 */ + +cairo_public cairo_pattern_t * +cairo_pattern_create_rgb (double red, double green, double blue); + +cairo_public cairo_pattern_t * +cairo_pattern_create_rgba (double red, double green, double blue, + double alpha); + +cairo_public cairo_pattern_t * +cairo_pattern_create_for_surface (cairo_surface_t *surface); + +cairo_public cairo_pattern_t * +cairo_pattern_create_linear (double x0, double y0, + double x1, double y1); + +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); + +cairo_public void +cairo_pattern_destroy (cairo_pattern_t *pattern); + +cairo_public unsigned int +cairo_pattern_get_reference_count (cairo_pattern_t *pattern); + +cairo_public cairo_status_t +cairo_pattern_status (cairo_pattern_t *pattern); + +cairo_public void * +cairo_pattern_get_user_data (cairo_pattern_t *pattern, + const cairo_user_data_key_t *key); + +cairo_public cairo_status_t +cairo_pattern_set_user_data (cairo_pattern_t *pattern, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +/** + * cairo_pattern_type_t: + * @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform) + * 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. + * + * The type of a pattern is determined by the function used to create + * it. The cairo_pattern_create_rgb() and cairo_pattern_create_rgba() + * functions create SOLID patterns. The remaining + * cairo_pattern_create functions map to pattern types in obvious + * ways. + * + * The pattern type can be queried with cairo_pattern_get_type() + * + * Most #cairo_pattern_t functions can be called with a pattern of any + * type, (though trying to change the extend or filter for a solid + * pattern will have no effect). A notable exception is + * cairo_pattern_add_color_stop_rgb() and + * cairo_pattern_add_color_stop_rgba() which must only be called with + * gradient patterns (either LINEAR or RADIAL). Otherwise the pattern + * will be shutdown and put into an error state. + * + * New entries may be added in future versions. + * + * Since: 1.2 + **/ +typedef enum _cairo_pattern_type { + CAIRO_PATTERN_TYPE_SOLID, + CAIRO_PATTERN_TYPE_SURFACE, + CAIRO_PATTERN_TYPE_LINEAR, + CAIRO_PATTERN_TYPE_RADIAL, + CAIRO_PATTERN_TYPE_MESH, + CAIRO_PATTERN_TYPE_RASTER_SOURCE +} cairo_pattern_type_t; + +cairo_public cairo_pattern_type_t +cairo_pattern_get_type (cairo_pattern_t *pattern); + +cairo_public void +cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, + double offset, + double red, double green, double blue); + +cairo_public void +cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, + double offset, + 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); + +cairo_public void +cairo_pattern_get_matrix (cairo_pattern_t *pattern, + cairo_matrix_t *matrix); + +/** + * cairo_extend_t: + * @CAIRO_EXTEND_NONE: pixels outside of the source pattern + * 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 (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) + * + * #cairo_extend_t is used to describe how pattern color/alpha will be + * determined for areas "outside" the pattern's natural area, (for + * 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, + CAIRO_EXTEND_REPEAT, + CAIRO_EXTEND_REFLECT, + CAIRO_EXTEND_PAD +} cairo_extend_t; + +cairo_public void +cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend); + +cairo_public cairo_extend_t +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 (Since 1.0) + * @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality + * similar to %CAIRO_FILTER_BILINEAR (Since 1.0) + * @CAIRO_FILTER_BEST: The highest-quality available, performance may + * 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. (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_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, + CAIRO_FILTER_BEST, + CAIRO_FILTER_NEAREST, + CAIRO_FILTER_BILINEAR, + CAIRO_FILTER_GAUSSIAN +} cairo_filter_t; + +cairo_public void +cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter); + +cairo_public cairo_filter_t +cairo_pattern_get_filter (cairo_pattern_t *pattern); + +cairo_public cairo_status_t +cairo_pattern_get_rgba (cairo_pattern_t *pattern, + double *red, double *green, + double *blue, double *alpha); + +cairo_public cairo_status_t +cairo_pattern_get_surface (cairo_pattern_t *pattern, + cairo_surface_t **surface); + + +cairo_public cairo_status_t +cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, + int index, double *offset, + double *red, double *green, + double *blue, double *alpha); + +cairo_public cairo_status_t +cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, + int *count); + +cairo_public cairo_status_t +cairo_pattern_get_linear_points (cairo_pattern_t *pattern, + double *x0, double *y0, + double *x1, double *y1); + +cairo_public cairo_status_t +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 +cairo_matrix_init (cairo_matrix_t *matrix, + double xx, double yx, + double xy, double yy, + double x0, double y0); + +cairo_public void +cairo_matrix_init_identity (cairo_matrix_t *matrix); + +cairo_public void +cairo_matrix_init_translate (cairo_matrix_t *matrix, + double tx, double ty); + +cairo_public void +cairo_matrix_init_scale (cairo_matrix_t *matrix, + double sx, double sy); + +cairo_public void +cairo_matrix_init_rotate (cairo_matrix_t *matrix, + double radians); + +cairo_public void +cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty); + +cairo_public void +cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy); + +cairo_public void +cairo_matrix_rotate (cairo_matrix_t *matrix, double radians); + +cairo_public cairo_status_t +cairo_matrix_invert (cairo_matrix_t *matrix); + +cairo_public void +cairo_matrix_multiply (cairo_matrix_t *result, + const cairo_matrix_t *a, + const cairo_matrix_t *b); + +cairo_public void +cairo_matrix_transform_distance (const cairo_matrix_t *matrix, + double *dx, double *dy); + +cairo_public void +cairo_matrix_transform_point (const cairo_matrix_t *matrix, + double *x, double *y); + +/* Region functions */ + +/** + * cairo_region_t: + * + * A #cairo_region_t represents a set of integer-aligned rectangles. + * + * It allows set-theoretical operations like cairo_region_union() and + * cairo_region_intersect() to be performed on them. + * + * Memory management of #cairo_region_t is done with + * cairo_region_reference() and cairo_region_destroy(). + * + * Since: 1.10 + **/ +typedef struct _cairo_region cairo_region_t; + +/** + * 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) + * + * Used as the return value for cairo_region_contains_rectangle(). + * + * Since: 1.10 + **/ +typedef enum _cairo_region_overlap { + CAIRO_REGION_OVERLAP_IN, /* completely inside region */ + CAIRO_REGION_OVERLAP_OUT, /* completely outside region */ + CAIRO_REGION_OVERLAP_PART /* partly inside region */ +} cairo_region_overlap_t; + +cairo_public cairo_region_t * +cairo_region_create (void); + +cairo_public cairo_region_t * +cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_region_t * +cairo_region_create_rectangles (const cairo_rectangle_int_t *rects, + int count); + +cairo_public cairo_region_t * +cairo_region_copy (const cairo_region_t *original); + +cairo_public cairo_region_t * +cairo_region_reference (cairo_region_t *region); + +cairo_public void +cairo_region_destroy (cairo_region_t *region); + +cairo_public cairo_bool_t +cairo_region_equal (const cairo_region_t *a, const cairo_region_t *b); + +cairo_public cairo_status_t +cairo_region_status (const cairo_region_t *region); + +cairo_public void +cairo_region_get_extents (const cairo_region_t *region, + cairo_rectangle_int_t *extents); + +cairo_public int +cairo_region_num_rectangles (const cairo_region_t *region); + +cairo_public void +cairo_region_get_rectangle (const cairo_region_t *region, + int nth, + cairo_rectangle_int_t *rectangle); + +cairo_public cairo_bool_t +cairo_region_is_empty (const cairo_region_t *region); + +cairo_public cairo_region_overlap_t +cairo_region_contains_rectangle (const cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_bool_t +cairo_region_contains_point (const cairo_region_t *region, int x, int y); + +cairo_public void +cairo_region_translate (cairo_region_t *region, int dx, int dy); + +cairo_public cairo_status_t +cairo_region_subtract (cairo_region_t *dst, const cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_subtract_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_intersect (cairo_region_t *dst, const cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_intersect_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_union (cairo_region_t *dst, const cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_union_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_xor (cairo_region_t *dst, const cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_xor_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +/* Functions to be used while debugging (not intended for use in production code) */ +cairo_public void +cairo_debug_reset_static_data (void); + + +CAIRO_END_DECLS + +#endif /* CAIRO_H */ diff --git a/gfx/cairo/cairo/src/cairo.pc.in b/gfx/cairo/cairo/src/cairo.pc.in new file mode 100644 index 0000000000..b361edf18f --- /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 new file mode 100644 index 0000000000..576dba1331 --- /dev/null +++ b/gfx/cairo/cairo/src/cairoint.h @@ -0,0 +1,2150 @@ +/* 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 + */ + +/* + * These definitions are solely for use by the implementation of cairo + * and constitute no kind of standard. If you need any of these + * functions, please drop me a note. Either the library needs new + * functionality, or there's a way to do what you need using the + * existing published interfaces. cworth@cworth.org + */ + +#ifndef _CAIROINT_H_ +#define _CAIROINT_H_ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _MSC_VER +#define cairo_public __declspec(dllexport) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#define _USE_MATH_DEFINES +#endif +#include +#include +#include + +#include "cairo.h" +#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 || \ + CAIRO_HAS_SVG_SURFACE || \ + CAIRO_HAS_WIN32_SURFACE +#define CAIRO_HAS_FONT_SUBSET 1 +#endif + +#if CAIRO_HAS_PS_SURFACE || \ + CAIRO_HAS_PDF_SURFACE || \ + CAIRO_HAS_FONT_SUBSET +#define CAIRO_HAS_PDF_OPERATORS 1 +#endif + +CAIRO_BEGIN_DECLS + +#if _WIN32 && !_WIN32_WCE /* Permissions on WinCE? No worries! */ +cairo_private FILE * +_cairo_win32_tmpfile (void); +#define tmpfile() _cairo_win32_tmpfile() +#endif + +#undef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#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 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif + +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.707106781186547524400844362104849039 +#endif + +#undef ARRAY_LENGTH +#define ARRAY_LENGTH(__array) ((int) (sizeof (__array) / sizeof (__array[0]))) + +#undef STRINGIFY +#undef STRINGIFY_ARG +#define STRINGIFY(macro_or_string) STRINGIFY_ARG (macro_or_string) +#define STRINGIFY_ARG(contents) #contents + +#if defined (__GNUC__) +#define cairo_container_of(ptr, type, member) ({ \ + const __typeof__ (((type *) 0)->member) *mptr__ = (ptr); \ + (type *) ((char *) mptr__ - offsetof (type, member)); \ +}) +#else +#define cairo_container_of(ptr, type, member) \ + ((type *)((char *) (ptr) - (char *) &((type *)0)->member)) +#endif + + +#define ASSERT_NOT_REACHED \ +do { \ + assert (!"reached"); \ +} while (0) +#define COMPILE_TIME_ASSERT1(condition, line) \ + typedef int compile_time_assertion_at_line_##line##_failed [(condition)?1:-1] +#define COMPILE_TIME_ASSERT0(condition, line) COMPILE_TIME_ASSERT1(condition, line) +#define COMPILE_TIME_ASSERT(condition) COMPILE_TIME_ASSERT0(condition, __LINE__) + +#define CAIRO_ALPHA_IS_CLEAR(alpha) ((alpha) <= ((double)0x00ff / (double)0xffff)) +#define CAIRO_ALPHA_SHORT_IS_CLEAR(alpha) ((alpha) <= 0x00ff) + +#define CAIRO_ALPHA_IS_OPAQUE(alpha) ((alpha) >= ((double)0xff00 / (double)0xffff)) +#define CAIRO_ALPHA_SHORT_IS_OPAQUE(alpha) ((alpha) >= 0xff00) +#define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0) + +#define CAIRO_COLOR_IS_CLEAR(color) CAIRO_ALPHA_SHORT_IS_CLEAR ((color)->alpha_short) +#define CAIRO_COLOR_IS_OPAQUE(color) CAIRO_ALPHA_SHORT_IS_OPAQUE ((color)->alpha_short) + +/* Reverse the bits in a byte with 7 operations (no 64-bit): + * Devised by Sean Anderson, July 13, 2001. + * Source: http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits + */ +#define CAIRO_BITSWAP8(c) ((((c) * 0x0802LU & 0x22110LU) | ((c) * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) + +/* Return the number of 1 bits in mask. + * + * GCC 3.4 supports a "population count" builtin, which on many targets is + * implemented with a single instruction. There is a fallback definition + * in libgcc in case a target does not have one, which should be just as + * good as the open-coded solution below, (which is "HACKMEM 169"). + */ +static inline int cairo_const +_cairo_popcount (uint32_t mask) +{ +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) + return __builtin_popcount (mask); +#else +#ifdef __cplusplus + int y; +#else + register int y; +#endif + + y = (mask >> 1) &033333333333; + y = mask - y - ((y >>1) & 033333333333); + return (((y + (y >> 3)) & 030707070707) % 077); +#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 +#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) CAIRO_BITSWAP8(c) +#endif + +#ifdef WORDS_BIGENDIAN + +#define cpu_to_be16(v) (v) +#define be16_to_cpu(v) (v) +#define cpu_to_be32(v) (v) +#define be32_to_cpu(v) (v) + +#else + +static inline uint16_t cairo_const +cpu_to_be16(uint16_t v) +{ + return (v << 8) | (v >> 8); +} + +static inline uint16_t cairo_const +be16_to_cpu(uint16_t v) +{ + return cpu_to_be16 (v); +} + +static inline uint32_t cairo_const +cpu_to_be32(uint32_t v) +{ + return (v >> 24) | ((v >> 8) & 0xff00) | ((v << 8) & 0xff0000) | (v << 24); +} + +static inline uint32_t cairo_const +be32_to_cpu(uint32_t v) +{ + return cpu_to_be32 (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. + */ + +static inline int cairo_const +_cairo_isspace (int c) +{ + return (c == 0x20 || (c >= 0x09 && c <= 0x0d)); +} + +static inline int cairo_const +_cairo_isdigit (int c) +{ + return (c >= '0' && c <= '9'); +} + +static inline int cairo_const +_cairo_isxdigit (int c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +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, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private void +_cairo_box_to_doubles (const cairo_box_t *box, + double *x1, double *y1, + double *x2, double *y2); + +cairo_private void +_cairo_box_from_rectangle (cairo_box_t *box, + const cairo_rectangle_int_t *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 = _cairo_unbounded_rectangle; +} + +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 (const cairo_box_t *box, + cairo_line_t *line) cairo_pure; + +cairo_private 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_pure; + +typedef struct { + const cairo_user_data_key_t *key; + void *user_data; + cairo_destroy_func_t destroy; +} cairo_user_data_slot_t; + +cairo_private void +_cairo_user_data_array_init (cairo_user_data_array_t *array); + +cairo_private void +_cairo_user_data_array_fini (cairo_user_data_array_t *array); + +cairo_private void * +_cairo_user_data_array_get_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key); + +cairo_private cairo_status_t +_cairo_user_data_array_set_data (cairo_user_data_array_t *array, + const cairo_user_data_key_t *key, + void *user_data, + cairo_destroy_func_t destroy); + +cairo_private cairo_status_t +_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, + void (*func) (const void *key, + void *elt, + void *closure), + void *closure); + +#define _CAIRO_HASH_INIT_VALUE 5381 + +cairo_private unsigned long +_cairo_hash_string (const char *c); + +cairo_private unsigned long +_cairo_hash_bytes (unsigned long hash, + const void *bytes, + unsigned int length); + +/* 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" + +struct _cairo_font_face { + /* hash_entry must be first */ + cairo_hash_entry_t hash_entry; + cairo_status_t status; + cairo_reference_count_t ref_count; + cairo_user_data_array_t user_data; + const cairo_font_face_backend_t *backend; +}; + +cairo_private void +_cairo_default_context_reset_static_data (void); + +cairo_private void +_cairo_toy_font_face_reset_static_data (void); + +cairo_private void +_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 { + cairo_bool_t (*destroy) (void *unscaled_font); +}; + +/* #cairo_toy_font_face_t - simple family/slant/weight font faces used for + * the built-in font API + */ + +typedef struct _cairo_toy_font_face { + cairo_font_face_t base; + const char *family; + cairo_bool_t owns_family; + cairo_font_slant_t slant; + cairo_font_weight_t weight; + + cairo_font_face_t *impl_face; /* The non-toy font face this actually uses */ +} cairo_toy_font_face_t; + +typedef enum _cairo_scaled_glyph_info { + 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_COLOR_SURFACE = (1 << 4) +} cairo_scaled_glyph_info_t; + +typedef struct _cairo_scaled_font_subset { + cairo_scaled_font_t *scaled_font; + unsigned int font_id; + unsigned int subset_id; + + /* Index of glyphs array is subset_glyph_index. + * Value of glyphs array is scaled_font_glyph_index. + */ + unsigned long *glyphs; + 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 { + cairo_font_type_t type; + + void + (*fini) (void *scaled_font); + + cairo_warn cairo_int_status_t + (*scaled_glyph_init) (void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info); + + /* A backend only needs to implement this or ucs4_to_index(), not + * both. This allows the backend to do something more sophisticated + * then just converting characters one by one. + */ + cairo_warn cairo_int_status_t + (*text_to_glyphs) (void *scaled_font, + 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); + + unsigned long + (*ucs4_to_index) (void *scaled_font, + uint32_t ucs4); + + /* 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, + long offset, + unsigned char *buffer, + unsigned long *length); + + /* ucs4 is set to -1 if the unicode character could not be found + * for the glyph */ + cairo_warn cairo_int_status_t + (*index_to_ucs4)(void *scaled_font, + unsigned long index, + 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 { + cairo_font_type_t type; + + cairo_warn cairo_status_t + (*create_for_toy) (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); + + /* The destroy() function is allowed to resurrect the font face + * by re-referencing. This is needed for the FreeType backend. + */ + cairo_bool_t + (*destroy) (void *font_face); + + cairo_warn cairo_status_t + (*scaled_font_create) (void *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **scaled_font); + + cairo_font_face_t * + (*get_implementation) (void *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options); +}; + +extern const cairo_private struct _cairo_font_face_backend _cairo_user_font_face_backend; + +/* concrete font backends */ +#if CAIRO_HAS_FT_FONT + +extern const cairo_private struct _cairo_font_face_backend _cairo_ft_font_face_backend; + +#endif + +#if CAIRO_HAS_WIN32_FONT + +extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_face_backend; + +#endif + +#if CAIRO_HAS_QUARTZ_FONT + +extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend; + +#endif + +#define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE +#define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD +#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD + +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; + +struct _cairo_surface_attributes { + cairo_matrix_t matrix; + cairo_extend_t extend; + cairo_filter_t filter; + cairo_bool_t has_component_alpha; + int x_offset; + int y_offset; + void *extra; +}; + +#define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL +#define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL + +#define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial" +#define CAIRO_QUARTZ_FONT_FAMILY_DEFAULT "Helvetica" +#define CAIRO_FT_FONT_FAMILY_DEFAULT "" +#define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:" + +#if CAIRO_HAS_WIN32_FONT + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend + +#elif CAIRO_HAS_QUARTZ_FONT + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_QUARTZ_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_quartz_font_face_backend + +#elif CAIRO_HAS_FT_FONT + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_ft_font_face_backend + +#else + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_FT_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_user_font_face_backend + +#endif + +#define CAIRO_GSTATE_OPERATOR_DEFAULT CAIRO_OPERATOR_OVER +#define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.1 +#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 +#define CAIRO_GSTATE_LINE_JOIN_DEFAULT CAIRO_LINE_JOIN_MITER +#define CAIRO_GSTATE_MITER_LIMIT_DEFAULT 10.0 +#define CAIRO_GSTATE_DEFAULT_FONT_SIZE 10.0 + +#define CAIRO_SURFACE_RESOLUTION_DEFAULT 72.0 +#define CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT 300.0 + +typedef struct _cairo_stroke_face { + cairo_point_t ccw; + 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 */ + +static inline double cairo_const +_cairo_restrict_value (double value, double min, double max) +{ + if (value < min) + return min; + else if (value > max) + return max; + else + return value; +} + +/* C99 round() rounds to the nearest integral value with halfway cases rounded + * away from 0. _cairo_round rounds halfway cases toward positive infinity. + * This matches the rounding behaviour of _cairo_lround. */ +static inline double cairo_const +_cairo_round (double r) +{ + return floor (r + .5); +} + +#if DISABLE_SOME_FLOATING_POINT +cairo_private int +_cairo_lround (double d) cairo_const; +#else +static inline int cairo_const +_cairo_lround (double r) +{ + return _cairo_round (r); +} +#endif + +cairo_private uint16_t +_cairo_half_from_float (float f) cairo_const; + +cairo_private cairo_bool_t +_cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_const; + +cairo_private cairo_bool_t +_cairo_operator_bounded_by_source (cairo_operator_t op) cairo_const; + +enum { + CAIRO_OPERATOR_BOUND_BY_MASK = 1 << 1, + CAIRO_OPERATOR_BOUND_BY_SOURCE = 1 << 2, +}; + +cairo_private uint32_t +_cairo_operator_bounded_by_either (cairo_operator_t op) cairo_const; +/* cairo-color.c */ +cairo_private const cairo_color_t * +_cairo_stock_color (cairo_stock_t stock) cairo_pure; + +#define CAIRO_COLOR_WHITE _cairo_stock_color (CAIRO_STOCK_WHITE) +#define CAIRO_COLOR_BLACK _cairo_stock_color (CAIRO_STOCK_BLACK) +#define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT) + +cairo_private uint16_t +_cairo_color_double_to_short (double d) cairo_const; + +cairo_private void +_cairo_color_init_rgba (cairo_color_t *color, + double red, double green, double blue, + double alpha); + +cairo_private void +_cairo_color_multiply_alpha (cairo_color_t *color, + double alpha); + +cairo_private void +_cairo_color_get_rgba (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha); + +cairo_private void +_cairo_color_get_rgba_premultiplied (cairo_color_t *color, + double *red, + double *green, + double *blue, + double *alpha); + +cairo_private cairo_bool_t +_cairo_color_equal (const cairo_color_t *color_a, + const cairo_color_t *color_b) cairo_pure; + +cairo_private cairo_bool_t +_cairo_color_stop_equal (const cairo_color_stop_t *color_a, + const cairo_color_stop_t *color_b) cairo_pure; + +cairo_private cairo_content_t +_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); + +cairo_private void +_cairo_unscaled_font_init (cairo_unscaled_font_t *font, + const cairo_unscaled_font_backend_t *backend); + +cairo_private_no_warn cairo_unscaled_font_t * +_cairo_unscaled_font_reference (cairo_unscaled_font_t *font); + +cairo_private void +_cairo_unscaled_font_destroy (cairo_unscaled_font_t *font); + +/* cairo-font-face-twin.c */ + +cairo_private cairo_font_face_t * +_cairo_font_face_twin_create_fallback (void); + +cairo_private cairo_status_t +_cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); + +/* cairo-font-face-twin-data.c */ + +extern const cairo_private int8_t _cairo_twin_outlines[]; +extern const cairo_private uint16_t _cairo_twin_charmap[128]; + +/* cairo-font-options.c */ + +cairo_private void +_cairo_font_options_init_default (cairo_font_options_t *options); + +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_round_glyph_positions (cairo_font_options_t *options, + cairo_round_glyph_positions_t round); + +cairo_private cairo_round_glyph_positions_t +_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options); + +/* cairo-hull.c */ +cairo_private cairo_status_t +_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices); + +/* cairo-lzw.c */ +cairo_private unsigned char * +_cairo_lzw_compress (unsigned char *data, unsigned long *size_in_out); + +/* cairo-misc.c */ +cairo_private cairo_status_t +_cairo_validate_text_clusters (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_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); + +cairo_private void +_cairo_path_fixed_init (cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_path_fixed_init_copy (cairo_path_fixed_t *path, + const cairo_path_fixed_t *other); + +cairo_private void +_cairo_path_fixed_fini (cairo_path_fixed_t *path); + +cairo_private void +_cairo_path_fixed_destroy (cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_path_fixed_move_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y); + +cairo_private void +_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path); + +cairo_private cairo_status_t +_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy); + +cairo_private cairo_status_t +_cairo_path_fixed_line_to (cairo_path_fixed_t *path, + cairo_fixed_t x, + cairo_fixed_t y); + +cairo_private cairo_status_t +_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path, + cairo_fixed_t dx, + cairo_fixed_t dy); + +cairo_private cairo_status_t +_cairo_path_fixed_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t x0, cairo_fixed_t y0, + cairo_fixed_t x1, cairo_fixed_t y1, + cairo_fixed_t x2, cairo_fixed_t y2); + +cairo_private cairo_status_t +_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path, + cairo_fixed_t dx0, cairo_fixed_t dy0, + cairo_fixed_t dx1, cairo_fixed_t dy1, + cairo_fixed_t dx2, cairo_fixed_t dy2); + +cairo_private cairo_status_t +_cairo_path_fixed_close_path (cairo_path_fixed_t *path); + +cairo_private cairo_bool_t +_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path, + cairo_fixed_t *x, + cairo_fixed_t *y); + +typedef cairo_status_t +(cairo_path_fixed_move_to_func_t) (void *closure, + const cairo_point_t *point); + +typedef cairo_status_t +(cairo_path_fixed_line_to_func_t) (void *closure, + const cairo_point_t *point); + +typedef cairo_status_t +(cairo_path_fixed_curve_to_func_t) (void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2); + +typedef cairo_status_t +(cairo_path_fixed_close_path_func_t) (void *closure); + +cairo_private cairo_status_t +_cairo_path_fixed_interpret (const cairo_path_fixed_t *path, + 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); + +cairo_private cairo_status_t +_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, + 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); + +cairo_private void +_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, + cairo_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_rectangle_int_t *extents); + +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 +_cairo_path_fixed_stroke_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_rectangle_int_t *extents); + +cairo_private void +_cairo_path_fixed_transform (cairo_path_fixed_t *path, + const cairo_matrix_t *matrix); + +cairo_private cairo_bool_t +_cairo_path_fixed_is_box (const cairo_path_fixed_t *path, + cairo_box_t *box); + +cairo_private cairo_bool_t +_cairo_path_fixed_is_rectangle (const cairo_path_fixed_t *path, + cairo_box_t *box); + +/* cairo-path-in-fill.c */ +cairo_private cairo_bool_t +_cairo_path_fixed_in_fill (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + double x, + double y); + +/* cairo-path-fill.c */ +cairo_private cairo_status_t +_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, + double tolerance, + cairo_polygon_t *polygon); + +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 * +_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_private cairo_status_t +_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_traps_t *traps); + +/* cairo-path-stroke.c */ +cairo_private 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_private 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); + +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_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, + const cairo_matrix_t *ctm_inverse, + 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, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_status_t (*add_triangle) (void *closure, + const cairo_point_t triangle[3]), + cairo_status_t (*add_triangle_fan) (void *closure, + const cairo_point_t *midpt, + const cairo_point_t *points, + int npoints), + cairo_status_t (*add_quad) (void *closure, + const cairo_point_t quad[4]), + void *closure); + +/* cairo-scaled-font.c */ + +cairo_private void +_cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font, + cairo_status_t status); + +cairo_private cairo_scaled_font_t * +_cairo_scaled_font_create_in_error (cairo_status_t status); + +cairo_private void +_cairo_scaled_font_reset_static_data (void); + +cairo_private cairo_status_t +_cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + const cairo_scaled_font_backend_t *backend); + +cairo_private cairo_status_t +_cairo_scaled_font_set_metrics (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *fs_metrics); + +/* This should only be called on an error path by a scaled_font constructor */ +cairo_private void +_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font); + +cairo_private cairo_status_t +_cairo_scaled_font_font_extents (cairo_scaled_font_t *scaled_font, + cairo_font_extents_t *extents); + +cairo_private cairo_status_t +_cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_rectangle_int_t *extents, + cairo_bool_t *overlap); + +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, + cairo_rectangle_int_t *extents); + +cairo_private cairo_status_t +_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, + cairo_operator_t op, + const cairo_pattern_t *source, + 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_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_path_fixed_t *path); + +cairo_private void +_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_text_extents_t *fs_metrics); + +cairo_private void +_cairo_scaled_glyph_set_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_image_surface_t *surface); + +cairo_private void +_cairo_scaled_glyph_set_path (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_path_fixed_t *path); + +cairo_private void +_cairo_scaled_glyph_set_recording_surface (cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font, + cairo_surface_t *recording_surface); + +cairo_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, + cairo_scaled_glyph_info_t info, + cairo_scaled_glyph_t **scaled_glyph_ret); + +cairo_private double +_cairo_scaled_font_get_max_scale (cairo_scaled_font_t *scaled_font); + +cairo_private void +_cairo_scaled_font_map_destroy (void); + +/* cairo-stroke-style.c */ + +cairo_private void +_cairo_stroke_style_init (cairo_stroke_style_t *style); + +cairo_private cairo_status_t +_cairo_stroke_style_init_copy (cairo_stroke_style_t *style, + const cairo_stroke_style_t *other); + +cairo_private void +_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); + +cairo_private double +_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style); + +cairo_private cairo_bool_t +_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance); + +cairo_private void +_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + double tolerance, + double *dash_offset, + double *dashes, + unsigned int *num_dashes); + + +/* cairo-surface.c */ + +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_no_warn cairo_int_status_t +_cairo_surface_set_error (cairo_surface_t *surface, + cairo_int_status_t status); + +cairo_private void +_cairo_surface_set_resolution (cairo_surface_t *surface, + double x_res, + double y_res); + +cairo_private cairo_surface_t * +_cairo_surface_create_for_rectangle_int (cairo_surface_t *target, + const cairo_rectangle_int_t *extents); + +cairo_private 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_private void +_cairo_surface_init (cairo_surface_t *surface, + const cairo_surface_backend_t *backend, + cairo_device_t *device, + 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_paint (cairo_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + 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, + const cairo_clip_t *clip); + +cairo_private cairo_status_t +_cairo_surface_fill_stroke (cairo_surface_t *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, + 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, + 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_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); + +cairo_private cairo_status_t +_cairo_surface_show_text_glyphs (cairo_surface_t *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_private cairo_status_t +_cairo_surface_tag (cairo_surface_t *surface, + cairo_bool_t begin, + const char *tag_name, + const char *attributes); + +cairo_private cairo_status_t +_cairo_surface_acquire_source_image (cairo_surface_t *surface, + cairo_image_surface_t **image_out, + void **image_extra); + +cairo_private void +_cairo_surface_release_source_image (cairo_surface_t *surface, + cairo_image_surface_t *image, + void *image_extra); + +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 void +_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; + +cairo_private void +_cairo_surface_release_device_reference (cairo_surface_t *surface); + +/* cairo-image-surface.c */ + +/* XXX: In cairo 1.2.0 we added a new %CAIRO_FORMAT_RGB16_565 but + * neglected to adjust this macro. The net effect is that it's + * impossible to externally create an image surface with this + * format. This is perhaps a good thing since we also neglected to fix + * up things like cairo_surface_write_to_png() for the new format + * (-Wswitch-enum will tell you where). Is it obvious that format was + * added in haste? + * + * The reason for the new format was to allow the xlib backend to be + * used on X servers with a 565 visual. So the new format did its job + * 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 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 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 + * an RGB16_565 value in the header files for the sake of code that + * might have that value in it.) + * + * If we do decide to start fully supporting RGB16_565 as an external + * format, then %CAIRO_FORMAT_VALID needs to be adjusted to include + * it. But that should not happen before all necessary code is fixed + * to support it (at least cairo_surface_write_to_png() and a few spots + * in cairo-xlib-surface.c--again see -Wswitch-enum). + */ +#define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \ + (format) <= CAIRO_FORMAT_RGBA128F) + +/* pixman-required stride alignment in bytes. */ +#define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t)) +#define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \ + ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT) + +#define CAIRO_CONTENT_VALID(content) ((content) && \ + (((content) & ~(CAIRO_CONTENT_COLOR | \ + CAIRO_CONTENT_ALPHA | \ + CAIRO_CONTENT_COLOR_ALPHA))\ + == 0)) + +cairo_private int +_cairo_format_bits_per_pixel (cairo_format_t format) cairo_const; + +cairo_private cairo_format_t +_cairo_format_from_content (cairo_content_t content) cairo_const; + +cairo_private cairo_format_t +_cairo_format_from_pixman_format (pixman_format_code_t pixman_format); + +cairo_private cairo_content_t +_cairo_content_from_format (cairo_format_t format) cairo_const; + +cairo_private cairo_content_t +_cairo_content_from_pixman_format (pixman_format_code_t pixman_format); + +cairo_private cairo_surface_t * +_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image, + pixman_format_code_t pixman_format); + +cairo_private pixman_format_code_t +_cairo_format_to_pixman_format_code (cairo_format_t format); + +cairo_private cairo_bool_t +_pixman_format_from_masks (cairo_format_masks_t *masks, + pixman_format_code_t *format_ret); + +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, + int width, + int height, + int stride); + +cairo_private cairo_surface_t * +_cairo_image_surface_create_with_content (cairo_content_t content, + int width, + int height); + +cairo_private void +_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface); + +cairo_private cairo_image_surface_t * +_cairo_image_surface_coerce (cairo_image_surface_t *surface); + +cairo_private cairo_image_surface_t * +_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, + cairo_format_t format); + +cairo_private cairo_image_transparency_t +_cairo_image_analyze_transparency (cairo_image_surface_t *image); + +cairo_private cairo_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, + double tolerance, + const cairo_matrix_t *ctm); + +cairo_private void +_cairo_pen_init_empty (cairo_pen_t *pen); + +cairo_private cairo_status_t +_cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other); + +cairo_private void +_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 int +_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope); + +cairo_private int +_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen, + const cairo_slope_t *slope); + +cairo_private 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); + +cairo_private 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); + +/* 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_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_no_warn cairo_status_t +_cairo_polygon_add_external_edge (void *polygon, + const cairo_point_t *p1, + const cairo_point_t *p2); + +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_reduce (cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_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 + +/* cairo-spline.c */ +cairo_private cairo_bool_t +_cairo_spline_init (cairo_spline_t *spline, + cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *a, const cairo_point_t *b, + const cairo_point_t *c, const cairo_point_t *d); + +cairo_private cairo_status_t +_cairo_spline_decompose (cairo_spline_t *spline, double tolerance); + +cairo_private cairo_status_t +_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, + void *closure, + const cairo_point_t *p0, const cairo_point_t *p1, + const cairo_point_t *p2, const cairo_point_t *p3); + +/* cairo-matrix.c */ +cairo_private void +_cairo_matrix_get_affine (const cairo_matrix_t *matrix, + double *xx, double *yx, + double *xy, double *yy, + double *x0, double *y0); + +cairo_private void +_cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix, + double *x1, double *y1, + double *x2, double *y2, + cairo_bool_t *is_tight); + +cairo_private void +_cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix, + cairo_box_t *bbox, + cairo_bool_t *is_tight); + +cairo_private cairo_bool_t +_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private cairo_bool_t +_cairo_matrix_is_scale_0 (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private double +_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) cairo_pure; + +cairo_private cairo_status_t +_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, + double *sx, double *sy, int x_major); + +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); +} + +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, + int *itx, int *ity); + +cairo_private cairo_bool_t +_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 double +_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, + double radius) cairo_pure; + +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_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_debug_print_matrix (FILE *file, const cairo_matrix_t *matrix); + +cairo_private void +_cairo_debug_print_rect (FILE *file, const cairo_rectangle_int_t *rect); + +cairo_private 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_private cairo_status_t +_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, + const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *out); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, + cairo_fill_rule_t fill_rule); + +cairo_private cairo_status_t +_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon, + cairo_fill_rule_t fill_rule, + cairo_boxes_t *boxes); + +cairo_private void +_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps, + cairo_trapezoid_t *src_traps, + int num_traps, + double tx, double ty, + double sx, double sy); + +#if CAIRO_HAS_DRM_SURFACE + +cairo_private void +_cairo_drm_device_reset_static_data (void); + +#endif + +cairo_private void +_cairo_clip_reset_static_data (void); + +cairo_private void +_cairo_pattern_reset_static_data (void); + +/* cairo-unicode.c */ + +cairo_private int +_cairo_utf8_get_char_validated (const char *p, + uint32_t *unicode); + +cairo_private cairo_status_t +_cairo_utf8_to_ucs4 (const char *str, + int len, + uint32_t **result, + int *items_written); + +cairo_private int +_cairo_ucs4_to_utf8 (uint32_t unicode, + char *utf8); + +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 +cairo_private cairo_status_t +_cairo_utf8_to_utf16 (const char *str, + int len, + uint16_t **result, + 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); +slim_hidden_proto (cairo_create); +slim_hidden_proto (cairo_curve_to); +slim_hidden_proto (cairo_destroy); +slim_hidden_proto (cairo_fill_preserve); +slim_hidden_proto (cairo_font_face_destroy); +slim_hidden_proto (cairo_font_face_get_user_data); +slim_hidden_proto_no_warn (cairo_font_face_reference); +slim_hidden_proto (cairo_font_face_set_user_data); +slim_hidden_proto (cairo_font_options_equal); +slim_hidden_proto (cairo_font_options_hash); +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_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); +slim_hidden_proto (cairo_glyph_free); +slim_hidden_proto (cairo_image_surface_create); +slim_hidden_proto (cairo_image_surface_create_for_data); +slim_hidden_proto (cairo_image_surface_get_data); +slim_hidden_proto (cairo_image_surface_get_format); +slim_hidden_proto (cairo_image_surface_get_height); +slim_hidden_proto (cairo_image_surface_get_stride); +slim_hidden_proto (cairo_image_surface_get_width); +slim_hidden_proto (cairo_line_to); +slim_hidden_proto (cairo_mask); +slim_hidden_proto (cairo_matrix_init); +slim_hidden_proto (cairo_matrix_init_identity); +slim_hidden_proto (cairo_matrix_init_rotate); +slim_hidden_proto (cairo_matrix_init_scale); +slim_hidden_proto (cairo_matrix_init_translate); +slim_hidden_proto (cairo_matrix_invert); +slim_hidden_proto (cairo_matrix_multiply); +slim_hidden_proto (cairo_matrix_scale); +slim_hidden_proto (cairo_matrix_transform_distance); +slim_hidden_proto (cairo_matrix_transform_point); +slim_hidden_proto (cairo_matrix_translate); +slim_hidden_proto (cairo_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); +slim_hidden_proto (cairo_scale); +slim_hidden_proto (cairo_scaled_font_create); +slim_hidden_proto (cairo_scaled_font_destroy); +slim_hidden_proto (cairo_scaled_font_extents); +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_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); +slim_hidden_proto (cairo_set_line_join); +slim_hidden_proto (cairo_set_line_width); +slim_hidden_proto (cairo_set_matrix); +slim_hidden_proto (cairo_set_operator); +slim_hidden_proto (cairo_set_source); +slim_hidden_proto (cairo_set_source_rgb); +slim_hidden_proto (cairo_set_source_surface); +slim_hidden_proto (cairo_set_tolerance); +slim_hidden_proto (cairo_status); +slim_hidden_proto (cairo_stroke); +slim_hidden_proto (cairo_stroke_preserve); +slim_hidden_proto (cairo_surface_copy_page); +slim_hidden_proto (cairo_surface_create_similar_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_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_has_show_text_glyphs); +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); +slim_hidden_proto (cairo_toy_font_face_get_slant); +slim_hidden_proto (cairo_toy_font_face_get_weight); +slim_hidden_proto (cairo_translate); +slim_hidden_proto (cairo_transform); +slim_hidden_proto (cairo_user_font_face_create); +slim_hidden_proto (cairo_user_font_face_set_init_func); +slim_hidden_proto (cairo_user_font_face_set_render_glyph_func); +slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func); +slim_hidden_proto (cairo_device_to_user); +slim_hidden_proto (cairo_user_to_device); +slim_hidden_proto (cairo_user_to_device_distance); +slim_hidden_proto (cairo_version_string); +slim_hidden_proto (cairo_region_create); +slim_hidden_proto (cairo_region_create_rectangle); +slim_hidden_proto (cairo_region_create_rectangles); +slim_hidden_proto (cairo_region_copy); +slim_hidden_proto (cairo_region_reference); +slim_hidden_proto (cairo_region_destroy); +slim_hidden_proto (cairo_region_equal); +slim_hidden_proto (cairo_region_status); +slim_hidden_proto (cairo_region_get_extents); +slim_hidden_proto (cairo_region_num_rectangles); +slim_hidden_proto (cairo_region_get_rectangle); +slim_hidden_proto (cairo_region_is_empty); +slim_hidden_proto (cairo_region_contains_rectangle); +slim_hidden_proto (cairo_region_contains_point); +slim_hidden_proto (cairo_region_translate); +slim_hidden_proto (cairo_region_subtract); +slim_hidden_proto (cairo_region_subtract_rectangle); +slim_hidden_proto (cairo_region_intersect); +slim_hidden_proto (cairo_region_intersect_rectangle); +slim_hidden_proto (cairo_region_union); +slim_hidden_proto (cairo_region_union_rectangle); +slim_hidden_proto (cairo_region_xor); +slim_hidden_proto (cairo_region_xor_rectangle); + +#if CAIRO_HAS_PNG_FUNCTIONS + +slim_hidden_proto (cairo_surface_write_to_png_stream); + +#endif + +CAIRO_END_DECLS + +#include "cairo-mutex-private.h" +#include "cairo-fixed-private.h" +#include "cairo-wideint-private.h" +#include "cairo-malloc-private.h" +#include "cairo-hash-private.h" + +#if HAVE_VALGRIND +#include + +#define VG(x) x + +cairo_private void +_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface); + +#else + +#define VG(x) +#define _cairo_debug_check_image_surface_is_defined(X) + +#endif + +cairo_private void +_cairo_debug_print_path (FILE *stream, const cairo_path_fixed_t *path); + +cairo_private void +_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 0000000000..beefb46a33 --- /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 0000000000..1fa8b8d223 --- /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 0000000000..762a48429b --- /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-has-hidden-symbols.c b/gfx/cairo/cairo/src/check-has-hidden-symbols.c new file mode 100644 index 0000000000..1204127769 --- /dev/null +++ b/gfx/cairo/cairo/src/check-has-hidden-symbols.c @@ -0,0 +1,3 @@ +#include "cairoint.h" + +CAIRO_HAS_HIDDEN_SYMBOLS diff --git a/gfx/cairo/cairo/src/check-headers.sh b/gfx/cairo/cairo/src/check-headers.sh new file mode 100755 index 0000000000..61232954ba --- /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 new file mode 100644 index 0000000000..2d943d644c --- /dev/null +++ b/gfx/cairo/cairo/src/check-link.c @@ -0,0 +1,17 @@ +#include +#include + +int +main (void) +{ + printf ("Check linking to the just built cairo library\n"); + if (cairo_version () == CAIRO_VERSION) { + return 0; + } else { + fprintf (stderr, + "Error: linked to cairo version %s instead of %s\n", + cairo_version_string (), + CAIRO_VERSION_STRING); + return 1; + } +} diff --git a/gfx/cairo/cairo/src/check-plt.sh b/gfx/cairo/cairo/src/check-plt.sh new file mode 100755 index 0000000000..5a9dae1269 --- /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 0000000000..b718f604ee --- /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 0000000000..c82f9331d8 --- /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 0000000000..ca18f7336c --- /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 0000000000..3b0efc2489 --- /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 0000000000..7585756dc5 --- /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 0000000000..a3f01fdf22 --- /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 0000000000..f40bb2f2ed --- /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 0000000000..8fc5f7a9dd --- /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 0000000000..1ef0a6f59e --- /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 0000000000..79568a63d7 --- /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 0000000000..8fa3b4bd2c --- /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 0000000000..4d1f491fa2 --- /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 0000000000..9d29e4753b --- /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 0000000000..b2be36f183 --- /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 0000000000..f27b238048 --- /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 0000000000..592235b12b --- /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 0000000000..2b47d8c37e --- /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 0000000000..ef6e9771e3 --- /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 0000000000..3ea9c6c362 --- /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 0000000000..a93ac12ab5 --- /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 0000000000..bfe5136675 --- /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/drm/cairo-drm-intel-ioctl-private.h b/gfx/cairo/cairo/src/drm/cairo-drm-intel-ioctl-private.h new file mode 100644 index 0000000000..4a766d7e13 --- /dev/null +++ b/gfx/cairo/cairo/src/drm/cairo-drm-intel-ioctl-private.h @@ -0,0 +1,45 @@ +/* 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_IOCTL_PRIVATE_H +#define CAIRO_DRM_INTEL_IOCTL_PRIVATE_H + +#include "cairo-drm-intel-command-private.h" + +#include + +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 0000000000..0cfded1bd8 --- /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 0000000000..8fe2c3ae3b --- /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 0000000000..e6fb83dd51 --- /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 0000000000..2db7f38d6d --- /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 0000000000..0768528359 --- /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 0000000000..9c9f8526c6 --- /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 0000000000..8bc91bfe08 --- /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 0000000000..8c4dd0ee81 --- /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 0000000000..661e181b62 --- /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/meson.build b/gfx/cairo/cairo/src/meson.build new file mode 100644 index 0000000000..f5d741eaee --- /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/moz.build b/gfx/cairo/cairo/src/moz.build new file mode 100755 index 0000000000..7017c2e83f --- /dev/null +++ b/gfx/cairo/cairo/src/moz.build @@ -0,0 +1,284 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.cairo += [ + 'cairo-deprecated.h', + 'cairo-features.h', + 'cairo-platform.h', + 'cairo-rename.h', + 'cairo-tee.h', + 'cairo-version.h', + 'cairo.h', + 'pixman-rename.h', +] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] not in ('cocoa', 'uikit'): + EXPORTS.cairo += [ + 'cairo-pdf.h', + ] + SOURCES += [ + 'cairo-base85-stream.c', + 'cairo-cff-subset.c', + 'cairo-deflate-stream.c', + 'cairo-pdf-interchange.c', + 'cairo-pdf-operators.c', + 'cairo-pdf-shading.c', + 'cairo-pdf-surface.c', + 'cairo-truetype-subset.c', + 'cairo-type1-fallback.c', + 'cairo-type1-glyph-names.c', + 'cairo-type1-subset.c', + 'cairo-type3-glyph-surface.c', + ] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': + DEFINES['MOZ_DISABLE_WINDOWS_WRAPPER'] = True + EXPORTS.cairo += [ + 'cairo-win32.h', + ] + SOURCES += [ + 'win32/cairo-dwrite-font.cpp', + 'win32/cairo-win32-device.c', + 'win32/cairo-win32-display-surface.c', + 'win32/cairo-win32-font.c', + 'win32/cairo-win32-gdi-compositor.c', + 'win32/cairo-win32-surface.c', + ] + DEFINES['DISABLE_SOME_FLOATING_POINT'] = True + DEFINES['CAIRO_WIN32_STATIC_BUILD'] = True + if CONFIG['NS_PRINTING']: + SOURCES += [ + 'win32/cairo-win32-printing-surface.c', + ] + else: + DEFINES['CAIRO_OMIT_WIN32_PRINTING'] = True +elif CONFIG['MOZ_WIDGET_TOOLKIT'] in {'cocoa', 'uikit'}: + EXPORTS.cairo += [ + 'cairo-quartz-image.h', + 'cairo-quartz.h', + ] + SOURCES += [ + 'cairo-quartz-font.c', + 'cairo-quartz-image-surface.c', + 'cairo-quartz-surface.c', + ] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'beos': + EXPORTS.cairo += [ + 'cairo-beos.h', + ] + SOURCES += [ + 'cairo-beos-surface.cpp', + ] + +if CONFIG['MOZ_X11']: + EXPORTS.cairo += [ + 'cairo-xlib-xrender.h', + 'cairo-xlib.h', + ] + SOURCES += [ + 'cairo-xlib-core-compositor.c', + 'cairo-xlib-display.c', + 'cairo-xlib-fallback-compositor.c', + 'cairo-xlib-render-compositor.c', + 'cairo-xlib-screen.c', + 'cairo-xlib-source.c', + 'cairo-xlib-surface-shm.c', + 'cairo-xlib-surface.c', + 'cairo-xlib-visual.c', + 'cairo-xlib-xcb-surface.c', + ] + +if CONFIG['MOZ_ENABLE_CAIRO_FT']: + EXPORTS.cairo += [ + 'cairo-ft.h', + ] + SOURCES += [ + 'cairo-ft-font.c', + ] + +SOURCES += [ + 'cairo-bentley-ottmann-rectangular.c', # redefinition of '_cairo_bo_trap' + 'cairo-bentley-ottmann-rectilinear.c', # redefinition of '_cairo_bo_trap' + 'cairo-bentley-ottmann.c', # redefinition of '_cairo_bo_trap' + 'cairo-boxes-intersect.c', # a bunch of redefinitions + 'cairo-clip-surface.c', # redefinition of '_cairo_path_fixed_add_box' + 'cairo-image-source.c', # redefinition of 'fill' + 'cairo-mask-compositor.c', # redefinition of 'fill' + 'cairo-path-stroke-traps.c', # redefinition of 'stroker' + 'cairo-path-stroke-tristrip.c', # redefinition of 'stroker' + 'cairo-polygon-intersect.c', # redefinition of 'edge_compare_for_y_against_x' + 'cairo-polygon-reduce.c', # redefinition of 'edge_compare_for_y_against_x' + 'cairo-rectangular-scan-converter.c', # redefinition of '_pqueue' + 'cairo-surface-wrapper.c', # redefinition of '_copy_transformed_pattern' + 'cairo-tor-scan-converter.c', # redefinition of 'struct cell' + 'cairo-traps-compositor.c', # redefinition of 'is_recording_pattern' +] + +UNIFIED_SOURCES += [ + 'cairo-analysis-surface.c', + 'cairo-arc.c', + 'cairo-array.c', + 'cairo-atomic.c', + 'cairo-base64-stream.c', + 'cairo-botor-scan-converter.c', + 'cairo-boxes.c', + 'cairo-cache.c', + 'cairo-clip-boxes.c', + 'cairo-clip-polygon.c', + 'cairo-clip-region.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-surface.c', + 'cairo-line.c', + 'cairo-lzw.c', + 'cairo-matrix.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.c', + 'cairo-path.c', + 'cairo-pattern.c', + 'cairo-pen.c', + 'cairo-polygon.c', + 'cairo-raster-source-pattern.c', + 'cairo-recording-surface.c', + 'cairo-rectangle.c', + 'cairo-region.c', + 'cairo-scaled-font-subsets.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-offset.c', + 'cairo-surface-snapshot.c', + 'cairo-surface-subsurface.c', + 'cairo-surface.c', + 'cairo-tag-attributes.c', + 'cairo-tag-stack.c', + 'cairo-tee-surface.c', + 'cairo-tor22-scan-converter.c', + 'cairo-toy-font-face.c', + 'cairo-traps.c', + 'cairo-tristrip.c', + 'cairo-unicode.c', + 'cairo-user-font.c', + 'cairo-version.c', + 'cairo-wideint.c', + 'cairo.c', +] + +# We allow warnings for third-party code that can be updated from upstream. +AllowCompilerWarnings() + +FINAL_LIBRARY = 'gkmedias' + +DEFINES['PACKAGE_VERSION'] = '"moz"' +DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"' + +for var in ('CAIRO_HAS_PTHREAD', '_GNU_SOURCE'): + DEFINES[var] = True + +if CONFIG['MOZ_TREE_PIXMAN']: + DEFINES['MOZ_TREE_PIXMAN'] = True + +if CONFIG['CC_TYPE'] in ('clang', 'gcc'): + # We would normally use autoconf to set these up, using AC_CHECK_SIZEOF. + # But AC_CHECK_SIZEOF requires running programs to determine the sizes, + # and that doesn't work so well with cross-compiling. So instead we + # use these magic macros, available since at least GCC 4.3, to define + # the preprocessor macros cairo wanted from autoconf. + DEFINES['SIZEOF_VOID_P'] = '__SIZEOF_POINTER__' + DEFINES['SIZEOF_INT'] = '__SIZEOF_INT__' + DEFINES['SIZEOF_LONG'] = '__SIZEOF_LONG__' + DEFINES['SIZEOF_LONG_LONG'] = '__SIZEOF_LONG_LONG__' + +# Normally determined by cairo's configure script. +DEFINES['HAVE_UINT64_T'] = True +DEFINES['HAVE_CXX11_ATOMIC_PRIMITIVES'] = True + +if CONFIG['MOZ_TREE_FREETYPE']: + DEFINES['HAVE_FT_LIBRARY_SETLCDFILTER'] = True + DEFINES['FT_LCD_FILTER_H'] = '../../../modules/freetype2/include/freetype/ftlcdfil.h' + +# Suppress warnings in third-party code. +CFLAGS += [ + '-Wno-enum-compare', + '-Wno-int-to-pointer-cast', + '-Wno-int-conversion', + '-Wno-incompatible-pointer-types', + '-Wno-sign-compare', + '-Wno-type-limits', + '-Wno-missing-field-initializers', + '-Wno-conversion', + '-Wno-narrowing', + '-Wno-switch', + '-Wno-unused', + '-Wno-unused-variable', + '-Wno-error=uninitialized', +] +if CONFIG['CC_TYPE'] in ('clang', 'clang-cl'): + CFLAGS += [ + '-Wno-absolute-value', + '-Wno-deprecated-register', + '-Wno-incompatible-pointer-types', + '-Wno-macro-redefined', + '-Wno-shift-negative-value', + '-Wno-tautological-compare', + '-Wno-tautological-constant-out-of-range-compare', + '-Wno-unreachable-code', + ] +else: + CFLAGS += ['-Wno-unused-but-set-variable'] + +# See bug 386897. +if CONFIG['CC_TYPE'] in ('clang', 'gcc') and CONFIG['OS_TARGET'] == 'Android' and CONFIG['MOZ_OPTIMIZE']: + CFLAGS += ['-O2'] + CXXFLAGS += ['-O2'] + +if CONFIG['MOZ_X11']: + CFLAGS += CONFIG['MOZ_X11_CFLAGS'] + +if CONFIG['MOZ_ENABLE_CAIRO_FT']: + CFLAGS += CONFIG['CAIRO_FT_CFLAGS'] + CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS'] diff --git a/gfx/cairo/cairo/src/pixman-rename.h b/gfx/cairo/cairo/src/pixman-rename.h new file mode 100644 index 0000000000..431cd92e96 --- /dev/null +++ b/gfx/cairo/cairo/src/pixman-rename.h @@ -0,0 +1,145 @@ +#ifdef MOZ_TREE_PIXMAN +#define pixman_composite_glyphs _moz_pixman_composite_glyphs +#define pixman_composite_glyphs_no_mask _moz_pixman_composite_glyphs_no_mask +#define pixman_glyph_cache_create _moz_pixman_glyph_cache_create +#define pixman_glyph_cache_destroy _moz_pixman_glyph_cache_destroy +#define pixman_glyph_cache_freeze _moz_pixman_glyph_cache_freeze +#define pixman_glyph_cache_insert _moz_pixman_glyph_cache_insert +#define pixman_glyph_cache_lookup _moz_pixman_glyph_cache_lookup +#define pixman_glyph_cache_remove _moz_pixman_glyph_cache_remove +#define pixman_glyph_cache_thaw _moz_pixman_glyph_cache_thaw +#define pixman_glyph_get_extents _moz_pixman_glyph_get_extents +#define pixman_glyph_get_mask_format _moz_pixman_glyph_get_mask_format +#define pixman_region_set_static_pointers _moz_pixman_region_set_static_pointers +#define pixman_region_init _moz_pixman_region_init +#define pixman_region_init_rect _moz_pixman_region_init_rect +#define pixman_region_init_rects _moz_pixman_region_init_rects +#define pixman_region_init_with_extents _moz_pixman_region_init_with_extents +#define pixman_region_fini _moz_pixman_region_fini +#define pixman_region_translate _moz_pixman_region_translate +#define pixman_region_copy _moz_pixman_region_copy +#define pixman_region_intersect _moz_pixman_region_intersect +#define pixman_region_union _moz_pixman_region_union +#define pixman_region_union_rect _moz_pixman_region_union_rect +#define pixman_region_subtract _moz_pixman_region_subtract +#define pixman_region_inverse _moz_pixman_region_inverse +#define pixman_region_contains_point _moz_pixman_region_contains_point +#define pixman_region_contains_rectangle _moz_pixman_region_contains_rectangle +#define pixman_region_not_empty _moz_pixman_region_not_empty +#define pixman_region_extents _moz_pixman_region_extents +#define pixman_region_n_rects _moz_pixman_region_n_rects +#define pixman_region_rectangles _moz_pixman_region_rectangles +#define pixman_region_equal _moz_pixman_region_equal +#define pixman_region_selfcheck _moz_pixman_region_selfcheck +#define pixman_region_reset _moz_pixman_region_reset +#define pixman_region_clear _moz_pixman_region_clear +#define pixman_region_print _moz_pixman_region_print +#define pixman_region32_init _moz_pixman_region32_init +#define pixman_region32_init_rect _moz_pixman_region32_init_rect +#define pixman_region32_init_rects _moz_pixman_region32_init_rects +#define pixman_region32_init_with_extents _moz_pixman_region32_init_with_extents +#define pixman_region32_init_from_image _moz_pixman_region32_init_from_image +#define pixman_region32_fini _moz_pixman_region32_fini +#define pixman_region32_translate _moz_pixman_region32_translate +#define pixman_region32_copy _moz_pixman_region32_copy +#define pixman_region32_intersect _moz_pixman_region32_intersect +#define pixman_region32_intersect_rect _moz_pixman_region32_intersect_rect +#define pixman_region32_union _moz_pixman_region32_union +#define pixman_region32_union_rect _moz_pixman_region32_union_rect +#define pixman_region32_subtract _moz_pixman_region32_subtract +#define pixman_region32_inverse _moz_pixman_region32_inverse +#define pixman_region32_contains_point _moz_pixman_region32_contains_point +#define pixman_region32_contains_rectangle _moz_pixman_region32_contains_rectangle +#define pixman_region32_not_empty _moz_pixman_region32_not_empty +#define pixman_region32_extents _moz_pixman_region32_extents +#define pixman_region32_n_rects _moz_pixman_region32_n_rects +#define pixman_region32_rectangles _moz_pixman_region32_rectangles +#define pixman_region32_equal _moz_pixman_region32_equal +#define pixman_region32_selfcheck _moz_pixman_region32_selfcheck +#define pixman_region32_reset _moz_pixman_region32_reset +#define pixman_region32_clear _moz_pixman_region32_clear +#define pixman_region32_print _moz_pixman_region32_print +#define pixman_blt _moz_pixman_blt +#define pixman_fill _moz_pixman_fill +#define pixman_transform_point_3d _moz_pixman_transform_point_3d +#define pixman_version _moz_pixman_version +#define pixman_version_string _moz_pixman_version_string +#define pixman_format_supported_destination _moz_pixman_format_supported_destination +#define pixman_format_supported_source _moz_pixman_format_supported_source +#define pixman_image_create_solid_fill _moz_pixman_image_create_solid_fill +#define pixman_image_create_linear_gradient _moz_pixman_image_create_linear_gradient +#define pixman_image_create_radial_gradient _moz_pixman_image_create_radial_gradient +#define pixman_image_create_conical_gradient _moz_pixman_image_create_conical_gradient +#define pixman_image_create_bits _moz_pixman_image_create_bits +#define pixman_image_ref _moz_pixman_image_ref +#define pixman_image_unref _moz_pixman_image_unref +#define pixman_image_set_clip_region _moz_pixman_image_set_clip_region +#define pixman_image_set_clip_region32 _moz_pixman_image_set_clip_region32 +#define pixman_image_set_has_client_clip _moz_pixman_image_set_has_client_clip +#define pixman_image_set_transform _moz_pixman_image_set_transform +#define pixman_image_set_repeat _moz_pixman_image_set_repeat +#define pixman_image_set_filter _moz_pixman_image_set_filter +#define pixman_image_set_source_clipping _moz_pixman_image_set_source_clipping +#define pixman_image_set_alpha_map _moz_pixman_image_set_alpha_map +#define pixman_image_set_component_alpha _moz_pixman_image_set_component_alpha +#define pixman_image_set_accessors _moz_pixman_image_set_accessors +#define pixman_image_set_indexed _moz_pixman_image_set_indexed +#define pixman_image_get_data _moz_pixman_image_get_data +#define pixman_image_get_width _moz_pixman_image_get_width +#define pixman_image_get_height _moz_pixman_image_get_height +#define pixman_image_get_stride _moz_pixman_image_get_stride +#define pixman_image_get_depth _moz_pixman_image_get_depth +#define pixman_image_fill_rectangles _moz_pixman_image_fill_rectangles +#define pixman_compute_composite_region _moz_pixman_compute_composite_region +#define pixman_image_composite _moz_pixman_image_composite +#define pixman_sample_ceil_y _moz_pixman_sample_ceil_y +#define pixman_sample_floor_y _moz_pixman_sample_floor_y +#define pixman_edge_step _moz_pixman_edge_step +#define pixman_edge_init _moz_pixman_edge_init +#define pixman_line_fixed_edge_init _moz_pixman_line_fixed_edge_init +#define pixman_rasterize_edges _moz_pixman_rasterize_edges +#define pixman_add_traps _moz_pixman_add_traps +#define pixman_add_trapezoids _moz_pixman_add_trapezoids +#define pixman_add_triangles _moz_pixman_add_triangles +#define pixman_composite_trapezoids _moz_pixman_composite_trapezoids +#define pixman_composite_triangles _moz_pixman_composite_triangles +#define pixman_rasterize_trapezoid _moz_pixman_rasterize_trapezoid +#define pixman_disable_out_of_bounds_workaround _moz_pixman_disable_out_of_bounds_workaround +#define pixman_f_transform_bounds _moz_pixman_f_transform_bounds +#define pixman_f_transform_from_pixman_transform _moz_pixman_f_transform_from_pixman_transform +#define pixman_f_transform_init_identity _moz_pixman_f_transform_init_identity +#define pixman_f_transform_init_rotate _moz_pixman_f_transform_init_rotate +#define pixman_f_transform_init_scale _moz_pixman_f_transform_init_scale +#define pixman_f_transform_init_translate _moz_pixman_f_transform_init_translate +#define pixman_f_transform_invert _moz_pixman_f_transform_invert +#define pixman_f_transform_multiply _moz_pixman_f_transform_multiply +#define pixman_f_transform_point _moz_pixman_f_transform_point +#define pixman_f_transform_point_3d _moz_pixman_f_transform_point_3d +#define pixman_f_transform_rotate _moz_pixman_f_transform_rotate +#define pixman_f_transform_scale _moz_pixman_f_transform_scale +#define pixman_f_transform_translate _moz_pixman_f_transform_translate +#define pixman_image_composite32 _moz_pixman_image_composite32 +#define pixman_image_fill_boxes _moz_pixman_image_fill_boxes +#define pixman_image_get_component_alpha _moz_pixman_image_get_component_alpha +#define pixman_image_get_destroy_data _moz_pixman_image_get_destroy_data +#define pixman_image_get_format _moz_pixman_image_get_format +#define pixman_image_set_destroy_function _moz_pixman_image_set_destroy_function +#define pixman_region_init_from_image _moz_pixman_region_init_from_image +#define pixman_region_intersect_rect _moz_pixman_region_intersect_rect +#define pixman_transform_bounds _moz_pixman_transform_bounds +#define pixman_transform_from_pixman_f_transform _moz_pixman_transform_from_pixman_f_transform +#define pixman_transform_init_identity _moz_pixman_transform_init_identity +#define pixman_transform_init_rotate _moz_pixman_transform_init_rotate +#define pixman_transform_init_scale _moz_pixman_transform_init_scale +#define pixman_transform_init_translate _moz_pixman_transform_init_translate +#define pixman_transform_invert _moz_pixman_transform_invert +#define pixman_transform_is_identity _moz_pixman_transform_is_identity +#define pixman_transform_is_int_translate _moz_pixman_transform_is_int_translate +#define pixman_transform_is_inverse _moz_pixman_transform_is_inverse +#define pixman_transform_is_scale _moz_pixman_transform_is_scale +#define pixman_transform_multiply _moz_pixman_transform_multiply +#define pixman_transform_point _moz_pixman_transform_point +#define pixman_transform_rotate _moz_pixman_transform_rotate +#define pixman_transform_scale _moz_pixman_transform_scale +#define pixman_transform_translate _moz_pixman_transform_translate +#endif 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 0000000000..ff84b10aff --- /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-compositor-surface-private.h b/gfx/cairo/cairo/src/test-compositor-surface-private.h new file mode 100644 index 0000000000..491f241bac --- /dev/null +++ b/gfx/cairo/cairo/src/test-compositor-surface-private.h @@ -0,0 +1,56 @@ +/* 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_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_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_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 0000000000..d6e04a122c --- /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 0000000000..8d8af2d540 --- /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-null-compositor-surface.c b/gfx/cairo/cairo/src/test-null-compositor-surface.c new file mode 100644 index 0000000000..35913a2b9f --- /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 0000000000..52d864b28d --- /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 new file mode 100644 index 0000000000..7967f7406c --- /dev/null +++ b/gfx/cairo/cairo/src/test-paginated-surface.c @@ -0,0 +1,289 @@ +/* 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 help exercise the paginated-surface paths in cairo. + * + * The defining feature of this backend is that it uses a paginated + * 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 paginated-surface-based + * backend. + */ + +#include "cairoint.h" + +#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; + cairo_surface_t *target; + cairo_paginated_mode_t paginated_mode; +} test_paginated_surface_t; + +static const cairo_surface_backend_t test_paginated_surface_backend; +static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend; + +cairo_surface_t * +_cairo_test_paginated_surface_create (cairo_surface_t *target) +{ + cairo_status_t status; + cairo_surface_t *paginated; + test_paginated_surface_t *surface; + + status = cairo_surface_status (target); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + 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, + TRUE); /* is_vector */ + + surface->target = cairo_surface_reference (target); + + paginated = _cairo_paginated_surface_create (&surface->base, + target->content, + &test_paginated_surface_paginated_backend); + status = paginated->status; + if (status == CAIRO_STATUS_SUCCESS) { + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + return paginated; + } + + cairo_surface_destroy (target); + free (surface); + return _cairo_surface_create_in_error (status); +} + +static cairo_status_t +_test_paginated_surface_finish (void *abstract_surface) +{ + test_paginated_surface_t *surface = abstract_surface; + + cairo_surface_destroy (surface->target); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_test_paginated_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + test_paginated_surface_t *surface = abstract_surface; + + return _cairo_surface_get_extents (surface->target, rectangle); +} + +static cairo_int_status_t +_test_paginated_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_paint (surface->target, op, source, clip); +} + +static cairo_int_status_t +_test_paginated_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_mask (surface->target, + op, source, mask, clip); +} + +static cairo_int_status_t +_test_paginated_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) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_stroke (surface->target, op, source, + path, style, + ctm, ctm_inverse, + tolerance, antialias, + clip); +} + +static cairo_int_status_t +_test_paginated_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) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_fill (surface->target, op, source, + path, fill_rule, + tolerance, antialias, + clip); +} + +static cairo_bool_t +_test_paginated_surface_has_show_text_glyphs (void *abstract_surface) +{ + test_paginated_surface_t *surface = abstract_surface; + + return cairo_surface_has_show_text_glyphs (surface->target); +} + +static cairo_int_status_t +_test_paginated_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) +{ + test_paginated_surface_t *surface = abstract_surface; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + return _cairo_surface_show_text_glyphs (surface->target, op, source, + utf8, utf8_len, + glyphs, num_glyphs, + clusters, num_clusters, + cluster_flags, + scaled_font, + clip); +} + + +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 */ + 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 */ + + _test_paginated_surface_get_extents, + NULL, /* get_font_options */ + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + /* Here is the more "modern" section of the surface backend + * interface which is mostly just drawing functions */ + + _test_paginated_surface_paint, + _test_paginated_surface_mask, + _test_paginated_surface_stroke, + _test_paginated_surface_fill, + NULL, /* fill-stroke */ + NULL, /* replaced by show_text_glyphs */ + _test_paginated_surface_has_show_text_glyphs, + _test_paginated_surface_show_text_glyphs +}; + +static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend = { + NULL, /* start_page */ + _test_paginated_surface_set_paginated_mode +}; diff --git a/gfx/cairo/cairo/src/test-paginated-surface.h b/gfx/cairo/cairo/src/test-paginated-surface.h new file mode 100644 index 0000000000..2bd98aa5ee --- /dev/null +++ b/gfx/cairo/cairo/src/test-paginated-surface.h @@ -0,0 +1,48 @@ +/* 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 + */ + +#ifndef TEST_PAGINATED_SURFACE_H +#define TEST_PAGINATED_SURFACE_H + +#include "cairo.h" + +CAIRO_BEGIN_DECLS + +cairo_surface_t * +_cairo_test_paginated_surface_create (cairo_surface_t *target); + +CAIRO_END_DECLS + +#endif /* TEST_PAGINATED_SURFACE_H */ diff --git a/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp new file mode 100644 index 0000000000..a362007be1 --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-dwrite-font.cpp @@ -0,0 +1,1424 @@ +/* -*- 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 + * + * 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 the Mozilla Foundation + * + * Contributor(s): + * Bas Schouten + */ + +#include "cairoint.h" + +#include "cairo-win32-private.h" +#include "cairo-pattern-private.h" +#include "cairo-surface-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-clip-private.h" +#include "cairo-win32-refptr.h" + +#include "cairo-dwrite-private.h" +#include "cairo-truetype-subset-private.h" +#include + +typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( + D2D1_FACTORY_TYPE factoryType, + REFIID iid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, + void **factory +); + +#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS + +// Forward declarations +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + const RECT &area); + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + cairo_dwrite_scaled_font_t *scaled_font, + const RECT &area); + +class D2DFactory +{ +public: + static ID2D1Factory *Instance() + { + if (!mFactoryInstance) { + D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory"); + if (createD2DFactory) { + D2D1_FACTORY_OPTIONS options; + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; + createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), + &options, + (void**)&mFactoryInstance); + } + } + return mFactoryInstance; + } + + static ID2D1DCRenderTarget *RenderTarget() + { + if (!mRenderTarget) { + if (!Instance()) { + return NULL; + } + // Create a DC render target. + D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat( + DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_PREMULTIPLIED), + 0, + 0, + D2D1_RENDER_TARGET_USAGE_NONE, + D2D1_FEATURE_LEVEL_DEFAULT + ); + + Instance()->CreateDCRenderTarget(&props, &mRenderTarget); + } + return mRenderTarget; + } + +private: + static ID2D1Factory *mFactoryInstance; + static ID2D1DCRenderTarget *mRenderTarget; +}; + +IDWriteFactory *DWriteFactory::mFactoryInstance = NULL; +IDWriteFontCollection *DWriteFactory::mSystemCollection = NULL; +IDWriteRenderingParams *DWriteFactory::mDefaultRenderingParams = NULL; +IDWriteRenderingParams *DWriteFactory::mCustomClearTypeRenderingParams = NULL; +IDWriteRenderingParams *DWriteFactory::mForceGDIClassicRenderingParams = NULL; +FLOAT DWriteFactory::mGamma = -1.0; +FLOAT DWriteFactory::mEnhancedContrast = -1.0; +FLOAT DWriteFactory::mClearTypeLevel = -1.0; +int DWriteFactory::mPixelGeometry = -1; +int DWriteFactory::mRenderingMode = -1; + +ID2D1Factory *D2DFactory::mFactoryInstance = NULL; +ID2D1DCRenderTarget *D2DFactory::mRenderTarget = NULL; + +/* Functions cairo_font_face_backend_t */ +static cairo_status_t +_cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); +static cairo_bool_t +_cairo_dwrite_font_face_destroy (void *font_face); + +static cairo_status_t +_cairo_dwrite_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); + +const cairo_font_face_backend_t _cairo_dwrite_font_face_backend = { + CAIRO_FONT_TYPE_DWRITE, + _cairo_dwrite_font_face_create_for_toy, + _cairo_dwrite_font_face_destroy, + _cairo_dwrite_font_face_scaled_font_create +}; + +/* Functions cairo_scaled_font_backend_t */ + +void _cairo_dwrite_scaled_font_fini(void *scaled_font); + +static cairo_warn cairo_int_status_t +_cairo_dwrite_scaled_glyph_init(void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info); + +cairo_int_status_t +_cairo_dwrite_load_truetype_table(void *scaled_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length); + +unsigned long +_cairo_dwrite_ucs4_to_index(void *scaled_font, + uint32_t ucs4); + +const cairo_scaled_font_backend_t _cairo_dwrite_scaled_font_backend = { + CAIRO_FONT_TYPE_DWRITE, + _cairo_dwrite_scaled_font_fini, + _cairo_dwrite_scaled_glyph_init, + NULL, /* text_to_glyphs */ + _cairo_dwrite_ucs4_to_index, + _cairo_dwrite_load_truetype_table, + NULL, /* index_to_ucs4 */ + NULL, /* is_synthetic */ + NULL, /* index_to_glyph_name */ + NULL, /* load_type1_data */ + NULL, /* has_color_glyphs */ +}; + +/* Helper conversion functions */ + + +/** + * Get a DirectWrite matrix from a cairo matrix. Note that DirectWrite uses row + * vectors where cairo uses column vectors. Hence the transposition. + * + * \param Cairo matrix + * \return DirectWrite matrix + */ +DWRITE_MATRIX +_cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix) +{ + DWRITE_MATRIX dwmat; + dwmat.m11 = (FLOAT)matrix->xx; + dwmat.m12 = (FLOAT)matrix->yx; + dwmat.m21 = (FLOAT)matrix->xy; + dwmat.m22 = (FLOAT)matrix->yy; + dwmat.dx = (FLOAT)matrix->x0; + dwmat.dy = (FLOAT)matrix->y0; + return dwmat; +} + +/* Helper functions for cairo_dwrite_scaled_glyph_init */ +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_metrics + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_surface + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_path + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +/* implement the font backend interface */ + +static cairo_status_t +_cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + WCHAR *face_name; + int face_name_len; + + if (!DWriteFactory::Instance()) { + return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; + } + + face_name_len = MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, NULL, 0); + face_name = new WCHAR[face_name_len]; + MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, face_name, face_name_len); + + IDWriteFontFamily *family = DWriteFactory::FindSystemFontFamily(face_name); + delete face_name; + if (!family) { + *font_face = (cairo_font_face_t*)&_cairo_font_face_nil; + return CAIRO_STATUS_FONT_TYPE_MISMATCH; + } + + DWRITE_FONT_WEIGHT weight; + switch (toy_face->weight) { + case CAIRO_FONT_WEIGHT_BOLD: + weight = DWRITE_FONT_WEIGHT_BOLD; + break; + case CAIRO_FONT_WEIGHT_NORMAL: + default: + weight = DWRITE_FONT_WEIGHT_NORMAL; + break; + } + + DWRITE_FONT_STYLE style; + switch (toy_face->slant) { + case CAIRO_FONT_SLANT_ITALIC: + style = DWRITE_FONT_STYLE_ITALIC; + break; + case CAIRO_FONT_SLANT_OBLIQUE: + style = DWRITE_FONT_STYLE_OBLIQUE; + break; + case CAIRO_FONT_SLANT_NORMAL: + default: + style = DWRITE_FONT_STYLE_NORMAL; + break; + } + + cairo_dwrite_font_face_t *face = (cairo_dwrite_font_face_t*)malloc(sizeof(cairo_dwrite_font_face_t)); + HRESULT hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &face->font); + if (SUCCEEDED(hr)) { + // Cannot use C++ style new since cairo deallocates this. + *font_face = (cairo_font_face_t*)face; + _cairo_font_face_init (&(*(_cairo_dwrite_font_face**)font_face)->base, &_cairo_dwrite_font_face_backend); + } else { + free(face); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_dwrite_font_face_destroy (void *font_face) +{ + cairo_dwrite_font_face_t *dwrite_font_face = static_cast(font_face); + if (dwrite_font_face->dwriteface) + dwrite_font_face->dwriteface->Release(); + if (dwrite_font_face->font) + dwrite_font_face->font->Release(); + return TRUE; +} + + +static inline unsigned short +read_short(const char *buf) +{ + return be16_to_cpu(*(unsigned short*)buf); +} + +void +_cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, + int num_glyphs, + cairo_dwrite_scaled_font_t *scaled_font, + AutoDWriteGlyphRun *run, + cairo_bool_t *transformed) +{ + run->allocate(num_glyphs); + + UINT16 *indices = const_cast(run->glyphIndices); + FLOAT *advances = const_cast(run->glyphAdvances); + DWRITE_GLYPH_OFFSET *offsets = const_cast(run->glyphOffsets); + + cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->base.font_face); + + run->bidiLevel = 0; + run->fontFace = dwriteff->dwriteface; + run->glyphCount = num_glyphs; + run->isSideways = FALSE; + + if (scaled_font->mat.xy == 0 && scaled_font->mat.yx == 0 && + scaled_font->mat.xx == scaled_font->base.font_matrix.xx && + scaled_font->mat.yy == scaled_font->base.font_matrix.yy) { + // Fast route, don't actually use a transform but just + // set the correct font size. + *transformed = 0; + + run->fontEmSize = (FLOAT)scaled_font->base.font_matrix.yy; + + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + + offsets[i].ascenderOffset = -(FLOAT)(glyphs[i].y); + offsets[i].advanceOffset = (FLOAT)(glyphs[i].x); + advances[i] = 0.0; + } + } else { + *transformed = 1; + // Transforming positions by the inverse matrix, then by the original + // matrix later may introduce small errors, especially because the + // D2D matrix is single-precision whereas the cairo one is double. + // This is a problem when glyph positions were originally at exactly + // half-pixel locations, which eventually round to whole pixels for + // GDI rendering - the errors introduced here cause them to round in + // unpredictable directions, instead of all rounding in a consistent + // way, leading to poor glyph spacing (bug 675383). + // To mitigate this, nudge the positions by a tiny amount to try and + // ensure that after the two transforms, they'll still round in a + // consistent direction. + const double EPSILON = 0.0001; + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + double x = glyphs[i].x + EPSILON; + double y = glyphs[i].y; + cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); + // Since we will multiply by our ctm matrix later for rotation effects + // and such, adjust positions by the inverse matrix now. Y-axis is + // inverted! Therefor the offset is -y. + offsets[i].ascenderOffset = -(FLOAT)y; + offsets[i].advanceOffset = (FLOAT)x; + advances[i] = 0.0; + } + // The font matrix takes care of the scaling if we have a transform, + // emSize should be 1. + run->fontEmSize = 1.0f; + } +} + +#define GASP_TAG 0x70736167 +#define GASP_DOGRAY 0x2 + +static cairo_bool_t +do_grayscale(IDWriteFontFace *dwface, unsigned int ppem) +{ + void *tableContext; + char *tableData; + UINT32 tableSize; + BOOL exists; + dwface->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); + + if (exists) { + if (tableSize < 4) { + dwface->ReleaseFontTable(tableContext); + return true; + } + struct gaspRange { + unsigned short maxPPEM; // Stored big-endian + unsigned short behavior; // Stored big-endian + }; + unsigned short numRanges = read_short(tableData + 2); + if (tableSize < (UINT)4 + numRanges * 4) { + dwface->ReleaseFontTable(tableContext); + return true; + } + gaspRange *ranges = (gaspRange *)(tableData + 4); + for (int i = 0; i < numRanges; i++) { + if (be16_to_cpu(ranges[i].maxPPEM) > ppem) { + if (!(be16_to_cpu(ranges[i].behavior) & GASP_DOGRAY)) { + dwface->ReleaseFontTable(tableContext); + return false; + } + break; + } + } + dwface->ReleaseFontTable(tableContext); + } + return true; +} + +static cairo_status_t +_cairo_dwrite_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) +{ + cairo_dwrite_font_face_t *font_face = static_cast(abstract_face); + + // Must do malloc and not C++ new, since Cairo frees this. + cairo_dwrite_scaled_font_t *dwriteFont = (cairo_dwrite_scaled_font_t*)malloc(sizeof(cairo_dwrite_scaled_font_t)); + *font = reinterpret_cast(dwriteFont); + _cairo_scaled_font_init(&dwriteFont->base, &font_face->base, font_matrix, ctm, options, &_cairo_dwrite_scaled_font_backend); + + cairo_font_extents_t extents; + + DWRITE_FONT_METRICS metrics; + font_face->dwriteface->GetMetrics(&metrics); + + extents.ascent = (FLOAT)metrics.ascent / metrics.designUnitsPerEm; + extents.descent = (FLOAT)metrics.descent / metrics.designUnitsPerEm; + extents.height = (FLOAT)(metrics.ascent + metrics.descent + metrics.lineGap) / metrics.designUnitsPerEm; + extents.max_x_advance = 14.0; + extents.max_y_advance = 0.0; + + dwriteFont->mat = dwriteFont->base.ctm; + cairo_matrix_multiply(&dwriteFont->mat, &dwriteFont->mat, font_matrix); + dwriteFont->mat_inverse = dwriteFont->mat; + cairo_matrix_invert (&dwriteFont->mat_inverse); + + cairo_antialias_t default_quality = CAIRO_ANTIALIAS_SUBPIXEL; + + dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_NATURAL; + + // The following code detects the system quality at scaled_font creation time, + // this means that if cleartype settings are changed but the scaled_fonts + // are re-used, they might not adhere to the new system setting until re- + // creation. + switch (cairo_win32_get_system_text_quality()) { + case CLEARTYPE_QUALITY: + default_quality = CAIRO_ANTIALIAS_SUBPIXEL; + break; + case ANTIALIASED_QUALITY: + default_quality = CAIRO_ANTIALIAS_GRAY; + dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + case DEFAULT_QUALITY: + // _get_system_quality() seems to think aliased is default! + default_quality = CAIRO_ANTIALIAS_NONE; + dwriteFont->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + } + + if (default_quality == CAIRO_ANTIALIAS_GRAY) { + if (!do_grayscale(font_face->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) { + default_quality = CAIRO_ANTIALIAS_NONE; + } + } + + if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) { + dwriteFont->antialias_mode = default_quality; + } else { + dwriteFont->antialias_mode = options->antialias; + } + + dwriteFont->rendering_mode = + default_quality == CAIRO_ANTIALIAS_SUBPIXEL ? + cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL : cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE; + + return _cairo_scaled_font_set_metrics (*font, &extents); +} + +/* Implementation cairo_dwrite_scaled_font_backend_t */ +void +_cairo_dwrite_scaled_font_fini(void *scaled_font) +{ +} + +static cairo_int_status_t +_cairo_dwrite_scaled_glyph_init(void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_dwrite_scaled_font_t *scaled_dwrite_font = static_cast(scaled_font); + cairo_int_status_t status; + + if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { + status = _cairo_dwrite_scaled_font_init_glyph_metrics (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + status = _cairo_dwrite_scaled_font_init_glyph_surface (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { + status = _cairo_dwrite_scaled_font_init_glyph_path (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +unsigned long +_cairo_dwrite_ucs4_to_index(void *scaled_font, + uint32_t ucs4) +{ + cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); + cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); + + UINT16 index; + face->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index); + return index; +} + +/* cairo_dwrite_scaled_glyph_init helper function bodies */ +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + UINT16 charIndex = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); + cairo_dwrite_font_face_t *font_face = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; + cairo_text_extents_t extents; + + DWRITE_GLYPH_METRICS metrics; + DWRITE_FONT_METRICS fontMetrics; + font_face->dwriteface->GetMetrics(&fontMetrics); + HRESULT hr = font_face->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + // TODO: Treat swap_xy. + extents.width = (FLOAT)(metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing) / + fontMetrics.designUnitsPerEm; + extents.height = (FLOAT)(metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing) / + fontMetrics.designUnitsPerEm; + extents.x_advance = (FLOAT)metrics.advanceWidth / fontMetrics.designUnitsPerEm; + extents.x_bearing = (FLOAT)metrics.leftSideBearing / fontMetrics.designUnitsPerEm; + extents.y_advance = 0.0; + extents.y_bearing = (FLOAT)(metrics.topSideBearing - metrics.verticalOriginY) / + fontMetrics.designUnitsPerEm; + + // We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics + // for the glyph outline, without accounting for hinting/gridfitting/antialiasing, + // and therefore it does not always cover all pixels that will actually be touched. + if (scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE && + extents.width > 0 && extents.height > 0) { + extents.width += scaled_font->mat_inverse.xx * 2; + extents.x_bearing -= scaled_font->mat_inverse.xx; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + return CAIRO_INT_STATUS_SUCCESS; +} + +/** + * Stack-based helper implementing IDWriteGeometrySink. + * Used to determine the path of the glyphs. + */ + +class GeometryRecorder : public IDWriteGeometrySink +{ +public: + GeometryRecorder(cairo_path_fixed_t *aCairoPath) + : mCairoPath(aCairoPath) {} + + // IUnknown interface + IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) + { + if (iid != __uuidof(IDWriteGeometrySink)) + return E_NOINTERFACE; + + *ppObject = static_cast(this); + + return S_OK; + } + + IFACEMETHOD_(ULONG, AddRef)() + { + return 1; + } + + IFACEMETHOD_(ULONG, Release)() + { + return 1; + } + + IFACEMETHODIMP_(void) SetFillMode(D2D1_FILL_MODE fillMode) + { + return; + } + + STDMETHODIMP Close() + { + return S_OK; + } + + IFACEMETHODIMP_(void) SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) + { + return; + } + + cairo_fixed_t GetFixedX(const D2D1_POINT_2F &point) + { + unsigned int control_word; + _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); + return _cairo_fixed_from_double(point.x); + } + + cairo_fixed_t GetFixedY(const D2D1_POINT_2F &point) + { + unsigned int control_word; + _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); + return _cairo_fixed_from_double(point.y); + } + + IFACEMETHODIMP_(void) BeginFigure( + D2D1_POINT_2F startPoint, + D2D1_FIGURE_BEGIN figureBegin) + { + mStartPoint = startPoint; + cairo_status_t status = _cairo_path_fixed_move_to(mCairoPath, + GetFixedX(startPoint), + GetFixedY(startPoint)); + } + + IFACEMETHODIMP_(void) EndFigure( + D2D1_FIGURE_END figureEnd) + { + if (figureEnd == D2D1_FIGURE_END_CLOSED) { + cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, + GetFixedX(mStartPoint), + GetFixedY(mStartPoint)); + } + } + + IFACEMETHODIMP_(void) AddBeziers( + const D2D1_BEZIER_SEGMENT *beziers, + UINT beziersCount) + { + for (unsigned int i = 0; i < beziersCount; i++) { + cairo_status_t status = _cairo_path_fixed_curve_to(mCairoPath, + GetFixedX(beziers[i].point1), + GetFixedY(beziers[i].point1), + GetFixedX(beziers[i].point2), + GetFixedY(beziers[i].point2), + GetFixedX(beziers[i].point3), + GetFixedY(beziers[i].point3)); + } + } + + IFACEMETHODIMP_(void) AddLines( + const D2D1_POINT_2F *points, + UINT pointsCount) + { + for (unsigned int i = 0; i < pointsCount; i++) { + cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, + GetFixedX(points[i]), + GetFixedY(points[i])); + } + } + +private: + cairo_path_fixed_t *mCairoPath; + D2D1_POINT_2F mStartPoint; +}; + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_path_fixed_t *path; + path = _cairo_path_fixed_create(); + GeometryRecorder recorder(path); + + DWRITE_GLYPH_OFFSET offset; + offset.advanceOffset = 0; + offset.ascenderOffset = 0; + UINT16 glyphId = (UINT16)_cairo_scaled_glyph_index(scaled_glyph); + FLOAT advance = 0.0; + cairo_dwrite_font_face_t *dwriteff = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; + dwriteff->dwriteface->GetGlyphRunOutline((FLOAT)scaled_font->base.font_matrix.yy, + &glyphId, + &advance, + &offset, + 1, + FALSE, + FALSE, + &recorder); + _cairo_path_fixed_close_path(path); + + /* Now apply our transformation to the drawn path. */ + _cairo_path_fixed_transform(path, &scaled_font->base.ctm); + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + return CAIRO_INT_STATUS_SUCCESS; +} + +/* Helper function adapted from _compute_mask in cairo-win32-font.c */ + +/* Compute an alpha-mask from a monochrome RGB24 image + */ +static cairo_surface_t * +_compute_a8_mask (cairo_surface_t *surface) +{ + cairo_image_surface_t *glyph; + cairo_image_surface_t *mask; + int i, j; + + glyph = (cairo_image_surface_t *)cairo_surface_map_to_image (surface, NULL); + if (unlikely (glyph->base.status)) + return &glyph->base; + + /* No quality param, just use the non-ClearType path */ + + /* Compute an alpha-mask by using the green channel of a (presumed monochrome) + * RGB24 image. + */ + mask = (cairo_image_surface_t *) + cairo_image_surface_create (CAIRO_FORMAT_A8, glyph->width, glyph->height); + if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { + for (i = 0; i < glyph->height; i++) { + uint32_t *p = (uint32_t *) (glyph->data + i * glyph->stride); + uint8_t *q = (uint8_t *) (mask->data + i * mask->stride); + + for (j = 0; j < glyph->width; j++) + *q++ = 255 - ((*p++ & 0x0000ff00) >> 8); + } + } + + cairo_surface_unmap_image (surface, &glyph->base); + return &mask->base; +} + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_int_status_t status; + cairo_glyph_t glyph; + cairo_win32_surface_t *surface; + cairo_t *cr; + cairo_surface_t *image; + int width, height; + double x1, y1, x2, y2; + + x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); + y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + width = (int)(x2 - x1); + height = (int)(y2 - y1); + + glyph.index = _cairo_scaled_glyph_index (scaled_glyph); + glyph.x = -x1; + glyph.y = -y1; + + DWRITE_GLYPH_RUN run; + FLOAT advance = 0; + UINT16 index = (UINT16)glyph.index; + DWRITE_GLYPH_OFFSET offset; + double x = glyph.x; + double y = glyph.y; + RECT area; + DWRITE_MATRIX matrix; + + 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_int_status_t)cairo_status (cr); + cairo_destroy(cr); + if (status) + goto FAIL; + + /** + * We transform by the inverse transformation here. This will put our glyph + * locations in the space in which we draw. Which is later transformed by + * the transformation matrix that we use. This will transform the + * glyph positions back to where they were before when drawing, but the + * glyph shapes will be transformed by the transformation matrix. + */ + cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); + offset.advanceOffset = (FLOAT)x; + /** Y-axis is inverted */ + offset.ascenderOffset = -(FLOAT)y; + + area.top = 0; + area.bottom = height; + area.left = 0; + area.right = width; + + run.glyphCount = 1; + run.glyphAdvances = &advance; + run.fontFace = ((cairo_dwrite_font_face_t*)scaled_font->base.font_face)->dwriteface; + run.fontEmSize = 1.0f; + run.bidiLevel = 0; + run.glyphIndices = &index; + run.isSideways = FALSE; + run.glyphOffsets = &offset; + + matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); + + status = _dwrite_draw_glyphs_to_gdi_surface_gdi (surface, &matrix, &run, + RGB(0,0,0), scaled_font, area); + if (status) + goto FAIL; + + GdiFlush(); + + image = _compute_a8_mask (&surface->base); + status = (cairo_int_status_t)image->status; + if (status) + goto FAIL; + + cairo_surface_set_device_offset (image, -x1, -y1); + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) image); + + FAIL: + cairo_surface_destroy (&surface->base); + + return status; +} + +cairo_int_status_t +_cairo_dwrite_load_truetype_table(void *scaled_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); + cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); + + const void *data; + UINT32 size; + void *tableContext; + BOOL exists; + face->dwriteface->TryGetFontTable(be32_to_cpu (tag), + &data, + &size, + &tableContext, + &exists); + + if (!exists) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (buffer && *length && (UINT32)offset < size) { + size = MIN(size - (UINT32)offset, *length); + memcpy(buffer, (const char*)data + offset, size); + } + *length = size; + + if (tableContext) { + face->dwriteface->ReleaseFontTable(tableContext); + } + return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; +} + +// WIN32 Helper Functions +cairo_font_face_t* +cairo_dwrite_font_face_create_for_dwrite_fontface(void* dwrite_font, void* dwrite_font_face) +{ + IDWriteFont *dwritefont = static_cast(dwrite_font); + IDWriteFontFace *dwriteface = static_cast(dwrite_font_face); + cairo_dwrite_font_face_t *face = new cairo_dwrite_font_face_t; + cairo_font_face_t *font_face; + + dwriteface->AddRef(); + + face->dwriteface = dwriteface; + face->font = NULL; + + font_face = (cairo_font_face_t*)face; + + _cairo_font_face_init (&((cairo_dwrite_font_face_t*)font_face)->base, &_cairo_dwrite_font_face_backend); + + return font_face; +} + +void +cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force) +{ + cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); + if (force && font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL) { + font->rendering_mode = cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC; + } else if (!force && font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC) { + font->rendering_mode = cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL; + } +} + +cairo_bool_t +cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font) +{ + cairo_dwrite_scaled_font_t *font = reinterpret_cast(dwrite_scaled_font); + return font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC; +} + +void +cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, + int geometry, int mode) +{ + DWriteFactory::SetRenderingParams(gamma, contrast, level, geometry, mode); +} + +int +cairo_dwrite_get_cleartype_rendering_mode() +{ + return DWriteFactory::GetClearTypeRenderingMode(); +} + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + cairo_dwrite_scaled_font_t *scaled_font, + const RECT &area) +{ + IDWriteGdiInterop *gdiInterop; + DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); + IDWriteBitmapRenderTarget *rt; + HRESULT rv; + + cairo_dwrite_scaled_font_t::TextRenderingState renderingState = + scaled_font->rendering_mode; + + rv = gdiInterop->CreateBitmapRenderTarget(surface->dc, + area.right - area.left, + area.bottom - area.top, + &rt); + + if (FAILED(rv)) { + if (rv == E_OUTOFMEMORY) { + return (cairo_int_status_t)CAIRO_STATUS_NO_MEMORY; + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if ((renderingState == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL || + renderingState == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC) && + !surface->base.permit_subpixel_antialiasing) { + renderingState = cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE; + IDWriteBitmapRenderTarget1* rt1; + rv = rt->QueryInterface(&rt1); + + if (SUCCEEDED(rv) && rt1) { + rt1->SetTextAntialiasMode(DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE); + rt1->Release(); + } + } + + IDWriteRenderingParams *params = + DWriteFactory::RenderingParams(renderingState); + + /** + * We set the number of pixels per DIP to 1.0. This is because we always want + * to draw in device pixels, and not device independent pixels. On high DPI + * systems this value will be higher than 1.0 and automatically upscale + * fonts, we don't want this since we do our own upscaling for various reasons. + */ + rt->SetPixelsPerDip(1.0); + + if (transform) { + rt->SetCurrentTransform(transform); + } + BitBlt(rt->GetMemoryDC(), + 0, 0, + area.right - area.left, area.bottom - area.top, + surface->dc, + area.left, area.top, + SRCCOPY | NOMIRRORBITMAP); + DWRITE_MEASURING_MODE measureMode; + switch (renderingState) { + case cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC: + case cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE: + measureMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + default: + measureMode = DWRITE_MEASURING_MODE_NATURAL; + break; + } + HRESULT hr = rt->DrawGlyphRun(0, 0, measureMode, run, params, color); + BitBlt(surface->dc, + area.left, area.top, + area.right - area.left, area.bottom - area.top, + rt->GetMemoryDC(), + 0, 0, + SRCCOPY | NOMIRRORBITMAP); + params->Release(); + rt->Release(); + gdiInterop->Release(); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + const RECT &area) +{ + HRESULT rv; + + ID2D1DCRenderTarget *rt = D2DFactory::RenderTarget(); + + // XXX don't we need to set RenderingParams on this RenderTarget? + + rv = rt->BindDC(surface->dc, &area); + + printf("Rendering to surface: %p\n", surface->dc); + + if (FAILED(rv)) { + rt->Release(); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + // D2D uses 0x00RRGGBB not 0x00BBGGRR like COLORREF. + color = (color & 0xFF) << 16 | + (color & 0xFF00) | + (color & 0xFF0000) >> 16; + ID2D1SolidColorBrush *brush; + rv = rt->CreateSolidColorBrush(D2D1::ColorF(color, 1.0), &brush); + + if (FAILED(rv)) { + rt->Release(); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (transform) { + rt->SetTransform(D2D1::Matrix3x2F(transform->m11, + transform->m12, + transform->m21, + transform->m22, + transform->dx, + transform->dy)); + } + rt->BeginDraw(); + rt->DrawGlyphRun(D2D1::Point2F(0, 0), run, brush); + rt->EndDraw(); + if (transform) { + rt->SetTransform(D2D1::Matrix3x2F::Identity()); + } + brush->Release(); + if (FAILED(rv)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +/* Surface helper function */ +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) +{ + // TODO: Check font & surface for types. + cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); + cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); + cairo_win32_surface_t *dst = reinterpret_cast(surface); + cairo_int_status_t status; + /* We can only handle dwrite fonts */ + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) + 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) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* If we have a fallback mask clip set on the dst, we have + * to go through the fallback path */ + if (!_cairo_surface_is_win32_printing (&dst->base)) { + if (clip != NULL) + _cairo_win32_display_surface_set_clip (to_win32_display_surface (dst), clip); + else + _cairo_win32_display_surface_unset_clip (to_win32_display_surface (dst)); + } + + /* It is vital that dx values for dxy_buf are calculated from the delta of + * _logical_ x coordinates (not user x coordinates) or else the sum of all + * previous dx values may start to diverge from the current glyph's x + * coordinate due to accumulated rounding error. As a result strings could + * be painted shorter or longer than expected. */ + + AutoDWriteGlyphRun run; + run.allocate(num_glyphs); + + UINT16 *indices = const_cast(run.glyphIndices); + FLOAT *advances = const_cast(run.glyphAdvances); + DWRITE_GLYPH_OFFSET *offsets = const_cast(run.glyphOffsets); + + BOOL transform = FALSE; + /* Needed to calculate bounding box for efficient blitting */ + INT32 smallestX = INT_MAX; + INT32 largestX = 0; + INT32 smallestY = INT_MAX; + INT32 largestY = 0; + for (int i = 0; i < num_glyphs; i++) { + if (glyphs[i].x < smallestX) { + smallestX = (INT32)glyphs[i].x; + } + if (glyphs[i].x > largestX) { + largestX = (INT32)glyphs[i].x; + } + if (glyphs[i].y < smallestY) { + smallestY = (INT32)glyphs[i].y; + } + if (glyphs[i].y > largestY) { + largestY = (INT32)glyphs[i].y; + } + } + /** + * Here we try to get a rough estimate of the area that this glyph run will + * cover on the surface. Since we use GDI interop to draw we will be copying + * data around the size of the area of the surface that we map. We will want + * to map an area as small as possible to prevent large surfaces to be + * copied around. We take the X/Y-size of the font as margin on the left/top + * twice the X/Y-size of the font as margin on the right/bottom. + * This should always cover the entire area where the glyphs are. + */ + RECT fontArea; + fontArea.left = (INT32)(smallestX - scaled_font->font_matrix.xx); + fontArea.right = (INT32)(largestX + scaled_font->font_matrix.xx * 2); + fontArea.top = (INT32)(smallestY - scaled_font->font_matrix.yy); + fontArea.bottom = (INT32)(largestY + scaled_font->font_matrix.yy * 2); + if (fontArea.left < 0) + fontArea.left = 0; + if (fontArea.top < 0) + fontArea.top = 0; + if (fontArea.bottom > dst->extents.height) { + fontArea.bottom = dst->extents.height; + } + if (fontArea.right > dst->extents.width) { + fontArea.right = dst->extents.width; + } + if (fontArea.right <= fontArea.left || + fontArea.bottom <= fontArea.top) { + return CAIRO_INT_STATUS_SUCCESS; + } + if (fontArea.right > dst->extents.width) { + fontArea.right = dst->extents.width; + } + if (fontArea.bottom > dst->extents.height) { + fontArea.bottom = dst->extents.height; + } + + run.bidiLevel = 0; + run.fontFace = dwriteff->dwriteface; + run.isSideways = FALSE; + if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 && + dwritesf->mat.xx == scaled_font->font_matrix.xx && + dwritesf->mat.yy == scaled_font->font_matrix.yy) { + + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + // Since we will multiply by our ctm matrix later for rotation effects + // and such, adjust positions by the inverse matrix now. + offsets[i].ascenderOffset = (FLOAT)(fontArea.top - glyphs[i].y); + offsets[i].advanceOffset = (FLOAT)(glyphs[i].x - fontArea.left); + advances[i] = 0.0; + } + run.fontEmSize = (FLOAT)scaled_font->font_matrix.yy; + } else { + transform = TRUE; + // See comment about EPSILON in _cairo_dwrite_glyph_run_from_glyphs + const double EPSILON = 0.0001; + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + double x = glyphs[i].x - fontArea.left + EPSILON; + double y = glyphs[i].y - fontArea.top; + cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); + /** + * Since we will multiply by our ctm matrix later for rotation effects + * and such, adjust positions by the inverse matrix now. The Y-axis + * is inverted so the offset becomes negative. + */ + offsets[i].ascenderOffset = -(FLOAT)y; + offsets[i].advanceOffset = (FLOAT)x; + advances[i] = 0.0; + } + run.fontEmSize = 1.0f; + } + + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; + COLORREF color = RGB(((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); + + DWRITE_MATRIX matrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); + + DWRITE_MATRIX *mat; + if (transform) { + mat = &matrix; + } else { + mat = NULL; + } + + RECT area; + area.left = dst->extents.x; + area.top = dst->extents.y; + area.right = area.left + dst->extents.width; + area.bottom = area.top + dst->extents.height; + +#ifdef CAIRO_TRY_D2D_TO_GDI + status = _dwrite_draw_glyphs_to_gdi_surface_d2d(dst, + mat, + &run, + color, + fontArea); + + if (status == (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED) { +#endif + status = _dwrite_draw_glyphs_to_gdi_surface_gdi(dst, + mat, + &run, + color, + dwritesf, + fontArea); + +#ifdef CAIRO_TRY_D2D_TO_GDI + } +#endif + + return status; +} + +#define ENHANCED_CONTRAST_REGISTRY_KEY \ + HKEY_CURRENT_USER, "Software\\Microsoft\\Avalon.Graphics\\DISPLAY1\\EnhancedContrastLevel" + +void +DWriteFactory::CreateRenderingParams() +{ + if (!Instance()) { + return; + } + + Instance()->CreateRenderingParams(&mDefaultRenderingParams); + + // For EnhancedContrast, we override the default if the user has not set it + // in the registry (by using the ClearType Tuner). + FLOAT contrast; + if (mEnhancedContrast >= 0.0 && mEnhancedContrast <= 10.0) { + contrast = mEnhancedContrast; + } else { + HKEY hKey; + if (RegOpenKeyExA(ENHANCED_CONTRAST_REGISTRY_KEY, + 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + contrast = mDefaultRenderingParams->GetEnhancedContrast(); + RegCloseKey(hKey); + } else { + contrast = 1.0; + } + } + + // For parameters that have not been explicitly set via the SetRenderingParams API, + // we copy values from default params (or our overridden value for contrast) + FLOAT gamma = + mGamma >= 1.0 && mGamma <= 2.2 ? + mGamma : mDefaultRenderingParams->GetGamma(); + FLOAT clearTypeLevel = + mClearTypeLevel >= 0.0 && mClearTypeLevel <= 1.0 ? + mClearTypeLevel : mDefaultRenderingParams->GetClearTypeLevel(); + DWRITE_PIXEL_GEOMETRY pixelGeometry = + mPixelGeometry >= DWRITE_PIXEL_GEOMETRY_FLAT && mPixelGeometry <= DWRITE_PIXEL_GEOMETRY_BGR ? + (DWRITE_PIXEL_GEOMETRY)mPixelGeometry : mDefaultRenderingParams->GetPixelGeometry(); + DWRITE_RENDERING_MODE renderingMode = + mRenderingMode >= DWRITE_RENDERING_MODE_DEFAULT && mRenderingMode <= DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC ? + (DWRITE_RENDERING_MODE)mRenderingMode : mDefaultRenderingParams->GetRenderingMode(); + Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, + pixelGeometry, renderingMode, + &mCustomClearTypeRenderingParams); + Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, + pixelGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, + &mForceGDIClassicRenderingParams); +} + +static cairo_bool_t +_name_tables_match (cairo_scaled_font_t *font1, + cairo_scaled_font_t *font2) +{ + unsigned long size1; + unsigned long size2; + cairo_int_status_t status1; + cairo_int_status_t status2; + unsigned char *buffer1; + unsigned char *buffer2; + cairo_bool_t result = false; + + if (!font1->backend || !font2->backend || + !font1->backend->load_truetype_table || + !font2->backend->load_truetype_table) + return false; + + status1 = font1->backend->load_truetype_table (font1, + TT_TAG_name, 0, NULL, &size1); + status2 = font2->backend->load_truetype_table (font2, + TT_TAG_name, 0, NULL, &size2); + if (status1 || status2) + return false; + if (size1 != size2) + return false; + + buffer1 = (unsigned char*)malloc (size1); + buffer2 = (unsigned char*)malloc (size2); + + if (buffer1 && buffer2) { + status1 = font1->backend->load_truetype_table (font1, + TT_TAG_name, 0, buffer1, &size1); + status2 = font2->backend->load_truetype_table (font2, + TT_TAG_name, 0, buffer2, &size2); + if (!status1 && !status2) { + result = memcmp (buffer1, buffer2, size1) == 0; + } + } + + free (buffer1); + free (buffer2); + return result; +} + +// Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent +// of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing +// paths or blitting glyph bitmaps. +cairo_int_status_t +_cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font, + cairo_scaled_font_t **new_font) +{ + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_font_face_t *face = cairo_scaled_font_get_font_face (scaled_font); + cairo_dwrite_font_face_t *dwface = reinterpret_cast(face); + + RefPtr gdiInterop; + DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); + if (!gdiInterop) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + LOGFONTW logfont; + if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + // DW must have been using an outline font, so we want GDI to use the same, + // even if there's also a bitmap face available + logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; + + cairo_font_face_t *win32_face = cairo_win32_font_face_create_for_logfontw (&logfont); + if (!win32_face) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_matrix_t font_matrix; + cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); + + cairo_matrix_t ctm; + cairo_scaled_font_get_ctm (scaled_font, &ctm); + + cairo_font_options_t options; + cairo_scaled_font_get_font_options (scaled_font, &options); + + cairo_scaled_font_t *font = cairo_scaled_font_create (win32_face, + &font_matrix, + &ctm, + &options); + cairo_font_face_destroy (win32_face); + + if (!font) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (!_name_tables_match (font, scaled_font)) { + // If the font name tables aren't equal, then GDI may have failed to + // find the right font and substituted a different font. + cairo_scaled_font_destroy (font); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *new_font = font; + return CAIRO_INT_STATUS_SUCCESS; +} diff --git a/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h b/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h new file mode 100644 index 0000000000..5f2994cbc0 --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-dwrite-private.h @@ -0,0 +1,229 @@ +/* -*- 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 + * + * 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 the Mozilla Foundation + * + * Contributor(s): + * Bas Schouten + */ +#include +#include + +// DirectWrite is not available on all platforms. +typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)( + DWRITE_FACTORY_TYPE factoryType, + REFIID iid, + IUnknown **factory +); + +/* cairo_scaled_font_t implementation */ +struct _cairo_dwrite_scaled_font { + cairo_scaled_font_t base; + cairo_matrix_t mat; + cairo_matrix_t mat_inverse; + cairo_antialias_t antialias_mode; + DWRITE_MEASURING_MODE measuring_mode; + enum TextRenderingState { + TEXT_RENDERING_UNINITIALIZED, + TEXT_RENDERING_NO_CLEARTYPE, + TEXT_RENDERING_NORMAL, + TEXT_RENDERING_GDI_CLASSIC + }; + TextRenderingState rendering_mode; +}; +typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t; + +class DWriteFactory +{ +public: + static IDWriteFactory *Instance() + { + if (!mFactoryInstance) { + DWriteCreateFactoryFunc createDWriteFactory = (DWriteCreateFactoryFunc) + GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"); + if (createDWriteFactory) { + HRESULT hr = createDWriteFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast(&mFactoryInstance)); + assert(SUCCEEDED(hr)); + } + } + return mFactoryInstance; + } + + static IDWriteFontCollection *SystemCollection() + { + if (!mSystemCollection) { + if (Instance()) { + HRESULT hr = Instance()->GetSystemFontCollection(&mSystemCollection); + assert(SUCCEEDED(hr)); + } + } + return mSystemCollection; + } + + static IDWriteFontFamily *FindSystemFontFamily(const WCHAR *aFamilyName) + { + UINT32 idx; + BOOL found; + if (!SystemCollection()) { + return NULL; + } + SystemCollection()->FindFamilyName(aFamilyName, &idx, &found); + if (!found) { + return NULL; + } + + IDWriteFontFamily *family; + SystemCollection()->GetFontFamily(idx, &family); + return family; + } + + static IDWriteRenderingParams *RenderingParams(cairo_dwrite_scaled_font_t::TextRenderingState mode) + { + if (!mDefaultRenderingParams || + !mForceGDIClassicRenderingParams || + !mCustomClearTypeRenderingParams) + { + CreateRenderingParams(); + } + IDWriteRenderingParams *params; + if (mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE) { + params = mDefaultRenderingParams; + } else if (mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC && mRenderingMode < 0) { + params = mForceGDIClassicRenderingParams; + } else { + params = mCustomClearTypeRenderingParams; + } + if (params) { + params->AddRef(); + } + return params; + } + + static void SetRenderingParams(FLOAT aGamma, + FLOAT aEnhancedContrast, + FLOAT aClearTypeLevel, + int aPixelGeometry, + int aRenderingMode) + { + mGamma = aGamma; + mEnhancedContrast = aEnhancedContrast; + mClearTypeLevel = aClearTypeLevel; + mPixelGeometry = aPixelGeometry; + mRenderingMode = aRenderingMode; + // discard any current RenderingParams objects + if (mCustomClearTypeRenderingParams) { + mCustomClearTypeRenderingParams->Release(); + mCustomClearTypeRenderingParams = NULL; + } + if (mForceGDIClassicRenderingParams) { + mForceGDIClassicRenderingParams->Release(); + mForceGDIClassicRenderingParams = NULL; + } + if (mDefaultRenderingParams) { + mDefaultRenderingParams->Release(); + mDefaultRenderingParams = NULL; + } + } + + static int GetClearTypeRenderingMode() { + return mRenderingMode; + } + +private: + static void CreateRenderingParams(); + + static IDWriteFactory *mFactoryInstance; + static IDWriteFontCollection *mSystemCollection; + static IDWriteRenderingParams *mDefaultRenderingParams; + static IDWriteRenderingParams *mCustomClearTypeRenderingParams; + static IDWriteRenderingParams *mForceGDIClassicRenderingParams; + static FLOAT mGamma; + static FLOAT mEnhancedContrast; + static FLOAT mClearTypeLevel; + static int mPixelGeometry; + static int mRenderingMode; +}; + +class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN +{ + static const int kNumAutoGlyphs = 256; + +public: + AutoDWriteGlyphRun() { + glyphCount = 0; + } + + ~AutoDWriteGlyphRun() { + if (glyphCount > kNumAutoGlyphs) { + delete[] glyphIndices; + delete[] glyphAdvances; + delete[] glyphOffsets; + } + } + + void allocate(int aNumGlyphs) { + glyphCount = aNumGlyphs; + if (aNumGlyphs <= kNumAutoGlyphs) { + glyphIndices = &mAutoIndices[0]; + glyphAdvances = &mAutoAdvances[0]; + glyphOffsets = &mAutoOffsets[0]; + } else { + glyphIndices = new UINT16[aNumGlyphs]; + glyphAdvances = new FLOAT[aNumGlyphs]; + glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs]; + } + } + +private: + DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs]; + FLOAT mAutoAdvances[kNumAutoGlyphs]; + UINT16 mAutoIndices[kNumAutoGlyphs]; +}; + +/* cairo_font_face_t implementation */ +struct _cairo_dwrite_font_face { + cairo_font_face_t base; + IDWriteFont *font; + IDWriteFontFace *dwriteface; +}; +typedef struct _cairo_dwrite_font_face cairo_dwrite_font_face_t; + +DWRITE_MATRIX _cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix); + +// This will initialize a DWrite glyph run from cairo glyphs and a scaled_font. +void +_cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, + int num_glyphs, + cairo_dwrite_scaled_font_t *scaled_font, + AutoDWriteGlyphRun *run, + cairo_bool_t *transformed); 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 0000000000..5a971bd614 --- /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 0000000000..6fce722ecf --- /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 0000000000..304d34aea0 --- /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/win32/cairo-win32-font.c b/gfx/cairo/cairo/src/win32/cairo-win32-font.c new file mode 100644 index 0000000000..3452c6cf4e --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-font.c @@ -0,0 +1,2334 @@ +/* -*- 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): + */ + +#define WIN32_LEAN_AND_MEAN +/* We require Windows 2000 features such as GetGlyphIndices */ +#if !defined(WINVER) || (WINVER < 0x0500) +# define WINVER 0x0500 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) +# define _WIN32_WINNT 0x0500 +#endif + +#include "cairoint.h" + +#include "cairo-win32-private.h" + +#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 + +#ifndef SPI_GETFONTSMOOTHINGTYPE +#define SPI_GETFONTSMOOTHINGTYPE 0x200a +#endif +#ifndef FE_FONTSMOOTHINGCLEARTYPE +#define FE_FONTSMOOTHINGCLEARTYPE 2 +#endif +#ifndef CLEARTYPE_QUALITY +#define CLEARTYPE_QUALITY 5 +#endif +#ifndef TT_PRIM_CSPLINE +#define TT_PRIM_CSPLINE 3 +#endif + +#define CMAP_TAG 0x70616d63 + +/** + * SECTION:cairo-win32-fonts + * @Title: Win32 Fonts + * @Short_Description: Font support for Microsoft Windows + * @See_Also: #cairo_font_face_t + * + * 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; + +typedef struct { + cairo_scaled_font_t base; + + LOGFONTW logfont; + + BYTE quality; + + /* We do drawing and metrics computation in a "logical space" which + * is similar to font space, except that it is scaled by a factor + * of the (desired font size) * (WIN32_FONT_LOGICAL_SCALE). The multiplication + * by WIN32_FONT_LOGICAL_SCALE allows for sub-pixel precision. + */ + double logical_scale; + + /* The size we should actually request the font at from Windows; differs + * from the logical_scale because it is quantized for orthogonal + * transformations + */ + double logical_size; + + /* Transformations from device <=> logical space + */ + cairo_matrix_t logical_to_device; + cairo_matrix_t device_to_logical; + + /* We special case combinations of 90-degree-rotations, scales and + * flips ... that is transformations that take the axes to the + * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y + * encode the 8 possibilities for orientation (4 rotation angles with + * and without a flip), and scale_x, scale_y the scale components. + */ + cairo_bool_t preserve_axes; + cairo_bool_t swap_axes; + cairo_bool_t swap_x; + cairo_bool_t swap_y; + double x_scale; + double y_scale; + + /* The size of the design unit of the font + */ + int em_square; + + HFONT scaled_hfont; + HFONT unscaled_hfont; + + 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 +_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) + +static HDC +_get_global_font_dc (void) +{ + 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) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + return NULL; + } + + if (!SetGraphicsMode (hdc, GM_ADVANCED)) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + DeleteDC (hdc); + return NULL; + } + + if (!TlsSetValue (hdc_tls_index, hdc)) { + DeleteDC (hdc); + return NULL; + } + } + + return hdc; +} + +static cairo_status_t +_compute_transform (cairo_win32_scaled_font_t *scaled_font, + cairo_matrix_t *sc) +{ + cairo_status_t status; + + if (NEARLY_ZERO (sc->yx) && NEARLY_ZERO (sc->xy) && + !NEARLY_ZERO(sc->xx) && !NEARLY_ZERO(sc->yy)) { + scaled_font->preserve_axes = TRUE; + scaled_font->x_scale = sc->xx; + scaled_font->swap_x = (sc->xx < 0); + scaled_font->y_scale = sc->yy; + scaled_font->swap_y = (sc->yy < 0); + scaled_font->swap_axes = FALSE; + + } else if (NEARLY_ZERO (sc->xx) && NEARLY_ZERO (sc->yy) && + !NEARLY_ZERO(sc->yx) && !NEARLY_ZERO(sc->xy)) { + scaled_font->preserve_axes = TRUE; + scaled_font->x_scale = sc->yx; + scaled_font->swap_x = (sc->yx < 0); + scaled_font->y_scale = sc->xy; + scaled_font->swap_y = (sc->xy < 0); + scaled_font->swap_axes = TRUE; + + } else { + scaled_font->preserve_axes = FALSE; + scaled_font->swap_x = scaled_font->swap_y = scaled_font->swap_axes = FALSE; + } + + if (scaled_font->preserve_axes) { + if (scaled_font->swap_x) + scaled_font->x_scale = - scaled_font->x_scale; + if (scaled_font->swap_y) + scaled_font->y_scale = - scaled_font->y_scale; + + scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; + scaled_font->logical_size = WIN32_FONT_LOGICAL_SCALE * + _cairo_lround (scaled_font->y_scale); + } + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. + */ + cairo_matrix_init (&scaled_font->logical_to_device, + sc->xx, sc->yx, sc->xy, sc->yy, 0, 0); + + if (!scaled_font->preserve_axes) { + status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->logical_to_device, + &scaled_font->x_scale, &scaled_font->y_scale, + TRUE); /* XXX: Handle vertical text */ + 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; + } + + cairo_matrix_scale (&scaled_font->logical_to_device, + 1.0 / scaled_font->logical_scale, + 1.0 / scaled_font->logical_scale); + + scaled_font->device_to_logical = scaled_font->logical_to_device; + + status = cairo_matrix_invert (&scaled_font->device_to_logical); + if (status) + cairo_matrix_init_identity (&scaled_font->device_to_logical); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_have_cleartype_quality (void) +{ + OSVERSIONINFO version_info; + + version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + + if (!GetVersionEx (&version_info)) { + _cairo_win32_print_gdi_error ("_have_cleartype_quality"); + return FALSE; + } + + return (version_info.dwMajorVersion > 5 || + (version_info.dwMajorVersion == 5 && + version_info.dwMinorVersion >= 1)); /* XP or newer */ +} + +BYTE +cairo_win32_get_system_text_quality (void) +{ + BOOL font_smoothing; + UINT smoothing_type; + + if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return DEFAULT_QUALITY; + } + + if (font_smoothing) { + if (_have_cleartype_quality ()) { + if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, + 0, &smoothing_type, 0)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return DEFAULT_QUALITY; + } + + if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) + return CLEARTYPE_QUALITY; + } + + return ANTIALIASED_QUALITY; + } else { + return DEFAULT_QUALITY; + } +} + +/* If face_hfont is non-%NULL then font_matrix must be a simple scale by some + * factor S, ctm must be the identity, logfont->lfHeight must be -S, + * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must + * all be 0, and face_hfont is the result of calling CreateFontIndirectW on + * logfont. + */ +static cairo_status_t +_win32_scaled_font_create (LOGFONTW *logfont, + HFONT face_hfont, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font_out) +{ + HDC hdc; + cairo_win32_scaled_font_t *f; + cairo_matrix_t scale; + cairo_status_t status; + + hdc = _get_global_font_dc (); + if (hdc == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f = _cairo_malloc (sizeof(cairo_win32_scaled_font_t)); + if (f == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f->logfont = *logfont; + + /* We don't have any control over the hinting style or subpixel + * order in the Win32 font API, so we ignore those parts of + * cairo_font_options_t. We use the 'antialias' field to set + * the 'quality'. + * + * XXX: The other option we could pay attention to, but don't + * here is the hint_metrics options. + */ + if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) + f->quality = cairo_win32_get_system_text_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 + f->quality = ANTIALIASED_QUALITY; + break; + case CAIRO_ANTIALIAS_DEFAULT: + ASSERT_NOT_REACHED; + } + } + + 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 && + options->antialias == CAIRO_ANTIALIAS_DEFAULT)) { + /* If face_hfont is non-NULL, then we can use it to avoid creating our + * own --- because the constraints on face_hfont mentioned above + * guarantee it was created in exactly the same way that + * _win32_scaled_font_get_scaled_hfont would create it. + */ + f->scaled_hfont = face_hfont; + } + /* don't delete the hfont if we're using the one passed in to us */ + f->delete_scaled_hfont = !f->scaled_hfont; + + cairo_matrix_multiply (&scale, font_matrix, ctm); + status = _compute_transform (f, &scale); + if (status) + goto FAIL; + + status = _cairo_scaled_font_init (&f->base, font_face, + font_matrix, ctm, options, + &_cairo_win32_scaled_font_backend); + if (status) + goto FAIL; + + status = _cairo_win32_scaled_font_set_metrics (f); + if (status) { + _cairo_scaled_font_fini (&f->base); + goto FAIL; + } + + *font_out = &f->base; + return CAIRO_STATUS_SUCCESS; + + FAIL: + free (f); + return status; +} + +static cairo_status_t +_win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font, + HDC hdc) +{ + XFORM xform; + + _cairo_matrix_to_win32_xform (&scaled_font->logical_to_device, &xform); + + if (!SetWorldTransform (hdc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_set_identity_transform (HDC hdc) +{ + if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font, + HFONT *hfont_out) +{ + if (!scaled_font->scaled_hfont) { + LOGFONTW logfont = scaled_font->logfont; + logfont.lfHeight = -scaled_font->logical_size; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = scaled_font->quality; + + scaled_font->scaled_hfont = CreateFontIndirectW (&logfont); + if (!scaled_font->scaled_hfont) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont"); + } + + *hfont_out = scaled_font->scaled_hfont; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, + HDC hdc, + HFONT *hfont_out) +{ + if (scaled_font->unscaled_hfont == NULL) { + OUTLINETEXTMETRIC *otm; + unsigned int otm_size; + HFONT scaled_hfont; + LOGFONTW logfont; + cairo_status_t status; + + status = _win32_scaled_font_get_scaled_hfont (scaled_font, + &scaled_hfont); + if (status) + return status; + + if (! SelectObject (hdc, scaled_hfont)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject"); + + otm_size = GetOutlineTextMetrics (hdc, 0, NULL); + if (! otm_size) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); + + otm = _cairo_malloc (otm_size); + if (otm == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (! GetOutlineTextMetrics (hdc, otm_size, otm)) { + status = _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); + free (otm); + return status; + } + + scaled_font->em_square = otm->otmEMSquare; + free (otm); + + logfont = scaled_font->logfont; + logfont.lfHeight = -scaled_font->em_square; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = scaled_font->quality; + + scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont); + if (! scaled_font->unscaled_hfont) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect"); + } + + *hfont_out = scaled_font->unscaled_hfont; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + + status = _win32_scaled_font_get_unscaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, hdc, &hfont); + if (status) + return status; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font"); + + status = _win32_scaled_font_set_identity_transform (hdc); + if (status) { + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) +{ + cairo_win32_scaled_font_t *win32_scaled_font; + + win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; + + return win32_scaled_font->is_type1; +} + +cairo_bool_t +_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font) +{ + cairo_win32_scaled_font_t *win32_scaled_font; + + win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; + + return win32_scaled_font->is_bitmap; +} + +static void +_cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font) +{ +} + +/* implement the font backend interface */ + +static cairo_status_t +_cairo_win32_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + LOGFONTW logfont; + uint16_t *face_name; + int face_name_len; + cairo_status_t status; + + status = _cairo_utf8_to_utf16 (toy_face->family, -1, + &face_name, &face_name_len); + if (status) + return status; + + if (face_name_len > LF_FACESIZE - 1) + face_name_len = LF_FACESIZE - 1; + + memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * face_name_len); + logfont.lfFaceName[face_name_len] = 0; + free (face_name); + + logfont.lfHeight = 0; /* filled in later */ + logfont.lfWidth = 0; /* filled in later */ + logfont.lfEscapement = 0; /* filled in later */ + logfont.lfOrientation = 0; /* filled in later */ + + switch (toy_face->weight) { + case CAIRO_FONT_WEIGHT_NORMAL: + default: + logfont.lfWeight = FW_NORMAL; + break; + case CAIRO_FONT_WEIGHT_BOLD: + logfont.lfWeight = FW_BOLD; + break; + } + + switch (toy_face->slant) { + case CAIRO_FONT_SLANT_NORMAL: + default: + logfont.lfItalic = FALSE; + break; + case CAIRO_FONT_SLANT_ITALIC: + case CAIRO_FONT_SLANT_OBLIQUE: + logfont.lfItalic = TRUE; + break; + } + + logfont.lfUnderline = FALSE; + logfont.lfStrikeOut = FALSE; + /* The docs for LOGFONT discourage using this, since the + * interpretation is locale-specific, but it's not clear what + * would be a better alternative. + */ + logfont.lfCharSet = DEFAULT_CHARSET; + logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; + logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ + logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + *font_face = cairo_win32_font_face_create_for_logfontw (&logfont); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_scaled_font_fini (void *abstract_font) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + + if (scaled_font == NULL) + return; + + if (scaled_font->scaled_hfont && scaled_font->delete_scaled_hfont) + DeleteObject (scaled_font->scaled_hfont); + + if (scaled_font->unscaled_hfont) + DeleteObject (scaled_font->unscaled_hfont); +} + +static cairo_int_status_t +_cairo_win32_scaled_font_type1_text_to_glyphs (cairo_win32_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + uint16_t *utf16; + int n16; + int i; + WORD *glyph_indices = NULL; + cairo_status_t status; + double x_pos, y_pos; + HDC hdc = NULL; + cairo_matrix_t mat; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); + if (status) + return status; + + glyph_indices = _cairo_malloc_ab (n16 + 1, sizeof (WORD)); + if (!glyph_indices) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL1; + } + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + goto FAIL2; + + if (GetGlyphIndicesW (hdc, utf16, n16, glyph_indices, 0) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_type1_text_to_glyphs:GetGlyphIndicesW"); + goto FAIL3; + } + + *num_glyphs = n16; + *glyphs = _cairo_malloc_ab (n16, sizeof (cairo_glyph_t)); + if (!*glyphs) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL3; + } + + x_pos = x; + y_pos = y; + + mat = scaled_font->base.ctm; + status = cairo_matrix_invert (&mat); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_scaled_font_freeze_cache (&scaled_font->base); + + for (i = 0; i < n16; i++) { + cairo_scaled_glyph_t *scaled_glyph; + + (*glyphs)[i].index = glyph_indices[i]; + (*glyphs)[i].x = x_pos; + (*glyphs)[i].y = y_pos; + + status = _cairo_scaled_glyph_lookup (&scaled_font->base, + glyph_indices[i], + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status) { + free (*glyphs); + *glyphs = NULL; + break; + } + + x = scaled_glyph->x_advance; + y = scaled_glyph->y_advance; + cairo_matrix_transform_distance (&mat, &x, &y); + x_pos += x; + y_pos += y; + } + + _cairo_scaled_font_thaw_cache (&scaled_font->base); + +FAIL3: + cairo_win32_scaled_font_done_font (&scaled_font->base); +FAIL2: + free (glyph_indices); +FAIL1: + free (utf16); + + return status; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_text_to_glyphs (void *abstract_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + uint16_t *utf16; + int n16; + GCP_RESULTSW gcp_results; + unsigned int buffer_size, i; + WCHAR *glyph_indices = NULL; + int *dx = NULL; + cairo_status_t status; + double x_pos, y_pos; + double x_incr, y_incr; + HDC hdc = NULL; + + /* GetCharacterPlacement() returns utf16 instead of glyph indices + * for Type 1 fonts. Use GetGlyphIndices for Type 1 fonts. */ + if (scaled_font->is_type1) + return _cairo_win32_scaled_font_type1_text_to_glyphs (scaled_font, + x, + y, + utf8, + glyphs, + num_glyphs); + + /* Compute a vector in user space along the baseline of length one logical space unit */ + x_incr = 1; + y_incr = 0; + cairo_matrix_transform_distance (&scaled_font->base.font_matrix, &x_incr, &y_incr); + x_incr /= scaled_font->logical_scale; + y_incr /= scaled_font->logical_scale; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); + if (status) + return status; + + gcp_results.lStructSize = sizeof (GCP_RESULTS); + gcp_results.lpOutString = NULL; + gcp_results.lpOrder = NULL; + gcp_results.lpCaretPos = NULL; + gcp_results.lpClass = NULL; + + buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */ + if (buffer_size > INT_MAX) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL1; + } + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + goto FAIL1; + + while (TRUE) { + 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)); + if (!glyph_indices || !dx) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + + gcp_results.nGlyphs = buffer_size; + gcp_results.lpDx = dx; + gcp_results.lpGlyphs = glyph_indices; + + if (!GetCharacterPlacementW (hdc, utf16, n16, + 0, + &gcp_results, + GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_text_to_glyphs"); + goto FAIL2; + } + + if (gcp_results.lpDx && gcp_results.lpGlyphs) + break; + + /* Too small a buffer, try again */ + + buffer_size += buffer_size / 2; + if (buffer_size > INT_MAX) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + } + + *num_glyphs = gcp_results.nGlyphs; + *glyphs = _cairo_malloc_ab (gcp_results.nGlyphs, sizeof (cairo_glyph_t)); + if (!*glyphs) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + + x_pos = x; + y_pos = y; + + for (i = 0; i < gcp_results.nGlyphs; i++) { + (*glyphs)[i].index = glyph_indices[i]; + (*glyphs)[i].x = x_pos ; + (*glyphs)[i].y = y_pos; + + x_pos += x_incr * dx[i]; + y_pos += y_incr * dx[i]; + } + + FAIL2: + free (glyph_indices); + free (dx); + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + FAIL1: + free (utf16); + + return status; +} + +static unsigned long +_cairo_win32_scaled_font_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + wchar_t unicode[2]; + WORD glyph_index; + HDC hdc = NULL; + cairo_status_t status; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return 0; + + unicode[0] = ucs4; + unicode[1] = 0; + if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) { + _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_ucs4_to_index:GetGlyphIndicesW"); + glyph_index = 0; + } + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + return glyph_index; +} + +static cairo_status_t +_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font) +{ + cairo_status_t status; + cairo_font_extents_t extents; + + TEXTMETRIC metrics = {0}; + HDC hdc; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + if (scaled_font->preserve_axes || scaled_font->base.options.hint_metrics == CAIRO_HINT_METRICS_OFF) { + /* For 90-degree rotations (including 0), we get the metrics + * from the GDI in logical space, then convert back to font space + */ + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + 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; + + extents.height = (metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->logical_scale; + extents.max_x_advance = metrics.tmMaxCharWidth / scaled_font->logical_scale; + extents.max_y_advance = 0; + + } else { + /* For all other transformations, we use the design metrics + * of the font. The GDI results from GetTextMetrics() on a + * transformed font are inexplicably large and we want to + * avoid them. + */ + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + if (status) + return status; + GetTextMetrics (hdc, &metrics); + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + + extents.ascent = (double)metrics.tmAscent / scaled_font->em_square; + extents.descent = (double)metrics.tmDescent / scaled_font->em_square; + extents.height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->em_square; + extents.max_x_advance = (double)(metrics.tmMaxCharWidth) / scaled_font->em_square; + extents.max_y_advance = 0; + + } + + scaled_font->is_bitmap = !(metrics.tmPitchAndFamily & TMPF_VECTOR); + + /* Need to determine if this is a Type 1 font for the special + * handling in _text_to_glyphs. Unlike TrueType or OpenType, + * Type1 fonts do not have a "cmap" table (or any other table). + * However GetFontData() will retrieve a Type1 font when + * requesting that GetFontData() retrieve data from the start of + * the file. This is to distinguish Type1 from stroke fonts such + * as "Script" and "Modern". The TMPF_TRUETYPE test is redundant + * but improves performance for the most common fonts. + */ + scaled_font->is_type1 = FALSE; + if (!(metrics.tmPitchAndFamily & TMPF_TRUETYPE) && + (metrics.tmPitchAndFamily & TMPF_VECTOR)) + { + if ((GetFontData (hdc, CMAP_TAG, 0, NULL, 0) == GDI_ERROR) && + (GetFontData (hdc, 0, 0, NULL, 0) != GDI_ERROR)) + { + scaled_font->is_type1 = TRUE; + } + } + + return _cairo_scaled_font_set_metrics (&scaled_font->base, &extents); +} + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + GLYPHMETRICS metrics; + cairo_status_t status; + cairo_text_extents_t extents; + HDC hdc; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + if (scaled_font->is_bitmap) { + /* GetGlyphOutline will not work. Assume that the glyph does not extend outside the font box. */ + cairo_font_extents_t font_extents; + INT width = 0; + UINT charIndex = _cairo_scaled_glyph_index (scaled_glyph); + + cairo_scaled_font_extents (&scaled_font->base, &font_extents); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + if (!GetCharWidth32(hdc, charIndex, charIndex, &width)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetCharWidth32"); + width = 0; + } + cairo_win32_scaled_font_done_font (&scaled_font->base); + if (status) + return status; + + extents.x_bearing = 0; + extents.y_bearing = scaled_font->base.ctm.yy * (-font_extents.ascent / scaled_font->y_scale); + extents.width = width / (WIN32_FONT_LOGICAL_SCALE * scaled_font->x_scale); + extents.height = scaled_font->base.ctm.yy * (font_extents.ascent + font_extents.descent) / scaled_font->y_scale; + extents.x_advance = extents.width; + extents.y_advance = 0; + } else if (scaled_font->preserve_axes && scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { + /* If we aren't rotating / skewing the axes, then we get the metrics + * from the GDI in device space and convert to font space. + */ + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix) == GDI_ERROR) { + memset (&metrics, 0, sizeof (GLYPHMETRICS)); + } else { + 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. + * To avoid clipping the glyphs when drawn by _cairo_surface_fallback_show_glyphs, + * for example, or other code that uses glyph extents to determine the area to update, + * we add a pixel of "slop" to left side of the nominal "black" area returned by GDI, + * and two pixels to the right (as tests show some glyphs bleed into this column). + */ + metrics.gmptGlyphOrigin.x -= 1; + metrics.gmBlackBoxX += 3; + } + } + cairo_win32_scaled_font_done_font (&scaled_font->base); + + if (scaled_font->swap_axes) { + extents.x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; + extents.y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; + extents.width = metrics.gmBlackBoxY / scaled_font->y_scale; + extents.height = metrics.gmBlackBoxX / scaled_font->x_scale; + extents.x_advance = metrics.gmCellIncY / scaled_font->x_scale; + extents.y_advance = metrics.gmCellIncX / scaled_font->y_scale; + } else { + extents.x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; + extents.y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; + extents.width = metrics.gmBlackBoxX / scaled_font->x_scale; + extents.height = metrics.gmBlackBoxY / scaled_font->y_scale; + extents.x_advance = metrics.gmCellIncX / scaled_font->x_scale; + extents.y_advance = metrics.gmCellIncY / scaled_font->y_scale; + } + + if (scaled_font->swap_x) { + extents.x_bearing = (- extents.x_bearing - extents.width); + extents.x_advance = - extents.x_advance; + } + + if (scaled_font->swap_y) { + 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. + */ + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + if (status) + return status; + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix) == GDI_ERROR) { + memset (&metrics, 0, sizeof (GLYPHMETRICS)); + } + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + + extents.x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square; + extents.y_bearing = - (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square; + extents.width = (double)metrics.gmBlackBoxX / scaled_font->em_square; + extents.height = (double)metrics.gmBlackBoxY / scaled_font->em_square; + extents.x_advance = (double)metrics.gmCellIncX / scaled_font->em_square; + extents.y_advance = (double)metrics.gmCellIncY / scaled_font->em_square; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + + return CAIRO_STATUS_SUCCESS; +} + +/* Not currently used code, but may be useful in the future if we add + * back the capability to the scaled font backend interface to get the + * actual device space bbox rather than computing it from the + * font-space metrics. + */ +#if 0 +static cairo_status_t +_cairo_win32_scaled_font_glyph_bbox (void *abstract_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + cairo_win32_scaled_font_t *scaled_font = abstract_font; + int x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + if (num_glyphs > 0) { + HDC hdc; + GLYPHMETRICS metrics; + cairo_status_t status; + int i; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + for (i = 0; i < num_glyphs; i++) { + int x = _cairo_lround (glyphs[i].x); + int y = _cairo_lround (glyphs[i].y); + + GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x) + x1 = x + metrics.gmptGlyphOrigin.x; + if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y) + y1 = y - metrics.gmptGlyphOrigin.y; + if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX) + x2 = x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX; + if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY) + y2 = y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY; + } + + cairo_win32_scaled_font_done_font (&scaled_font->base); + } + + bbox->p1.x = _cairo_fixed_from_int (x1); + bbox->p1.y = _cairo_fixed_from_int (y1); + bbox->p2.x = _cairo_fixed_from_int (x2); + bbox->p2.y = _cairo_fixed_from_int (y2); + + return CAIRO_STATUS_SUCCESS; +} +#endif + +typedef struct { + cairo_win32_scaled_font_t *scaled_font; + HDC hdc; + + cairo_array_t glyphs; + cairo_array_t dx; + + int start_x; + int last_x; + int last_y; +} cairo_glyph_state_t; + +static void +_start_glyphs (cairo_glyph_state_t *state, + cairo_win32_scaled_font_t *scaled_font, + HDC hdc) +{ + state->hdc = hdc; + state->scaled_font = scaled_font; + + _cairo_array_init (&state->glyphs, sizeof (WCHAR)); + _cairo_array_init (&state->dx, sizeof (int)); +} + +static cairo_status_t +_flush_glyphs (cairo_glyph_state_t *state) +{ + cairo_status_t status; + int dx = 0; + WCHAR * elements; + int * dx_elements; + + status = _cairo_array_append (&state->dx, &dx); + if (status) + return status; + + elements = _cairo_array_index (&state->glyphs, 0); + dx_elements = _cairo_array_index (&state->dx, 0); + if (!ExtTextOutW (state->hdc, + state->start_x, state->last_y, + ETO_GLYPH_INDEX, + NULL, + elements, + state->glyphs.num_elements, + dx_elements)) { + return _cairo_win32_print_gdi_error ("_flush_glyphs"); + } + + _cairo_array_truncate (&state->glyphs, 0); + _cairo_array_truncate (&state->dx, 0); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_add_glyph (cairo_glyph_state_t *state, + unsigned long index, + double device_x, + double device_y) +{ + cairo_status_t status; + double user_x = device_x; + double user_y = device_y; + WCHAR glyph_index = index; + int logical_x, logical_y; + + cairo_matrix_transform_point (&state->scaled_font->device_to_logical, &user_x, &user_y); + + logical_x = _cairo_lround (user_x); + logical_y = _cairo_lround (user_y); + + if (state->glyphs.num_elements > 0) { + int dx; + + if (logical_y != state->last_y) { + status = _flush_glyphs (state); + if (status) + return status; + state->start_x = logical_x; + } else { + dx = logical_x - state->last_x; + status = _cairo_array_append (&state->dx, &dx); + if (status) + return status; + } + } else { + state->start_x = logical_x; + } + + state->last_x = logical_x; + state->last_y = logical_y; + + status = _cairo_array_append (&state->glyphs, &glyph_index); + if (status) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_finish_glyphs (cairo_glyph_state_t *state) +{ + cairo_status_t status; + + status = _flush_glyphs (state); + + _cairo_array_fini (&state->glyphs); + _cairo_array_fini (&state->dx); + + return status; +} + +static cairo_status_t +_draw_glyphs_on_surface (cairo_win32_surface_t *surface, + cairo_win32_scaled_font_t *scaled_font, + COLORREF color, + int x_offset, + int y_offset, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_glyph_state_t state; + cairo_status_t status, status2; + int i; + + if (!SaveDC (surface->dc)) + return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc); + if (status) + goto FAIL1; + + SetTextColor (surface->dc, color); + SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); + SetBkMode (surface->dc, TRANSPARENT); + + _start_glyphs (&state, scaled_font, surface->dc); + + for (i = 0; i < num_glyphs; i++) { + status = _add_glyph (&state, glyphs[i].index, + glyphs[i].x - x_offset, glyphs[i].y - y_offset); + if (status) + goto FAIL2; + } + + FAIL2: + status2 = _finish_glyphs (&state); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + cairo_win32_scaled_font_done_font (&scaled_font->base); + FAIL1: + RestoreDC (surface->dc, -1); + + return status; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + cairo_status_t status; + + if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { + status = _cairo_win32_scaled_font_init_glyph_metrics (scaled_font, scaled_glyph); + if (status) + return status; + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + status = _cairo_win32_scaled_font_init_glyph_surface (scaled_font, scaled_glyph); + if (status) + return status; + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { + status = _cairo_win32_scaled_font_init_glyph_path (scaled_font, scaled_glyph); + if (status) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_load_truetype_table (void *abstract_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + 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&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; + + 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); + + return status; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, + unsigned long index, + uint32_t *ucs4) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + GLYPHSET *glyph_set; + uint16_t *utf16 = NULL; + WORD *glyph_indices = NULL; + HDC hdc = NULL; + int res; + unsigned int i, j, num_glyphs; + cairo_status_t status; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + res = GetFontUnicodeRanges(hdc, NULL); + if (res == 0) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); + goto exit1; + } + + glyph_set = _cairo_malloc (res); + if (glyph_set == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit1; + } + + res = GetFontUnicodeRanges(hdc, glyph_set); + if (res == 0) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); + goto exit1; + } + + *ucs4 = (uint32_t) -1; + for (i = 0; i < glyph_set->cRanges; i++) { + num_glyphs = glyph_set->ranges[i].cGlyphs; + + utf16 = _cairo_malloc_ab (num_glyphs + 1, sizeof (uint16_t)); + if (utf16 == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit1; + } + + glyph_indices = _cairo_malloc_ab (num_glyphs + 1, sizeof (WORD)); + if (glyph_indices == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit2; + } + + for (j = 0; j < num_glyphs; j++) + utf16[j] = glyph_set->ranges[i].wcLow + j; + utf16[j] = 0; + + if (GetGlyphIndicesW (hdc, utf16, num_glyphs, glyph_indices, 0) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetGlyphIndicesW"); + goto exit2; + } + + for (j = 0; j < num_glyphs; j++) { + if (glyph_indices[j] == index) { + *ucs4 = utf16[j]; + goto exit2; + } + } + + free (glyph_indices); + glyph_indices = NULL; + free (utf16); + utf16 = NULL; + } + +exit2: + free (glyph_indices); + free (utf16); + free (glyph_set); +exit1: + cairo_win32_scaled_font_done_font (&scaled_font->base); + + 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_surface_t *surface; + cairo_surface_t *image; + int width, height; + int x1, y1, x2, y2; + + x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); + y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + width = x2 - x1; + height = y2 - y1; + + 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 (to_win32_surface (surface), + scaled_font, RGB(0,0,0), + 0, 0, &glyph, 1); + if (status) + goto FAIL; + + image = _compute_mask (surface, scaled_font->quality); + status = image->status; + if (status) + goto FAIL; + + cairo_surface_set_device_offset (image, -x1, -y1); + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) image); + + FAIL: + cairo_surface_destroy (surface); + + return status; +} + +static void +_cairo_win32_transform_FIXED_to_fixed (cairo_matrix_t *matrix, + FIXED Fx, FIXED Fy, + cairo_fixed_t *fx, cairo_fixed_t *fy) +{ + double x = Fx.value + Fx.fract / 65536.0; + double y = Fy.value + Fy.fract / 65536.0; + cairo_matrix_transform_point (matrix, &x, &y); + *fx = _cairo_fixed_from_double (x); + *fy = _cairo_fixed_from_double (y); +} + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, -1 } }; + cairo_status_t status; + GLYPHMETRICS metrics; + HDC hdc; + DWORD bytesGlyph; + unsigned char *buffer, *ptr; + cairo_path_fixed_t *path; + cairo_matrix_t transform; + cairo_fixed_t x, y; + + if (scaled_font->is_bitmap) + return CAIRO_INT_STATUS_UNSUPPORTED; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + path = _cairo_path_fixed_create (); + if (!path) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) { + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + transform = scaled_font->base.scale; + cairo_matrix_scale (&transform, 1.0/scaled_font->em_square, 1.0/scaled_font->em_square); + } else { + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + cairo_matrix_init_identity(&transform); + } + if (status) + goto CLEANUP_PATH; + + bytesGlyph = GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (bytesGlyph == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + goto CLEANUP_FONT; + } + + ptr = buffer = _cairo_malloc (bytesGlyph); + if (!buffer && bytesGlyph != 0) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_FONT; + } + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, bytesGlyph, buffer, &matrix) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + goto CLEANUP_BUFFER; + } + + while (ptr < buffer + bytesGlyph) { + TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)ptr; + unsigned char *endPoly = ptr + header->cb; + + ptr += sizeof (TTPOLYGONHEADER); + + _cairo_win32_transform_FIXED_to_fixed (&transform, + header->pfxStart.x, + header->pfxStart.y, + &x, &y); + status = _cairo_path_fixed_move_to (path, x, y); + if (status) + goto CLEANUP_BUFFER; + + while (ptr < endPoly) { + TTPOLYCURVE *curve = (TTPOLYCURVE *)ptr; + POINTFX *points = curve->apfx; + int i; + switch (curve->wType) { + case TT_PRIM_LINE: + for (i = 0; i < curve->cpfx; i++) { + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &x, &y); + status = _cairo_path_fixed_line_to (path, x, y); + if (status) + goto CLEANUP_BUFFER; + } + break; + case TT_PRIM_QSPLINE: + for (i = 0; i < curve->cpfx - 1; i++) { + cairo_fixed_t p1x, p1y, p2x, p2y, cx, cy, c1x, c1y, c2x, c2y; + if (! _cairo_path_fixed_get_current_point (path, &p1x, &p1y)) + goto CLEANUP_BUFFER; + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &cx, &cy); + + if (i + 1 == curve->cpfx - 1) { + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &p2x, &p2y); + } else { + /* records with more than one curve use interpolation for + control points, per http://support.microsoft.com/kb/q87115/ */ + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &x, &y); + p2x = (cx + x) / 2; + p2y = (cy + y) / 2; + } + + c1x = 2 * cx / 3 + p1x / 3; + c1y = 2 * cy / 3 + p1y / 3; + c2x = 2 * cx / 3 + p2x / 3; + c2y = 2 * cy / 3 + p2y / 3; + + status = _cairo_path_fixed_curve_to (path, c1x, c1y, c2x, c2y, p2x, p2y); + if (status) + goto CLEANUP_BUFFER; + } + break; + case TT_PRIM_CSPLINE: + for (i = 0; i < curve->cpfx - 2; i += 2) { + cairo_fixed_t x1, y1, x2, y2; + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &x, &y); + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &x1, &y1); + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 2].x, + points[i + 2].y, + &x2, &y2); + status = _cairo_path_fixed_curve_to (path, x, y, x1, y1, x2, y2); + if (status) + goto CLEANUP_BUFFER; + } + break; + } + ptr += sizeof(TTPOLYCURVE) + sizeof (POINTFX) * (curve->cpfx - 1); + } + status = _cairo_path_fixed_close_path (path); + if (status) + goto CLEANUP_BUFFER; + } + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + + CLEANUP_BUFFER: + free (buffer); + + CLEANUP_FONT: + if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + else + cairo_win32_scaled_font_done_font (&scaled_font->base); + + CLEANUP_PATH: + if (status != CAIRO_STATUS_SUCCESS) + _cairo_path_fixed_destroy (path); + + return status; +} + +const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = { + CAIRO_FONT_TYPE_WIN32, + _cairo_win32_scaled_font_fini, + _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_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 */ + +typedef struct _cairo_win32_font_face cairo_win32_font_face_t; + +/* If hfont is non-%NULL then logfont->lfHeight must be -S for some S, + * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must + * all be 0, and hfont is the result of calling CreateFontIndirectW on + * logfont. + */ +struct _cairo_win32_font_face { + cairo_font_face_t base; + LOGFONTW logfont; + HFONT hfont; +}; + +/* 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 + * #cairo_font_face_t => #cairo_scaled_font_t works. Once the + * corresponding #cairo_font_face_t objects fall out of downstream + * caches, we don't need them in this hash table anymore. + * + * Modifications to this hash table are protected by + * _cairo_win32_font_face_mutex. + */ + +static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL; + +static int +_cairo_win32_font_face_keys_equal (const void *key_a, + const void *key_b); + +static void +_cairo_win32_font_face_hash_table_destroy (void) +{ + cairo_hash_table_t *hash_table; + + /* We manually acquire the lock rather than calling + * _cairo_win32_font_face_hash_table_lock simply to avoid creating + * the table only to destroy it again. */ + CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); + hash_table = cairo_win32_font_face_hash_table; + cairo_win32_font_face_hash_table = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); + + if (hash_table != NULL) + _cairo_hash_table_destroy (hash_table); +} + +static cairo_hash_table_t * +_cairo_win32_font_face_hash_table_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); + + if (unlikely (cairo_win32_font_face_hash_table == NULL)) + { + cairo_win32_font_face_hash_table = + _cairo_hash_table_create (_cairo_win32_font_face_keys_equal); + + if (unlikely (cairo_win32_font_face_hash_table == NULL)) { + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + } + + return cairo_win32_font_face_hash_table; +} + +static void +_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, + HFONT font) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + + key->logfont = *logfont; + key->hfont = font; + + hash = _cairo_hash_bytes (0, logfont->lfFaceName, 2*wcslen(logfont->lfFaceName)); + hash = _cairo_hash_bytes (hash, &logfont->lfWeight, sizeof(logfont->lfWeight)); + hash = _cairo_hash_bytes (hash, &logfont->lfItalic, sizeof(logfont->lfItalic)); + + key->base.hash_entry.hash = hash; +} + +static int +_cairo_win32_font_face_keys_equal (const void *key_a, + const void *key_b) +{ + const cairo_win32_font_face_t *face_a = key_a; + const cairo_win32_font_face_t *face_b = key_b; + + if (face_a->logfont.lfWeight == face_b->logfont.lfWeight && + face_a->logfont.lfItalic == face_b->logfont.lfItalic && + face_a->logfont.lfUnderline == face_b->logfont.lfUnderline && + face_a->logfont.lfStrikeOut == face_b->logfont.lfStrikeOut && + face_a->logfont.lfCharSet == face_b->logfont.lfCharSet && + face_a->logfont.lfOutPrecision == face_b->logfont.lfOutPrecision && + face_a->logfont.lfClipPrecision == face_b->logfont.lfClipPrecision && + face_a->logfont.lfPitchAndFamily == face_b->logfont.lfPitchAndFamily && + (wcscmp (face_a->logfont.lfFaceName, face_b->logfont.lfFaceName) == 0)) + return TRUE; + else + return FALSE; +} + +/* implement the platform-specific interface */ + +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 +}; + +/** + * cairo_win32_font_face_create_for_logfontw_hfont: + * @logfont: A #LOGFONTW structure specifying the font to use. + * If @font is %NULL then the lfHeight, lfWidth, lfOrientation and lfEscapement + * fields of this structure are ignored. Otherwise lfWidth, lfOrientation and + * lfEscapement must be zero. + * @font: An #HFONT that can be used when the font matrix is a scale by + * -lfHeight and the CTM is identity. + * + * Creates a new font for the Win32 font backend based on a + * #LOGFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * 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) +{ + cairo_win32_font_face_t *font_face, key; + cairo_hash_table_t *hash_table; + cairo_status_t status; + + 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); + + /* 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); + _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 = _cairo_malloc (sizeof (cairo_win32_font_face_t)); + if (!font_face) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + _cairo_win32_font_face_init_key (font_face, logfont, font); + _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend); + + 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: + _cairo_win32_font_face_hash_table_unlock (); + return (cairo_font_face_t *)&_cairo_font_face_nil; +} + +/** + * cairo_win32_font_face_create_for_logfontw: + * @logfont: A #LOGFONTW structure specifying the font to use. + * The lfHeight, lfWidth, lfOrientation and lfEscapement + * fields of this structure are ignored. + * + * Creates a new font for the Win32 font backend based on a + * #LOGFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * 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) +{ + return cairo_win32_font_face_create_for_logfontw_hfont (logfont, NULL); +} + +/** + * cairo_win32_font_face_create_for_hfont: + * @font: An #HFONT structure specifying the font to use. + * + * Creates a new font for the Win32 font backend based on a + * #HFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * 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) +{ + LOGFONTW logfont; + GetObjectW (font, sizeof(logfont), &logfont); + + if (logfont.lfEscapement != 0 || logfont.lfOrientation != 0 || + logfont.lfWidth != 0) { + /* We can't use this font because that optimization requires that + * lfEscapement, lfOrientation and lfWidth be zero. */ + font = NULL; + } + + return cairo_win32_font_face_create_for_logfontw_hfont (&logfont, font); +} + +static cairo_bool_t +_cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->backend == &_cairo_win32_scaled_font_backend; +} + +/** + * 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_font_face_create_for_logfontw(). + * @hdc: a device context + * + * Selects the font into the given device context and changes the + * map mode and world transformation of the device context to match + * that of the font. This function is intended for use when using + * layout APIs such as Uniscribe to do text layout with the + * cairo font. After finishing using the device context, you must call + * cairo_win32_scaled_font_done_font() to release any resources allocated + * by this function. + * + * See cairo_win32_scaled_font_get_metrics_factor() for converting logical + * coordinates from the device context to font space. + * + * Normally, calls to SaveDC() and RestoreDC() would be made around + * the use of this function to preserve the original graphics state. + * + * 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, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + int old_mode; + + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + return _cairo_error (CAIRO_STATUS_FONT_TYPE_MISMATCH); + } + + if (scaled_font->status) + return scaled_font->status; + + status = _win32_scaled_font_get_scaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, &hfont); + if (status) + return status; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SelectObject"); + + old_mode = SetGraphicsMode (hdc, GM_ADVANCED); + if (!old_mode) { + status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SetGraphicsMode"); + SelectObject (hdc, old_hfont); + return status; + } + + status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc); + if (status) { + SetGraphicsMode (hdc, old_mode); + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_win32_scaled_font_done_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) +{ + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + } +} + +/** + * cairo_win32_scaled_font_get_metrics_factor: + * @scaled_font: a scaled font from the Win32 font backend + * + * Gets a scale factor between logical coordinates in the coordinate + * space used by cairo_win32_scaled_font_select_font() (that is, the + * coordinate system used by the Windows functions to return metrics) and + * font space coordinates. + * + * 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) +{ + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + return 1.; + } + return 1. / ((cairo_win32_scaled_font_t *)scaled_font)->logical_scale; +} + +/** + * cairo_win32_scaled_font_get_logical_to_device: + * @scaled_font: a scaled font from the Win32 font backend + * @logical_to_device: matrix to return + * + * Gets the transformation mapping the logical space used by @scaled_font + * to device space. + * + * Since: 1.4 + **/ +void +cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *logical_to_device) +{ + cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + cairo_matrix_init_identity (logical_to_device); + return; + } + *logical_to_device = win_font->logical_to_device; +} + +/** + * cairo_win32_scaled_font_get_device_to_logical: + * @scaled_font: a scaled font from the Win32 font backend + * @device_to_logical: matrix to return + * + * Gets the transformation mapping device space to the logical space + * used by @scaled_font. + * + * Since: 1.4 + **/ +void +cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *device_to_logical) +{ + cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + cairo_matrix_init_identity (device_to_logical); + return; + } + *device_to_logical = win_font->device_to_logical; +} + +void +_cairo_win32_font_reset_static_data (void) +{ + _cairo_win32_font_face_hash_table_destroy (); +} 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 0000000000..1d1d7f8737 --- /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/win32/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c new file mode 100644 index 0000000000..96e65893e0 --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c @@ -0,0 +1,2262 @@ +/* -*- 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, 2008 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 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 + * 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-paginated-private.h" + +#include "cairo-clip-private.h" +#include "cairo-composite-rectangles-private.h" +#include "cairo-win32-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 + +#if !defined(POSTSCRIPT_IDENTIFY) +# define POSTSCRIPT_IDENTIFY 0x1015 +#endif + +#if !defined(PSIDENT_GDICENTRIC) +# define PSIDENT_GDICENTRIC 0x0000 +#endif + +#if !defined(GET_PS_FEATURESETTING) +# define GET_PS_FEATURESETTING 0x1019 +#endif + +#if !defined(FEATURESETTING_PSLEVEL) +# define FEATURESETTING_PSLEVEL 0x0002 +#endif + +#if !defined(GRADIENT_FILL_RECT_H) +# define GRADIENT_FILL_RECT_H 0x00 +#endif + +#if !defined(CHECKJPEGFORMAT) +# define CHECKJPEGFORMAT 0x1017 +#endif + +#if !defined(CHECKPNGFORMAT) +# define CHECKPNGFORMAT 0x1018 +#endif + +#define PELS_72DPI ((LONG)(72. / 0.0254)) + +static const 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_printing_surface_t *surface) +{ + DWORD word; + INT ps_feature, ps_level; + + word = PSIDENT_GDICENTRIC; + if (ExtEscape (surface->win32.dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0) + return; + + ps_feature = FEATURESETTING_PSLEVEL; + 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->win32.flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; +} + +static void +_cairo_win32_printing_surface_init_image_support (cairo_win32_printing_surface_t *surface) +{ + DWORD word; + + word = CHECKJPEGFORMAT; + 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->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 + * work unless the GDI function GdiInitializeLanguagePack() has been + * called. + * + * http://m-a-tech.blogspot.com/2009/04/emf-buffer-idiocracy.html + * + * The only information I could find on the how to use this + * undocumented function is the use in: + * + * http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/render_process.cc?view=markup + * + * to solve the same problem. The above code first checks if LPK.DLL + * is already loaded. If it is not it calls + * GdiInitializeLanguagePack() using the prototype + * BOOL GdiInitializeLanguagePack (int) + * and argument 0. + */ +static void +_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; + HMODULE module; + + if (GetModuleHandleW (L"LPK.DLL")) + return; + + module = GetModuleHandleW (L"GDI32.DLL"); + if (module) { + gdi_init_lang_pack = (gdi_init_lang_pack_func_t) + GetProcAddress (module, "GdiInitializeLanguagePack"); + if (gdi_init_lang_pack) + gdi_init_lang_pack (0); + } +} + +/** + * _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; + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + _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_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: + ASSERT_NOT_REACHED; + case CAIRO_IMAGE_IS_OPAQUE: + status = CAIRO_STATUS_SUCCESS; + break; + + case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: + case CAIRO_IMAGE_HAS_ALPHA: + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + break; + } + + _cairo_win32_printing_surface_release_image_pattern (surface, pattern, &image_pattern, image_extra); + + return status; +} + +static cairo_bool_t +surface_pattern_supported (const cairo_surface_pattern_t *pattern) +{ + if (_cairo_surface_is_recording (pattern->surface)) + return TRUE; + + if (pattern->surface->backend->acquire_source_image == NULL) + { + return FALSE; + } + + return TRUE; +} + +static cairo_bool_t +pattern_supported (cairo_win32_printing_surface_t *surface, const cairo_pattern_t *pattern) +{ + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + return TRUE; + + case CAIRO_PATTERN_TYPE_LINEAR: + return surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; + + case CAIRO_PATTERN_TYPE_RADIAL: + case CAIRO_PATTERN_TYPE_MESH: + 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_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; + + if (!(op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_OVER || + op == CAIRO_OPERATOR_CLEAR)) + 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) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + + if (op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_CLEAR) + return CAIRO_STATUS_SUCCESS; + + /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If + * the pattern contains transparency, we return + * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis + * surface. If the analysis surface determines that there is + * anything drawn under this operation, a fallback image will be + * used. Otherwise the operation will be replayed during the + * render stage and we blend the transarency into the white + * background to convert the pattern to opaque. + */ + + 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; + else + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; +} + +static cairo_bool_t +_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, extents) != CAIRO_INT_STATUS_UNSUPPORTED) + return TRUE; + else + return FALSE; +} + +static void +_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) + _cairo_pattern_init_solid (color, CAIRO_COLOR_WHITE); + else + _cairo_pattern_init_solid (color, CAIRO_COLOR_BLACK); +} + +static COLORREF +_cairo_win32_printing_surface_flatten_transparency (cairo_win32_printing_surface_t *surface, + const cairo_color_t *color) +{ + COLORREF c; + BYTE red, green, blue; + + red = color->red_short >> 8; + green = color->green_short >> 8; + blue = color->blue_short >> 8; + + if (!CAIRO_COLOR_IS_OPAQUE(color)) { + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { + /* Blend into white */ + uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8); + + red = (color->red_short >> 8) + one_minus_alpha; + green = (color->green_short >> 8) + one_minus_alpha; + blue = (color->blue_short >> 8) + one_minus_alpha; + } else { + /* Blend into black */ + red = (color->red_short >> 8); + green = (color->green_short >> 8); + blue = (color->blue_short >> 8); + } + } + c = RGB (red, green, blue); + + return c; +} + +static cairo_status_t +_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; + COLORREF color; + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &pattern->color); + 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->win32.dc, surface->brush); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_printing_surface_done_solid_brush (cairo_win32_printing_surface_t *surface) +{ + if (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_printing_surface_t *surface, + RECT *clip) +{ + XFORM xform; + + _cairo_matrix_to_win32_xform (&surface->ctm, &xform); + if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); + GetClipBox (surface->win32.dc, clip); + + _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &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_printing_surface_t *surface, + const cairo_pattern_t *pattern) +{ + RECT clip; + cairo_status_t status; + + GetClipBox (surface->win32.dc, &clip); + status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern); + if (status) + return status; + + 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_printing_surface_t *surface, + cairo_surface_pattern_t *pattern, + cairo_surface_t *source) +{ + cairo_content_t old_content; + cairo_matrix_t old_ctm; + cairo_bool_t old_has_ctm; + cairo_rectangle_int_t recording_extents; + cairo_int_status_t status; + cairo_extend_t extend; + cairo_matrix_t p2d; + XFORM xform; + int x_tile, y_tile, left, right, top, bottom; + RECT clip; + cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) source; + cairo_box_t bbox; + 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_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->win32.dc); + _cairo_matrix_to_win32_xform (&p2d, &xform); + + 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) + 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)); + right = ceil (clip.right / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); + top = floor (clip.top / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); + bottom = ceil (clip.bottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); + } else { + left = 0; + right = 1; + top = 0; + bottom = 1; + } + + old_content = surface->content; + if (recording_surface->base.content == CAIRO_CONTENT_COLOR) { + surface->content = CAIRO_CONTENT_COLOR; + status = _cairo_win32_printing_surface_paint_solid_pattern (surface, + &_cairo_pattern_black.base); + if (status) + goto err; + } + + for (y_tile = top; y_tile < bottom; y_tile++) { + for (x_tile = left; x_tile < right; x_tile++) { + cairo_matrix_t m; + double x, y; + + SaveDC (surface->win32.dc); + m = p2d; + cairo_matrix_translate (&m, + x_tile*recording_extents.width, + y_tile*recording_extents.height); + if (extend == CAIRO_EXTEND_REFLECT) { + if (x_tile % 2) { + cairo_matrix_translate (&m, recording_extents.width, 0); + cairo_matrix_scale (&m, -1, 1); + } + if (y_tile % 2) { + cairo_matrix_translate (&m, 0, recording_extents.height); + cairo_matrix_scale (&m, 1, -1); + } + } + surface->ctm = m; + surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + + /* Set clip path around bbox of the pattern. */ + BeginPath (surface->win32.dc); + + x = 0; + y = 0; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + 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->win32.dc, (int) x, (int) y); + + x = recording_extents.width; + y = recording_extents.height; + cairo_matrix_transform_point (&surface->ctm, &x, &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->win32.dc, (int) x, (int) y); + + CloseFigure (surface->win32.dc); + EndPath (surface->win32.dc); + SelectClipPath (surface->win32.dc, RGN_AND); + + 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->win32.dc, -2); + + if (status) + goto err; + } + } + + surface->content = old_content; + surface->ctm = old_ctm; + surface->has_ctm = old_has_ctm; + RestoreDC (surface->win32.dc, -1); + + err: + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_check_jpeg (cairo_win32_printing_surface_t *surface, + cairo_surface_t *source, + const unsigned char **data, + unsigned long *length, + cairo_image_info_t *info) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_int_status_t status; + DWORD result; + + 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, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length); + if (status) + return status; + + result = 0; + if (ExtEscape(surface->win32.dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data, + sizeof(result), (char *) &result) <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (result != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *data = mime_data; + *length = mime_data_length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_check_png (cairo_win32_printing_surface_t *surface, + cairo_surface_t *source, + const unsigned char **data, + unsigned long *length, + cairo_image_info_t *info) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + + cairo_int_status_t status; + DWORD result; + + 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, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length); + if (status) + return status; + + result = 0; + if (ExtEscape(surface->win32.dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data, + sizeof(result), (char *) &result) <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (result != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *data = mime_data; + *length = mime_data_length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_printing_surface_t *surface, + const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents) +{ + 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; + BITMAPINFO bi; + cairo_matrix_t m; + 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; + unsigned long mime_size; + cairo_image_info_t mime_info; + cairo_bool_t use_mime; + DWORD mime_type; + + /* If we can't use StretchDIBits with this surface, we can't do anything + * here. + */ + if (!(surface->win32.flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) + background_color = CAIRO_COLOR_WHITE; + else + background_color = CAIRO_COLOR_BLACK; + + 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; + } + + if (image->width == 0 || image->height == 0) { + status = CAIRO_STATUS_SUCCESS; + goto CLEANUP_IMAGE; + } + + mime_type = BI_JPEG; + status = _cairo_win32_printing_surface_check_jpeg (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, + image_pattern.surface, + &mime_data, + &mime_size, + &mime_info); + } + if (_cairo_int_status_is_error (status)) + return status; + + use_mime = (status == CAIRO_INT_STATUS_SUCCESS); + + 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; + + opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + image->width, + image->height); + if (opaque_surface->status) { + status = opaque_surface->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); + status = _cairo_surface_paint (opaque_surface, + CAIRO_OPERATOR_OVER, + &image_pattern.base, + NULL); + _cairo_pattern_fini (&image_pattern.base); + if (status) + goto CLEANUP_OPAQUE_IMAGE; + + opaque_image = (cairo_image_surface_t *) opaque_surface; + } else { + opaque_image = image; + } + + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biWidth = use_mime ? mime_info.width : opaque_image->width; + bi.bmiHeader.biHeight = use_mime ? - mime_info.height : -opaque_image->height; + bi.bmiHeader.biSizeImage = use_mime ? mime_size : 0; + bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB; + bi.bmiHeader.biClrUsed = 0; + bi.bmiHeader.biClrImportant = 0; + + 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->win32.dc, &xform)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern"); + goto CLEANUP_OPAQUE_IMAGE; + } + + oldmode = SetStretchBltMode(surface->win32.dc, HALFTONE); + + 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); + bottom = ceil (clip.bottom / (double) opaque_image->height); + } else { + left = 0; + right = 1; + top = 0; + bottom = 1; + } + + for (y_tile = top; y_tile < bottom; y_tile++) { + for (x_tile = left; x_tile < right; x_tile++) { + if (!StretchDIBits (surface->win32.dc, + x_tile*opaque_image->width, + y_tile*opaque_image->height, + opaque_image->width, + opaque_image->height, + 0, + 0, + use_mime ? mime_info.width : opaque_image->width, + use_mime ? mime_info.height : opaque_image->height, + use_mime ? mime_data : opaque_image->data, + &bi, + DIB_RGB_COLORS, + SRCCOPY)) + { + status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint(StretchDIBits)"); + goto CLEANUP_OPAQUE_IMAGE; + } + } + } + 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_win32_printing_surface_release_image_pattern (surface, pattern, &image_pattern, image_extra); + + return status; +} + +static void +vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color) +{ + /* MSDN says that the range here is 0x0000 .. 0xff00; + * that may well be a typo, but just chop the low bits + * here. */ + vert->Alpha = 0xff00; + vert->Red = color->red_short & 0xff00; + vert->Green = color->green_short & 0xff00; + vert->Blue = color->blue_short & 0xff00; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_printing_surface_t *surface, + cairo_linear_pattern_t *pattern) +{ + TRIVERTEX *vert; + GRADIENT_RECT *rect; + RECT clip; + XFORM xform; + int i, num_stops; + cairo_matrix_t mat, rot; + double p1x, p1y, p2x, p2y, xd, yd, d, sn, cs; + cairo_extend_t extend; + int range_start, range_stop, num_ranges, num_rects, stop; + int total_verts, total_rects; + cairo_status_t status; + + extend = cairo_pattern_get_extend (&pattern->base.base); + SaveDC (surface->win32.dc); + + mat = pattern->base.base.matrix; + status = cairo_matrix_invert (&mat); + /* _cairo_pattern_set_matrix guarantees invertibility */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&mat, &surface->ctm, &mat); + + 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; + yd = p2y - p1y; + d = sqrt (xd*xd + yd*yd); + sn = yd/d; + cs = xd/d; + cairo_matrix_init (&rot, + cs, sn, + -sn, cs, + 0, 0); + cairo_matrix_multiply (&mat, &rot, &mat); + + _cairo_matrix_to_win32_xform (&mat, &xform); + + if (!SetWorldTransform (surface->win32.dc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2"); + + GetClipBox (surface->win32.dc, &clip); + + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + range_start = floor (clip.left / d); + range_stop = ceil (clip.right / d); + } else { + range_start = 0; + range_stop = 1; + } + num_ranges = range_stop - range_start; + num_stops = pattern->base.n_stops; + num_rects = num_stops - 1; + + /* Add an extra four points and two rectangles for EXTEND_PAD */ + 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; + if (i%num_rects == 0) { + stop = 0; + if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) + stop = num_rects; + vert[i*2].x = (LONG)(d*(range_start + i/num_rects)); + vertex_set_color (&vert[i*2], &pattern->base.stops[stop].color); + } else { + vert[i*2].x = vert[i*2-1].x; + vert[i*2].Red = vert[i*2-1].Red; + vert[i*2].Green = vert[i*2-1].Green; + vert[i*2].Blue = vert[i*2-1].Blue; + vert[i*2].Alpha = vert[i*2-1].Alpha; + } + + stop = i%num_rects + 1; + vert[i*2+1].x = (LONG)(d*(range_start + i/num_rects + pattern->base.stops[stop].offset)); + vert[i*2+1].y = (LONG) clip.bottom; + if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) + stop = num_rects - stop; + vertex_set_color (&vert[i*2+1], &pattern->base.stops[stop].color); + + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + } + total_verts = 2*num_ranges*num_rects; + total_rects = num_ranges*num_rects; + + if (extend == CAIRO_EXTEND_PAD) { + vert[i*2].x = vert[i*2-1].x; + vert[i*2].y = (LONG) clip.top; + vert[i*2].Red = vert[i*2-1].Red; + vert[i*2].Green = vert[i*2-1].Green; + vert[i*2].Blue = vert[i*2-1].Blue; + vert[i*2].Alpha = 0xff00; + vert[i*2+1].x = clip.right; + vert[i*2+1].y = (LONG) clip.bottom; + vert[i*2+1].Red = vert[i*2-1].Red; + vert[i*2+1].Green = vert[i*2-1].Green; + vert[i*2+1].Blue = vert[i*2-1].Blue; + vert[i*2+1].Alpha = 0xff00; + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + + i++; + + vert[i*2].x = clip.left; + vert[i*2].y = (LONG) clip.top; + vert[i*2].Red = vert[0].Red; + vert[i*2].Green = vert[0].Green; + vert[i*2].Blue = vert[0].Blue; + vert[i*2].Alpha = 0xff00; + vert[i*2+1].x = vert[0].x; + vert[i*2+1].y = (LONG) clip.bottom; + vert[i*2+1].Red = vert[0].Red; + vert[i*2+1].Green = vert[0].Green; + vert[i*2+1].Blue = vert[0].Blue; + vert[i*2+1].Alpha = 0xff00; + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + + total_verts += 4; + total_rects += 2; + } + + if (!GradientFill (surface->win32.dc, + vert, total_verts, + rect, total_rects, + GRADIENT_FILL_RECT_H)) + return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:GradientFill"); + + free (rect); + free (vert); + RestoreDC (surface->win32.dc, -1); + + return 0; +} + +static cairo_int_status_t +_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; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + status = _cairo_win32_printing_surface_paint_solid_pattern (surface, pattern); + if (status) + return status; + break; + + case CAIRO_PATTERN_TYPE_SURFACE: { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + cairo_surface_t *source = surface_pattern->surface; + cairo_surface_t *to_destroy = NULL; + + if (_cairo_surface_is_snapshot (source)) + to_destroy = source = _cairo_surface_snapshot_get_target (source); + + if ( _cairo_surface_is_recording (source)) + status = _cairo_win32_printing_surface_paint_recording_pattern (surface, surface_pattern, source); + else + status = _cairo_win32_printing_surface_paint_image_pattern (surface, pattern, extents); + + cairo_surface_destroy (to_destroy); + + 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; + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _cairo_win32_printing_surface_paint_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); + if (status) + return status; + break; + + 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_printing_surface_t *surface; +} win32_path_info_t; + +static cairo_status_t +_cairo_win32_printing_surface_path_move_to (void *closure, + const cairo_point_t *point) +{ + win32_path_info_t *path_info = closure; + + if (path_info->surface->has_ctm) { + double x, y; + + 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->win32.dc, (int) x, (int) y, NULL); + } else { + MoveToEx (path_info->surface->win32.dc, + _cairo_fixed_integer_part (point->x), + _cairo_fixed_integer_part (point->y), + NULL); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_line_to (void *closure, + const cairo_point_t *point) +{ + win32_path_info_t *path_info = closure; + + path_info->surface->path_empty = FALSE; + if (path_info->surface->has_ctm) { + double x, y; + + 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->win32.dc, (int) x, (int) y); + } else { + LineTo (path_info->surface->win32.dc, + _cairo_fixed_integer_part (point->x), + _cairo_fixed_integer_part (point->y)); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + win32_path_info_t *path_info = closure; + POINT points[3]; + + path_info->surface->path_empty = FALSE; + if (path_info->surface->has_ctm) { + double x, y; + + x = _cairo_fixed_to_double (b->x); + y = _cairo_fixed_to_double (b->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[0].x = (LONG) x; + points[0].y = (LONG) y; + + x = _cairo_fixed_to_double (c->x); + y = _cairo_fixed_to_double (c->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[1].x = (LONG) x; + points[1].y = (LONG) y; + + x = _cairo_fixed_to_double (d->x); + y = _cairo_fixed_to_double (d->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[2].x = (LONG) x; + points[2].y = (LONG) y; + } else { + points[0].x = _cairo_fixed_integer_part (b->x); + points[0].y = _cairo_fixed_integer_part (b->y); + points[1].x = _cairo_fixed_integer_part (c->x); + points[1].y = _cairo_fixed_integer_part (c->y); + points[2].x = _cairo_fixed_integer_part (d->x); + points[2].y = _cairo_fixed_integer_part (d->y); + } + PolyBezierTo (path_info->surface->win32.dc, points, 3); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_close_path (void *closure) +{ + win32_path_info_t *path_info = closure; + + CloseFigure (path_info->surface->win32.dc); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_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_win32_printing_surface_path_move_to, + _cairo_win32_printing_surface_path_line_to, + _cairo_win32_printing_surface_path_curve_to, + _cairo_win32_printing_surface_path_close_path, + &path_info); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_show_page (void *abstract_surface) +{ + cairo_win32_printing_surface_t *surface = abstract_surface; + + /* Undo both SaveDC's that we did in start_page */ + 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; +} + +static cairo_status_t +_cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_win32_printing_surface_t *surface = cairo_container_of (clipper, + cairo_win32_printing_surface_t, + clipper); + cairo_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + if (path == NULL) { + RestoreDC (surface->win32.dc, -1); + SaveDC (surface->win32.dc); + + return CAIRO_STATUS_SUCCESS; + } + + BeginPath (surface->win32.dc); + status = _cairo_win32_printing_surface_emit_path (surface, path); + EndPath (surface->win32.dc); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + SetPolyFillMode (surface->win32.dc, WINDING); + break; + case CAIRO_FILL_RULE_EVEN_ODD: + SetPolyFillMode (surface->win32.dc, ALTERNATE); + break; + default: + ASSERT_NOT_REACHED; + } + + 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) +{ + _cairo_font_options_init_default (options); + + 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_ON); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_win32_printing_surface_t *surface = abstract_surface; + cairo_solid_pattern_t clear; + cairo_composite_rectangles_t extents; + cairo_status_t 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) { + 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, &extents.bounded)); + + status = _cairo_win32_printing_surface_paint_pattern (surface, source, &extents.bounded); + + cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; +} + +static int +_cairo_win32_line_cap (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: + return PS_ENDCAP_FLAT; + case CAIRO_LINE_CAP_ROUND: + return PS_ENDCAP_ROUND; + case CAIRO_LINE_CAP_SQUARE: + return PS_ENDCAP_SQUARE; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static int +_cairo_win32_line_join (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: + return PS_JOIN_MITER; + case CAIRO_LINE_JOIN_ROUND: + return PS_JOIN_ROUND; + case CAIRO_LINE_JOIN_BEVEL: + return PS_JOIN_BEVEL; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static void +_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) +{ + double s; + + 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); + *scale = s; + s = 1.0/s; + cairo_matrix_scale (m, s, s); +} + +static cairo_int_status_t +_cairo_win32_printing_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 *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + 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 *dash_array; + HGDIOBJ obj; + unsigned int i; + cairo_solid_pattern_t clear; + cairo_matrix_t mat; + double scale; + 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 (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) { + /* Win32 does not support a dash offset. */ + if (style->num_dashes > 0 && style->dash_offset != 0.0) + status = CAIRO_INT_STATUS_UNSUPPORTED; + else + 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, &extents.bounded)); + assert (!(style->num_dashes > 0 && style->dash_offset != 0.0)); + + cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm); + _cairo_matrix_factor_out_scale (&mat, &scale); + + pen_style = PS_GEOMETRIC; + dash_array = NULL; + if (style->num_dashes) { + pen_style |= PS_USERSTYLE; + dash_array = calloc (sizeof (DWORD), style->num_dashes); + for (i = 0; i < style->num_dashes; i++) { + dash_array[i] = (DWORD) (scale * style->dash[i]); + } + } else { + pen_style |= PS_SOLID; + } + + 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; + + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &solid->color); + } else { + /* Color not used as the pen will only be used by WidenPath() */ + color = RGB (0,0,0); + } + brush.lbStyle = BS_SOLID; + brush.lbColor = color; + brush.lbHatch = 0; + pen_style |= _cairo_win32_line_cap (style->line_cap); + pen_style |= _cairo_win32_line_join (style->line_join); + pen = ExtCreatePen(pen_style, + scale * style->line_width, + &brush, + style->num_dashes, + dash_array); + if (pen == NULL) { + status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen"); + goto cleanup_composite; + } + + 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->win32.dc); + if (unlikely (status)) + goto cleanup_composite; + + /* + * Switch to user space to set line parameters + */ + SaveDC (surface->win32.dc); + + _cairo_matrix_to_win32_xform (&mat, &xform); + xform.eDx = 0.0f; + xform.eDy = 0.0f; + + 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->win32.dc); + } else { + 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->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->win32.dc, -1); + DeleteObject (pen); + free (dash_array); + +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_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_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 (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) { + 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, &extents.bounded)); + + surface->path_empty = TRUE; + BeginPath (surface->win32.dc); + status = _cairo_win32_printing_surface_emit_path (surface, path); + EndPath (surface->win32.dc); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + SetPolyFillMode (surface->win32.dc, WINDING); + break; + case CAIRO_FILL_RULE_EVEN_ODD: + SetPolyFillMode (surface->win32.dc, ALTERNATE); + break; + default: + ASSERT_NOT_REACHED; + } + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + status = _cairo_win32_printing_surface_select_solid_brush (surface, source); + if (unlikely (status)) + goto cleanup_composite; + + FillPath (surface->win32.dc); + _cairo_win32_printing_surface_done_solid_brush (surface); + } else if (surface->path_empty == FALSE) { + 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_printing_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_matrix_t ctm; + cairo_glyph_t *unicode_glyphs; + cairo_scaled_font_subsets_glyph_t subset_glyph; + int i, first; + cairo_bool_t sequence_is_unicode; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + /* Where possible reverse the glyph indices back to unicode + * characters. Strings of glyphs that could not be reversed to + * unicode will be printed with ETO_GLYPH_INDEX. + * + * As _cairo_win32_scaled_font_index_to_ucs4() is a slow + * operation, the font subsetting function + * _cairo_scaled_font_subsets_map_glyph() is used to obtain + * the unicode value because it caches the reverse mapping in + * the subsets. + */ + + if (surface->has_ctm) { + for (i = 0; i < num_glyphs; i++) + cairo_matrix_transform_point (&surface->ctm, &glyphs[i].x, &glyphs[i].y); + cairo_matrix_multiply (&ctm, &scaled_font->ctm, &surface->ctm); + scaled_font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &ctm, + &scaled_font->options); + } + + unicode_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unicode_glyphs == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (unicode_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets, + scaled_font, + glyphs[i].index, + NULL, 0, + &subset_glyph); + if (status) + goto fail; + + unicode_glyphs[i].index = subset_glyph.unicode; + } + + i = 0; + first = 0; + sequence_is_unicode = unicode_glyphs[0].index <= 0xffff; + while (i < num_glyphs) { + if (i == num_glyphs - 1 || + ((unicode_glyphs[i + 1].index < 0xffff) != 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; + } + i++; + } + +fail: + if (surface->has_ctm) + cairo_scaled_font_destroy (scaled_font); + + free (unicode_glyphs); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_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_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; + int i; + cairo_matrix_t old_ctm; + cairo_bool_t old_has_ctm; + cairo_solid_pattern_t clear; + cairo_composite_rectangles_t extents; + cairo_bool_t overlap; + cairo_scaled_font_t *local_scaled_font = NULL; + + 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 (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) { + /* When printing bitmap fonts to a printer DC, Windows may + * substitute an outline font for bitmap font. As the win32 + * font backend always uses a screen DC when obtaining the + * font metrics the metrics of the substituted font will not + * match the metrics that the win32 font backend returns. + * + * If we are printing a bitmap font, use fallback images to + * ensure the font is not substituted. + */ +#if CAIRO_HAS_WIN32_FONT + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) { + if (_cairo_win32_scaled_font_is_bitmap (scaled_font)) { + 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) { + status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup_composite; + } +#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) + break; + } + _cairo_scaled_font_thaw_cache (scaled_font); + if (unlikely (status)) + goto cleanup_composite; + + status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup_composite; + } + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + COLORREF color; + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &solid->color); + opaque = cairo_pattern_create_rgb (GetRValue (color) / 255.0, + GetGValue (color) / 255.0, + GetBValue (color) / 255.0); + 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) + { + status = _cairo_win32_printing_surface_emit_win32_glyphs (surface, + op, + source, + glyphs, + num_glyphs, + scaled_font, + clip); + goto cleanup_composite; + } +#endif + + SaveDC (surface->win32.dc); + old_ctm = surface->ctm; + old_has_ctm = surface->has_ctm; + surface->has_ctm = TRUE; + surface->path_empty = TRUE; + _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, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (status) + break; + surface->ctm = old_ctm; + cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y); + status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path); + } + 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 (unlikely (status)) + goto cleanup_composite; + + SetPolyFillMode (surface->win32.dc, WINDING); + FillPath (surface->win32.dc); + _cairo_win32_printing_surface_done_solid_brush (surface); + } else { + SelectClipPath (surface->win32.dc, RGN_AND); + status = _cairo_win32_printing_surface_paint_pattern (surface, source, &extents.bounded); + } + } + RestoreDC (surface->win32.dc, -1); + + if (opaque) + cairo_pattern_destroy (opaque); + +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + + if (local_scaled_font) + cairo_scaled_font_destroy (local_scaled_font); + + 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, + int width, + int height) +{ + cairo_rectangle_t extents; + + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + return cairo_recording_surface_create (content, &extents); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_start_page (void *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; + + /* 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 + * rounding errors. For example the obvious way to paint a recording + * pattern is to: + * + * SaveDC() + * transform the device context DC by the pattern to device matrix + * replay the recording surface + * RestoreDC() + * + * The problem here is that if the pattern to device matrix is + * [100 0 0 100 0 0], coordinates in the recording pattern such as + * (1.56, 2.23) which correspond to (156, 223) in device space + * will be rounded to (100, 200) due to (1.56, 2.23) being + * truncated to integers. + * + * This is solved by saving the current GDI CTM in surface->ctm, + * switch the GDI CTM to identity, and transforming all + * coordinates by surface->ctm before passing them to GDI. When + * painting a recording pattern, surface->ctm is transformed by the + * pattern to device matrix. + * + * For printing device contexts where 1 unit is 1 dpi, switching + * the GDI CTM to identity maximises the possible resolution of + * coordinates. + * + * If the device context is an EMF file, using an identity + * 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 + * will be recorded in the EMF followed by all the graphics + * functions by their coordinateds multiplied by 16. + * + * 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->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; + surface->gdi_ctm.xy = xform.eM21; + surface->gdi_ctm.yx = xform.eM12; + surface->gdi_ctm.yy = xform.eM22; + surface->gdi_ctm.x0 = xform.eDx; + surface->gdi_ctm.y0 = xform.eDy; + } else { + surface->ctm.xx = xform.eM11; + surface->ctm.xy = xform.eM21; + surface->ctm.yx = xform.eM12; + surface->ctm.yy = xform.eM22; + surface->ctm.x0 = xform.eDx; + surface->ctm.y0 = xform.eDy; + cairo_matrix_init_identity (&surface->gdi_ctm); + if (!ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); + } + + surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + surface->has_gdi_ctm = !_cairo_matrix_is_identity (&surface->gdi_ctm); + inverse_ctm = surface->ctm; + status = cairo_matrix_invert (&inverse_ctm); + if (status) + return status; + + 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->win32.base, x_res, y_res); + + SaveDC (surface->win32.dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */ + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t paginated_mode) +{ + cairo_win32_printing_surface_t *surface = abstract_surface; + + surface->paginated_mode = paginated_mode; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + return TRUE; +} + +/** + * cairo_win32_printing_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 DC should be a printing DC; + * antialiasing will be ignored, and GDI will be used as much as + * possible to draw to the surface. + * + * The returned surface will be wrapped using the paginated surface to + * provide correct complex rendering behaviour; cairo_surface_show_page() and + * associated methods must be used for correct output. + * + * Return value: the newly created surface + * + * Since: 1.6 + **/ +cairo_surface_t * +cairo_win32_printing_surface_create (HDC hdc) +{ + cairo_win32_printing_surface_t *surface; + cairo_surface_t *paginated; + + 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->win32.format = CAIRO_FORMAT_RGB24; + surface->content = CAIRO_CONTENT_COLOR_ALPHA; + + surface->win32.dc = hdc; + surface->extents_valid = FALSE; + + surface->brush = NULL; + surface->old_brush = NULL; + surface->font_subsets = _cairo_scaled_font_subsets_create_scaled (); + if (surface->font_subsets == NULL) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + 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->win32.base, + &cairo_win32_printing_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA, + TRUE); /* is_vector */ + + 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->win32.base); + + return paginated; +} + +cairo_bool_t +_cairo_surface_is_win32_printing (const cairo_surface_t *surface) +{ + return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING; +} + +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, + 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 */ + _cairo_win32_printing_surface_show_page, + + _cairo_win32_printing_surface_get_extents, + _cairo_win32_printing_surface_get_font_options, + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _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, /* 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 = { + _cairo_win32_printing_surface_start_page, + _cairo_win32_printing_surface_set_paginated_mode, + NULL, /* set_bounding_box */ + NULL, /* _cairo_win32_printing_surface_has_fallback_images, */ + _cairo_win32_printing_surface_supports_fine_grained_fallbacks, +}; diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-private.h b/gfx/cairo/cairo/src/win32/cairo-win32-private.h new file mode 100644 index 0000000000..e6be49b032 --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-private.h @@ -0,0 +1,275 @@ +/* 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 + */ + +#ifndef CAIRO_WIN32_PRIVATE_H +#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 +#endif +#ifndef SB_NONE +#define SB_NONE 0 +#endif + +#define WIN32_FONT_LOGICAL_SCALE 32 + +CAIRO_BEGIN_DECLS + +/* Surface DC flag values */ +enum { + /* If this is a surface created for printing or not */ + CAIRO_WIN32_SURFACE_FOR_PRINTING = (1<<0), + + /* Whether the DC is a display DC or not */ + CAIRO_WIN32_SURFACE_IS_DISPLAY = (1<<1), + + /* Whether we can use BitBlt with this surface */ + CAIRO_WIN32_SURFACE_CAN_BITBLT = (1<<2), + + /* Whether we can use AlphaBlend with this surface */ + CAIRO_WIN32_SURFACE_CAN_ALPHABLEND = (1<<3), + + /* Whether we can use StretchBlt with this surface */ + CAIRO_WIN32_SURFACE_CAN_STRETCHBLT = (1<<4), + + /* Whether we can use StretchDIBits with this surface */ + CAIRO_WIN32_SURFACE_CAN_STRETCHDIB = (1<<5), + + /* Whether we can use GradientFill rectangles with this surface */ + CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6), + + /* Whether we can use the CHECKJPEGFORMAT escape function */ + CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG = (1<<7), + + /* Whether we can use the CHECKPNGFORMAT escape function */ + CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8), + + /* 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 (const cairo_surface_t *surface); + +cairo_bool_t +_cairo_surface_is_win32_printing (const cairo_surface_t *surface); + +cairo_private void +_cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface); + +cairo_bool_t +_cairo_win32_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle); + +uint32_t +_cairo_win32_flags_for_dc (HDC dc, cairo_format_t format); + +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); + +static inline void +_cairo_matrix_to_win32_xform (const cairo_matrix_t *m, + XFORM *xform) +{ + xform->eM11 = (FLOAT) m->xx; + xform->eM21 = (FLOAT) m->xy; + xform->eM12 = (FLOAT) m->yx; + xform->eM22 = (FLOAT) m->yy; + xform->eDx = (FLOAT) m->x0; + xform->eDy = (FLOAT) m->y0; +} + +cairo_status_t +_cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface, + cairo_clip_t *clip); + +void +_cairo_win32_display_surface_unset_clip (cairo_win32_display_surface_t *surface); + +void +_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header); + +cairo_bool_t +_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/win32/cairo-win32-refptr.h b/gfx/cairo/cairo/src/win32/cairo-win32-refptr.h new file mode 100644 index 0000000000..f045ddf627 --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-refptr.h @@ -0,0 +1,178 @@ +/* -*- 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 + * + * 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 the Mozilla Foundation + * + * Contributor(s): + * Bas Schouten + */ +#ifndef CAIRO_WIN32_REFPTR_H +#define CAIRO_WIN32_REFPTR_H + +template class TemporaryRef; + +/** + * RefPtr points to a refcounted thing that has AddRef and Release + * methods to increase/decrease the refcount, respectively. After a + * RefPtr is assigned a T*, the T* can be used through the RefPtr + * as if it were a T*. + * + * A RefPtr can forget its underlying T*, which results in the T* + * being wrapped in a temporary object until the T* is either + * re-adopted from or released by the temporary. + */ +template +class RefPtr +{ + // To allow them to use unref() + friend class TemporaryRef; + + struct dontRef {}; + +public: + RefPtr() : ptr(0) { } + RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {} + RefPtr(const TemporaryRef& o) : ptr(o.drop()) {} + RefPtr(T* t) : ptr(ref(t)) {} + + template + RefPtr(const RefPtr& o) : ptr(ref(o.get())) {} + + ~RefPtr() { unref(ptr); } + + RefPtr& operator=(const RefPtr& o) { + assign(ref(o.ptr)); + return *this; + } + RefPtr& operator=(const TemporaryRef& o) { + assign(o.drop()); + return *this; + } + RefPtr& operator=(T* t) { + assign(ref(t)); + return *this; + } + + template + RefPtr& operator=(const RefPtr& o) { + assign(ref(o.get())); + return *this; + } + + TemporaryRef forget() { + T* tmp = ptr; + ptr = 0; + return TemporaryRef(tmp, dontRef()); + } + + T* get() const { return ptr; } + operator T*() const { return ptr; } + T* operator->() const { return ptr; } + T& operator*() const { return *ptr; } + template + operator TemporaryRef() { return TemporaryRef(ptr); } + + /** + * WARNING for ease of use, passing a reference will release/clear out ptr! + * We null out the ptr before returning its address so people passing byref + * as input will most likely get functions returning errors rather than accessing + * freed memory. Further more accessing it after this point if it hasn't + * been set will produce a null pointer dereference. + */ + T** operator&() + { + if (ptr) { + ptr->Release(); + ptr = NULL; + } + return &ptr; + } + +private: + void assign(T* t) { + unref(ptr); + ptr = t; + } + + T* ptr; + + static inline T* ref(T* t) { + if (t) { + t->AddRef(); + } + return t; + } + + static inline void unref(T* t) { + if (t) { + t->Release(); + } + } +}; + +/** + * TemporaryRef represents an object that holds a temporary + * reference to a T. TemporaryRef objects can't be manually ref'd or + * unref'd (being temporaries, not lvalues), so can only relinquish + * references to other objects, or unref on destruction. + */ +template +class TemporaryRef +{ + // To allow it to construct TemporaryRef from a bare T* + friend class RefPtr; + + typedef typename RefPtr::dontRef dontRef; + +public: + TemporaryRef(T* t) : ptr(RefPtr::ref(t)) {} + TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} + + template + TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} + + ~TemporaryRef() { RefPtr::unref(ptr); } + + T* drop() const { + T* tmp = ptr; + ptr = 0; + return tmp; + } + +private: + TemporaryRef(T* t, const dontRef&) : ptr(t) {} + + mutable T* ptr; + + TemporaryRef(); + TemporaryRef& operator=(const TemporaryRef&); +}; + +#endif 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 0000000000..e1e6494cce --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-surface.c @@ -0,0 +1,405 @@ +/* -*- 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-backend-private.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; +} + +HDC +cairo_win32_get_dc_with_clip (cairo_t *cr) +{ + cairo_surface_t *surface = cairo_get_target (cr); + if (cr->backend->type == CAIRO_TYPE_DEFAULT) { + cairo_default_context_t *c = (cairo_default_context_t *) cr; + cairo_clip_t *clip = _cairo_clip_copy (_cairo_gstate_get_clip (c->gstate)); + if (_cairo_surface_is_win32 (surface)) { + cairo_win32_display_surface_t *winsurf = (cairo_win32_display_surface_t *) surface; + + _cairo_win32_display_surface_set_clip (winsurf, clip); + + _cairo_clip_destroy (clip); + return winsurf->win32.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_printing_surface_t *psurf = (cairo_win32_printing_surface_t *) target; + + status = _cairo_surface_clipper_set_clip (&psurf->clipper, clip); + + _cairo_clip_destroy (clip); + + if (status) + return NULL; + + return psurf->win32.dc; + } +#endif + } + _cairo_clip_destroy (clip); + } + 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 + **/ +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_DWRITE_FONT + if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { + if (!glyph_indexing) return CAIRO_INT_STATUS_UNSUPPORTED; + + // FIXME: fake values for params that aren't currently passed in here + cairo_operator_t op = CAIRO_OPERATOR_SOURCE; + cairo_clip_t *clip = NULL; + return _cairo_dwrite_show_glyphs_on_surface (dst, op, source, glyphs, num_glyphs, scaled_font, clip /* , glyph_indexing */ ); + } +#endif +#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 + +cairo_status_t +cairo_win32_surface_get_size (const cairo_surface_t *surface, int *width, int *height) +{ + if (surface->type != CAIRO_SURFACE_TYPE_WIN32) + return CAIRO_STATUS_SURFACE_TYPE_MISMATCH; + + const cairo_win32_surface_t *winsurface = (const cairo_win32_surface_t *) surface; + + *width = winsurface->extents.width; + *height = winsurface->extents.height; + + return CAIRO_STATUS_SUCCESS; +} + diff --git a/gfx/cairo/cairo/src/win32/cairo-win32-system.c b/gfx/cairo/cairo/src/win32/cairo-win32-system.c new file mode 100644 index 0000000000..878553009f --- /dev/null +++ b/gfx/cairo/cairo/src/win32/cairo-win32-system.c @@ -0,0 +1,89 @@ +/* -*- 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 + */ + +/* This file should include code that is system-specific, not + * feature-specific. For example, the DLL initialization/finalization + * code on Win32 or OS/2 must live here (not in cairo-whatever-surface.c). + * Same about possible ELF-specific code. + * + * And no other function should live here. + */ + + +#include "cairoint.h" + +#if CAIRO_MUTEX_IMPL_WIN32 +#if !CAIRO_WIN32_STATIC_BUILD + +#define WIN32_LEAN_AND_MEAN +/* We require Windows 2000 features such as ETO_PDY */ +#if !defined(WINVER) || (WINVER < 0x0500) +# define WINVER 0x0500 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) +# define _WIN32_WINNT 0x0500 +#endif + +#include + +/* declare to avoid "no previous prototype for 'DllMain'" warning */ +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved); + +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + CAIRO_MUTEX_INITIALIZE (); + break; + + case DLL_PROCESS_DETACH: + CAIRO_MUTEX_FINALIZE (); + break; + } + + return TRUE; +} + +#endif +#endif diff --git a/gfx/cairo/glitz/src/glitz.def b/gfx/cairo/glitz/src/glitz.def new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gfx/cairo/glitz/src/wgl/glitz-wgl.def b/gfx/cairo/glitz/src/wgl/glitz-wgl.def new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gfx/cairo/libpixman/AUTHORS b/gfx/cairo/libpixman/AUTHORS new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gfx/cairo/libpixman/COPYING b/gfx/cairo/libpixman/COPYING new file mode 100644 index 0000000000..6168dea56f --- /dev/null +++ b/gfx/cairo/libpixman/COPYING @@ -0,0 +1,42 @@ +The following is the MIT license, agreed upon by most contributors. +Copyright holders of new code should use this license statement where +possible. They may also add themselves to the list below. + +/* + * Copyright 1987, 1988, 1989, 1998 The Open Group + * Copyright 1987, 1988, 1989 Digital Equipment Corporation + * Copyright 1999, 2004, 2008 Keith Packard + * Copyright 2000 SuSE, Inc. + * Copyright 2000 Keith Packard, member of The XFree86 Project, Inc. + * Copyright 2004, 2005, 2007, 2008, 2009, 2010 Red Hat, Inc. + * Copyright 2004 Nicholas Miell + * Copyright 2005 Lars Knoll & Zack Rusin, Trolltech + * Copyright 2005 Trolltech AS + * Copyright 2007 Luca Barbato + * Copyright 2008 Aaron Plattner, NVIDIA Corporation + * Copyright 2008 Rodrigo Kumpera + * Copyright 2008 André Tupinambá + * Copyright 2008 Mozilla Corporation + * Copyright 2008 Frederic Plourde + * Copyright 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright 2009, 2010 Nokia 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. + */ diff --git a/gfx/cairo/libpixman/INSTALL b/gfx/cairo/libpixman/INSTALL new file mode 100644 index 0000000000..5458714e1e --- /dev/null +++ b/gfx/cairo/libpixman/INSTALL @@ -0,0 +1,234 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/gfx/cairo/libpixman/README b/gfx/cairo/libpixman/README new file mode 100644 index 0000000000..961a8529bd --- /dev/null +++ b/gfx/cairo/libpixman/README @@ -0,0 +1,140 @@ +Pixman +====== + +Pixman is a library that provides low-level pixel manipulation +features such as image compositing and trapezoid rasterization. + +Questions should be directed to the pixman mailing list: + + https://lists.freedesktop.org/mailman/listinfo/pixman + +You can also file bugs at + + https://gitlab.freedesktop.org/pixman/pixman/-/issues/new + +or submit improvements in form of a Merge Request via + + https://gitlab.freedesktop.org/pixman/pixman/-/merge_requests + +For real time discussions about pixman, feel free to join the IRC +channels #cairo and #xorg-devel on the FreeNode IRC network. + + +Contributing +------------ + +In order to contribute to pixman, you will need a working knowledge of +the git version control system. For a quick getting started guide, +there is the "Everyday Git With 20 Commands Or So guide" + + https://www.kernel.org/pub/software/scm/git/docs/everyday.html + +from the Git homepage. For more in depth git documentation, see the +resources on the Git community documentation page: + + https://git-scm.com/documentation + +Pixman uses the infrastructure from the freedesktop.org umbrella +project. For instructions about how to use the git service on +freedesktop.org, see: + + https://www.freedesktop.org/wiki/Infrastructure/git/Developers + +The Pixman master repository can be found at: + + https://gitlab.freedesktop.org/pixman/pixman + + +Sending patches +--------------- + +Patches should be submitted in form of Merge Requests via Gitlab. + +You will first need to create a fork of the main pixman repository at + + https://gitlab.freedesktop.org/pixman/pixman + +via the Fork button on the top right. Once that is done you can add your +personal repository as a remote to your local pixman development git checkout: + + git remote add my-gitlab git@gitlab.freedesktop.org:YOURUSERNAME/pixman.git + + git fetch my-gitlab + +Make sure to have added ssh keys to your gitlab profile at + + https://gitlab.freedesktop.org/profile/keys + +Once that is set up, the general workflow for sending patches is to create a +new local branch with your improvements and once it's ready push it to your +personal pixman fork: + + git checkout -b fix-some-bug + ... + git push my-gitlab + +The output of the `git push` command will include a link that allows you to +create a Merge Request against the official pixman repository. + +Whenever you make changes to your branch (add new commits or fix up commits) +you push them back to your personal pixman fork: + + git push -f my-gitlab + +If there is an open Merge Request Gitlab will automatically pick up the +changes from your branch and pixman developers can review them anew. + +In order for your patches to be accepted, please consider the +following guidelines: + + - At each point in the series, pixman should compile and the test + suite should pass. + + The exception here is if you are changing the test suite to + demonstrate a bug. In this case, make one commit that makes the + test suite fail due to the bug, and then another commit that fixes + the bug. + + You can run the test suite with + + make check + + if you built pixman with autotools or + + meson test -C builddir + + if you built pixman with meson. + + It will take around two minutes to run on a modern PC. + + - Follow the coding style described in the CODING_STYLE file + + - For bug fixes, include an update to the test suite to make sure + the bug doesn't reappear. + + - For new features, add tests of the feature to the test + suite. Also, add a program demonstrating the new feature to the + demos/ directory. + + - Write descriptive commit messages. Useful information to include: + - Benchmark results, before and after + - Description of the bug that was fixed + - Detailed rationale for any new API + - Alternative approaches that were rejected (and why they + don't work) + - If review comments were incorporated, a brief version + history describing what those changes were. + + - For big patch series, write an introductory post with an overall + description of the patch series, including benchmarks and + motivation. Each commit message should still be descriptive and + include enough information to understand why this particular commit + was necessary. + +Pixman has high standards for code quality and so almost everybody +should expect to have the first versions of their patches rejected. + +If you think that the reviewers are wrong about something, or that the +guidelines above are wrong, feel free to discuss the issue. The purpose +of the guidelines and code review is to ensure high code quality; it is +not an exercise in compliance. diff --git a/gfx/cairo/libpixman/src/dither/blue-noise-64x64.h b/gfx/cairo/libpixman/src/dither/blue-noise-64x64.h new file mode 100644 index 0000000000..93c8805b51 --- /dev/null +++ b/gfx/cairo/libpixman/src/dither/blue-noise-64x64.h @@ -0,0 +1,77 @@ +/* WARNING: This file is generated by make-blue-noise.c + * Please edit that file instead of this one. + */ + +#ifndef BLUE_NOISE_64X64_H +#define BLUE_NOISE_64X64_H + +#include + +static const uint16_t dither_blue_noise_64x64[4096] = { + 3039, 1368, 3169, 103, 2211, 1248, 2981, 668, 2633, 37, 3963, 2903, 384, 2564, 3115, 1973, 3348, 830, 2505, 1293, 3054, 1060, 1505, 3268, 400, 1341, 593, 3802, 3384, 429, 4082, 1411, 2503, 3863, 126, 1292, 1887, 2855, 205, 2094, 2977, 1899, 3924, 356, 3088, 2500, 3942, 1409, 2293, 1734, 3732, 1291, 3227, 277, 2054, 786, 2871, 411, 2425, 1678, 3986, 455, 2879, 2288, + 388, 1972, 3851, 778, 2768, 3697, 944, 2123, 1501, 3533, 937, 1713, 1381, 3888, 156, 1242, 516, 2888, 1607, 3676, 632, 2397, 3804, 2673, 1898, 3534, 2593, 1777, 1170, 2299, 3013, 1838, 523, 3053, 1647, 3601, 3197, 959, 1520, 3633, 893, 2437, 3367, 2187, 1258, 137, 1965, 401, 3546, 643, 3087, 2498, 733, 2786, 3371, 4053, 1266, 1977, 3663, 183, 2570, 2107, 1183, 3708, + 907, 2473, 1151, 3363, 1527, 1902, 232, 3903, 3060, 496, 2486, 3206, 2165, 861, 2387, 3653, 2101, 3972, 132, 2162, 3437, 1827, 215, 895, 3114, 271, 969, 2932, 197, 1598, 878, 3696, 1140, 2120, 904, 2431, 302, 3846, 2675, 481, 3187, 66, 1440, 650, 3833, 2826, 3435, 901, 2936, 2111, 250, 1875, 3609, 1174, 1747, 162, 2346, 3420, 913, 3172, 1383, 752, 3298, 1735, + 3540, 2938, 249, 2324, 526, 3099, 2561, 1324, 2347, 1861, 1200, 3702, 257, 3442, 1514, 2999, 992, 1766, 2735, 1163, 478, 2943, 1279, 3635, 2177, 1464, 3672, 2386, 3871, 3340, 2690, 64, 3489, 2811, 3999, 633, 1948, 1243, 2269, 1807, 1143, 2750, 3729, 1790, 2363, 1053, 1537, 2636, 4065, 1076, 1476, 3869, 450, 2200, 2676, 658, 2979, 1548, 544, 1913, 2838, 3911, 116, 2698, + 517, 1295, 3997, 1739, 3665, 1083, 3509, 599, 3400, 118, 2956, 720, 2689, 1907, 567, 2523, 284, 3397, 711, 3219, 2450, 3985, 1665, 2549, 562, 3011, 1855, 729, 1355, 528, 1908, 2456, 1384, 337, 1540, 2654, 3138, 3513, 703, 4080, 3314, 2047, 855, 3037, 209, 3317, 577, 1828, 17, 2336, 3193, 2748, 962, 3441, 1450, 3246, 1075, 3878, 2615, 3497, 1033, 2310, 1442, 2183, + 1654, 3254, 2061, 738, 2832, 148, 2030, 1670, 909, 3850, 2109, 1533, 4046, 1085, 3098, 3897, 1378, 2248, 3829, 1495, 1966, 23, 797, 3427, 1124, 4057, 95, 2787, 2190, 3074, 3950, 742, 3194, 1999, 3386, 1113, 16, 1657, 2804, 201, 1543, 383, 2559, 1325, 3604, 2068, 2493, 3771, 1284, 3460, 710, 1716, 2447, 80, 3811, 2032, 347, 2227, 15, 1689, 397, 3084, 662, 3798, + 973, 43, 2608, 3143, 1459, 2423, 4066, 2770, 3191, 1283, 2630, 314, 3235, 2289, 72, 1822, 2840, 924, 350, 2653, 1057, 3715, 2235, 2775, 346, 2083, 1553, 3292, 1081, 274, 1686, 1188, 2327, 3743, 578, 2234, 3916, 2519, 1011, 3056, 2207, 3438, 3890, 537, 1617, 837, 3094, 373, 2795, 1980, 276, 3951, 1353, 3015, 844, 1724, 3651, 2923, 1316, 4092, 2504, 3627, 1936, 2854, + 2461, 3929, 1193, 421, 3746, 820, 1180, 286, 2261, 532, 3625, 1812, 802, 1327, 3527, 670, 3730, 2025, 3124, 3565, 529, 2960, 1769, 1390, 3196, 2494, 3756, 796, 3618, 2602, 3463, 2847, 166, 953, 1745, 2900, 438, 2070, 1418, 3741, 639, 1205, 1891, 2882, 2282, 4012, 1182, 1696, 3630, 951, 2904, 2170, 3530, 375, 2320, 2742, 1132, 701, 3216, 2023, 847, 1230, 310, 3431, + 770, 1961, 3531, 1702, 2181, 3370, 1877, 3072, 1571, 3389, 1071, 2415, 3782, 2803, 1610, 2454, 1211, 182, 1655, 2322, 1282, 3372, 287, 3935, 704, 1232, 415, 1910, 2286, 1399, 556, 1964, 4068, 2444, 3605, 1272, 3345, 816, 3526, 256, 2402, 2777, 955, 345, 3289, 111, 2727, 635, 2396, 1488, 3331, 600, 1032, 1575, 4026, 515, 3507, 2433, 1605, 460, 3364, 2783, 1810, 1397, + 2334, 223, 2945, 688, 2533, 99, 2705, 624, 3944, 2073, 46, 2978, 508, 2132, 269, 3173, 3453, 2631, 4076, 694, 1892, 2586, 972, 2178, 3470, 1695, 2849, 3141, 77, 3884, 994, 3029, 1536, 673, 3083, 124, 2583, 1722, 2821, 1944, 4027, 1661, 3176, 3728, 1337, 1813, 3503, 2035, 3930, 157, 2537, 1865, 3096, 2646, 1941, 3252, 1449, 135, 2836, 3758, 2139, 84, 3678, 3106, + 3862, 1545, 3307, 1320, 3955, 1031, 3664, 1306, 2460, 776, 1487, 3294, 1187, 3990, 1903, 1021, 549, 1484, 943, 3027, 97, 3853, 1499, 2880, 198, 2575, 3995, 1089, 1587, 2475, 3282, 339, 2657, 1158, 2105, 1493, 3943, 580, 3232, 1287, 846, 48, 2480, 2112, 771, 2534, 459, 3134, 850, 1298, 3790, 325, 3652, 1249, 193, 940, 2202, 3895, 1829, 911, 1366, 2577, 1069, 534, + 2104, 1009, 2667, 392, 1983, 2917, 1645, 324, 3439, 2869, 3705, 1767, 2592, 756, 2916, 3683, 2276, 2850, 2053, 3594, 2403, 3181, 634, 3699, 1933, 906, 519, 2150, 3673, 764, 1770, 2220, 3795, 3336, 502, 3547, 2339, 1110, 301, 2210, 3354, 3643, 569, 1518, 2940, 3973, 1138, 1613, 2773, 2127, 2983, 1671, 769, 2161, 3800, 2730, 3127, 1179, 533, 3259, 2284, 4014, 1651, 2820, + 3566, 653, 1839, 3455, 2399, 789, 3149, 2244, 1863, 1099, 474, 2307, 158, 3541, 1312, 1711, 0, 3902, 360, 1629, 1091, 395, 1781, 1191, 2374, 3353, 1419, 3225, 206, 2931, 3553, 1046, 54, 1646, 2470, 910, 1860, 3137, 3770, 2635, 1562, 2809, 1215, 3788, 222, 2199, 3335, 67, 3606, 524, 1001, 3309, 2410, 3473, 591, 1619, 291, 2502, 3629, 2891, 335, 741, 3378, 168, + 2384, 3129, 4051, 22, 1444, 3613, 543, 3893, 186, 2665, 4062, 933, 3058, 2142, 449, 2711, 3224, 849, 1330, 3349, 2195, 2670, 3484, 2993, 32, 3774, 2722, 1859, 2548, 1268, 583, 2027, 3165, 2807, 4029, 227, 2897, 1434, 721, 1816, 195, 905, 2066, 3258, 1754, 970, 2674, 1880, 2338, 3915, 1485, 2660, 14, 1313, 2914, 2046, 4074, 791, 1917, 1301, 1725, 2687, 2019, 1443, + 418, 1186, 1664, 2859, 1049, 2056, 2741, 1226, 1589, 3186, 2042, 1377, 3449, 1574, 3941, 1063, 1930, 2501, 3751, 2930, 671, 4031, 888, 2081, 1544, 684, 1117, 351, 4052, 1698, 2393, 3881, 1439, 785, 1277, 2013, 3488, 441, 2459, 3980, 3061, 3481, 2543, 419, 3020, 609, 3515, 1350, 799, 2878, 348, 2034, 3966, 1824, 950, 3281, 1394, 2239, 3452, 55, 3922, 3119, 892, 3785, + 3023, 2140, 782, 2492, 3817, 241, 3355, 2424, 856, 3639, 612, 2556, 245, 2858, 705, 2316, 3562, 495, 1748, 128, 1912, 1454, 280, 2552, 3905, 3130, 2274, 3472, 834, 3055, 240, 2692, 471, 2272, 3301, 2632, 1080, 3693, 2136, 1029, 1364, 590, 1611, 4067, 1190, 2360, 3827, 261, 3180, 1768, 3471, 1103, 3003, 520, 3674, 151, 2571, 555, 3033, 982, 2353, 504, 1259, 2555, + 149, 3889, 3380, 493, 3178, 1681, 663, 1924, 2990, 49, 1792, 3861, 1192, 1987, 3273, 297, 1457, 3043, 1177, 2292, 3249, 2829, 3682, 1154, 1758, 428, 2872, 1993, 1500, 3703, 1129, 3421, 1840, 3754, 163, 659, 1733, 3182, 38, 2875, 1957, 3614, 2237, 78, 1873, 2801, 1513, 2121, 1074, 2516, 667, 3710, 1429, 2430, 2088, 2830, 1072, 3557, 1531, 2733, 1955, 3286, 3590, 1826, + 2778, 1068, 1932, 1452, 2279, 1185, 3564, 3952, 1391, 2726, 3313, 2331, 870, 3709, 1674, 2772, 4085, 808, 2596, 3848, 927, 538, 2335, 3334, 773, 3597, 1347, 109, 2663, 608, 2108, 2994, 936, 1524, 2922, 3968, 2422, 1467, 845, 3870, 321, 2704, 1073, 3308, 3680, 823, 430, 3375, 4030, 112, 2171, 2695, 267, 3374, 731, 1627, 3919, 1871, 352, 3839, 1370, 234, 794, 1532, + 3245, 647, 3575, 74, 3045, 2766, 285, 2174, 498, 1059, 1551, 385, 3125, 2598, 143, 1128, 2095, 3395, 318, 1590, 3524, 1345, 1969, 242, 2759, 2092, 947, 3926, 3244, 2356, 1658, 6, 3593, 2554, 1172, 1995, 371, 2755, 3417, 2294, 1570, 3164, 748, 2517, 1401, 3111, 2420, 1662, 2910, 1276, 3276, 854, 1804, 4000, 1253, 2987, 229, 2344, 3184, 649, 2196, 2921, 4095, 2389, + 1289, 2193, 2579, 4023, 757, 1858, 986, 3199, 2514, 3475, 4021, 2154, 651, 1432, 3468, 2404, 574, 1799, 3105, 2145, 86, 2614, 3218, 1565, 4088, 2481, 3079, 1815, 323, 1212, 3837, 759, 2159, 435, 3223, 784, 3659, 1114, 1888, 550, 1221, 3786, 1803, 499, 2117, 185, 3763, 942, 589, 2001, 3838, 1483, 3154, 2256, 468, 2544, 3403, 898, 1208, 2610, 3622, 967, 1929, 378, + 3781, 220, 1656, 1115, 3347, 2428, 3822, 1577, 712, 1959, 110, 2765, 1762, 3854, 979, 2928, 3714, 1371, 746, 3969, 2884, 975, 3779, 641, 1142, 159, 1460, 702, 3485, 2866, 2495, 3330, 1305, 3937, 1635, 2229, 2962, 146, 4055, 3091, 2417, 100, 3508, 2933, 4006, 1167, 1920, 2760, 3552, 2545, 433, 2845, 142, 1056, 1886, 3616, 1435, 2099, 3803, 1749, 27, 1446, 3350, 2843, + 884, 3310, 2948, 2103, 447, 1351, 187, 2895, 3655, 1256, 3036, 932, 3325, 2257, 451, 1915, 40, 2780, 2438, 1112, 1814, 423, 2290, 1905, 2898, 3419, 2306, 3760, 1938, 486, 1019, 1791, 3010, 2628, 203, 3408, 1269, 2507, 1606, 862, 2779, 2078, 952, 1529, 2638, 708, 3332, 1413, 2, 1726, 1156, 3500, 2392, 3791, 3076, 812, 107, 2861, 501, 3050, 3487, 2455, 594, 1731, + 2685, 1498, 680, 3908, 2621, 3529, 1786, 2236, 342, 2569, 1526, 3722, 230, 1290, 3203, 3947, 1609, 3516, 467, 3267, 3685, 1461, 3140, 3569, 367, 1759, 928, 2754, 1332, 2219, 4034, 260, 655, 1984, 978, 3814, 617, 2086, 3525, 279, 3841, 1373, 3361, 319, 2251, 3066, 407, 2382, 3918, 3133, 2168, 762, 1523, 507, 2641, 1677, 4025, 2413, 1584, 793, 2049, 1109, 3962, 2218, + 1194, 3692, 266, 1687, 981, 3103, 740, 3983, 1005, 3434, 570, 2383, 1942, 2718, 676, 2462, 1007, 2089, 1308, 2222, 233, 2568, 829, 1241, 2669, 3987, 514, 3303, 69, 3142, 1603, 3560, 2295, 3288, 1497, 2696, 1764, 2865, 1058, 3271, 1914, 477, 2529, 3927, 1736, 1273, 3752, 2029, 1012, 565, 2798, 4078, 1949, 3305, 1175, 2179, 380, 3366, 1195, 3849, 2637, 416, 2959, 125, + 3396, 2467, 2036, 3234, 2340, 68, 2819, 1436, 2011, 3139, 1704, 4073, 860, 3582, 1468, 2969, 211, 3157, 4056, 866, 2935, 2000, 3923, 31, 2157, 1477, 2429, 1147, 3792, 2557, 774, 2802, 1153, 3747, 464, 3192, 42, 3904, 539, 1474, 2283, 803, 2876, 1061, 75, 3477, 747, 2893, 1538, 3626, 251, 1322, 2506, 189, 2791, 3667, 939, 2991, 1971, 175, 3195, 1416, 3648, 1857, + 3052, 454, 851, 3789, 1271, 1906, 3694, 2484, 406, 2757, 26, 1189, 2909, 296, 2215, 3784, 1864, 637, 2715, 1673, 3445, 581, 1572, 3059, 3469, 761, 2984, 1737, 2058, 440, 1414, 1921, 121, 2527, 894, 2223, 1302, 2377, 3077, 2666, 3759, 3198, 1811, 3661, 2166, 2731, 1883, 359, 3285, 2458, 1805, 3459, 926, 3834, 675, 1893, 1496, 2612, 657, 3523, 1763, 2354, 564, 961, + 1367, 3977, 1588, 2714, 322, 3446, 1088, 625, 3887, 1354, 3535, 2090, 3316, 1760, 1127, 483, 3491, 1421, 2301, 94, 1202, 3740, 2311, 1014, 1878, 3836, 180, 3412, 991, 2868, 3953, 3450, 3081, 1632, 4071, 1882, 3543, 726, 1719, 179, 1171, 364, 1420, 622, 3090, 1490, 946, 4007, 2212, 1102, 619, 2739, 2189, 1669, 2937, 3426, 39, 3940, 2191, 1264, 887, 4091, 2792, 2135, + 4, 2883, 2281, 631, 3044, 1641, 2232, 3243, 1773, 2319, 827, 2591, 629, 3938, 2426, 3222, 2629, 1044, 3879, 3293, 1952, 2749, 275, 2590, 472, 1372, 2496, 660, 3669, 2264, 208, 915, 2167, 561, 2828, 307, 3265, 1104, 3964, 2155, 3425, 1951, 4077, 2391, 283, 3387, 2581, 115, 1415, 3069, 3896, 141, 3158, 1214, 442, 2405, 1349, 3085, 425, 2528, 3002, 312, 1602, 3588, + 1137, 3323, 1963, 1002, 3578, 2521, 127, 925, 2970, 273, 3737, 1573, 167, 2863, 1509, 800, 147, 2059, 2942, 409, 921, 3151, 1451, 3909, 3333, 2844, 2096, 1512, 3136, 1210, 1798, 2709, 1331, 3586, 1034, 1521, 2441, 2926, 488, 2585, 775, 3031, 2693, 879, 3602, 1173, 2028, 3654, 2781, 841, 1975, 1507, 3646, 768, 3991, 2012, 996, 3544, 1666, 3810, 1990, 3360, 753, 2597, + 3736, 304, 1473, 3828, 485, 1334, 4008, 2072, 3495, 1136, 2806, 2004, 3236, 1010, 2130, 3819, 1750, 3567, 644, 2515, 1794, 3636, 698, 2137, 1162, 832, 3761, 326, 2613, 513, 3302, 3820, 357, 3163, 2259, 3733, 101, 1922, 1386, 3587, 1640, 28, 1286, 2141, 1761, 2918, 693, 1639, 457, 3250, 2434, 365, 2599, 1729, 3284, 2643, 306, 2793, 689, 1090, 104, 1309, 2305, 1831, + 2776, 859, 2446, 2915, 1778, 3337, 2677, 614, 1508, 2409, 469, 4033, 1321, 3563, 402, 3131, 2720, 1093, 1569, 4042, 1229, 2277, 216, 3046, 1817, 57, 3006, 1684, 4059, 2016, 795, 2440, 1652, 1960, 610, 2763, 920, 3864, 3110, 1026, 2326, 3762, 3233, 521, 3856, 173, 2457, 3939, 2138, 1262, 3572, 989, 3021, 2238, 119, 1445, 3832, 1809, 2297, 3467, 2700, 3684, 3102, 394, + 4036, 2050, 3256, 89, 2198, 1079, 248, 1845, 3805, 3104, 880, 1779, 2688, 717, 2373, 1375, 262, 2249, 3071, 13, 2813, 3429, 1600, 3984, 2416, 3603, 1299, 2298, 998, 3492, 1393, 2951, 10, 4009, 1247, 3462, 1679, 2204, 414, 2736, 316, 1894, 2816, 1050, 3373, 1462, 3107, 817, 3464, 21, 1835, 4070, 568, 1178, 3718, 875, 3168, 466, 2974, 1458, 2084, 616, 1564, 1018, + 1693, 546, 1244, 3899, 716, 3160, 3608, 2877, 1220, 334, 3443, 2270, 44, 3000, 1843, 3928, 3405, 766, 3686, 2040, 587, 993, 2647, 387, 930, 2753, 630, 3274, 150, 2808, 453, 3638, 1092, 2352, 3030, 239, 2562, 700, 3240, 1257, 4016, 730, 1515, 2203, 2551, 417, 1866, 1123, 2348, 2902, 1550, 2678, 2075, 3238, 1630, 2531, 2115, 1255, 4054, 840, 290, 3874, 2477, 3399, + 2250, 3577, 2817, 1626, 2576, 1356, 2315, 792, 2087, 2618, 1612, 3855, 1263, 3637, 1036, 494, 1535, 2553, 1198, 1715, 3867, 3170, 1359, 1954, 3483, 1539, 2069, 3886, 1772, 2487, 1534, 2045, 3242, 806, 1578, 2018, 3948, 1423, 3596, 2076, 2466, 3424, 139, 3688, 871, 4049, 2852, 3342, 547, 3719, 327, 852, 3505, 207, 2794, 542, 3600, 45, 2411, 3324, 1788, 3012, 1235, 61, + 2655, 917, 253, 1986, 3738, 313, 1706, 4072, 120, 3229, 957, 597, 2024, 3262, 2453, 2857, 2002, 3190, 210, 2784, 2206, 300, 2400, 3766, 553, 3152, 218, 1150, 2988, 883, 3753, 627, 2664, 3831, 437, 3385, 1008, 2957, 60, 1636, 891, 2899, 1776, 3062, 1315, 2026, 194, 1643, 2079, 1296, 3201, 2465, 1379, 1927, 3898, 1125, 1847, 2846, 1552, 1028, 2725, 2169, 787, 3202, + 1441, 3982, 3032, 1052, 3251, 605, 2639, 3073, 1431, 3642, 2329, 2949, 341, 1634, 833, 129, 4020, 916, 3571, 669, 1506, 3411, 821, 2856, 1207, 2337, 2683, 3448, 340, 2214, 3128, 235, 1738, 1288, 2833, 2419, 606, 1884, 2668, 552, 3765, 1176, 399, 2302, 596, 3591, 2634, 767, 3845, 2767, 995, 3967, 491, 3057, 814, 2300, 3422, 691, 3797, 254, 3645, 509, 3478, 1836, + 2119, 475, 2445, 1525, 2175, 3539, 914, 1926, 473, 1157, 1800, 3971, 2701, 3739, 2129, 3486, 1333, 1784, 2366, 2982, 1070, 4089, 1802, 73, 1642, 3958, 835, 1837, 1480, 4043, 1217, 2469, 3416, 2113, 88, 3668, 1240, 3255, 3920, 2355, 3167, 2003, 2645, 3936, 3228, 1592, 1144, 3474, 2394, 79, 1820, 2241, 1594, 3656, 2584, 153, 1448, 3034, 2005, 2511, 1692, 1335, 3913, 217, + 2822, 3391, 745, 3813, 192, 1274, 2941, 3847, 2489, 3440, 744, 161, 1422, 1086, 572, 3004, 2617, 338, 3807, 2031, 236, 2472, 3065, 2098, 3358, 362, 2163, 3574, 497, 2788, 1970, 948, 3885, 685, 3100, 1712, 2228, 292, 1408, 1016, 164, 3537, 1417, 941, 34, 2172, 3001, 358, 1491, 3147, 699, 3356, 258, 1149, 2946, 1787, 3931, 382, 1146, 3291, 818, 2890, 2379, 1096, + 3679, 1328, 1901, 3162, 2747, 1730, 2253, 5, 1556, 2818, 2093, 3166, 2522, 3410, 2287, 1701, 956, 3237, 620, 1596, 3300, 1307, 511, 3701, 1020, 2939, 1362, 2532, 3208, 749, 3641, 160, 1522, 2624, 1095, 4086, 826, 2841, 3583, 2173, 1727, 723, 2925, 1911, 2482, 3726, 863, 1962, 4028, 1111, 2835, 3773, 2449, 2022, 582, 3278, 923, 2619, 2152, 4039, 92, 1934, 3145, 677, + 2530, 53, 2303, 1003, 458, 3989, 739, 3321, 1064, 369, 3556, 877, 1900, 426, 3876, 1, 3617, 2106, 1197, 2805, 3634, 857, 2706, 1504, 2418, 682, 3868, 20, 1139, 1688, 2333, 3311, 2907, 1945, 265, 2385, 3433, 1601, 636, 2620, 3095, 4044, 386, 3382, 1184, 527, 2814, 3414, 2342, 465, 1889, 1343, 874, 3479, 1502, 2233, 3689, 1385, 559, 2745, 1463, 3465, 376, 1718, + 3217, 4045, 1580, 3612, 2525, 1228, 3018, 1958, 3725, 2358, 1361, 3996, 1581, 3063, 1224, 2737, 1475, 2442, 3946, 191, 1796, 2128, 3975, 134, 1916, 3318, 1597, 2071, 3749, 2672, 403, 1278, 602, 3745, 3220, 1374, 445, 2064, 3830, 243, 1252, 2390, 1563, 2724, 3875, 1818, 1346, 165, 1650, 3264, 2680, 117, 2998, 4081, 343, 2799, 9, 3122, 1743, 3724, 1040, 2231, 3842, 1209, + 900, 398, 2851, 697, 1797, 3482, 293, 2679, 1649, 566, 2954, 91, 2697, 714, 2060, 3211, 781, 480, 3040, 1038, 2611, 666, 2989, 3458, 1201, 2796, 548, 2975, 839, 3121, 1850, 4001, 2208, 1631, 790, 2558, 2972, 1148, 3213, 1849, 3624, 971, 2102, 108, 772, 3101, 2589, 3777, 1042, 656, 3907, 2097, 1615, 2540, 805, 1935, 1231, 3494, 2451, 268, 2995, 750, 2682, 2020, + 3024, 1392, 2124, 3279, 106, 2217, 1387, 822, 3214, 3825, 2160, 1000, 2395, 3691, 228, 4038, 1872, 3413, 1608, 2225, 3536, 303, 1653, 886, 2541, 224, 4037, 2252, 1428, 172, 3504, 958, 2848, 113, 3628, 1834, 3979, 19, 2317, 779, 2797, 518, 3174, 3549, 1482, 2266, 444, 2014, 3555, 2439, 1213, 3113, 535, 1135, 3204, 3858, 2309, 931, 623, 2009, 3359, 1566, 140, 3550, + 1808, 3872, 2488, 1152, 3764, 2892, 3960, 2412, 353, 1223, 1825, 3444, 3116, 1717, 1082, 2313, 1280, 2661, 82, 3852, 1389, 3200, 2330, 3812, 2038, 3581, 1728, 1039, 3339, 2427, 586, 2580, 1238, 3328, 2280, 1047, 595, 2662, 1363, 3338, 1620, 3934, 2497, 1881, 1054, 3954, 3215, 864, 2887, 1801, 320, 3519, 2378, 3704, 1753, 424, 2958, 1660, 4005, 2601, 1116, 3912, 2381, 573, + 2740, 200, 828, 1667, 432, 1931, 1035, 1616, 3598, 2640, 728, 264, 1437, 557, 3501, 2966, 372, 3734, 974, 1978, 758, 2719, 1145, 452, 1433, 725, 2681, 408, 3843, 1918, 1547, 3906, 1996, 503, 1456, 3019, 3493, 1700, 3742, 355, 2134, 176, 1311, 615, 2867, 315, 1680, 1314, 8, 3297, 1494, 783, 1950, 83, 2656, 1382, 3561, 138, 2834, 1404, 330, 1904, 3156, 1027, + 1357, 3381, 3041, 3666, 2729, 734, 3415, 177, 3051, 2021, 4079, 2823, 3775, 2186, 2616, 869, 1668, 3148, 2367, 3315, 393, 4075, 1870, 2920, 3343, 2362, 3188, 1303, 2782, 825, 3171, 259, 2905, 3717, 2538, 184, 2074, 838, 2860, 2407, 1024, 3496, 3008, 3706, 1985, 2349, 3623, 2582, 4058, 2184, 2694, 3873, 2964, 990, 3346, 690, 2033, 1066, 2201, 3490, 2971, 718, 3700, 2188, + 4061, 391, 1989, 2325, 1430, 3150, 2125, 2526, 592, 1403, 976, 2351, 1165, 1851, 114, 3921, 2063, 613, 1358, 2785, 1623, 2254, 25, 3542, 1045, 246, 1852, 3554, 87, 2243, 3615, 1169, 727, 1705, 968, 3957, 3185, 1251, 500, 4063, 1751, 2622, 842, 1519, 90, 3393, 819, 490, 1874, 999, 571, 1275, 2271, 1586, 4040, 2448, 3126, 3731, 436, 885, 1708, 2421, 24, 1599, + 889, 2563, 1199, 645, 70, 4013, 1237, 3723, 1694, 3499, 3, 3266, 484, 2997, 3390, 1233, 2842, 3687, 152, 3480, 1084, 3698, 881, 2490, 1542, 3992, 2209, 692, 1690, 3022, 1470, 2625, 2114, 3512, 2359, 381, 2684, 1897, 3368, 1395, 3080, 289, 2065, 3981, 2758, 1141, 3097, 1472, 2870, 3352, 3707, 225, 3159, 505, 1895, 214, 1222, 1774, 2686, 3978, 3275, 1196, 3518, 2825, + 3270, 1720, 3796, 3466, 2650, 1841, 298, 899, 2862, 2091, 2671, 1744, 3735, 801, 1560, 349, 2262, 903, 1833, 2524, 512, 3117, 1793, 2827, 476, 3038, 1216, 2550, 3826, 980, 431, 4048, 35, 2992, 1265, 1595, 765, 3675, 76, 2247, 696, 3456, 1254, 2452, 664, 1757, 2133, 3750, 145, 2332, 1554, 1981, 3580, 2712, 868, 3640, 2919, 638, 2275, 1427, 309, 2595, 2006, 492, + 2226, 178, 2911, 836, 1528, 3028, 2240, 3327, 404, 3970, 707, 1294, 2464, 2131, 4032, 2600, 3319, 1406, 2913, 3974, 2156, 1425, 221, 3877, 2017, 811, 3662, 272, 3287, 1988, 2408, 3357, 1746, 598, 3239, 3823, 2182, 2934, 1078, 2604, 3840, 1697, 2906, 413, 3210, 3880, 331, 2644, 1260, 848, 3042, 2535, 1077, 1438, 3261, 2365, 1561, 3799, 85, 3082, 1876, 674, 3932, 1101, + 3644, 1344, 1943, 2401, 390, 3835, 1048, 2572, 1541, 1133, 3075, 3584, 308, 2889, 1065, 1869, 601, 3783, 282, 1181, 736, 3312, 2368, 1126, 3383, 1675, 2734, 1426, 628, 2873, 1317, 843, 2717, 2048, 1004, 2536, 333, 1782, 3295, 1517, 219, 2153, 815, 3502, 1579, 2268, 987, 3409, 1780, 4018, 354, 665, 3914, 47, 1956, 456, 1006, 2010, 3406, 1130, 3621, 2894, 1549, 3092, + 2485, 640, 3993, 3179, 1270, 3436, 585, 1925, 3757, 2304, 136, 1976, 1486, 646, 3520, 50, 3155, 1637, 2435, 3522, 1937, 2756, 3748, 661, 2224, 58, 3230, 2357, 1830, 3892, 170, 3607, 1447, 3949, 190, 3392, 1336, 584, 4010, 918, 3016, 3670, 1155, 2406, 52, 1304, 3009, 607, 2085, 2699, 3205, 1848, 2291, 3402, 2764, 3865, 3048, 2508, 735, 2710, 443, 2341, 897, 263, + 1785, 2769, 983, 56, 2197, 1685, 2703, 202, 2944, 810, 3377, 2626, 3787, 3047, 2055, 1236, 2752, 2122, 945, 3093, 96, 1624, 439, 3014, 1388, 4015, 977, 448, 3506, 1098, 2242, 3026, 506, 2361, 2952, 1862, 3619, 2790, 1992, 2483, 525, 1868, 2652, 4093, 1998, 3595, 2478, 3816, 122, 1412, 929, 3716, 1166, 1648, 813, 1300, 199, 1489, 3998, 1771, 1310, 3808, 2052, 3423, + 434, 3712, 1625, 3558, 2955, 853, 4019, 1348, 3511, 1732, 1246, 487, 934, 1672, 2510, 3965, 788, 3711, 396, 1369, 4090, 1055, 2603, 1879, 3528, 2518, 2067, 3005, 1516, 2588, 751, 1740, 3418, 1131, 1576, 686, 2296, 1118, 18, 3263, 1365, 3401, 294, 737, 3177, 410, 867, 1633, 2963, 3579, 2375, 252, 2881, 479, 2471, 3576, 2180, 3306, 332, 2255, 3035, 41, 2648, 1396, + 2929, 2230, 1219, 2512, 446, 2008, 3189, 2388, 626, 2164, 2831, 4047, 2376, 174, 3272, 368, 1469, 3226, 2578, 1991, 2874, 2263, 3681, 876, 188, 1239, 683, 3776, 226, 3183, 4083, 2148, 63, 2649, 3859, 299, 3086, 3933, 1585, 2185, 3767, 988, 1707, 2908, 1407, 1844, 2771, 2245, 1161, 560, 1755, 3376, 2051, 4064, 3135, 1832, 652, 2853, 1051, 3649, 760, 3290, 1105, 3945, + 872, 154, 3207, 713, 3780, 1453, 281, 1087, 3695, 30, 3299, 1919, 1400, 3551, 1119, 1890, 2314, 618, 1703, 3428, 724, 295, 3146, 1557, 3341, 2896, 1683, 2723, 1974, 1017, 541, 1380, 3720, 804, 3280, 2082, 997, 2567, 777, 2961, 213, 2707, 2328, 3632, 1025, 3891, 3304, 255, 4003, 3108, 2587, 1323, 743, 1479, 105, 1013, 3901, 1618, 2044, 2627, 1465, 1846, 576, 1994, + 2560, 3521, 1742, 2118, 2800, 3404, 1783, 2609, 2968, 1582, 1022, 412, 2713, 687, 2976, 3857, 2761, 3620, 62, 1108, 3844, 1340, 2100, 540, 2345, 3925, 405, 3457, 1319, 2468, 3362, 2815, 1867, 2372, 1281, 1714, 3690, 482, 3498, 1842, 1285, 3994, 558, 2039, 81, 2499, 678, 1481, 1923, 964, 12, 3824, 2980, 2205, 2762, 3432, 2398, 181, 3247, 462, 4094, 2350, 3589, 3089, + 1555, 1094, 4041, 247, 1267, 908, 3959, 2041, 732, 3860, 2343, 3132, 3769, 2144, 1621, 237, 912, 1329, 3025, 2146, 2642, 1775, 3721, 2746, 1121, 1953, 902, 2285, 130, 3671, 1659, 278, 3153, 522, 2721, 123, 2996, 1466, 2380, 377, 3231, 873, 1510, 3476, 3123, 1250, 2147, 3650, 2839, 3451, 2323, 1122, 3545, 379, 1765, 1218, 603, 3768, 1360, 938, 2885, 133, 1245, 363, + 2364, 554, 2743, 3344, 2474, 530, 3112, 169, 1297, 3430, 536, 1741, 98, 1043, 2574, 3253, 2246, 1854, 4022, 510, 3283, 204, 858, 3398, 36, 3118, 1478, 3794, 2986, 706, 2176, 922, 3559, 1097, 3976, 3322, 2149, 1160, 2810, 3883, 2007, 2513, 2953, 328, 1721, 3793, 422, 2566, 807, 329, 1638, 1967, 648, 2520, 3727, 3109, 2116, 2927, 2491, 1939, 3365, 1709, 2728, 3815, + 2037, 3120, 831, 1405, 1896, 3592, 1622, 2369, 2864, 2151, 1107, 2542, 3532, 1410, 3917, 427, 3568, 709, 2509, 1503, 1037, 2973, 2436, 1604, 4035, 2594, 563, 1819, 2659, 1234, 4004, 2565, 1511, 2273, 1823, 336, 882, 3772, 575, 1628, 171, 3570, 1120, 2260, 2716, 935, 3064, 1806, 1342, 3144, 3900, 2744, 3296, 985, 1546, 238, 896, 1663, 305, 3660, 695, 2213, 960, 3407, + 144, 1795, 3894, 2267, 51, 2708, 1023, 3818, 366, 1821, 4087, 2985, 755, 2057, 2912, 949, 1583, 2774, 231, 3447, 2258, 3866, 1982, 672, 1225, 2077, 3320, 1062, 370, 3241, 1968, 7, 3068, 681, 3631, 2573, 1567, 3175, 2321, 1067, 3070, 722, 1856, 3744, 642, 1471, 4084, 131, 3514, 2443, 531, 1227, 155, 2265, 4024, 2658, 3326, 3910, 1168, 3078, 1530, 3956, 489, 1424, + 3647, 1203, 420, 2924, 3755, 719, 3248, 1376, 3067, 890, 196, 1559, 3269, 270, 2432, 1885, 3212, 1164, 3778, 1752, 579, 1338, 344, 3585, 3017, 288, 3658, 2371, 3882, 1691, 611, 2789, 3809, 1339, 389, 2950, 2015, 59, 3548, 2751, 2158, 4011, 1352, 29, 3388, 2370, 2812, 1946, 954, 2110, 1558, 2947, 3573, 1909, 1326, 679, 1853, 2312, 551, 2702, 33, 2414, 3209, 2824, + 2547, 2143, 3379, 966, 1492, 1979, 2479, 463, 2194, 3657, 2738, 2318, 1261, 3713, 604, 4002, 11, 2192, 2967, 919, 2607, 3369, 2837, 1676, 2539, 984, 1568, 93, 2901, 1318, 3538, 1041, 2216, 1756, 3454, 1030, 4050, 1402, 798, 1723, 311, 3277, 2546, 2886, 2043, 461, 1206, 3677, 361, 3260, 3988, 809, 2605, 470, 3007, 3517, 102, 3221, 1398, 2062, 3611, 1134, 1928, 865, + 4060, 621, 1710, 2606, 3510, 317, 4017, 1682, 3329, 1159, 1940, 654, 3461, 1789, 1015, 2691, 1455, 3599, 374, 1947, 4069, 71, 2126, 763, 3961, 2278, 3161, 1997, 824, 2623, 2080, 244, 3257, 780, 2732, 2308, 545, 3351, 2476, 3806, 1204, 588, 1591, 963, 3610, 1699, 754, 3049, 2651, 1106, 65, 2221, 1644, 3821, 1100, 2463, 1614, 3801, 965, 2965, 715, 3394, 1593, 212, +}; + +#endif /* BLUE_NOISE_64X64_H */ diff --git a/gfx/cairo/libpixman/src/dither/make-blue-noise.c b/gfx/cairo/libpixman/src/dither/make-blue-noise.c new file mode 100644 index 0000000000..f9974b4d44 --- /dev/null +++ b/gfx/cairo/libpixman/src/dither/make-blue-noise.c @@ -0,0 +1,679 @@ +/* Blue noise generation using the void-and-cluster method as described in + * + * The void-and-cluster method for dither array generation + * Ulichney, Robert A (1993) + * + * http://cv.ulichney.com/papers/1993-void-cluster.pdf + * + * Note that running with openmp (-DUSE_OPENMP) will trigger additional + * randomness due to computing reductions in parallel, and is not recommended + * unless generating very large dither arrays. + */ + +#include +#include +#include +#include +#include + +/* Booleans and utility functions */ + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +typedef int bool_t; + +int +imin (int x, int y) +{ + return x < y ? x : y; +} + +/* Memory allocation */ +void * +malloc_abc (unsigned int a, unsigned int b, unsigned int c) +{ + if (a >= INT32_MAX / b) + return NULL; + else if (a * b >= INT32_MAX / c) + return NULL; + else + return malloc (a * b * c); +} + +/* Random number generation */ +typedef uint32_t xorwow_state_t[5]; + +uint32_t +xorwow_next (xorwow_state_t *state) +{ + uint32_t s = (*state)[0], + t = (*state)[3]; + (*state)[3] = (*state)[2]; + (*state)[2] = (*state)[1]; + (*state)[1] = s; + + t ^= t >> 2; + t ^= t << 1; + t ^= s ^ (s << 4); + + (*state)[0] = t; + (*state)[4] += 362437; + + return t + (*state)[4]; +} + +float +xorwow_float (xorwow_state_t *s) +{ + return (xorwow_next (s) >> 9) / (float)((1 << 23) - 1); +} + +/* Floating point matrices + * + * Used to cache the cluster sizes. + */ +typedef struct matrix_t { + int width; + int height; + float *buffer; +} matrix_t; + +bool_t +matrix_init (matrix_t *matrix, int width, int height) +{ + float *buffer; + + if (!matrix) + return FALSE; + + buffer = malloc_abc (width, height, sizeof (float)); + + if (!buffer) + return FALSE; + + matrix->buffer = buffer; + matrix->width = width; + matrix->height = height; + + return TRUE; +} + +bool_t +matrix_copy (matrix_t *dst, matrix_t const *src) +{ + float *srcbuf = src->buffer, + *srcend = src->buffer + src->width * src->height, + *dstbuf = dst->buffer; + + if (dst->width != src->width || dst->height != src->height) + return FALSE; + + while (srcbuf < srcend) + *dstbuf++ = *srcbuf++; + + return TRUE; +} + +float * +matrix_get (matrix_t *matrix, int x, int y) +{ + return &matrix->buffer[y * matrix->width + x]; +} + +void +matrix_destroy (matrix_t *matrix) +{ + free (matrix->buffer); +} + +/* Binary patterns */ +typedef struct pattern_t { + int width; + int height; + bool_t *buffer; +} pattern_t; + +bool_t +pattern_init (pattern_t *pattern, int width, int height) +{ + bool_t *buffer; + + if (!pattern) + return FALSE; + + buffer = malloc_abc (width, height, sizeof (bool_t)); + + if (!buffer) + return FALSE; + + pattern->buffer = buffer; + pattern->width = width; + pattern->height = height; + + return TRUE; +} + +bool_t +pattern_copy (pattern_t *dst, pattern_t const *src) +{ + bool_t *srcbuf = src->buffer, + *srcend = src->buffer + src->width * src->height, + *dstbuf = dst->buffer; + + if (dst->width != src->width || dst->height != src->height) + return FALSE; + + while (srcbuf < srcend) + *dstbuf++ = *srcbuf++; + + return TRUE; +} + +bool_t * +pattern_get (pattern_t *pattern, int x, int y) +{ + return &pattern->buffer[y * pattern->width + x]; +} + +void +pattern_fill_white_noise (pattern_t *pattern, float fraction, + xorwow_state_t *s) +{ + bool_t *buffer = pattern->buffer; + bool_t *end = buffer + (pattern->width * pattern->height); + + while (buffer < end) + *buffer++ = xorwow_float (s) < fraction; +} + +void +pattern_destroy (pattern_t *pattern) +{ + free (pattern->buffer); +} + +/* Dither arrays */ +typedef struct array_t { + int width; + int height; + uint32_t *buffer; +} array_t; + +bool_t +array_init (array_t *array, int width, int height) +{ + uint32_t *buffer; + + if (!array) + return FALSE; + + buffer = malloc_abc (width, height, sizeof (uint32_t)); + + if (!buffer) + return FALSE; + + array->buffer = buffer; + array->width = width; + array->height = height; + + return TRUE; +} + +uint32_t * +array_get (array_t *array, int x, int y) +{ + return &array->buffer[y * array->width + x]; +} + +bool_t +array_save_ppm (array_t *array, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + + int i = 0; + int bpp = 2; + uint8_t buffer[1024]; + + if (!f) + return FALSE; + + if (array->width * array->height - 1 < 256) + bpp = 1; + + fprintf(f, "P5 %d %d %d\n", array->width, array->height, + array->width * array->height - 1); + while (i < array->width * array->height) + { + int j = 0; + for (; j < 1024 / bpp && j < array->width * array->height; ++j) + { + uint32_t v = array->buffer[i + j]; + if (bpp == 2) + { + buffer[2 * j] = v & 0xff; + buffer[2 * j + 1] = (v & 0xff00) >> 8; + } else { + buffer[j] = v; + } + } + + fwrite((void *)buffer, bpp, j, f); + i += j; + } + + if (fclose(f) != 0) + return FALSE; + + return TRUE; +} + +bool_t +array_save (array_t *array, const char *filename) +{ + int x, y; + FILE *f = fopen(filename, "wb"); + + if (!f) + return FALSE; + + fprintf (f, +"/* WARNING: This file is generated by make-blue-noise.c\n" +" * Please edit that file instead of this one.\n" +" */\n" +"\n" +"#ifndef BLUE_NOISE_%dX%d_H\n" +"#define BLUE_NOISE_%dX%d_H\n" +"\n" +"#include \n" +"\n", array->width, array->height, array->width, array->height); + + fprintf (f, "static const uint16_t dither_blue_noise_%dx%d[%d] = {\n", + array->width, array->height, array->width * array->height); + + for (y = 0; y < array->height; ++y) + { + fprintf (f, " "); + for (x = 0; x < array->width; ++x) + { + if (x != 0) + fprintf (f, ", "); + + fprintf (f, "%d", *array_get (array, x, y)); + } + + fprintf (f, ",\n"); + } + fprintf (f, "};\n"); + + fprintf (f, "\n#endif /* BLUE_NOISE_%dX%d_H */\n", + array->width, array->height); + + if (fclose(f) != 0) + return FALSE; + + return TRUE; +} + +void +array_destroy (array_t *array) +{ + free (array->buffer); +} + +/* Dither array generation */ +bool_t +compute_cluster_sizes (pattern_t *pattern, matrix_t *matrix) +{ + int width = pattern->width, + height = pattern->height; + + if (matrix->width != width || matrix->height != height) + return FALSE; + + int px, py, qx, qy, dx, dy; + float tsqsi = 2.f * 1.5f * 1.5f; + +#ifdef USE_OPENMP +#pragma omp parallel for default (none) \ + private (py, px, qy, qx, dx, dy) \ + shared (height, width, pattern, matrix, tsqsi) +#endif + for (py = 0; py < height; ++py) + { + for (px = 0; px < width; ++px) + { + bool_t pixel = *pattern_get (pattern, px, py); + float dist = 0.f; + + for (qx = 0; qx < width; ++qx) + { + dx = imin (abs (qx - px), width - abs (qx - px)); + dx = dx * dx; + + for (qy = 0; qy < height; ++qy) + { + dy = imin (abs (qy - py), height - abs (qy - py)); + dy = dy * dy; + + dist += (pixel == *pattern_get (pattern, qx, qy)) + * expf (- (dx + dy) / tsqsi); + } + } + + *matrix_get (matrix, px, py) = dist; + } + } + + return TRUE; +} + +bool_t +swap_pixel (pattern_t *pattern, matrix_t *matrix, int x, int y) +{ + int width = pattern->width, + height = pattern->height; + + bool_t new; + + float f, + dist = 0.f, + tsqsi = 2.f * 1.5f * 1.5f; + + int px, py, dx, dy; + bool_t b; + + new = !*pattern_get (pattern, x, y); + *pattern_get (pattern, x, y) = new; + + if (matrix->width != width || matrix->height != height) + return FALSE; + + +#ifdef USE_OPENMP +#pragma omp parallel for reduction (+:dist) default (none) \ + private (px, py, dx, dy, b, f) \ + shared (x, y, width, height, pattern, matrix, new, tsqsi) +#endif + for (py = 0; py < height; ++py) + { + dy = imin (abs (py - y), height - abs (py - y)); + dy = dy * dy; + + for (px = 0; px < width; ++px) + { + dx = imin (abs (px - x), width - abs (px - x)); + dx = dx * dx; + + b = (*pattern_get (pattern, px, py) == new); + f = expf (- (dx + dy) / tsqsi); + *matrix_get (matrix, px, py) += (2 * b - 1) * f; + + dist += b * f; + } + } + + *matrix_get (matrix, x, y) = dist; + return TRUE; +} + +void +largest_cluster (pattern_t *pattern, matrix_t *matrix, + bool_t pixel, int *xmax, int *ymax) +{ + int width = pattern->width, + height = pattern->height; + + int x, y; + + float vmax = -INFINITY; + +#ifdef USE_OPENMP +#pragma omp parallel default (none) \ + private (x, y) \ + shared (height, width, pattern, matrix, pixel, xmax, ymax, vmax) +#endif + { + int xbest = -1, + ybest = -1; + +#ifdef USE_OPENMP + float vbest = -INFINITY; + +#pragma omp for reduction (max: vmax) collapse (2) +#endif + for (y = 0; y < height; ++y) + { + for (x = 0; x < width; ++x) + { + if (*pattern_get (pattern, x, y) != pixel) + continue; + + if (*matrix_get (matrix, x, y) > vmax) + { + vmax = *matrix_get (matrix, x, y); +#ifdef USE_OPENMP + vbest = vmax; +#endif + xbest = x; + ybest = y; + } + } + } + +#ifdef USE_OPENMP +#pragma omp barrier +#pragma omp critical + { + if (vmax == vbest) + { + *xmax = xbest; + *ymax = ybest; + } + } +#else + *xmax = xbest; + *ymax = ybest; +#endif + } + + assert (vmax > -INFINITY); +} + +void +generate_initial_binary_pattern (pattern_t *pattern, matrix_t *matrix) +{ + int xcluster = 0, + ycluster = 0, + xvoid = 0, + yvoid = 0; + + for (;;) + { + largest_cluster (pattern, matrix, TRUE, &xcluster, &ycluster); + assert (*pattern_get (pattern, xcluster, ycluster) == TRUE); + swap_pixel (pattern, matrix, xcluster, ycluster); + + largest_cluster (pattern, matrix, FALSE, &xvoid, &yvoid); + assert (*pattern_get (pattern, xvoid, yvoid) == FALSE); + swap_pixel (pattern, matrix, xvoid, yvoid); + + if (xcluster == xvoid && ycluster == yvoid) + return; + } +} + +bool_t +generate_dither_array (array_t *array, + pattern_t const *prototype, matrix_t const *matrix, + pattern_t *temp_pattern, matrix_t *temp_matrix) +{ + int width = prototype->width, + height = prototype->height; + + int x, y, rank; + + int initial_rank = 0; + + if (array->width != width || array->height != height) + return FALSE; + + // Make copies of the prototype and associated sizes matrix since we will + // trash them + if (!pattern_copy (temp_pattern, prototype)) + return FALSE; + + if (!matrix_copy (temp_matrix, matrix)) + return FALSE; + + // Compute initial rank + for (y = 0; y < height; ++y) + { + for (x = 0; x < width; ++x) + { + if (*pattern_get (temp_pattern, x, y)) + initial_rank += 1; + + *array_get (array, x, y) = 0; + } + } + + // Phase 1 + for (rank = initial_rank; rank > 0; --rank) + { + largest_cluster (temp_pattern, temp_matrix, TRUE, &x, &y); + swap_pixel (temp_pattern, temp_matrix, x, y); + *array_get (array, x, y) = rank - 1; + } + + // Make copies again for phases 2 & 3 + if (!pattern_copy (temp_pattern, prototype)) + return FALSE; + + if (!matrix_copy (temp_matrix, matrix)) + return FALSE; + + // Phase 2 & 3 + for (rank = initial_rank; rank < width * height; ++rank) + { + largest_cluster (temp_pattern, temp_matrix, FALSE, &x, &y); + swap_pixel (temp_pattern, temp_matrix, x, y); + *array_get (array, x, y) = rank; + } + + return TRUE; +} + +bool_t +generate (int size, xorwow_state_t *s, + char const *c_filename, char const *ppm_filename) +{ + bool_t ok = TRUE; + + pattern_t prototype, temp_pattern; + array_t array; + matrix_t matrix, temp_matrix; + + printf ("Generating %dx%d blue noise...\n", size, size); + + if (!pattern_init (&prototype, size, size)) + return FALSE; + + if (!pattern_init (&temp_pattern, size, size)) + { + pattern_destroy (&prototype); + return FALSE; + } + + if (!matrix_init (&matrix, size, size)) + { + pattern_destroy (&temp_pattern); + pattern_destroy (&prototype); + return FALSE; + } + + if (!matrix_init (&temp_matrix, size, size)) + { + matrix_destroy (&matrix); + pattern_destroy (&temp_pattern); + pattern_destroy (&prototype); + return FALSE; + } + + if (!array_init (&array, size, size)) + { + matrix_destroy (&temp_matrix); + matrix_destroy (&matrix); + pattern_destroy (&temp_pattern); + pattern_destroy (&prototype); + return FALSE; + } + + printf("Filling initial binary pattern with white noise...\n"); + pattern_fill_white_noise (&prototype, .1, s); + + printf("Initializing cluster sizes...\n"); + if (!compute_cluster_sizes (&prototype, &matrix)) + { + fprintf (stderr, "Error while computing cluster sizes\n"); + ok = FALSE; + goto out; + } + + printf("Generating initial binary pattern...\n"); + generate_initial_binary_pattern (&prototype, &matrix); + + printf("Generating dither array...\n"); + if (!generate_dither_array (&array, &prototype, &matrix, + &temp_pattern, &temp_matrix)) + { + fprintf (stderr, "Error while generating dither array\n"); + ok = FALSE; + goto out; + } + + printf("Saving dither array...\n"); + if (!array_save (&array, c_filename)) + { + fprintf (stderr, "Error saving dither array\n"); + ok = FALSE; + goto out; + } + +#if SAVE_PPM + if (!array_save_ppm (&array, ppm_filename)) + { + fprintf (stderr, "Error saving dither array PPM\n"); + ok = FALSE; + goto out; + } +#else + (void)ppm_filename; +#endif + + printf("All done!\n"); + +out: + array_destroy (&array); + matrix_destroy (&temp_matrix); + matrix_destroy (&matrix); + pattern_destroy (&temp_pattern); + pattern_destroy (&prototype); + return ok; +} + +int +main (void) +{ + xorwow_state_t s = {1185956906, 12385940, 983948, 349208051, 901842}; + + if (!generate (64, &s, "blue-noise-64x64.h", "blue-noise-64x64.ppm")) + return -1; + + return 0; +} diff --git a/gfx/cairo/libpixman/src/loongson-mmintrin.h b/gfx/cairo/libpixman/src/loongson-mmintrin.h new file mode 100644 index 0000000000..0e79e86484 --- /dev/null +++ b/gfx/cairo/libpixman/src/loongson-mmintrin.h @@ -0,0 +1,412 @@ +/* The gcc-provided loongson intrinsic functions are way too fucking broken + * to be of any use, otherwise I'd use them. + * + * - The hardware instructions are very similar to MMX or iwMMXt. Certainly + * close enough that they could have implemented the _mm_*-style intrinsic + * interface and had a ton of optimized code available to them. Instead they + * implemented something much, much worse. + * + * - pshuf takes a dead first argument, causing extra instructions to be + * generated. + * + * - There are no 64-bit shift or logical intrinsics, which means you have + * to implement them with inline assembly, but this is a nightmare because + * gcc doesn't understand that the integer vector datatypes are actually in + * floating-point registers, so you end up with braindead code like + * + * punpcklwd $f9,$f9,$f5 + * dmtc1 v0,$f8 + * punpcklwd $f19,$f19,$f5 + * dmfc1 t9,$f9 + * dmtc1 v0,$f9 + * dmtc1 t9,$f20 + * dmfc1 s0,$f19 + * punpcklbh $f20,$f20,$f2 + * + * where crap just gets copied back and forth between integer and floating- + * point registers ad nauseum. + * + * Instead of trying to workaround the problems from these crap intrinsics, I + * just implement the _mm_* intrinsics needed for pixman-mmx.c using inline + * assembly. + */ + +#include + +/* vectors are stored in 64-bit floating-point registers */ +typedef double __m64; +/* having a 32-bit datatype allows us to use 32-bit loads in places like load8888 */ +typedef float __m32; + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_setzero_si64 (void) +{ + return 0.0; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_add_pi16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("paddh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_add_pi32 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("paddw %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_adds_pu16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("paddush %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_adds_pu8 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("paddusb %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_and_si64 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("and %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_cmpeq_pi32 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("pcmpeqw %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_empty (void) +{ + +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_madd_pi16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("pmaddhw %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_mulhi_pu16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("pmulhuh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_mullo_pi16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("pmullh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_or_si64 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("or %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_packs_pu16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("packushb %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_packs_pi32 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("packsswh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +#define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \ + (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | (fp0)) +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_set_pi16 (uint16_t __w3, uint16_t __w2, uint16_t __w1, uint16_t __w0) +{ + if (__builtin_constant_p (__w3) && + __builtin_constant_p (__w2) && + __builtin_constant_p (__w1) && + __builtin_constant_p (__w0)) + { + uint64_t val = ((uint64_t)__w3 << 48) + | ((uint64_t)__w2 << 32) + | ((uint64_t)__w1 << 16) + | ((uint64_t)__w0 << 0); + return *(__m64 *)&val; + } + else if (__w3 == __w2 && __w2 == __w1 && __w1 == __w0) + { + /* TODO: handle other cases */ + uint64_t val = __w3; + uint64_t imm = _MM_SHUFFLE (0, 0, 0, 0); + __m64 ret; + asm("pshufh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (*(__m64 *)&val), "f" (*(__m64 *)&imm) + ); + return ret; + } else { + uint64_t val = ((uint64_t)__w3 << 48) + | ((uint64_t)__w2 << 32) + | ((uint64_t)__w1 << 16) + | ((uint64_t)__w0 << 0); + return *(__m64 *)&val; + } +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_set_pi32 (unsigned __i1, unsigned __i0) +{ + if (__builtin_constant_p (__i1) && + __builtin_constant_p (__i0)) + { + uint64_t val = ((uint64_t)__i1 << 32) + | ((uint64_t)__i0 << 0); + return *(__m64 *)&val; + } + else if (__i1 == __i0) + { + uint64_t imm = _MM_SHUFFLE (1, 0, 1, 0); + __m64 ret; + asm("pshufh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (*(__m32 *)&__i1), "f" (*(__m64 *)&imm) + ); + return ret; + } else { + uint64_t val = ((uint64_t)__i1 << 32) + | ((uint64_t)__i0 << 0); + return *(__m64 *)&val; + } +} +#undef _MM_SHUFFLE + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_shuffle_pi16 (__m64 __m, int64_t __n) +{ + __m64 ret; + asm("pshufh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m), "f" (*(__m64 *)&__n) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_slli_pi16 (__m64 __m, int64_t __count) +{ + __m64 ret; + asm("psllh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m), "f" (*(__m64 *)&__count) + ); + return ret; +} +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_slli_si64 (__m64 __m, int64_t __count) +{ + __m64 ret; + asm("dsll %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m), "f" (*(__m64 *)&__count) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_srli_pi16 (__m64 __m, int64_t __count) +{ + __m64 ret; + asm("psrlh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m), "f" (*(__m64 *)&__count) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_srli_pi32 (__m64 __m, int64_t __count) +{ + __m64 ret; + asm("psrlw %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m), "f" (*(__m64 *)&__count) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_srli_si64 (__m64 __m, int64_t __count) +{ + __m64 ret; + asm("dsrl %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m), "f" (*(__m64 *)&__count) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_sub_pi16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("psubh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_unpackhi_pi8 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("punpckhbh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_unpackhi_pi16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("punpckhhw %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_unpacklo_pi8 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("punpcklbh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +/* Since punpcklbh doesn't care about the high 32-bits, we use the __m32 datatype which + * allows load8888 to use 32-bit loads */ +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_unpacklo_pi8_f (__m32 __m1, __m64 __m2) +{ + __m64 ret; + asm("punpcklbh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_unpacklo_pi16 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("punpcklhw %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_xor_si64 (__m64 __m1, __m64 __m2) +{ + __m64 ret; + asm("xor %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +loongson_extract_pi16 (__m64 __m, int64_t __pos) +{ + __m64 ret; + asm("pextrh %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m), "f" (*(__m64 *)&__pos) + ); + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +loongson_insert_pi16 (__m64 __m1, __m64 __m2, int64_t __pos) +{ + __m64 ret; + asm("pinsrh_%3 %0, %1, %2\n\t" + : "=f" (ret) + : "f" (__m1), "f" (__m2), "i" (__pos) + ); + return ret; +} diff --git a/gfx/cairo/libpixman/src/meson.build b/gfx/cairo/libpixman/src/meson.build new file mode 100644 index 0000000000..652cda3418 --- /dev/null +++ b/gfx/cairo/libpixman/src/meson.build @@ -0,0 +1,142 @@ +# Copyright © 2018 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. + +config_h = configure_file( + configuration : config, + output : 'config.h' +) + +version_h = configure_file( + configuration : version_conf, + input : 'pixman-version.h.in', + output : 'pixman-version.h', + install_dir : join_paths(get_option('prefix'), get_option('includedir'), 'pixman-1') +) + +libpixman_extra_cargs = [] +if cc.has_function_attribute('dllexport') + libpixman_extra_cargs = ['-DPIXMAN_API=__declspec(dllexport)'] +endif + +pixman_simd_libs = [] +simds = [ + # the mmx library can be compiled with mmx on x86/x86_64, iwmmxt on + # some arm cores, or loongson mmi on loongson mips systems. The + # libraries will all have the same name, "pixman-mmx", but there is + # no chance of more than one version being built in the same build + # because no system could have mmx, iwmmxt, and mmi, and it + # simplifies the build logic to give them the same name. + ['mmx', have_mmx, mmx_flags, []], + ['mmx', have_loongson_mmi, loongson_mmi_flags, []], + ['mmx', have_iwmmxt, iwmmxt_flags, []], + + ['sse2', have_sse2, sse2_flags, []], + ['ssse3', have_ssse3, ssse3_flags, []], + ['vmx', have_vmx, vmx_flags, []], + ['arm-simd', have_armv6_simd, [], + ['pixman-arm-simd-asm.S', 'pixman-arm-simd-asm-scaled.S']], + ['arm-neon', have_neon, [], + ['pixman-arm-neon-asm.S', 'pixman-arm-neon-asm-bilinear.S']], + ['arm-neon', have_a64neon, [], + ['pixman-arma64-neon-asm.S', 'pixman-arma64-neon-asm-bilinear.S']], + ['mips-dspr2', have_mips_dspr2, mips_dspr2_flags, + ['pixman-mips-dspr2-asm.S', 'pixman-mips-memcpy-asm.S']], +] + +foreach simd : simds + if simd[1] + name = 'pixman-' + simd[0] + pixman_simd_libs += static_library( + name, + [name + '.c', config_h, version_h, simd[3]], + c_args : simd[2] + ) + endif +endforeach + +pixman_files = files( + 'pixman.c', + 'pixman-access.c', + 'pixman-access-accessors.c', + 'pixman-bits-image.c', + 'pixman-combine32.c', + 'pixman-combine-float.c', + 'pixman-conical-gradient.c', + 'pixman-filter.c', + 'pixman-x86.c', + 'pixman-mips.c', + 'pixman-arm.c', + 'pixman-ppc.c', + 'pixman-edge.c', + 'pixman-edge-accessors.c', + 'pixman-fast-path.c', + 'pixman-glyph.c', + 'pixman-general.c', + 'pixman-gradient-walker.c', + 'pixman-image.c', + 'pixman-implementation.c', + 'pixman-linear-gradient.c', + 'pixman-matrix.c', + 'pixman-noop.c', + 'pixman-radial-gradient.c', + 'pixman-region16.c', + 'pixman-region32.c', + 'pixman-solid-fill.c', + 'pixman-timer.c', + 'pixman-trap.c', + 'pixman-utils.c', +) + +# Android cpu-features +cpu_features_path = get_option('cpu-features-path') +cpu_features_sources = [] +cpu_features_inc = [] +if cpu_features_path != '' + message('Using cpu-features.[ch] from ' + cpu_features_path) + cpu_features_sources = files( + cpu_features_path / 'cpu-features.h', + cpu_features_path / 'cpu-features.c', + ) + cpu_features_inc = include_directories(cpu_features_path) +endif + +libpixman = library( + 'pixman-1', + [pixman_files, config_h, version_h, cpu_features_sources], + link_with: pixman_simd_libs, + c_args : libpixman_extra_cargs, + dependencies : [dep_m, dep_threads], + include_directories : cpu_features_inc, + version : meson.project_version(), + install : true, +) + +inc_pixman = include_directories('.') + +idep_pixman = declare_dependency( + link_with: libpixman, + include_directories : inc_pixman, +) + +if meson.version().version_compare('>= 0.54.0') + meson.override_dependency('pixman-1', idep_pixman) +endif + +install_headers('pixman.h', subdir : 'pixman-1') diff --git a/gfx/cairo/libpixman/src/moz.build b/gfx/cairo/libpixman/src/moz.build new file mode 100644 index 0000000000..6d5cee9c8d --- /dev/null +++ b/gfx/cairo/libpixman/src/moz.build @@ -0,0 +1,132 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS += [ + 'pixman-version.h', + 'pixman.h', +] + +SOURCES += [ + 'pixman-access-accessors.c', + 'pixman-access.c', + 'pixman-arm.c', + 'pixman-bits-image.c', + 'pixman-combine-float.c', + 'pixman-combine32.c', + 'pixman-conical-gradient.c', + 'pixman-edge-accessors.c', + 'pixman-edge.c', + 'pixman-fast-path.c', + 'pixman-filter.c', + 'pixman-general.c', + 'pixman-glyph.c', + 'pixman-gradient-walker.c', + 'pixman-image.c', + 'pixman-implementation.c', + 'pixman-linear-gradient.c', + 'pixman-matrix.c', + 'pixman-mips.c', + 'pixman-noop.c', + 'pixman-ppc.c', + 'pixman-radial-gradient.c', + 'pixman-region16.c', + 'pixman-region32.c', + 'pixman-solid-fill.c', + 'pixman-trap.c', + 'pixman-utils.c', + 'pixman-x86.c', + 'pixman.c', +] + +# We allow warnings for third-party code that can be updated from upstream. +AllowCompilerWarnings() + +FINAL_LIBRARY = 'gkmedias' +LOCAL_INCLUDES += [ + '../../cairo/src', +] + +if CONFIG['MOZ_USE_PTHREADS']: + DEFINES['HAVE_PTHREADS'] = True + +DEFINES['PACKAGE'] = 'mozpixman' + +if CONFIG['INTEL_ARCHITECTURE']: + DEFINES['USE_X86_MMX'] = True + DEFINES['USE_SSE2'] = True + DEFINES['USE_SSSE3'] = True + SOURCES += [ + 'pixman-mmx.c', + 'pixman-sse2.c', + 'pixman-ssse3.c', + ] + SOURCES['pixman-mmx.c'].flags += CONFIG['MMX_FLAGS'] + SOURCES['pixman-sse2.c'].flags += CONFIG['SSE_FLAGS'] + CONFIG['SSE2_FLAGS'] + SOURCES['pixman-ssse3.c'].flags += CONFIG['SSSE3_FLAGS'] +# AArch64 NEON optimizations don't build on Windows and Mac out of the box. +elif CONFIG['TARGET_CPU'] == 'aarch64' and CONFIG['OS_TARGET'] in ('Android', 'Linux', 'Darwin'): + DEFINES['USE_ARM_A64_NEON'] = True + if CONFIG['OS_TARGET'] == 'Darwin': + DEFINES['ASM_LEADING_UNDERSCORE'] = True + SOURCES += [ + 'pixman-arm-neon.c', + 'pixman-arma64-neon-asm-bilinear.S', + 'pixman-arma64-neon-asm.S', + ] + SOURCES['pixman-arm-neon.c'].flags += ['-march=armv8-a'] +elif CONFIG['TARGET_CPU'] == 'arm': + if CONFIG['HAVE_ARM_NEON']: + DEFINES['USE_ARM_NEON'] = True + SOURCES += [ + 'pixman-arm-neon-asm-bilinear.S', + 'pixman-arm-neon-asm.S', + 'pixman-arm-neon.c', + ] + SOURCES['pixman-arm-neon.c'].flags += CONFIG['NEON_FLAGS'] + if CONFIG['HAVE_ARM_SIMD']: + DEFINES['USE_ARM_SIMD'] = True + SOURCES += [ + 'pixman-arm-simd-asm-scaled.S', + 'pixman-arm-simd-asm.S', + 'pixman-arm-simd.c', + ] + if CONFIG['OS_TARGET'] == 'Android': + # For cpu-features.h + LOCAL_INCLUDES += [ + '%%%s/sources/android/cpufeatures' % CONFIG['ANDROID_NDK'] + ] + SOURCES += [ + '%%%s/sources/android/cpufeatures/cpu-features.c' % CONFIG['ANDROID_NDK'], + ] +elif CONFIG['TARGET_CPU'] in ('ppc', 'ppc64'): + if CONFIG['CC_TYPE'] in ('clang', 'gcc'): + DEFINES['USE_VMX'] = True + SOURCES += ['pixman-vmx.c'] + SOURCES['pixman-vmx.c'].flags += ['-maltivec'] + +# Suppress warnings in third-party code. +CFLAGS += [ + '-Wno-address', + '-Wno-braced-scalar-init', + '-Wno-missing-field-initializers', + '-Wno-sign-compare', + '-Wno-incompatible-pointer-types', + '-Wno-unused', # too many unused warnings; ignore +] +if CONFIG['CC_TYPE'] in ('clang', 'clang-cl'): + CFLAGS += [ + '-Wno-incompatible-pointer-types', + '-Wno-tautological-compare', + '-Wno-tautological-constant-out-of-range-compare', + ] +if CONFIG['CC_TYPE'] == 'clang-cl': + CFLAGS += [ + '-Wno-unused-variable', + ] + +# See bug 386897. +if CONFIG['OS_TARGET'] == 'Android' and CONFIG['MOZ_OPTIMIZE']: + CFLAGS += ['-O2'] diff --git a/gfx/cairo/libpixman/src/pixman-access-accessors.c b/gfx/cairo/libpixman/src/pixman-access-accessors.c new file mode 100644 index 0000000000..3263582f18 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-access-accessors.c @@ -0,0 +1,3 @@ +#define PIXMAN_FB_ACCESSORS + +#include "pixman-access.c" diff --git a/gfx/cairo/libpixman/src/pixman-access.c b/gfx/cairo/libpixman/src/pixman-access.c new file mode 100644 index 0000000000..7c5ce783f9 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-access.c @@ -0,0 +1,1559 @@ +/* + * + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. + * 2005 Lars Knoll & Zack Rusin, Trolltech + * 2008 Aaron Plattner, NVIDIA Corporation + * + * 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. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "pixman-accessor.h" +#include "pixman-private.h" + +#define CONVERT_RGB24_TO_Y15(s) \ + (((((s) >> 16) & 0xff) * 153 + \ + (((s) >> 8) & 0xff) * 301 + \ + (((s) ) & 0xff) * 58) >> 2) + +#define CONVERT_RGB24_TO_RGB15(s) \ + ((((s) >> 3) & 0x001f) | \ + (((s) >> 6) & 0x03e0) | \ + (((s) >> 9) & 0x7c00)) + +/* Fetch macros */ + +#ifdef WORDS_BIGENDIAN +#define FETCH_1(img,l,o) \ + (((READ ((img), ((uint32_t *)(l)) + ((o) >> 5))) >> (0x1f - ((o) & 0x1f))) & 0x1) +#else +#define FETCH_1(img,l,o) \ + ((((READ ((img), ((uint32_t *)(l)) + ((o) >> 5))) >> ((o) & 0x1f))) & 0x1) +#endif + +#define FETCH_8(img,l,o) (READ (img, (((uint8_t *)(l)) + ((o) >> 3)))) + +#ifdef WORDS_BIGENDIAN +#define FETCH_4(img,l,o) \ + (((4 * (o)) & 4) ? (FETCH_8 (img,l, 4 * (o)) & 0xf) : (FETCH_8 (img,l,(4 * (o))) >> 4)) +#else +#define FETCH_4(img,l,o) \ + (((4 * (o)) & 4) ? (FETCH_8 (img, l, 4 * (o)) >> 4) : (FETCH_8 (img, l, (4 * (o))) & 0xf)) +#endif + +#ifdef WORDS_BIGENDIAN +#define FETCH_24(img,l,o) \ + ((uint32_t)(READ (img, (((uint8_t *)(l)) + ((o) * 3) + 0)) << 16) | \ + (uint32_t)(READ (img, (((uint8_t *)(l)) + ((o) * 3) + 1)) << 8) | \ + (uint32_t)(READ (img, (((uint8_t *)(l)) + ((o) * 3) + 2)) << 0)) +#else +#define FETCH_24(img,l,o) \ + ((uint32_t)(READ (img, (((uint8_t *)(l)) + ((o) * 3) + 0)) << 0) | \ + (uint32_t)(READ (img, (((uint8_t *)(l)) + ((o) * 3) + 1)) << 8) | \ + (uint32_t)(READ (img, (((uint8_t *)(l)) + ((o) * 3) + 2)) << 16)) +#endif + +/* Store macros */ + +#ifdef WORDS_BIGENDIAN +#define STORE_1(img,l,o,v) \ + do \ + { \ + uint32_t *__d = ((uint32_t *)(l)) + ((o) >> 5); \ + uint32_t __m, __v; \ + \ + __m = 1U << (0x1f - ((o) & 0x1f)); \ + __v = (v)? __m : 0; \ + \ + WRITE((img), __d, (READ((img), __d) & ~__m) | __v); \ + } \ + while (0) +#else +#define STORE_1(img,l,o,v) \ + do \ + { \ + uint32_t *__d = ((uint32_t *)(l)) + ((o) >> 5); \ + uint32_t __m, __v; \ + \ + __m = 1U << ((o) & 0x1f); \ + __v = (v)? __m : 0; \ + \ + WRITE((img), __d, (READ((img), __d) & ~__m) | __v); \ + } \ + while (0) +#endif + +#define STORE_8(img,l,o,v) (WRITE (img, (uint8_t *)(l) + ((o) >> 3), (v))) + +#ifdef WORDS_BIGENDIAN +#define STORE_4(img,l,o,v) \ + do \ + { \ + int bo = 4 * (o); \ + int v4 = (v) & 0x0f; \ + \ + STORE_8 (img, l, bo, ( \ + bo & 4 ? \ + (FETCH_8 (img, l, bo) & 0xf0) | (v4) : \ + (FETCH_8 (img, l, bo) & 0x0f) | (v4 << 4))); \ + } while (0) +#else +#define STORE_4(img,l,o,v) \ + do \ + { \ + int bo = 4 * (o); \ + int v4 = (v) & 0x0f; \ + \ + STORE_8 (img, l, bo, ( \ + bo & 4 ? \ + (FETCH_8 (img, l, bo) & 0x0f) | (v4 << 4) : \ + (FETCH_8 (img, l, bo) & 0xf0) | (v4))); \ + } while (0) +#endif + +#ifdef WORDS_BIGENDIAN +#define STORE_24(img,l,o,v) \ + do \ + { \ + uint8_t *__tmp = (l) + 3 * (o); \ + \ + WRITE ((img), __tmp++, ((v) & 0x00ff0000) >> 16); \ + WRITE ((img), __tmp++, ((v) & 0x0000ff00) >> 8); \ + WRITE ((img), __tmp++, ((v) & 0x000000ff) >> 0); \ + } \ + while (0) +#else +#define STORE_24(img,l,o,v) \ + do \ + { \ + uint8_t *__tmp = (l) + 3 * (o); \ + \ + WRITE ((img), __tmp++, ((v) & 0x000000ff) >> 0); \ + WRITE ((img), __tmp++, ((v) & 0x0000ff00) >> 8); \ + WRITE ((img), __tmp++, ((v) & 0x00ff0000) >> 16); \ + } \ + while (0) +#endif + +/* + * YV12 setup and access macros + */ + +#define YV12_SETUP(image) \ + bits_image_t *__bits_image = (bits_image_t *)image; \ + uint32_t *bits = __bits_image->bits; \ + int stride = __bits_image->rowstride; \ + int offset0 = stride < 0 ? \ + ((-stride) >> 1) * ((__bits_image->height - 1) >> 1) - stride : \ + stride * __bits_image->height; \ + int offset1 = stride < 0 ? \ + offset0 + ((-stride) >> 1) * ((__bits_image->height) >> 1) : \ + offset0 + (offset0 >> 2) + +/* Note no trailing semicolon on the above macro; if it's there, then + * the typical usage of YV12_SETUP(image); will have an extra trailing ; + * that some compilers will interpret as a statement -- and then any further + * variable declarations will cause an error. + */ + +#define YV12_Y(line) \ + ((uint8_t *) ((bits) + (stride) * (line))) + +#define YV12_U(line) \ + ((uint8_t *) ((bits) + offset1 + \ + ((stride) >> 1) * ((line) >> 1))) + +#define YV12_V(line) \ + ((uint8_t *) ((bits) + offset0 + \ + ((stride) >> 1) * ((line) >> 1))) + +/* Misc. helpers */ + +static force_inline void +get_shifts (pixman_format_code_t format, + int *a, + int *r, + int *g, + int *b) +{ + switch (PIXMAN_FORMAT_TYPE (format)) + { + case PIXMAN_TYPE_A: + *b = 0; + *g = 0; + *r = 0; + *a = 0; + break; + + case PIXMAN_TYPE_ARGB: + case PIXMAN_TYPE_ARGB_SRGB: + *b = 0; + *g = *b + PIXMAN_FORMAT_B (format); + *r = *g + PIXMAN_FORMAT_G (format); + *a = *r + PIXMAN_FORMAT_R (format); + break; + + case PIXMAN_TYPE_ABGR: + *r = 0; + *g = *r + PIXMAN_FORMAT_R (format); + *b = *g + PIXMAN_FORMAT_G (format); + *a = *b + PIXMAN_FORMAT_B (format); + break; + + case PIXMAN_TYPE_BGRA: + /* With BGRA formats we start counting at the high end of the pixel */ + *b = PIXMAN_FORMAT_BPP (format) - PIXMAN_FORMAT_B (format); + *g = *b - PIXMAN_FORMAT_B (format); + *r = *g - PIXMAN_FORMAT_G (format); + *a = *r - PIXMAN_FORMAT_R (format); + break; + + case PIXMAN_TYPE_RGBA: + /* With BGRA formats we start counting at the high end of the pixel */ + *r = PIXMAN_FORMAT_BPP (format) - PIXMAN_FORMAT_R (format); + *g = *r - PIXMAN_FORMAT_R (format); + *b = *g - PIXMAN_FORMAT_G (format); + *a = *b - PIXMAN_FORMAT_B (format); + break; + + default: + assert (0); + break; + } +} + +static force_inline uint32_t +convert_channel (uint32_t pixel, uint32_t def_value, + int n_from_bits, int from_shift, + int n_to_bits, int to_shift) +{ + uint32_t v; + + if (n_from_bits && n_to_bits) + v = unorm_to_unorm (pixel >> from_shift, n_from_bits, n_to_bits); + else if (n_to_bits) + v = def_value; + else + v = 0; + + return (v & ((1 << n_to_bits) - 1)) << to_shift; +} + +static force_inline uint32_t +convert_pixel (pixman_format_code_t from, pixman_format_code_t to, uint32_t pixel) +{ + int a_from_shift, r_from_shift, g_from_shift, b_from_shift; + int a_to_shift, r_to_shift, g_to_shift, b_to_shift; + uint32_t a, r, g, b; + + get_shifts (from, &a_from_shift, &r_from_shift, &g_from_shift, &b_from_shift); + get_shifts (to, &a_to_shift, &r_to_shift, &g_to_shift, &b_to_shift); + + a = convert_channel (pixel, ~0, + PIXMAN_FORMAT_A (from), a_from_shift, + PIXMAN_FORMAT_A (to), a_to_shift); + + r = convert_channel (pixel, 0, + PIXMAN_FORMAT_R (from), r_from_shift, + PIXMAN_FORMAT_R (to), r_to_shift); + + g = convert_channel (pixel, 0, + PIXMAN_FORMAT_G (from), g_from_shift, + PIXMAN_FORMAT_G (to), g_to_shift); + + b = convert_channel (pixel, 0, + PIXMAN_FORMAT_B (from), b_from_shift, + PIXMAN_FORMAT_B (to), b_to_shift); + + return a | r | g | b; +} + +static force_inline uint32_t +convert_pixel_to_a8r8g8b8 (bits_image_t *image, + pixman_format_code_t format, + uint32_t pixel) +{ + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_GRAY || + PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_COLOR) + { + return image->indexed->rgba[pixel]; + } + else + { + return convert_pixel (format, PIXMAN_a8r8g8b8, pixel); + } +} + +static force_inline uint32_t +convert_pixel_from_a8r8g8b8 (pixman_image_t *image, + pixman_format_code_t format, uint32_t pixel) +{ + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_GRAY) + { + pixel = CONVERT_RGB24_TO_Y15 (pixel); + + return image->bits.indexed->ent[pixel & 0x7fff]; + } + else if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_COLOR) + { + pixel = convert_pixel (PIXMAN_a8r8g8b8, PIXMAN_x1r5g5b5, pixel); + + return image->bits.indexed->ent[pixel & 0x7fff]; + } + else + { + return convert_pixel (PIXMAN_a8r8g8b8, format, pixel); + } +} + +static force_inline uint32_t +fetch_and_convert_pixel (bits_image_t * image, + const uint8_t * bits, + int offset, + pixman_format_code_t format) +{ + uint32_t pixel; + + switch (PIXMAN_FORMAT_BPP (format)) + { + case 1: + pixel = FETCH_1 (image, bits, offset); + break; + + case 4: + pixel = FETCH_4 (image, bits, offset); + break; + + case 8: + pixel = READ (image, bits + offset); + break; + + case 16: + pixel = READ (image, ((uint16_t *)bits + offset)); + break; + + case 24: + pixel = FETCH_24 (image, bits, offset); + break; + + case 32: + pixel = READ (image, ((uint32_t *)bits + offset)); + break; + + default: + pixel = 0xffff00ff; /* As ugly as possible to detect the bug */ + break; + } + + return convert_pixel_to_a8r8g8b8 (image, format, pixel); +} + +static force_inline void +convert_and_store_pixel (bits_image_t * image, + uint8_t * dest, + int offset, + pixman_format_code_t format, + uint32_t pixel) +{ + uint32_t converted = convert_pixel_from_a8r8g8b8 ( + (pixman_image_t *)image, format, pixel); + + switch (PIXMAN_FORMAT_BPP (format)) + { + case 1: + STORE_1 (image, dest, offset, converted & 0x01); + break; + + case 4: + STORE_4 (image, dest, offset, converted & 0xf); + break; + + case 8: + WRITE (image, (dest + offset), converted & 0xff); + break; + + case 16: + WRITE (image, ((uint16_t *)dest + offset), converted & 0xffff); + break; + + case 24: + STORE_24 (image, dest, offset, converted); + break; + + case 32: + WRITE (image, ((uint32_t *)dest + offset), converted); + break; + + default: + *dest = 0x0; + break; + } +} + +#define MAKE_ACCESSORS(format) \ + static void \ + fetch_scanline_ ## format (bits_image_t *image, \ + int x, \ + int y, \ + int width, \ + uint32_t * buffer, \ + const uint32_t *mask) \ + { \ + uint8_t *bits = \ + (uint8_t *)(image->bits + y * image->rowstride); \ + int i; \ + \ + for (i = 0; i < width; ++i) \ + { \ + *buffer++ = \ + fetch_and_convert_pixel (image, bits, x + i, PIXMAN_ ## format); \ + } \ + } \ + \ + static void \ + store_scanline_ ## format (bits_image_t * image, \ + int x, \ + int y, \ + int width, \ + const uint32_t *values) \ + { \ + uint8_t *dest = \ + (uint8_t *)(image->bits + y * image->rowstride); \ + int i; \ + \ + for (i = 0; i < width; ++i) \ + { \ + convert_and_store_pixel ( \ + image, dest, i + x, PIXMAN_ ## format, values[i]); \ + } \ + } \ + \ + static uint32_t \ + fetch_pixel_ ## format (bits_image_t *image, \ + int offset, \ + int line) \ + { \ + uint8_t *bits = \ + (uint8_t *)(image->bits + line * image->rowstride); \ + \ + return fetch_and_convert_pixel ( \ + image, bits, offset, PIXMAN_ ## format); \ + } \ + \ + static const void *const __dummy__ ## format + +MAKE_ACCESSORS(a8r8g8b8); +MAKE_ACCESSORS(x8r8g8b8); +MAKE_ACCESSORS(a8b8g8r8); +MAKE_ACCESSORS(x8b8g8r8); +MAKE_ACCESSORS(x14r6g6b6); +MAKE_ACCESSORS(b8g8r8a8); +MAKE_ACCESSORS(b8g8r8x8); +MAKE_ACCESSORS(r8g8b8x8); +MAKE_ACCESSORS(r8g8b8a8); +MAKE_ACCESSORS(r8g8b8); +MAKE_ACCESSORS(b8g8r8); +MAKE_ACCESSORS(r5g6b5); +MAKE_ACCESSORS(b5g6r5); +MAKE_ACCESSORS(a1r5g5b5); +MAKE_ACCESSORS(x1r5g5b5); +MAKE_ACCESSORS(a1b5g5r5); +MAKE_ACCESSORS(x1b5g5r5); +MAKE_ACCESSORS(a4r4g4b4); +MAKE_ACCESSORS(x4r4g4b4); +MAKE_ACCESSORS(a4b4g4r4); +MAKE_ACCESSORS(x4b4g4r4); +MAKE_ACCESSORS(a8); +MAKE_ACCESSORS(c8); +MAKE_ACCESSORS(g8); +MAKE_ACCESSORS(r3g3b2); +MAKE_ACCESSORS(b2g3r3); +MAKE_ACCESSORS(a2r2g2b2); +MAKE_ACCESSORS(a2b2g2r2); +MAKE_ACCESSORS(x4a4); +MAKE_ACCESSORS(a4); +MAKE_ACCESSORS(g4); +MAKE_ACCESSORS(c4); +MAKE_ACCESSORS(r1g2b1); +MAKE_ACCESSORS(b1g2r1); +MAKE_ACCESSORS(a1r1g1b1); +MAKE_ACCESSORS(a1b1g1r1); +MAKE_ACCESSORS(a1); +MAKE_ACCESSORS(g1); + +/********************************** Fetch ************************************/ +/* Table mapping sRGB-encoded 8 bit numbers to linearly encoded + * floating point numbers. We assume that single precision + * floating point follows the IEEE 754 format. + */ +static const uint32_t to_linear_u[256] = +{ + 0x00000000, 0x399f22b4, 0x3a1f22b4, 0x3a6eb40e, 0x3a9f22b4, 0x3ac6eb61, + 0x3aeeb40e, 0x3b0b3e5d, 0x3b1f22b4, 0x3b33070b, 0x3b46eb61, 0x3b5b518a, + 0x3b70f18a, 0x3b83e1c5, 0x3b8fe614, 0x3b9c87fb, 0x3ba9c9b5, 0x3bb7ad6d, + 0x3bc63547, 0x3bd5635f, 0x3be539bd, 0x3bf5ba70, 0x3c0373b5, 0x3c0c6152, + 0x3c15a703, 0x3c1f45bc, 0x3c293e68, 0x3c3391f4, 0x3c3e4149, 0x3c494d43, + 0x3c54b6c7, 0x3c607eb1, 0x3c6ca5df, 0x3c792d22, 0x3c830aa8, 0x3c89af9e, + 0x3c9085db, 0x3c978dc5, 0x3c9ec7c0, 0x3ca63432, 0x3cadd37d, 0x3cb5a601, + 0x3cbdac20, 0x3cc5e639, 0x3cce54ab, 0x3cd6f7d2, 0x3cdfd00e, 0x3ce8ddb9, + 0x3cf2212c, 0x3cfb9ac1, 0x3d02a569, 0x3d0798dc, 0x3d0ca7e4, 0x3d11d2ae, + 0x3d171963, 0x3d1c7c2e, 0x3d21fb3a, 0x3d2796af, 0x3d2d4ebb, 0x3d332380, + 0x3d39152b, 0x3d3f23e3, 0x3d454fd0, 0x3d4b991c, 0x3d51ffeb, 0x3d588466, + 0x3d5f26b7, 0x3d65e6fe, 0x3d6cc564, 0x3d73c210, 0x3d7add25, 0x3d810b65, + 0x3d84b793, 0x3d88732e, 0x3d8c3e48, 0x3d9018f4, 0x3d940343, 0x3d97fd48, + 0x3d9c0714, 0x3da020b9, 0x3da44a48, 0x3da883d6, 0x3daccd70, 0x3db12728, + 0x3db59110, 0x3dba0b38, 0x3dbe95b2, 0x3dc3308f, 0x3dc7dbe0, 0x3dcc97b4, + 0x3dd1641c, 0x3dd6412a, 0x3ddb2eec, 0x3de02d75, 0x3de53cd3, 0x3dea5d16, + 0x3def8e52, 0x3df4d091, 0x3dfa23e5, 0x3dff885e, 0x3e027f06, 0x3e05427f, + 0x3e080ea2, 0x3e0ae376, 0x3e0dc104, 0x3e10a752, 0x3e139669, 0x3e168e50, + 0x3e198f0e, 0x3e1c98ab, 0x3e1fab2e, 0x3e22c6a0, 0x3e25eb08, 0x3e29186a, + 0x3e2c4ed0, 0x3e2f8e42, 0x3e32d6c4, 0x3e362861, 0x3e39831e, 0x3e3ce702, + 0x3e405416, 0x3e43ca5e, 0x3e4749e4, 0x3e4ad2ae, 0x3e4e64c2, 0x3e520027, + 0x3e55a4e6, 0x3e595303, 0x3e5d0a8a, 0x3e60cb7c, 0x3e6495e0, 0x3e6869bf, + 0x3e6c4720, 0x3e702e08, 0x3e741e7f, 0x3e78188c, 0x3e7c1c34, 0x3e8014c0, + 0x3e822039, 0x3e84308b, 0x3e8645b8, 0x3e885fc3, 0x3e8a7eb0, 0x3e8ca281, + 0x3e8ecb3a, 0x3e90f8df, 0x3e932b72, 0x3e9562f6, 0x3e979f6f, 0x3e99e0e0, + 0x3e9c274e, 0x3e9e72b8, 0x3ea0c322, 0x3ea31892, 0x3ea57308, 0x3ea7d28a, + 0x3eaa3718, 0x3eaca0b7, 0x3eaf0f69, 0x3eb18332, 0x3eb3fc16, 0x3eb67a15, + 0x3eb8fd34, 0x3ebb8576, 0x3ebe12de, 0x3ec0a56e, 0x3ec33d2a, 0x3ec5da14, + 0x3ec87c30, 0x3ecb2380, 0x3ecdd008, 0x3ed081ca, 0x3ed338c9, 0x3ed5f508, + 0x3ed8b68a, 0x3edb7d52, 0x3ede4962, 0x3ee11abe, 0x3ee3f168, 0x3ee6cd64, + 0x3ee9aeb6, 0x3eec955d, 0x3eef815d, 0x3ef272ba, 0x3ef56976, 0x3ef86594, + 0x3efb6717, 0x3efe6e02, 0x3f00bd2b, 0x3f02460c, 0x3f03d1a5, 0x3f055ff8, + 0x3f06f105, 0x3f0884ce, 0x3f0a1b54, 0x3f0bb499, 0x3f0d509f, 0x3f0eef65, + 0x3f1090ef, 0x3f12353c, 0x3f13dc50, 0x3f15862a, 0x3f1732cc, 0x3f18e237, + 0x3f1a946d, 0x3f1c4970, 0x3f1e013f, 0x3f1fbbde, 0x3f21794c, 0x3f23398c, + 0x3f24fca0, 0x3f26c286, 0x3f288b42, 0x3f2a56d3, 0x3f2c253d, 0x3f2df680, + 0x3f2fca9d, 0x3f31a195, 0x3f337b6a, 0x3f35581e, 0x3f3737b1, 0x3f391a24, + 0x3f3aff7a, 0x3f3ce7b2, 0x3f3ed2d0, 0x3f40c0d2, 0x3f42b1bc, 0x3f44a58e, + 0x3f469c49, 0x3f4895ee, 0x3f4a9280, 0x3f4c91ff, 0x3f4e946c, 0x3f5099c8, + 0x3f52a216, 0x3f54ad55, 0x3f56bb88, 0x3f58ccae, 0x3f5ae0cb, 0x3f5cf7de, + 0x3f5f11ec, 0x3f612ef0, 0x3f634eef, 0x3f6571ea, 0x3f6797e1, 0x3f69c0d6, + 0x3f6beccb, 0x3f6e1bc0, 0x3f704db6, 0x3f7282af, 0x3f74baac, 0x3f76f5ae, + 0x3f7933b6, 0x3f7b74c6, 0x3f7db8de, 0x3f800000 +}; + +static const float * const to_linear = (const float *)to_linear_u; + +static uint8_t +to_srgb (float f) +{ + uint8_t low = 0; + uint8_t high = 255; + + while (high - low > 1) + { + uint8_t mid = (low + high) / 2; + + if (to_linear[mid] > f) + high = mid; + else + low = mid; + } + + if (to_linear[high] - f < f - to_linear[low]) + return high; + else + return low; +} + +static void +fetch_scanline_a8r8g8b8_sRGB_float (bits_image_t * image, + int x, + int y, + int width, + uint32_t * b, + const uint32_t *mask) +{ + const uint32_t *bits = image->bits + y * image->rowstride; + const uint32_t *pixel = bits + x; + const uint32_t *end = pixel + width; + argb_t *buffer = (argb_t *)b; + + while (pixel < end) + { + uint32_t p = READ (image, pixel++); + argb_t *argb = buffer; + + argb->a = pixman_unorm_to_float ((p >> 24) & 0xff, 8); + + argb->r = to_linear [(p >> 16) & 0xff]; + argb->g = to_linear [(p >> 8) & 0xff]; + argb->b = to_linear [(p >> 0) & 0xff]; + + buffer++; + } +} + +/* Expects a float buffer */ +static void +fetch_scanline_a2r10g10b10_float (bits_image_t * image, + int x, + int y, + int width, + uint32_t * b, + const uint32_t *mask) +{ + const uint32_t *bits = image->bits + y * image->rowstride; + const uint32_t *pixel = bits + x; + const uint32_t *end = pixel + width; + argb_t *buffer = (argb_t *)b; + + while (pixel < end) + { + uint32_t p = READ (image, pixel++); + uint64_t a = p >> 30; + uint64_t r = (p >> 20) & 0x3ff; + uint64_t g = (p >> 10) & 0x3ff; + uint64_t b = p & 0x3ff; + + buffer->a = pixman_unorm_to_float (a, 2); + buffer->r = pixman_unorm_to_float (r, 10); + buffer->g = pixman_unorm_to_float (g, 10); + buffer->b = pixman_unorm_to_float (b, 10); + + buffer++; + } +} + +/* Expects a float buffer */ +#ifndef PIXMAN_FB_ACCESSORS +static void +fetch_scanline_rgbf_float (bits_image_t *image, + int x, + int y, + int width, + uint32_t * b, + const uint32_t *mask) +{ + const float *bits = (float *)image->bits + y * image->rowstride; + const float *pixel = bits + x * 3; + argb_t *buffer = (argb_t *)b; + + for (; width--; buffer++) { + buffer->r = *pixel++; + buffer->g = *pixel++; + buffer->b = *pixel++; + buffer->a = 1.f; + } +} + +static void +fetch_scanline_rgbaf_float (bits_image_t *image, + int x, + int y, + int width, + uint32_t * b, + const uint32_t *mask) +{ + const float *bits = (float *)image->bits + y * image->rowstride; + const float *pixel = bits + x * 4; + argb_t *buffer = (argb_t *)b; + + for (; width--; buffer++) { + buffer->r = *pixel++; + buffer->g = *pixel++; + buffer->b = *pixel++; + buffer->a = *pixel++; + } +} +#endif + +static void +fetch_scanline_x2r10g10b10_float (bits_image_t *image, + int x, + int y, + int width, + uint32_t * b, + const uint32_t *mask) +{ + const uint32_t *bits = image->bits + y * image->rowstride; + const uint32_t *pixel = (uint32_t *)bits + x; + const uint32_t *end = pixel + width; + argb_t *buffer = (argb_t *)b; + + while (pixel < end) + { + uint32_t p = READ (image, pixel++); + uint64_t r = (p >> 20) & 0x3ff; + uint64_t g = (p >> 10) & 0x3ff; + uint64_t b = p & 0x3ff; + + buffer->a = 1.0; + buffer->r = pixman_unorm_to_float (r, 10); + buffer->g = pixman_unorm_to_float (g, 10); + buffer->b = pixman_unorm_to_float (b, 10); + + buffer++; + } +} + +/* Expects a float buffer */ +static void +fetch_scanline_a2b10g10r10_float (bits_image_t *image, + int x, + int y, + int width, + uint32_t * b, + const uint32_t *mask) +{ + const uint32_t *bits = image->bits + y * image->rowstride; + const uint32_t *pixel = bits + x; + const uint32_t *end = pixel + width; + argb_t *buffer = (argb_t *)b; + + while (pixel < end) + { + uint32_t p = READ (image, pixel++); + uint64_t a = p >> 30; + uint64_t b = (p >> 20) & 0x3ff; + uint64_t g = (p >> 10) & 0x3ff; + uint64_t r = p & 0x3ff; + + buffer->a = pixman_unorm_to_float (a, 2); + buffer->r = pixman_unorm_to_float (r, 10); + buffer->g = pixman_unorm_to_float (g, 10); + buffer->b = pixman_unorm_to_float (b, 10); + + buffer++; + } +} + +/* Expects a float buffer */ +static void +fetch_scanline_x2b10g10r10_float (bits_image_t *image, + int x, + int y, + int width, + uint32_t * b, + const uint32_t *mask) +{ + const uint32_t *bits = image->bits + y * image->rowstride; + const uint32_t *pixel = (uint32_t *)bits + x; + const uint32_t *end = pixel + width; + argb_t *buffer = (argb_t *)b; + + while (pixel < end) + { + uint32_t p = READ (image, pixel++); + uint64_t b = (p >> 20) & 0x3ff; + uint64_t g = (p >> 10) & 0x3ff; + uint64_t r = p & 0x3ff; + + buffer->a = 1.0; + buffer->r = pixman_unorm_to_float (r, 10); + buffer->g = pixman_unorm_to_float (g, 10); + buffer->b = pixman_unorm_to_float (b, 10); + + buffer++; + } +} + +static void +fetch_scanline_yuy2 (bits_image_t *image, + int x, + int line, + int width, + uint32_t * buffer, + const uint32_t *mask) +{ + const uint32_t *bits = image->bits + image->rowstride * line; + int i; + + for (i = 0; i < width; i++) + { + int16_t y, u, v; + int32_t r, g, b; + + y = ((uint8_t *) bits)[(x + i) << 1] - 16; + u = ((uint8_t *) bits)[(((x + i) << 1) & - 4) + 1] - 128; + v = ((uint8_t *) bits)[(((x + i) << 1) & - 4) + 3] - 128; + + /* R = 1.164(Y - 16) + 1.596(V - 128) */ + r = 0x012b27 * y + 0x019a2e * v; + /* G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) */ + g = 0x012b27 * y - 0x00d0f2 * v - 0x00647e * u; + /* B = 1.164(Y - 16) + 2.018(U - 128) */ + b = 0x012b27 * y + 0x0206a2 * u; + + *buffer++ = 0xff000000 | + (r >= 0 ? r < 0x1000000 ? r & 0xff0000 : 0xff0000 : 0) | + (g >= 0 ? g < 0x1000000 ? (g >> 8) & 0x00ff00 : 0x00ff00 : 0) | + (b >= 0 ? b < 0x1000000 ? (b >> 16) & 0x0000ff : 0x0000ff : 0); + } +} + +static void +fetch_scanline_yv12 (bits_image_t *image, + int x, + int line, + int width, + uint32_t * buffer, + const uint32_t *mask) +{ + YV12_SETUP (image); + uint8_t *y_line = YV12_Y (line); + uint8_t *u_line = YV12_U (line); + uint8_t *v_line = YV12_V (line); + int i; + + for (i = 0; i < width; i++) + { + int16_t y, u, v; + int32_t r, g, b; + + y = y_line[x + i] - 16; + u = u_line[(x + i) >> 1] - 128; + v = v_line[(x + i) >> 1] - 128; + + /* R = 1.164(Y - 16) + 1.596(V - 128) */ + r = 0x012b27 * y + 0x019a2e * v; + /* G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) */ + g = 0x012b27 * y - 0x00d0f2 * v - 0x00647e * u; + /* B = 1.164(Y - 16) + 2.018(U - 128) */ + b = 0x012b27 * y + 0x0206a2 * u; + + *buffer++ = 0xff000000 | + (r >= 0 ? r < 0x1000000 ? r & 0xff0000 : 0xff0000 : 0) | + (g >= 0 ? g < 0x1000000 ? (g >> 8) & 0x00ff00 : 0x00ff00 : 0) | + (b >= 0 ? b < 0x1000000 ? (b >> 16) & 0x0000ff : 0x0000ff : 0); + } +} + +/**************************** Pixel wise fetching *****************************/ + +#ifndef PIXMAN_FB_ACCESSORS +static argb_t +fetch_pixel_rgbf_float (bits_image_t *image, + int offset, + int line) +{ + float *bits = (float *)image->bits + line * image->rowstride; + argb_t argb; + + argb.r = bits[offset * 3]; + argb.g = bits[offset * 3 + 1]; + argb.b = bits[offset * 3 + 2]; + argb.a = 1.f; + + return argb; +} + +static argb_t +fetch_pixel_rgbaf_float (bits_image_t *image, + int offset, + int line) +{ + float *bits = (float *)image->bits + line * image->rowstride; + argb_t argb; + + argb.r = bits[offset * 4]; + argb.g = bits[offset * 4 + 1]; + argb.b = bits[offset * 4 + 2]; + argb.a = bits[offset * 4 + 3]; + + return argb; +} +#endif + +static argb_t +fetch_pixel_x2r10g10b10_float (bits_image_t *image, + int offset, + int line) +{ + uint32_t *bits = image->bits + line * image->rowstride; + uint32_t p = READ (image, bits + offset); + uint64_t r = (p >> 20) & 0x3ff; + uint64_t g = (p >> 10) & 0x3ff; + uint64_t b = p & 0x3ff; + argb_t argb; + + argb.a = 1.0; + argb.r = pixman_unorm_to_float (r, 10); + argb.g = pixman_unorm_to_float (g, 10); + argb.b = pixman_unorm_to_float (b, 10); + + return argb; +} + +static argb_t +fetch_pixel_a2r10g10b10_float (bits_image_t *image, + int offset, + int line) +{ + uint32_t *bits = image->bits + line * image->rowstride; + uint32_t p = READ (image, bits + offset); + uint64_t a = p >> 30; + uint64_t r = (p >> 20) & 0x3ff; + uint64_t g = (p >> 10) & 0x3ff; + uint64_t b = p & 0x3ff; + argb_t argb; + + argb.a = pixman_unorm_to_float (a, 2); + argb.r = pixman_unorm_to_float (r, 10); + argb.g = pixman_unorm_to_float (g, 10); + argb.b = pixman_unorm_to_float (b, 10); + + return argb; +} + +static argb_t +fetch_pixel_a2b10g10r10_float (bits_image_t *image, + int offset, + int line) +{ + uint32_t *bits = image->bits + line * image->rowstride; + uint32_t p = READ (image, bits + offset); + uint64_t a = p >> 30; + uint64_t b = (p >> 20) & 0x3ff; + uint64_t g = (p >> 10) & 0x3ff; + uint64_t r = p & 0x3ff; + argb_t argb; + + argb.a = pixman_unorm_to_float (a, 2); + argb.r = pixman_unorm_to_float (r, 10); + argb.g = pixman_unorm_to_float (g, 10); + argb.b = pixman_unorm_to_float (b, 10); + + return argb; +} + +static argb_t +fetch_pixel_x2b10g10r10_float (bits_image_t *image, + int offset, + int line) +{ + uint32_t *bits = image->bits + line * image->rowstride; + uint32_t p = READ (image, bits + offset); + uint64_t b = (p >> 20) & 0x3ff; + uint64_t g = (p >> 10) & 0x3ff; + uint64_t r = p & 0x3ff; + argb_t argb; + + argb.a = 1.0; + argb.r = pixman_unorm_to_float (r, 10); + argb.g = pixman_unorm_to_float (g, 10); + argb.b = pixman_unorm_to_float (b, 10); + + return argb; +} + +static argb_t +fetch_pixel_a8r8g8b8_sRGB_float (bits_image_t *image, + int offset, + int line) +{ + uint32_t *bits = image->bits + line * image->rowstride; + uint32_t p = READ (image, bits + offset); + argb_t argb; + + argb.a = pixman_unorm_to_float ((p >> 24) & 0xff, 8); + + argb.r = to_linear [(p >> 16) & 0xff]; + argb.g = to_linear [(p >> 8) & 0xff]; + argb.b = to_linear [(p >> 0) & 0xff]; + + return argb; +} + +static uint32_t +fetch_pixel_yuy2 (bits_image_t *image, + int offset, + int line) +{ + const uint32_t *bits = image->bits + image->rowstride * line; + + int16_t y, u, v; + int32_t r, g, b; + + y = ((uint8_t *) bits)[offset << 1] - 16; + u = ((uint8_t *) bits)[((offset << 1) & - 4) + 1] - 128; + v = ((uint8_t *) bits)[((offset << 1) & - 4) + 3] - 128; + + /* R = 1.164(Y - 16) + 1.596(V - 128) */ + r = 0x012b27 * y + 0x019a2e * v; + + /* G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) */ + g = 0x012b27 * y - 0x00d0f2 * v - 0x00647e * u; + + /* B = 1.164(Y - 16) + 2.018(U - 128) */ + b = 0x012b27 * y + 0x0206a2 * u; + + return 0xff000000 | + (r >= 0 ? r < 0x1000000 ? r & 0xff0000 : 0xff0000 : 0) | + (g >= 0 ? g < 0x1000000 ? (g >> 8) & 0x00ff00 : 0x00ff00 : 0) | + (b >= 0 ? b < 0x1000000 ? (b >> 16) & 0x0000ff : 0x0000ff : 0); +} + +static uint32_t +fetch_pixel_yv12 (bits_image_t *image, + int offset, + int line) +{ + YV12_SETUP (image); + int16_t y = YV12_Y (line)[offset] - 16; + int16_t u = YV12_U (line)[offset >> 1] - 128; + int16_t v = YV12_V (line)[offset >> 1] - 128; + int32_t r, g, b; + + /* R = 1.164(Y - 16) + 1.596(V - 128) */ + r = 0x012b27 * y + 0x019a2e * v; + + /* G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128) */ + g = 0x012b27 * y - 0x00d0f2 * v - 0x00647e * u; + + /* B = 1.164(Y - 16) + 2.018(U - 128) */ + b = 0x012b27 * y + 0x0206a2 * u; + + return 0xff000000 | + (r >= 0 ? r < 0x1000000 ? r & 0xff0000 : 0xff0000 : 0) | + (g >= 0 ? g < 0x1000000 ? (g >> 8) & 0x00ff00 : 0x00ff00 : 0) | + (b >= 0 ? b < 0x1000000 ? (b >> 16) & 0x0000ff : 0x0000ff : 0); +} + +/*********************************** Store ************************************/ + +#ifndef PIXMAN_FB_ACCESSORS +static void +store_scanline_rgbaf_float (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *v) +{ + float *bits = (float *)image->bits + image->rowstride * y + 4 * x; + const argb_t *values = (argb_t *)v; + + for (; width; width--, values++) + { + *bits++ = values->r; + *bits++ = values->g; + *bits++ = values->b; + *bits++ = values->a; + } +} + +static void +store_scanline_rgbf_float (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *v) +{ + float *bits = (float *)image->bits + image->rowstride * y + 3 * x; + const argb_t *values = (argb_t *)v; + + for (; width; width--, values++) + { + *bits++ = values->r; + *bits++ = values->g; + *bits++ = values->b; + } +} +#endif + +static void +store_scanline_a2r10g10b10_float (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *v) +{ + uint32_t *bits = image->bits + image->rowstride * y; + uint32_t *pixel = bits + x; + argb_t *values = (argb_t *)v; + int i; + + for (i = 0; i < width; ++i) + { + uint32_t a, r, g, b; + + a = pixman_float_to_unorm (values[i].a, 2); + r = pixman_float_to_unorm (values[i].r, 10); + g = pixman_float_to_unorm (values[i].g, 10); + b = pixman_float_to_unorm (values[i].b, 10); + + WRITE (image, pixel++, + (a << 30) | (r << 20) | (g << 10) | b); + } +} + +static void +store_scanline_x2r10g10b10_float (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *v) +{ + uint32_t *bits = image->bits + image->rowstride * y; + uint32_t *pixel = bits + x; + argb_t *values = (argb_t *)v; + int i; + + for (i = 0; i < width; ++i) + { + uint32_t r, g, b; + + r = pixman_float_to_unorm (values[i].r, 10); + g = pixman_float_to_unorm (values[i].g, 10); + b = pixman_float_to_unorm (values[i].b, 10); + + WRITE (image, pixel++, + (r << 20) | (g << 10) | b); + } +} + +static void +store_scanline_a2b10g10r10_float (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *v) +{ + uint32_t *bits = image->bits + image->rowstride * y; + uint32_t *pixel = bits + x; + argb_t *values = (argb_t *)v; + int i; + + for (i = 0; i < width; ++i) + { + uint32_t a, r, g, b; + + a = pixman_float_to_unorm (values[i].a, 2); + r = pixman_float_to_unorm (values[i].r, 10); + g = pixman_float_to_unorm (values[i].g, 10); + b = pixman_float_to_unorm (values[i].b, 10); + + WRITE (image, pixel++, + (a << 30) | (b << 20) | (g << 10) | r); + } +} + +static void +store_scanline_x2b10g10r10_float (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *v) +{ + uint32_t *bits = image->bits + image->rowstride * y; + uint32_t *pixel = bits + x; + argb_t *values = (argb_t *)v; + int i; + + for (i = 0; i < width; ++i) + { + uint32_t r, g, b; + + r = pixman_float_to_unorm (values[i].r, 10); + g = pixman_float_to_unorm (values[i].g, 10); + b = pixman_float_to_unorm (values[i].b, 10); + + WRITE (image, pixel++, + (b << 20) | (g << 10) | r); + } +} + +static void +store_scanline_a8r8g8b8_sRGB_float (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *v) +{ + uint32_t *bits = image->bits + image->rowstride * y; + uint32_t *pixel = bits + x; + argb_t *values = (argb_t *)v; + int i; + + for (i = 0; i < width; ++i) + { + uint32_t a, r, g, b; + + a = pixman_float_to_unorm (values[i].a, 8); + r = to_srgb (values[i].r); + g = to_srgb (values[i].g); + b = to_srgb (values[i].b); + + WRITE (image, pixel++, + (a << 24) | (r << 16) | (g << 8) | b); + } +} + +/* + * Contracts a floating point image to 32bpp and then stores it using a + * regular 32-bit store proc. Despite the type, this function expects an + * argb_t buffer. + */ +static void +store_scanline_generic_float (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *values) +{ + uint32_t *argb8_pixels; + + assert (image->common.type == BITS); + + argb8_pixels = pixman_malloc_ab (width, sizeof(uint32_t)); + if (!argb8_pixels) + return; + + /* Contract the scanline. We could do this in place if values weren't + * const. + */ + pixman_contract_from_float (argb8_pixels, (argb_t *)values, width); + + image->store_scanline_32 (image, x, y, width, argb8_pixels); + + free (argb8_pixels); +} + +static void +fetch_scanline_generic_float (bits_image_t * image, + int x, + int y, + int width, + uint32_t * buffer, + const uint32_t *mask) +{ + image->fetch_scanline_32 (image, x, y, width, buffer, NULL); + + pixman_expand_to_float ((argb_t *)buffer, buffer, image->format, width); +} + +/* The 32_sRGB paths should be deleted after narrow processing + * is no longer invoked for formats that are considered wide. + * (Also see fetch_pixel_generic_lossy_32) */ +static void +fetch_scanline_a8r8g8b8_32_sRGB (bits_image_t *image, + int x, + int y, + int width, + uint32_t *buffer, + const uint32_t *mask) +{ + const uint32_t *bits = image->bits + y * image->rowstride; + const uint32_t *pixel = (uint32_t *)bits + x; + const uint32_t *end = pixel + width; + uint32_t tmp; + + while (pixel < end) + { + uint32_t a, r, g, b; + + tmp = READ (image, pixel++); + + a = (tmp >> 24) & 0xff; + r = (tmp >> 16) & 0xff; + g = (tmp >> 8) & 0xff; + b = (tmp >> 0) & 0xff; + + r = to_linear[r] * 255.0f + 0.5f; + g = to_linear[g] * 255.0f + 0.5f; + b = to_linear[b] * 255.0f + 0.5f; + + *buffer++ = (a << 24) | (r << 16) | (g << 8) | (b << 0); + } +} + +static uint32_t +fetch_pixel_a8r8g8b8_32_sRGB (bits_image_t *image, + int offset, + int line) +{ + uint32_t *bits = image->bits + line * image->rowstride; + uint32_t tmp = READ (image, bits + offset); + uint32_t a, r, g, b; + + a = (tmp >> 24) & 0xff; + r = (tmp >> 16) & 0xff; + g = (tmp >> 8) & 0xff; + b = (tmp >> 0) & 0xff; + + r = to_linear[r] * 255.0f + 0.5f; + g = to_linear[g] * 255.0f + 0.5f; + b = to_linear[b] * 255.0f + 0.5f; + + return (a << 24) | (r << 16) | (g << 8) | (b << 0); +} + +static void +store_scanline_a8r8g8b8_32_sRGB (bits_image_t *image, + int x, + int y, + int width, + const uint32_t *v) +{ + uint32_t *bits = image->bits + image->rowstride * y; + uint64_t *values = (uint64_t *)v; + uint32_t *pixel = bits + x; + uint64_t tmp; + int i; + + for (i = 0; i < width; ++i) + { + uint32_t a, r, g, b; + + tmp = values[i]; + + a = (tmp >> 24) & 0xff; + r = (tmp >> 16) & 0xff; + g = (tmp >> 8) & 0xff; + b = (tmp >> 0) & 0xff; + + r = to_srgb (r * (1/255.0f)); + g = to_srgb (g * (1/255.0f)); + b = to_srgb (b * (1/255.0f)); + + WRITE (image, pixel++, a | (r << 16) | (g << 8) | (b << 0)); + } +} + +static argb_t +fetch_pixel_generic_float (bits_image_t *image, + int offset, + int line) +{ + uint32_t pixel32 = image->fetch_pixel_32 (image, offset, line); + argb_t f; + + pixman_expand_to_float (&f, &pixel32, image->format, 1); + + return f; +} + +/* + * XXX: The transformed fetch path only works at 32-bpp so far. When all + * paths have wide versions, this can be removed. + * + * WARNING: This function loses precision! + */ +static uint32_t +fetch_pixel_generic_lossy_32 (bits_image_t *image, + int offset, + int line) +{ + argb_t pixel64 = image->fetch_pixel_float (image, offset, line); + uint32_t result; + + pixman_contract_from_float (&result, &pixel64, 1); + + return result; +} + +typedef struct +{ + pixman_format_code_t format; + fetch_scanline_t fetch_scanline_32; + fetch_scanline_t fetch_scanline_float; + fetch_pixel_32_t fetch_pixel_32; + fetch_pixel_float_t fetch_pixel_float; + store_scanline_t store_scanline_32; + store_scanline_t store_scanline_float; +} format_info_t; + +#define FORMAT_INFO(format) \ + { \ + PIXMAN_ ## format, \ + fetch_scanline_ ## format, \ + fetch_scanline_generic_float, \ + fetch_pixel_ ## format, \ + fetch_pixel_generic_float, \ + store_scanline_ ## format, \ + store_scanline_generic_float \ + } + +static const format_info_t accessors[] = +{ +/* 32 bpp formats */ + FORMAT_INFO (a8r8g8b8), + FORMAT_INFO (x8r8g8b8), + FORMAT_INFO (a8b8g8r8), + FORMAT_INFO (x8b8g8r8), + FORMAT_INFO (b8g8r8a8), + FORMAT_INFO (b8g8r8x8), + FORMAT_INFO (r8g8b8a8), + FORMAT_INFO (r8g8b8x8), + FORMAT_INFO (x14r6g6b6), + +/* sRGB formats */ + { PIXMAN_a8r8g8b8_sRGB, + fetch_scanline_a8r8g8b8_32_sRGB, fetch_scanline_a8r8g8b8_sRGB_float, + fetch_pixel_a8r8g8b8_32_sRGB, fetch_pixel_a8r8g8b8_sRGB_float, + store_scanline_a8r8g8b8_32_sRGB, store_scanline_a8r8g8b8_sRGB_float, + }, + +/* 24bpp formats */ + FORMAT_INFO (r8g8b8), + FORMAT_INFO (b8g8r8), + +/* 16bpp formats */ + FORMAT_INFO (r5g6b5), + FORMAT_INFO (b5g6r5), + + FORMAT_INFO (a1r5g5b5), + FORMAT_INFO (x1r5g5b5), + FORMAT_INFO (a1b5g5r5), + FORMAT_INFO (x1b5g5r5), + FORMAT_INFO (a4r4g4b4), + FORMAT_INFO (x4r4g4b4), + FORMAT_INFO (a4b4g4r4), + FORMAT_INFO (x4b4g4r4), + +/* 8bpp formats */ + FORMAT_INFO (a8), + FORMAT_INFO (r3g3b2), + FORMAT_INFO (b2g3r3), + FORMAT_INFO (a2r2g2b2), + FORMAT_INFO (a2b2g2r2), + + FORMAT_INFO (c8), + + FORMAT_INFO (g8), + +#define fetch_scanline_x4c4 fetch_scanline_c8 +#define fetch_pixel_x4c4 fetch_pixel_c8 +#define store_scanline_x4c4 store_scanline_c8 + FORMAT_INFO (x4c4), + +#define fetch_scanline_x4g4 fetch_scanline_g8 +#define fetch_pixel_x4g4 fetch_pixel_g8 +#define store_scanline_x4g4 store_scanline_g8 + FORMAT_INFO (x4g4), + + FORMAT_INFO (x4a4), + +/* 4bpp formats */ + FORMAT_INFO (a4), + FORMAT_INFO (r1g2b1), + FORMAT_INFO (b1g2r1), + FORMAT_INFO (a1r1g1b1), + FORMAT_INFO (a1b1g1r1), + + FORMAT_INFO (c4), + + FORMAT_INFO (g4), + +/* 1bpp formats */ + FORMAT_INFO (a1), + FORMAT_INFO (g1), + +/* Wide formats */ +#ifndef PIXMAN_FB_ACCESSORS + { PIXMAN_rgba_float, + NULL, fetch_scanline_rgbaf_float, + fetch_pixel_generic_lossy_32, fetch_pixel_rgbaf_float, + NULL, store_scanline_rgbaf_float }, + + { PIXMAN_rgb_float, + NULL, fetch_scanline_rgbf_float, + fetch_pixel_generic_lossy_32, fetch_pixel_rgbf_float, + NULL, store_scanline_rgbf_float }, +#endif + + { PIXMAN_a2r10g10b10, + NULL, fetch_scanline_a2r10g10b10_float, + fetch_pixel_generic_lossy_32, fetch_pixel_a2r10g10b10_float, + NULL, store_scanline_a2r10g10b10_float }, + + { PIXMAN_x2r10g10b10, + NULL, fetch_scanline_x2r10g10b10_float, + fetch_pixel_generic_lossy_32, fetch_pixel_x2r10g10b10_float, + NULL, store_scanline_x2r10g10b10_float }, + + { PIXMAN_a2b10g10r10, + NULL, fetch_scanline_a2b10g10r10_float, + fetch_pixel_generic_lossy_32, fetch_pixel_a2b10g10r10_float, + NULL, store_scanline_a2b10g10r10_float }, + + { PIXMAN_x2b10g10r10, + NULL, fetch_scanline_x2b10g10r10_float, + fetch_pixel_generic_lossy_32, fetch_pixel_x2b10g10r10_float, + NULL, store_scanline_x2b10g10r10_float }, + +/* YUV formats */ + { PIXMAN_yuy2, + fetch_scanline_yuy2, fetch_scanline_generic_float, + fetch_pixel_yuy2, fetch_pixel_generic_float, + NULL, NULL }, + + { PIXMAN_yv12, + fetch_scanline_yv12, fetch_scanline_generic_float, + fetch_pixel_yv12, fetch_pixel_generic_float, + NULL, NULL }, + + { PIXMAN_null }, +}; + +static void +setup_accessors (bits_image_t *image) +{ + const format_info_t *info = accessors; + + while (info->format != PIXMAN_null) + { + if (info->format == image->format) + { + image->fetch_scanline_32 = info->fetch_scanline_32; + image->fetch_scanline_float = info->fetch_scanline_float; + image->fetch_pixel_32 = info->fetch_pixel_32; + image->fetch_pixel_float = info->fetch_pixel_float; + image->store_scanline_32 = info->store_scanline_32; + image->store_scanline_float = info->store_scanline_float; + + return; + } + + info++; + } +} + +#ifndef PIXMAN_FB_ACCESSORS +void +_pixman_bits_image_setup_accessors_accessors (bits_image_t *image); + +void +_pixman_bits_image_setup_accessors (bits_image_t *image) +{ + if (image->read_func || image->write_func) + _pixman_bits_image_setup_accessors_accessors (image); + else + setup_accessors (image); +} + +#else + +void +_pixman_bits_image_setup_accessors_accessors (bits_image_t *image) +{ + setup_accessors (image); +} + +#endif diff --git a/gfx/cairo/libpixman/src/pixman-accessor.h b/gfx/cairo/libpixman/src/pixman-accessor.h new file mode 100644 index 0000000000..8e0b03621b --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-accessor.h @@ -0,0 +1,25 @@ +#ifdef PIXMAN_FB_ACCESSORS + +#define READ(img, ptr) \ + (((bits_image_t *)(img))->read_func ((ptr), sizeof(*(ptr)))) +#define WRITE(img, ptr,val) \ + (((bits_image_t *)(img))->write_func ((ptr), (val), sizeof (*(ptr)))) + +#define MEMSET_WRAPPED(img, dst, val, size) \ + do { \ + size_t _i; \ + uint8_t *_dst = (uint8_t*)(dst); \ + for(_i = 0; _i < (size_t) size; _i++) { \ + WRITE((img), _dst +_i, (val)); \ + } \ + } while (0) + +#else + +#define READ(img, ptr) (*(ptr)) +#define WRITE(img, ptr, val) (*(ptr) = (val)) +#define MEMSET_WRAPPED(img, dst, val, size) \ + memset(dst, val, size) + +#endif + diff --git a/gfx/cairo/libpixman/src/pixman-arm-asm.h b/gfx/cairo/libpixman/src/pixman-arm-asm.h new file mode 100644 index 0000000000..e7093623e0 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-asm.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2008 Mozilla Corporation + * Copyright © 2010 Nokia Corporation + * + * 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 Mozilla Corporation not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Mozilla Corporation makes 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. + * + * Author: Jeff Muizelaar (jeff@infidigm.net) + * + */ + +/* Supplementary macro for setting function attributes */ +.macro pixman_asm_function_impl fname +#ifdef ASM_HAVE_FUNC_DIRECTIVE + .func \fname +#endif + .global \fname +#ifdef __ELF__ + .hidden \fname + .type \fname, %function +#endif +\fname: +.endm + +.macro pixman_asm_function fname +#ifdef ASM_LEADING_UNDERSCORE + pixman_asm_function_impl _\fname +#else + pixman_asm_function_impl \fname +#endif +.endm + +.macro pixman_end_asm_function +#ifdef ASM_HAVE_FUNC_DIRECTIVE + .endfunc +#endif +.endm diff --git a/gfx/cairo/libpixman/src/pixman-arm-common.h b/gfx/cairo/libpixman/src/pixman-arm-common.h new file mode 100644 index 0000000000..9537688306 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-common.h @@ -0,0 +1,419 @@ +/* + * Copyright © 2010 Nokia 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. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + */ + +#ifndef PIXMAN_ARM_COMMON_H +#define PIXMAN_ARM_COMMON_H + +#include "pixman-inlines.h" + +/* Define some macros which can expand into proxy functions between + * ARM assembly optimized functions and the rest of pixman fast path API. + * + * All the low level ARM assembly functions have to use ARM EABI + * calling convention and take up to 8 arguments: + * width, height, dst, dst_stride, src, src_stride, mask, mask_stride + * + * The arguments are ordered with the most important coming first (the + * first 4 arguments are passed to function in registers, the rest are + * on stack). The last arguments are optional, for example if the + * function is not using mask, then 'mask' and 'mask_stride' can be + * omitted when doing a function call. + * + * Arguments 'src' and 'mask' contain either a pointer to the top left + * pixel of the composited rectangle or a pixel color value depending + * on the function type. In the case of just a color value (solid source + * or mask), the corresponding stride argument is unused. + */ + +#define SKIP_ZERO_SRC 1 +#define SKIP_ZERO_MASK 2 + +#define PIXMAN_ARM_BIND_FAST_PATH_SRC_DST(cputype, name, \ + src_type, src_cnt, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_##cputype (int32_t w, \ + int32_t h, \ + dst_type *dst, \ + int32_t dst_stride, \ + src_type *src, \ + int32_t src_stride); \ + \ +static void \ +cputype##_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line; \ + src_type *src_line; \ + int32_t dst_stride, src_stride; \ + \ + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ + src_stride, src_line, src_cnt); \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + \ + pixman_composite_##name##_asm_##cputype (width, height, \ + dst_line, dst_stride, \ + src_line, src_stride); \ +} + +#define PIXMAN_ARM_BIND_FAST_PATH_N_DST(flags, cputype, name, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_##cputype (int32_t w, \ + int32_t h, \ + dst_type *dst, \ + int32_t dst_stride, \ + uint32_t src); \ + \ +static void \ +cputype##_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line; \ + int32_t dst_stride; \ + uint32_t src; \ + \ + src = _pixman_image_get_solid ( \ + imp, src_image, dest_image->bits.format); \ + \ + if ((flags & SKIP_ZERO_SRC) && src == 0) \ + return; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + \ + pixman_composite_##name##_asm_##cputype (width, height, \ + dst_line, dst_stride, \ + src); \ +} + +#define PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST(flags, cputype, name, \ + mask_type, mask_cnt, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_##cputype (int32_t w, \ + int32_t h, \ + dst_type *dst, \ + int32_t dst_stride, \ + uint32_t src, \ + int32_t unused, \ + mask_type *mask, \ + int32_t mask_stride); \ + \ +static void \ +cputype##_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line; \ + mask_type *mask_line; \ + int32_t dst_stride, mask_stride; \ + uint32_t src; \ + \ + src = _pixman_image_get_solid ( \ + imp, src_image, dest_image->bits.format); \ + \ + if ((flags & SKIP_ZERO_SRC) && src == 0) \ + return; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type, \ + mask_stride, mask_line, mask_cnt); \ + \ + pixman_composite_##name##_asm_##cputype (width, height, \ + dst_line, dst_stride, \ + src, 0, \ + mask_line, mask_stride); \ +} + +#define PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST(flags, cputype, name, \ + src_type, src_cnt, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_##cputype (int32_t w, \ + int32_t h, \ + dst_type *dst, \ + int32_t dst_stride, \ + src_type *src, \ + int32_t src_stride, \ + uint32_t mask); \ + \ +static void \ +cputype##_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line; \ + src_type *src_line; \ + int32_t dst_stride, src_stride; \ + uint32_t mask; \ + \ + mask = _pixman_image_get_solid ( \ + imp, mask_image, dest_image->bits.format); \ + \ + if ((flags & SKIP_ZERO_MASK) && mask == 0) \ + return; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ + src_stride, src_line, src_cnt); \ + \ + pixman_composite_##name##_asm_##cputype (width, height, \ + dst_line, dst_stride, \ + src_line, src_stride, \ + mask); \ +} + +#define PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST(cputype, name, \ + src_type, src_cnt, \ + mask_type, mask_cnt, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_##cputype (int32_t w, \ + int32_t h, \ + dst_type *dst, \ + int32_t dst_stride, \ + src_type *src, \ + int32_t src_stride, \ + mask_type *mask, \ + int32_t mask_stride); \ + \ +static void \ +cputype##_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line; \ + src_type *src_line; \ + mask_type *mask_line; \ + int32_t dst_stride, src_stride, mask_stride; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ + src_stride, src_line, src_cnt); \ + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type, \ + mask_stride, mask_line, mask_cnt); \ + \ + pixman_composite_##name##_asm_##cputype (width, height, \ + dst_line, dst_stride, \ + src_line, src_stride, \ + mask_line, mask_stride); \ +} + +#define PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST(cputype, name, op, \ + src_type, dst_type) \ +void \ +pixman_scaled_nearest_scanline_##name##_##op##_asm_##cputype ( \ + int32_t w, \ + dst_type * dst, \ + const src_type * src, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx); \ + \ +static force_inline void \ +scaled_nearest_scanline_##cputype##_##name##_##op (dst_type * pd, \ + const src_type * ps, \ + int32_t w, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t zero_src) \ +{ \ + pixman_scaled_nearest_scanline_##name##_##op##_asm_##cputype (w, pd, ps, \ + vx, unit_x, \ + max_vx); \ +} \ + \ +FAST_NEAREST_MAINLOOP (cputype##_##name##_cover_##op, \ + scaled_nearest_scanline_##cputype##_##name##_##op, \ + src_type, dst_type, COVER) \ +FAST_NEAREST_MAINLOOP (cputype##_##name##_none_##op, \ + scaled_nearest_scanline_##cputype##_##name##_##op, \ + src_type, dst_type, NONE) \ +FAST_NEAREST_MAINLOOP (cputype##_##name##_pad_##op, \ + scaled_nearest_scanline_##cputype##_##name##_##op, \ + src_type, dst_type, PAD) \ +FAST_NEAREST_MAINLOOP (cputype##_##name##_normal_##op, \ + scaled_nearest_scanline_##cputype##_##name##_##op, \ + src_type, dst_type, NORMAL) + +#define PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_A8_DST(flags, cputype, name, op, \ + src_type, dst_type) \ +void \ +pixman_scaled_nearest_scanline_##name##_##op##_asm_##cputype ( \ + int32_t w, \ + dst_type * dst, \ + const src_type * src, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + const uint8_t * mask); \ + \ +static force_inline void \ +scaled_nearest_scanline_##cputype##_##name##_##op (const uint8_t * mask, \ + dst_type * pd, \ + const src_type * ps, \ + int32_t w, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t zero_src) \ +{ \ + if ((flags & SKIP_ZERO_SRC) && zero_src) \ + return; \ + pixman_scaled_nearest_scanline_##name##_##op##_asm_##cputype (w, pd, ps, \ + vx, unit_x, \ + max_vx, \ + mask); \ +} \ + \ +FAST_NEAREST_MAINLOOP_COMMON (cputype##_##name##_cover_##op, \ + scaled_nearest_scanline_##cputype##_##name##_##op,\ + src_type, uint8_t, dst_type, COVER, TRUE, FALSE)\ +FAST_NEAREST_MAINLOOP_COMMON (cputype##_##name##_none_##op, \ + scaled_nearest_scanline_##cputype##_##name##_##op,\ + src_type, uint8_t, dst_type, NONE, TRUE, FALSE) \ +FAST_NEAREST_MAINLOOP_COMMON (cputype##_##name##_pad_##op, \ + scaled_nearest_scanline_##cputype##_##name##_##op,\ + src_type, uint8_t, dst_type, PAD, TRUE, FALSE) \ +FAST_NEAREST_MAINLOOP_COMMON (cputype##_##name##_normal_##op, \ + scaled_nearest_scanline_##cputype##_##name##_##op,\ + src_type, uint8_t, dst_type, NORMAL, TRUE, FALSE) + +/* Provide entries for the fast path table */ +#define PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH(op,s,d,func) \ + SIMPLE_NEAREST_A8_MASK_FAST_PATH (op,s,d,func), \ + SIMPLE_NEAREST_A8_MASK_FAST_PATH_NORMAL (op,s,d,func) + +/*****************************************************************************/ + +#define PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST(flags, cputype, name, op, \ + src_type, dst_type) \ +void \ +pixman_scaled_bilinear_scanline_##name##_##op##_asm_##cputype ( \ + dst_type * dst, \ + const src_type * top, \ + const src_type * bottom, \ + int wt, \ + int wb, \ + pixman_fixed_t x, \ + pixman_fixed_t ux, \ + int width); \ + \ +static force_inline void \ +scaled_bilinear_scanline_##cputype##_##name##_##op ( \ + dst_type * dst, \ + const uint32_t * mask, \ + const src_type * src_top, \ + const src_type * src_bottom, \ + int32_t w, \ + int wt, \ + int wb, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t zero_src) \ +{ \ + if ((flags & SKIP_ZERO_SRC) && zero_src) \ + return; \ + pixman_scaled_bilinear_scanline_##name##_##op##_asm_##cputype ( \ + dst, src_top, src_bottom, wt, wb, vx, unit_x, w); \ +} \ + \ +FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_cover_##op, \ + scaled_bilinear_scanline_##cputype##_##name##_##op, \ + src_type, uint32_t, dst_type, COVER, FLAG_NONE) \ +FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_none_##op, \ + scaled_bilinear_scanline_##cputype##_##name##_##op, \ + src_type, uint32_t, dst_type, NONE, FLAG_NONE) \ +FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_pad_##op, \ + scaled_bilinear_scanline_##cputype##_##name##_##op, \ + src_type, uint32_t, dst_type, PAD, FLAG_NONE) \ +FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_normal_##op, \ + scaled_bilinear_scanline_##cputype##_##name##_##op, \ + src_type, uint32_t, dst_type, NORMAL, \ + FLAG_NONE) + + +#define PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST(flags, cputype, name, op, \ + src_type, dst_type) \ +void \ +pixman_scaled_bilinear_scanline_##name##_##op##_asm_##cputype ( \ + dst_type * dst, \ + const uint8_t * mask, \ + const src_type * top, \ + const src_type * bottom, \ + int wt, \ + int wb, \ + pixman_fixed_t x, \ + pixman_fixed_t ux, \ + int width); \ + \ +static force_inline void \ +scaled_bilinear_scanline_##cputype##_##name##_##op ( \ + dst_type * dst, \ + const uint8_t * mask, \ + const src_type * src_top, \ + const src_type * src_bottom, \ + int32_t w, \ + int wt, \ + int wb, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t zero_src) \ +{ \ + if ((flags & SKIP_ZERO_SRC) && zero_src) \ + return; \ + pixman_scaled_bilinear_scanline_##name##_##op##_asm_##cputype ( \ + dst, mask, src_top, src_bottom, wt, wb, vx, unit_x, w); \ +} \ + \ +FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_cover_##op, \ + scaled_bilinear_scanline_##cputype##_##name##_##op, \ + src_type, uint8_t, dst_type, COVER, \ + FLAG_HAVE_NON_SOLID_MASK) \ +FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_none_##op, \ + scaled_bilinear_scanline_##cputype##_##name##_##op, \ + src_type, uint8_t, dst_type, NONE, \ + FLAG_HAVE_NON_SOLID_MASK) \ +FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_pad_##op, \ + scaled_bilinear_scanline_##cputype##_##name##_##op, \ + src_type, uint8_t, dst_type, PAD, \ + FLAG_HAVE_NON_SOLID_MASK) \ +FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_normal_##op, \ + scaled_bilinear_scanline_##cputype##_##name##_##op, \ + src_type, uint8_t, dst_type, NORMAL, \ + FLAG_HAVE_NON_SOLID_MASK) + + +#endif diff --git a/gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S b/gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S new file mode 100644 index 0000000000..ce4d5f84e3 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S @@ -0,0 +1,1358 @@ +/* + * Copyright © 2011 SCore 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. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + * Author: Taekyun Kim (tkq.kim@samsung.com) + */ + +/* + * This file contains scaled bilinear scanline functions implemented + * using older siarhei's bilinear macro template. + * + * << General scanline function procedures >> + * 1. bilinear interpolate source pixels + * 2. load mask pixels + * 3. load destination pixels + * 4. duplicate mask to fill whole register + * 5. interleave source & destination pixels + * 6. apply mask to source pixels + * 7. combine source & destination pixels + * 8, Deinterleave final result + * 9. store destination pixels + * + * All registers with single number (i.e. src0, tmp0) are 64-bits registers. + * Registers with double numbers(src01, dst01) are 128-bits registers. + * All temp registers can be used freely outside the code block. + * Assume that symbol(register .req) OUT and MASK are defined at caller of these macro blocks. + * + * Remarks + * There can be lots of pipeline stalls inside code block and between code blocks. + * Further optimizations will be done by new macro templates using head/tail_head/tail scheme. + */ + +/* Prevent the stack from becoming executable for no reason... */ +#if defined(__linux__) && defined (__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +.text +.fpu neon +.arch armv7a +.object_arch armv4 +.eabi_attribute 10, 0 +.eabi_attribute 12, 0 +.arm +.altmacro +.p2align 2 + +#include "pixman-private.h" +#include "pixman-arm-asm.h" +#include "pixman-arm-neon-asm.h" + +/* + * Bilinear macros from pixman-arm-neon-asm.S + */ + +/* + * Bilinear scaling support code which tries to provide pixel fetching, color + * format conversion, and interpolation as separate macros which can be used + * as the basic building blocks for constructing bilinear scanline functions. + */ + +.macro bilinear_load_8888 reg1, reg2, tmp + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + vld1.32 {\reg1}, [TMP1], STRIDE + vld1.32 {\reg2}, [TMP1] +.endm + +.macro bilinear_load_0565 reg1, reg2, tmp + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + vld1.32 {\reg2[0]}, [TMP1], STRIDE + vld1.32 {\reg2[1]}, [TMP1] + convert_four_0565_to_x888_packed \reg2, \reg1, \reg2, \tmp +.endm + +.macro bilinear_load_and_vertical_interpolate_two_8888 \ + acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 + + bilinear_load_8888 \reg1, \reg2, \tmp1 + vmull.u8 \acc1, \reg1, d28 + vmlal.u8 \acc1, \reg2, d29 + bilinear_load_8888 \reg3, \reg4, \tmp2 + vmull.u8 \acc2, \reg3, d28 + vmlal.u8 \acc2, \reg4, d29 +.endm + +.macro bilinear_load_and_vertical_interpolate_four_8888 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + bilinear_load_and_vertical_interpolate_two_8888 \ + \xacc1, \xacc2, \xreg1, \xreg2, \xreg3, \xreg4, \xacc2lo, \xacc2hi + bilinear_load_and_vertical_interpolate_two_8888 \ + \yacc1, \yacc2, \yreg1, \yreg2, \yreg3, \yreg4, \yacc2lo, \yacc2hi +.endm + +.macro bilinear_load_and_vertical_interpolate_two_0565 \ + acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 + vld1.32 {\acc2lo[0]}, [TMP1], STRIDE + vld1.32 {\acc2hi[0]}, [TMP2], STRIDE + vld1.32 {\acc2lo[1]}, [TMP1] + vld1.32 {\acc2hi[1]}, [TMP2] + convert_0565_to_x888 \acc2, \reg3, \reg2, \reg1 + vzip.u8 \reg1, \reg3 + vzip.u8 \reg2, \reg4 + vzip.u8 \reg3, \reg4 + vzip.u8 \reg1, \reg2 + vmull.u8 \acc1, \reg1, d28 + vmlal.u8 \acc1, \reg2, d29 + vmull.u8 \acc2, \reg3, d28 + vmlal.u8 \acc2, \reg4, d29 +.endm + +.macro bilinear_load_and_vertical_interpolate_four_0565 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 + vld1.32 {\xacc2lo[0]}, [TMP1], STRIDE + vld1.32 {\xacc2hi[0]}, [TMP2], STRIDE + vld1.32 {\xacc2lo[1]}, [TMP1] + vld1.32 {\xacc2hi[1]}, [TMP2] + convert_0565_to_x888 \xacc2, \xreg3, \xreg2, \xreg1 + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 + vld1.32 {\yacc2lo[0]}, [TMP1], STRIDE + vzip.u8 \xreg1, \xreg3 + vld1.32 {\yacc2hi[0]}, [TMP2], STRIDE + vzip.u8 \xreg2, \xreg4 + vld1.32 {\yacc2lo[1]}, [TMP1] + vzip.u8 \xreg3, \xreg4 + vld1.32 {\yacc2hi[1]}, [TMP2] + vzip.u8 \xreg1, \xreg2 + convert_0565_to_x888 \yacc2, \yreg3, \yreg2, \yreg1 + vmull.u8 \xacc1, \xreg1, d28 + vzip.u8 \yreg1, \yreg3 + vmlal.u8 \xacc1, \xreg2, d29 + vzip.u8 \yreg2, \yreg4 + vmull.u8 \xacc2, \xreg3, d28 + vzip.u8 \yreg3, \yreg4 + vmlal.u8 \xacc2, \xreg4, d29 + vzip.u8 \yreg1, \yreg2 + vmull.u8 \yacc1, \yreg1, d28 + vmlal.u8 \yacc1, \yreg2, d29 + vmull.u8 \yacc2, \yreg3, d28 + vmlal.u8 \yacc2, \yreg4, d29 +.endm + +.macro bilinear_store_8888 numpix, tmp1, tmp2 +.if \numpix == 4 + vst1.32 {d0, d1}, [OUT]! +.elseif \numpix == 2 + vst1.32 {d0}, [OUT]! +.elseif \numpix == 1 + vst1.32 {d0[0]}, [OUT, :32]! +.else + .error bilinear_store_8888 numpix is unsupported +.endif +.endm + +.macro bilinear_store_0565 numpix, tmp1, tmp2 + vuzp.u8 d0, d1 + vuzp.u8 d2, d3 + vuzp.u8 d1, d3 + vuzp.u8 d0, d2 + convert_8888_to_0565 d2, d1, d0, q1, \tmp1, \tmp2 +.if \numpix == 4 + vst1.16 {d2}, [OUT]! +.elseif \numpix == 2 + vst1.32 {d2[0]}, [OUT]! +.elseif \numpix == 1 + vst1.16 {d2[0]}, [OUT]! +.else + .error bilinear_store_0565 numpix is unsupported +.endif +.endm + + +/* + * Macros for loading mask pixels into register 'mask'. + * vdup must be done in somewhere else. + */ +.macro bilinear_load_mask_x numpix, mask +.endm + +.macro bilinear_load_mask_8 numpix, mask +.if \numpix == 4 + vld1.32 {\mask[0]}, [MASK]! +.elseif \numpix == 2 + vld1.16 {\mask[0]}, [MASK]! +.elseif \numpix == 1 + vld1.8 {\mask[0]}, [MASK]! +.else + .error bilinear_load_mask_8 \numpix is unsupported +.endif + pld [MASK, #prefetch_offset] +.endm + +.macro bilinear_load_mask mask_fmt, numpix, mask + bilinear_load_mask_\()\mask_fmt \numpix, \mask +.endm + + +/* + * Macros for loading destination pixels into register 'dst0' and 'dst1'. + * Interleave should be done somewhere else. + */ +.macro bilinear_load_dst_0565_src numpix, dst0, dst1, dst01 +.endm + +.macro bilinear_load_dst_8888_src numpix, dst0, dst1, dst01 +.endm + +.macro bilinear_load_dst_8888 numpix, dst0, dst1, dst01 +.if \numpix == 4 + vld1.32 {\dst0, \dst1}, [OUT] +.elseif \numpix == 2 + vld1.32 {\dst0}, [OUT] +.elseif \numpix == 1 + vld1.32 {\dst0[0]}, [OUT] +.else + .error bilinear_load_dst_8888 \numpix is unsupported +.endif + pld [OUT, #(prefetch_offset * 4)] +.endm + +.macro bilinear_load_dst_8888_over numpix, dst0, dst1, dst01 + bilinear_load_dst_8888 \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_load_dst_8888_add numpix, dst0, dst1, dst01 + bilinear_load_dst_8888 \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_load_dst dst_fmt, op, numpix, dst0, dst1, dst01 + bilinear_load_dst_\()\dst_fmt\()_\()\op \numpix, \dst0, \dst1, \dst01 +.endm + +/* + * Macros for duplicating partially loaded mask to fill entire register. + * We will apply mask to interleaved source pixels, that is + * (r0, r1, r2, r3, g0, g1, g2, g3) x (m0, m1, m2, m3, m0, m1, m2, m3) + * (b0, b1, b2, b3, a0, a1, a2, a3) x (m0, m1, m2, m3, m0, m1, m2, m3) + * So, we need to duplicate loaded mask into whole register. + * + * For two pixel case + * (r0, r1, x, x, g0, g1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) + * (b0, b1, x, x, a0, a1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) + * We can do some optimizations for this including last pixel cases. + */ +.macro bilinear_duplicate_mask_x numpix, mask +.endm + +.macro bilinear_duplicate_mask_8 numpix, mask +.if \numpix == 4 + vdup.32 \mask, \mask[0] +.elseif \numpix == 2 + vdup.16 \mask, \mask[0] +.elseif \numpix == 1 + vdup.8 \mask, \mask[0] +.else + .error bilinear_duplicate_mask_8 is unsupported +.endif +.endm + +.macro bilinear_duplicate_mask mask_fmt, numpix, mask + bilinear_duplicate_mask_\()\mask_fmt \numpix, \mask +.endm + +/* + * Macros for interleaving src and dst pixels to rrrr gggg bbbb aaaa form. + * Interleave should be done when maks is enabled or operator is 'over'. + */ +.macro bilinear_interleave src0, src1, dst0, dst1 + vuzp.8 \src0, \src1 + vuzp.8 \dst0, \dst1 + vuzp.8 \src0, \src1 + vuzp.8 \dst0, \dst1 +.endm + +.macro bilinear_interleave_src_dst_x_src \ + numpix, src0, src1, src01, dst0, dst1, dst01 +.endm + +.macro bilinear_interleave_src_dst_x_over \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \dst0, \dst1 +.endm + +.macro bilinear_interleave_src_dst_x_add \ + numpix, src0, src1, src01, dst0, dst1, dst01 +.endm + +.macro bilinear_interleave_src_dst_8_src \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \dst0, \dst1 +.endm + +.macro bilinear_interleave_src_dst_8_over \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \dst0, \dst1 +.endm + +.macro bilinear_interleave_src_dst_8_add \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \dst0, \dst1 +.endm + +.macro bilinear_interleave_src_dst \ + mask_fmt, op, numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave_src_dst_\()\mask_fmt\()_\()\op \ + \numpix, \src0, \src1, \src01, \dst0, \dst1, \dst01 +.endm + + +/* + * Macros for applying masks to src pixels. (see combine_mask_u() function) + * src, dst should be in interleaved form. + * mask register should be in form (m0, m1, m2, m3). + */ +.macro bilinear_apply_mask_to_src_x \ + numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 +.endm + +.macro bilinear_apply_mask_to_src_8 \ + numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + + vmull.u8 \tmp01, \src0, \mask + vmull.u8 \tmp23, \src1, \mask + /* bubbles */ + vrshr.u16 \tmp45, \tmp01, #8 + vrshr.u16 \tmp67, \tmp23, #8 + /* bubbles */ + vraddhn.u16 \src0, \tmp45, \tmp01 + vraddhn.u16 \src1, \tmp67, \tmp23 +.endm + +.macro bilinear_apply_mask_to_src \ + mask_fmt, numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + + bilinear_apply_mask_to_src_\()\mask_fmt \ + \numpix, \src0, \src1, \src01, \mask, \ + \tmp01, \tmp23, \tmp45, \tmp67 +.endm + + +/* + * Macros for combining src and destination pixels. + * Interleave or not is depending on operator 'op'. + */ +.macro bilinear_combine_src \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 +.endm + +.macro bilinear_combine_over \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + + vdup.32 \tmp8, \src1[1] + /* bubbles */ + vmvn.8 \tmp8, \tmp8 + /* bubbles */ + vmull.u8 \tmp01, \dst0, \tmp8 + /* bubbles */ + vmull.u8 \tmp23, \dst1, \tmp8 + /* bubbles */ + vrshr.u16 \tmp45, \tmp01, #8 + vrshr.u16 \tmp67, \tmp23, #8 + /* bubbles */ + vraddhn.u16 \dst0, \tmp45, \tmp01 + vraddhn.u16 \dst1, \tmp67, \tmp23 + /* bubbles */ + vqadd.u8 \src01, \dst01, \src01 +.endm + +.macro bilinear_combine_add \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + + vqadd.u8 \src01, \dst01, \src01 +.endm + +.macro bilinear_combine \ + op, numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + + bilinear_combine_\()\op \ + \numpix, \src0, \src1, \src01, \dst0, \dst1, \dst01, \ + \tmp01, \tmp23, \tmp45, \tmp67, \tmp8 +.endm + +/* + * Macros for final deinterleaving of destination pixels if needed. + */ +.macro bilinear_deinterleave numpix, dst0, dst1, dst01 + vuzp.8 \dst0, \dst1 + /* bubbles */ + vuzp.8 \dst0, \dst1 +.endm + +.macro bilinear_deinterleave_dst_x_src numpix, dst0, dst1, dst01 +.endm + +.macro bilinear_deinterleave_dst_x_over numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst_x_add numpix, dst0, dst1, dst01 +.endm + +.macro bilinear_deinterleave_dst_8_src numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst_8_over numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst_8_add numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst mask_fmt, op, numpix, dst0, dst1, dst01 + bilinear_deinterleave_dst_\()\mask_fmt\()_\()\op \numpix, \dst0, \dst1, \dst01 +.endm + + +.macro bilinear_interpolate_last_pixel src_fmt, mask_fmt, dst_fmt, op + bilinear_load_\()\src_fmt d0, d1, d2 + bilinear_load_mask \mask_fmt, 1, d4 + bilinear_load_dst \dst_fmt, \op, 1, d18, d19, q9 + vmull.u8 q1, d0, d28 + vmlal.u8 q1, d1, d29 + /* 5 cycles bubble */ + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + /* 5 cycles bubble */ + bilinear_duplicate_mask \mask_fmt, 1, d4 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + /* 3 cycles bubble */ + vmovn.u16 d0, q0 + /* 1 cycle bubble */ + bilinear_interleave_src_dst \ + \mask_fmt, \op, 1, d0, d1, q0, d18, d19, q9 + bilinear_apply_mask_to_src \ + \mask_fmt, 1, d0, d1, q0, d4, \ + q3, q8, q10, q11 + bilinear_combine \ + \op, 1, d0, d1, q0, d18, d19, q9, \ + q3, q8, q10, q11, d5 + bilinear_deinterleave_dst \mask_fmt, \op, 1, d0, d1, q0 + bilinear_store_\()\dst_fmt 1, q2, q3 +.endm + +.macro bilinear_interpolate_two_pixels src_fmt, mask_fmt, dst_fmt, op + bilinear_load_and_vertical_interpolate_two_\()\src_fmt \ + q1, q11, d0, d1, d20, d21, d22, d23 + bilinear_load_mask \mask_fmt, 2, d4 + bilinear_load_dst \dst_fmt, \op, 2, d18, d19, q9 + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q10, d22, d31 + vmlal.u16 q10, d23, d31 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + bilinear_duplicate_mask \mask_fmt, 2, d4 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vmovn.u16 d0, q0 + bilinear_interleave_src_dst \ + \mask_fmt, \op, 2, d0, d1, q0, d18, d19, q9 + bilinear_apply_mask_to_src \ + \mask_fmt, 2, d0, d1, q0, d4, \ + q3, q8, q10, q11 + bilinear_combine \ + \op, 2, d0, d1, q0, d18, d19, q9, \ + q3, q8, q10, q11, d5 + bilinear_deinterleave_dst \mask_fmt, \op, 2, d0, d1, q0 + bilinear_store_\()\dst_fmt 2, q2, q3 +.endm + +.macro bilinear_interpolate_four_pixels src_fmt, mask_fmt, dst_fmt, op + bilinear_load_and_vertical_interpolate_four_\()\src_fmt \ + q1, q11, d0, d1, d20, d21, d22, d23 \ + q3, q9, d4, d5, d16, d17, d18, d19 + pld [TMP1, PF_OFFS] + sub TMP1, TMP1, STRIDE + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q10, d22, d31 + vmlal.u16 q10, d23, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshll.u16 q2, d6, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d6, d30 + vmlal.u16 q2, d7, d30 + vshll.u16 q8, d18, #BILINEAR_INTERPOLATION_BITS + bilinear_load_mask \mask_fmt, 4, d22 + bilinear_load_dst \dst_fmt, \op, 4, d2, d3, q1 + pld [TMP1, PF_OFFS] + vmlsl.u16 q8, d18, d31 + vmlal.u16 q8, d19, d31 + vadd.u16 q12, q12, q13 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d5, q8, #(2 * BILINEAR_INTERPOLATION_BITS) + bilinear_duplicate_mask \mask_fmt, 4, d22 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d0, q0 + vmovn.u16 d1, q2 + vadd.u16 q12, q12, q13 + bilinear_interleave_src_dst \ + \mask_fmt, \op, 4, d0, d1, q0, d2, d3, q1 + bilinear_apply_mask_to_src \ + \mask_fmt, 4, d0, d1, q0, d22, \ + q3, q8, q9, q10 + bilinear_combine \ + \op, 4, d0, d1, q0, d2, d3, q1, \ + q3, q8, q9, q10, d23 + bilinear_deinterleave_dst \mask_fmt, \op, 4, d0, d1, q0 + bilinear_store_\()\dst_fmt 4, q2, q3 +.endm + +.set BILINEAR_FLAG_USE_MASK, 1 +.set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 + +/* + * Main template macro for generating NEON optimized bilinear scanline functions. + * + * Bilinear scanline generator macro take folling arguments: + * fname - name of the function to generate + * src_fmt - source color format (8888 or 0565) + * dst_fmt - destination color format (8888 or 0565) + * src/dst_bpp_shift - (1 << bpp_shift) is the size of src/dst pixel in bytes + * process_last_pixel - code block that interpolate one pixel and does not + * update horizontal weight + * process_two_pixels - code block that interpolate two pixels and update + * horizontal weight + * process_four_pixels - code block that interpolate four pixels and update + * horizontal weight + * process_pixblock_head - head part of middle loop + * process_pixblock_tail - tail part of middle loop + * process_pixblock_tail_head - tail_head of middle loop + * pixblock_size - number of pixels processed in a single middle loop + * prefetch_distance - prefetch in the source image by that many pixels ahead + */ + +.macro generate_bilinear_scanline_func \ + fname, \ + src_fmt, dst_fmt, src_bpp_shift, dst_bpp_shift, \ + bilinear_process_last_pixel, \ + bilinear_process_two_pixels, \ + bilinear_process_four_pixels, \ + bilinear_process_pixblock_head, \ + bilinear_process_pixblock_tail, \ + bilinear_process_pixblock_tail_head, \ + pixblock_size, \ + prefetch_distance, \ + flags + +pixman_asm_function \fname +.if \pixblock_size == 8 +.elseif \pixblock_size == 4 +.else + .error unsupported pixblock size +.endif + +.if ((\flags) & BILINEAR_FLAG_USE_MASK) == 0 + OUT .req r0 + TOP .req r1 + BOTTOM .req r2 + WT .req r3 + WB .req r4 + X .req r5 + UX .req r6 + WIDTH .req ip + TMP1 .req r3 + TMP2 .req r4 + PF_OFFS .req r7 + TMP3 .req r8 + TMP4 .req r9 + STRIDE .req r2 + + mov ip, sp + push {r4, r5, r6, r7, r8, r9} + mov PF_OFFS, #\prefetch_distance + ldmia ip, {WB, X, UX, WIDTH} +.else + OUT .req r0 + MASK .req r1 + TOP .req r2 + BOTTOM .req r3 + WT .req r4 + WB .req r5 + X .req r6 + UX .req r7 + WIDTH .req ip + TMP1 .req r4 + TMP2 .req r5 + PF_OFFS .req r8 + TMP3 .req r9 + TMP4 .req r10 + STRIDE .req r3 + + .set prefetch_offset, \prefetch_distance + + mov ip, sp + push {r4, r5, r6, r7, r8, r9, r10, ip} + mov PF_OFFS, #\prefetch_distance + ldmia ip, {WT, WB, X, UX, WIDTH} +.endif + + mul PF_OFFS, PF_OFFS, UX + +.if ((\flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 + vpush {d8-d15} +.endif + + sub STRIDE, BOTTOM, TOP + .unreq BOTTOM + + cmp WIDTH, #0 + ble 3f + + vdup.u16 q12, X + vdup.u16 q13, UX + vdup.u8 d28, WT + vdup.u8 d29, WB + vadd.u16 d25, d25, d26 + + /* ensure good destination alignment */ + cmp WIDTH, #1 + blt 0f + tst OUT, #(1 << \dst_bpp_shift) + beq 0f + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + \bilinear_process_last_pixel + sub WIDTH, WIDTH, #1 +0: + vadd.u16 q13, q13, q13 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + + cmp WIDTH, #2 + blt 0f + tst OUT, #(1 << (\dst_bpp_shift + 1)) + beq 0f + \bilinear_process_two_pixels + sub WIDTH, WIDTH, #2 +0: +.if \pixblock_size == 8 + cmp WIDTH, #4 + blt 0f + tst OUT, #(1 << (\dst_bpp_shift + 2)) + beq 0f + \bilinear_process_four_pixels + sub WIDTH, WIDTH, #4 +0: +.endif + subs WIDTH, WIDTH, #\pixblock_size + blt 1f + mov PF_OFFS, PF_OFFS, asr #(16 - \src_bpp_shift) + \bilinear_process_pixblock_head + subs WIDTH, WIDTH, #\pixblock_size + blt 5f +0: + \bilinear_process_pixblock_tail_head + subs WIDTH, WIDTH, #\pixblock_size + bge 0b +5: + \bilinear_process_pixblock_tail +1: +.if \pixblock_size == 8 + tst WIDTH, #4 + beq 2f + \bilinear_process_four_pixels +2: +.endif + /* handle the remaining trailing pixels */ + tst WIDTH, #2 + beq 2f + \bilinear_process_two_pixels +2: + tst WIDTH, #1 + beq 3f + \bilinear_process_last_pixel +3: +.if ((\flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 + vpop {d8-d15} +.endif + +.if ((\flags) & BILINEAR_FLAG_USE_MASK) == 0 + pop {r4, r5, r6, r7, r8, r9} +.else + pop {r4, r5, r6, r7, r8, r9, r10, ip} +.endif + bx lr + + .unreq OUT + .unreq TOP + .unreq WT + .unreq WB + .unreq X + .unreq UX + .unreq WIDTH + .unreq TMP1 + .unreq TMP2 + .unreq PF_OFFS + .unreq TMP3 + .unreq TMP4 + .unreq STRIDE +.if ((\flags) & BILINEAR_FLAG_USE_MASK) != 0 + .unreq MASK +.endif + +pixman_end_asm_function + +.endm + +/* src_8888_8_8888 */ +.macro bilinear_src_8888_8_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 8888, src +.endm + +.macro bilinear_src_8888_8_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, 8, 8888, src +.endm + +.macro bilinear_src_8888_8_8888_process_four_pixels + bilinear_interpolate_four_pixels 8888, 8, 8888, src +.endm + +.macro bilinear_src_8888_8_8888_process_pixblock_head + bilinear_src_8888_8_8888_process_four_pixels +.endm + +.macro bilinear_src_8888_8_8888_process_pixblock_tail +.endm + +.macro bilinear_src_8888_8_8888_process_pixblock_tail_head + bilinear_src_8888_8_8888_process_pixblock_tail + bilinear_src_8888_8_8888_process_pixblock_head +.endm + +/* src_8888_8_0565 */ +.macro bilinear_src_8888_8_0565_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 0565, src +.endm + +.macro bilinear_src_8888_8_0565_process_two_pixels + bilinear_interpolate_two_pixels 8888, 8, 0565, src +.endm + +.macro bilinear_src_8888_8_0565_process_four_pixels + bilinear_interpolate_four_pixels 8888, 8, 0565, src +.endm + +.macro bilinear_src_8888_8_0565_process_pixblock_head + bilinear_src_8888_8_0565_process_four_pixels +.endm + +.macro bilinear_src_8888_8_0565_process_pixblock_tail +.endm + +.macro bilinear_src_8888_8_0565_process_pixblock_tail_head + bilinear_src_8888_8_0565_process_pixblock_tail + bilinear_src_8888_8_0565_process_pixblock_head +.endm + +/* src_0565_8_x888 */ +.macro bilinear_src_0565_8_x888_process_last_pixel + bilinear_interpolate_last_pixel 0565, 8, 8888, src +.endm + +.macro bilinear_src_0565_8_x888_process_two_pixels + bilinear_interpolate_two_pixels 0565, 8, 8888, src +.endm + +.macro bilinear_src_0565_8_x888_process_four_pixels + bilinear_interpolate_four_pixels 0565, 8, 8888, src +.endm + +.macro bilinear_src_0565_8_x888_process_pixblock_head + bilinear_src_0565_8_x888_process_four_pixels +.endm + +.macro bilinear_src_0565_8_x888_process_pixblock_tail +.endm + +.macro bilinear_src_0565_8_x888_process_pixblock_tail_head + bilinear_src_0565_8_x888_process_pixblock_tail + bilinear_src_0565_8_x888_process_pixblock_head +.endm + +/* src_0565_8_0565 */ +.macro bilinear_src_0565_8_0565_process_last_pixel + bilinear_interpolate_last_pixel 0565, 8, 0565, src +.endm + +.macro bilinear_src_0565_8_0565_process_two_pixels + bilinear_interpolate_two_pixels 0565, 8, 0565, src +.endm + +.macro bilinear_src_0565_8_0565_process_four_pixels + bilinear_interpolate_four_pixels 0565, 8, 0565, src +.endm + +.macro bilinear_src_0565_8_0565_process_pixblock_head + bilinear_src_0565_8_0565_process_four_pixels +.endm + +.macro bilinear_src_0565_8_0565_process_pixblock_tail +.endm + +.macro bilinear_src_0565_8_0565_process_pixblock_tail_head + bilinear_src_0565_8_0565_process_pixblock_tail + bilinear_src_0565_8_0565_process_pixblock_head +.endm + +/* over_8888_8888 */ +.macro bilinear_over_8888_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, x, 8888, over +.endm + +.macro bilinear_over_8888_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, x, 8888, over +.endm + +.macro bilinear_over_8888_8888_process_four_pixels + bilinear_interpolate_four_pixels 8888, x, 8888, over +.endm + +.macro bilinear_over_8888_8888_process_pixblock_head + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + + vld1.32 {d22}, [TMP1], STRIDE + vld1.32 {d23}, [TMP1] + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + vmull.u8 q8, d22, d28 + vmlal.u8 q8, d23, d29 + + vld1.32 {d22}, [TMP2], STRIDE + vld1.32 {d23}, [TMP2] + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vmull.u8 q9, d22, d28 + vmlal.u8 q9, d23, d29 + + vld1.32 {d22}, [TMP3], STRIDE + vld1.32 {d23}, [TMP3] + vmull.u8 q10, d22, d28 + vmlal.u8 q10, d23, d29 + + vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d16, d30 + vmlal.u16 q0, d17, d30 + + pld [TMP4, PF_OFFS] + vld1.32 {d16}, [TMP4], STRIDE + vld1.32 {d17}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q11, d16, d28 + vmlal.u8 q11, d17, d29 + + vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q1, d18, d31 + vmlal.u16 q1, d19, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 +.endm + +.macro bilinear_over_8888_8888_process_pixblock_tail + vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d20, d30 + vmlal.u16 q2, d21, d30 + vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q3, d22, d31 + vmlal.u16 q3, d23, d31 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vld1.32 {d2, d3}, [OUT, :128] + pld [OUT, #(prefetch_offset * 4)] + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d6, q0 + vmovn.u16 d7, q2 + vuzp.8 d6, d7 + vuzp.8 d2, d3 + vuzp.8 d6, d7 + vuzp.8 d2, d3 + vdup.32 d4, d7[1] + vmvn.8 d4, d4 + vmull.u8 q11, d2, d4 + vmull.u8 q2, d3, d4 + vrshr.u16 q1, q11, #8 + vrshr.u16 q10, q2, #8 + vraddhn.u16 d2, q1, q11 + vraddhn.u16 d3, q10, q2 + vqadd.u8 q3, q1, q3 + vuzp.8 d6, d7 + vuzp.8 d6, d7 + vadd.u16 q12, q12, q13 + vst1.32 {d6, d7}, [OUT, :128]! +.endm + +.macro bilinear_over_8888_8888_process_pixblock_tail_head + vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + vmlsl.u16 q2, d20, d30 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + vmlal.u16 q2, d21, d30 + vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS + vld1.32 {d20}, [TMP1], STRIDE + vmlsl.u16 q3, d22, d31 + vmlal.u16 q3, d23, d31 + vld1.32 {d21}, [TMP1] + vmull.u8 q8, d20, d28 + vmlal.u8 q8, d21, d29 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vld1.32 {d2, d3}, [OUT, :128] + pld [OUT, PF_OFFS] + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vld1.32 {d22}, [TMP2], STRIDE + vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d6, q0 + vld1.32 {d23}, [TMP2] + vmull.u8 q9, d22, d28 + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vmlal.u8 q9, d23, d29 + vmovn.u16 d7, q2 + vld1.32 {d22}, [TMP3], STRIDE + vuzp.8 d6, d7 + vuzp.8 d2, d3 + vuzp.8 d6, d7 + vuzp.8 d2, d3 + vdup.32 d4, d7[1] + vld1.32 {d23}, [TMP3] + vmvn.8 d4, d4 + vmull.u8 q10, d22, d28 + vmlal.u8 q10, d23, d29 + vmull.u8 q11, d2, d4 + vmull.u8 q2, d3, d4 + vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d16, d30 + vrshr.u16 q1, q11, #8 + vmlal.u16 q0, d17, d30 + vrshr.u16 q8, q2, #8 + vraddhn.u16 d2, q1, q11 + vraddhn.u16 d3, q8, q2 + pld [TMP4, PF_OFFS] + vld1.32 {d16}, [TMP4], STRIDE + vqadd.u8 q3, q1, q3 + vld1.32 {d17}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q11, d16, d28 + vmlal.u8 q11, d17, d29 + vuzp.8 d6, d7 + vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS + vuzp.8 d6, d7 + vmlsl.u16 q1, d18, d31 + vadd.u16 q12, q12, q13 + vmlal.u16 q1, d19, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vst1.32 {d6, d7}, [OUT, :128]! +.endm + +/* over_8888_8_8888 */ +.macro bilinear_over_8888_8_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 8888, over +.endm + +.macro bilinear_over_8888_8_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, 8, 8888, over +.endm + +.macro bilinear_over_8888_8_8888_process_four_pixels + bilinear_interpolate_four_pixels 8888, 8, 8888, over +.endm + +.macro bilinear_over_8888_8_8888_process_pixblock_head + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + vld1.32 {d0}, [TMP1], STRIDE + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + vld1.32 {d1}, [TMP1] + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + vld1.32 {d2}, [TMP2], STRIDE + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vld1.32 {d3}, [TMP2] + vmull.u8 q2, d0, d28 + vmull.u8 q3, d2, d28 + vmlal.u8 q2, d1, d29 + vmlal.u8 q3, d3, d29 + vshll.u16 q0, d4, #BILINEAR_INTERPOLATION_BITS + vshll.u16 q1, d6, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d4, d30 + vmlsl.u16 q1, d6, d31 + vmlal.u16 q0, d5, d30 + vmlal.u16 q1, d7, d31 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vld1.32 {d2}, [TMP3], STRIDE + vld1.32 {d3}, [TMP3] + pld [TMP4, PF_OFFS] + vld1.32 {d4}, [TMP4], STRIDE + vld1.32 {d5}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q3, d2, d28 + vmlal.u8 q3, d3, d29 + vmull.u8 q1, d4, d28 + vmlal.u8 q1, d5, d29 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vld1.32 {d22[0]}, [MASK]! + pld [MASK, #prefetch_offset] + vadd.u16 q12, q12, q13 + vmovn.u16 d16, q0 +.endm + +.macro bilinear_over_8888_8_8888_process_pixblock_tail + vshll.u16 q9, d6, #BILINEAR_INTERPOLATION_BITS + vshll.u16 q10, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q9, d6, d30 + vmlsl.u16 q10, d2, d31 + vmlal.u16 q9, d7, d30 + vmlal.u16 q10, d3, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vdup.32 d22, d22[0] + vshrn.u32 d18, q9, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d19, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d17, q9 + vld1.32 {d18, d19}, [OUT, :128] + pld [OUT, PF_OFFS] + vuzp.8 d16, d17 + vuzp.8 d18, d19 + vuzp.8 d16, d17 + vuzp.8 d18, d19 + vmull.u8 q10, d16, d22 + vmull.u8 q11, d17, d22 + vrsra.u16 q10, q10, #8 + vrsra.u16 q11, q11, #8 + vrshrn.u16 d16, q10, #8 + vrshrn.u16 d17, q11, #8 + vdup.32 d22, d17[1] + vmvn.8 d22, d22 + vmull.u8 q10, d18, d22 + vmull.u8 q11, d19, d22 + vrshr.u16 q9, q10, #8 + vrshr.u16 q0, q11, #8 + vraddhn.u16 d18, q9, q10 + vraddhn.u16 d19, q0, q11 + vqadd.u8 q9, q8, q9 + vuzp.8 d18, d19 + vuzp.8 d18, d19 + vst1.32 {d18, d19}, [OUT, :128]! +.endm + +.macro bilinear_over_8888_8_8888_process_pixblock_tail_head + vshll.u16 q9, d6, #BILINEAR_INTERPOLATION_BITS + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + vshll.u16 q10, d2, #BILINEAR_INTERPOLATION_BITS + vld1.32 {d0}, [TMP1], STRIDE + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + vmlsl.u16 q9, d6, d30 + vmlsl.u16 q10, d2, d31 + vld1.32 {d1}, [TMP1] + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + vmlal.u16 q9, d7, d30 + vmlal.u16 q10, d3, d31 + vld1.32 {d2}, [TMP2], STRIDE + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vld1.32 {d3}, [TMP2] + vdup.32 d22, d22[0] + vshrn.u32 d18, q9, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d19, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + vmull.u8 q2, d0, d28 + vmull.u8 q3, d2, d28 + vmovn.u16 d17, q9 + vld1.32 {d18, d19}, [OUT, :128] + pld [OUT, #(prefetch_offset * 4)] + vmlal.u8 q2, d1, d29 + vmlal.u8 q3, d3, d29 + vuzp.8 d16, d17 + vuzp.8 d18, d19 + vshll.u16 q0, d4, #BILINEAR_INTERPOLATION_BITS + vshll.u16 q1, d6, #BILINEAR_INTERPOLATION_BITS + vuzp.8 d16, d17 + vuzp.8 d18, d19 + vmlsl.u16 q0, d4, d30 + vmlsl.u16 q1, d6, d31 + vmull.u8 q10, d16, d22 + vmull.u8 q11, d17, d22 + vmlal.u16 q0, d5, d30 + vmlal.u16 q1, d7, d31 + vrsra.u16 q10, q10, #8 + vrsra.u16 q11, q11, #8 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vrshrn.u16 d16, q10, #8 + vrshrn.u16 d17, q11, #8 + vld1.32 {d2}, [TMP3], STRIDE + vdup.32 d22, d17[1] + vld1.32 {d3}, [TMP3] + vmvn.8 d22, d22 + pld [TMP4, PF_OFFS] + vld1.32 {d4}, [TMP4], STRIDE + vmull.u8 q10, d18, d22 + vmull.u8 q11, d19, d22 + vld1.32 {d5}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q3, d2, d28 + vrshr.u16 q9, q10, #8 + vrshr.u16 q15, q11, #8 + vmlal.u8 q3, d3, d29 + vmull.u8 q1, d4, d28 + vraddhn.u16 d18, q9, q10 + vraddhn.u16 d19, q15, q11 + vmlal.u8 q1, d5, d29 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vqadd.u8 q9, q8, q9 + vld1.32 {d22[0]}, [MASK]! + vuzp.8 d18, d19 + vadd.u16 q12, q12, q13 + vuzp.8 d18, d19 + vmovn.u16 d16, q0 + vst1.32 {d18, d19}, [OUT, :128]! +.endm + +/* add_8888_8888 */ +.macro bilinear_add_8888_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, x, 8888, add +.endm + +.macro bilinear_add_8888_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, x, 8888, add +.endm + +.macro bilinear_add_8888_8888_process_four_pixels + bilinear_interpolate_four_pixels 8888, x, 8888, add +.endm + +.macro bilinear_add_8888_8888_process_pixblock_head + bilinear_add_8888_8888_process_four_pixels +.endm + +.macro bilinear_add_8888_8888_process_pixblock_tail +.endm + +.macro bilinear_add_8888_8888_process_pixblock_tail_head + bilinear_add_8888_8888_process_pixblock_tail + bilinear_add_8888_8888_process_pixblock_head +.endm + +/* add_8888_8_8888 */ +.macro bilinear_add_8888_8_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 8888, add +.endm + +.macro bilinear_add_8888_8_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, 8, 8888, add +.endm + +.macro bilinear_add_8888_8_8888_process_four_pixels + bilinear_interpolate_four_pixels 8888, 8, 8888, add +.endm + +.macro bilinear_add_8888_8_8888_process_pixblock_head + bilinear_add_8888_8_8888_process_four_pixels +.endm + +.macro bilinear_add_8888_8_8888_process_pixblock_tail +.endm + +.macro bilinear_add_8888_8_8888_process_pixblock_tail_head + bilinear_add_8888_8_8888_process_pixblock_tail + bilinear_add_8888_8_8888_process_pixblock_head +.endm + + +/* Bilinear scanline functions */ +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8_8888_SRC_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_src_8888_8_8888_process_last_pixel, \ + bilinear_src_8888_8_8888_process_two_pixels, \ + bilinear_src_8888_8_8888_process_four_pixels, \ + bilinear_src_8888_8_8888_process_pixblock_head, \ + bilinear_src_8888_8_8888_process_pixblock_tail, \ + bilinear_src_8888_8_8888_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8_0565_SRC_asm_neon, \ + 8888, 0565, 2, 1, \ + bilinear_src_8888_8_0565_process_last_pixel, \ + bilinear_src_8888_8_0565_process_two_pixels, \ + bilinear_src_8888_8_0565_process_four_pixels, \ + bilinear_src_8888_8_0565_process_pixblock_head, \ + bilinear_src_8888_8_0565_process_pixblock_tail, \ + bilinear_src_8888_8_0565_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_0565_8_x888_SRC_asm_neon, \ + 0565, 8888, 1, 2, \ + bilinear_src_0565_8_x888_process_last_pixel, \ + bilinear_src_0565_8_x888_process_two_pixels, \ + bilinear_src_0565_8_x888_process_four_pixels, \ + bilinear_src_0565_8_x888_process_pixblock_head, \ + bilinear_src_0565_8_x888_process_pixblock_tail, \ + bilinear_src_0565_8_x888_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_0565_8_0565_SRC_asm_neon, \ + 0565, 0565, 1, 1, \ + bilinear_src_0565_8_0565_process_last_pixel, \ + bilinear_src_0565_8_0565_process_two_pixels, \ + bilinear_src_0565_8_0565_process_four_pixels, \ + bilinear_src_0565_8_0565_process_pixblock_head, \ + bilinear_src_0565_8_0565_process_pixblock_tail, \ + bilinear_src_0565_8_0565_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8888_OVER_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_over_8888_8888_process_last_pixel, \ + bilinear_over_8888_8888_process_two_pixels, \ + bilinear_over_8888_8888_process_four_pixels, \ + bilinear_over_8888_8888_process_pixblock_head, \ + bilinear_over_8888_8888_process_pixblock_tail, \ + bilinear_over_8888_8888_process_pixblock_tail_head, \ + 4, 28, 0 + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8_8888_OVER_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_over_8888_8_8888_process_last_pixel, \ + bilinear_over_8888_8_8888_process_two_pixels, \ + bilinear_over_8888_8_8888_process_four_pixels, \ + bilinear_over_8888_8_8888_process_pixblock_head, \ + bilinear_over_8888_8_8888_process_pixblock_tail, \ + bilinear_over_8888_8_8888_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8888_ADD_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_add_8888_8888_process_last_pixel, \ + bilinear_add_8888_8888_process_two_pixels, \ + bilinear_add_8888_8888_process_four_pixels, \ + bilinear_add_8888_8888_process_pixblock_head, \ + bilinear_add_8888_8888_process_pixblock_tail, \ + bilinear_add_8888_8888_process_pixblock_tail_head, \ + 4, 28, 0 + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8_8888_ADD_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_add_8888_8_8888_process_last_pixel, \ + bilinear_add_8888_8_8888_process_two_pixels, \ + bilinear_add_8888_8_8888_process_four_pixels, \ + bilinear_add_8888_8_8888_process_pixblock_head, \ + bilinear_add_8888_8_8888_process_pixblock_tail, \ + bilinear_add_8888_8_8888_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK diff --git a/gfx/cairo/libpixman/src/pixman-arm-neon-asm.S b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.S new file mode 100644 index 0000000000..35eca116d1 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.S @@ -0,0 +1,3633 @@ +/* + * Copyright © 2009 Nokia 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. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + */ + +/* + * This file contains implementations of NEON optimized pixel processing + * functions. There is no full and detailed tutorial, but some functions + * (those which are exposing some new or interesting features) are + * extensively commented and can be used as examples. + * + * You may want to have a look at the comments for following functions: + * - pixman_composite_over_8888_0565_asm_neon + * - pixman_composite_over_n_8_0565_asm_neon + */ + +#ifdef __clang__ +#define ldrgeb ldrbge +#define subges subsge +#define subpls subspl +#endif + +/* Prevent the stack from becoming executable for no reason... */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + + .text + .fpu neon + .arch armv7a + .object_arch armv4 + .eabi_attribute 10, 0 /* suppress Tag_FP_arch */ + .eabi_attribute 12, 0 /* suppress Tag_Advanced_SIMD_arch */ + .arm + .altmacro + .p2align 2 + +#include "pixman-private.h" +#include "pixman-arm-asm.h" +#include "pixman-arm-neon-asm.h" + +/* Global configuration options and preferences */ + +/* + * The code can optionally make use of unaligned memory accesses to improve + * performance of handling leading/trailing pixels for each scanline. + * Configuration variable RESPECT_STRICT_ALIGNMENT can be set to 0 for + * example in linux if unaligned memory accesses are not configured to + * generate.exceptions. + */ +.set RESPECT_STRICT_ALIGNMENT, 1 + +/* + * Set default prefetch type. There is a choice between the following options: + * + * PREFETCH_TYPE_NONE (may be useful for the ARM cores where PLD is set to work + * as NOP to workaround some HW bugs or for whatever other reason) + * + * PREFETCH_TYPE_SIMPLE (may be useful for simple single-issue ARM cores where + * advanced prefetch intruduces heavy overhead) + * + * PREFETCH_TYPE_ADVANCED (useful for superscalar cores such as ARM Cortex-A8 + * which can run ARM and NEON instructions simultaneously so that extra ARM + * instructions do not add (many) extra cycles, but improve prefetch efficiency) + * + * Note: some types of function can't support advanced prefetch and fallback + * to simple one (those which handle 24bpp pixels) + */ +.set PREFETCH_TYPE_DEFAULT, PREFETCH_TYPE_ADVANCED + +/* Prefetch distance in pixels for simple prefetch */ +.set PREFETCH_DISTANCE_SIMPLE, 64 + +/* + * Implementation of pixman_composite_over_8888_0565_asm_neon + * + * This function takes a8r8g8b8 source buffer, r5g6b5 destination buffer and + * performs OVER compositing operation. Function fast_composite_over_8888_0565 + * from pixman-fast-path.c does the same in C and can be used as a reference. + * + * First we need to have some NEON assembly code which can do the actual + * operation on the pixels and provide it to the template macro. + * + * Template macro quite conveniently takes care of emitting all the necessary + * code for memory reading and writing (including quite tricky cases of + * handling unaligned leading/trailing pixels), so we only need to deal with + * the data in NEON registers. + * + * NEON registers allocation in general is recommented to be the following: + * d0, d1, d2, d3 - contain loaded source pixel data + * d4, d5, d6, d7 - contain loaded destination pixels (if they are needed) + * d24, d25, d26, d27 - contain loading mask pixel data (if mask is used) + * d28, d29, d30, d31 - place for storing the result (destination pixels) + * + * As can be seen above, four 64-bit NEON registers are used for keeping + * intermediate pixel data and up to 8 pixels can be processed in one step + * for 32bpp formats (16 pixels for 16bpp, 32 pixels for 8bpp). + * + * This particular function uses the following registers allocation: + * d0, d1, d2, d3 - contain loaded source pixel data + * d4, d5 - contain loaded destination pixels (they are needed) + * d28, d29 - place for storing the result (destination pixels) + */ + +/* + * Step one. We need to have some code to do some arithmetics on pixel data. + * This is implemented as a pair of macros: '*_head' and '*_tail'. When used + * back-to-back, they take pixel data from {d0, d1, d2, d3} and {d4, d5}, + * perform all the needed calculations and write the result to {d28, d29}. + * The rationale for having two macros and not just one will be explained + * later. In practice, any single monolitic function which does the work can + * be split into two parts in any arbitrary way without affecting correctness. + * + * There is one special trick here too. Common template macro can optionally + * make our life a bit easier by doing R, G, B, A color components + * deinterleaving for 32bpp pixel formats (and this feature is used in + * 'pixman_composite_over_8888_0565_asm_neon' function). So it means that + * instead of having 8 packed pixels in {d0, d1, d2, d3} registers, we + * actually use d0 register for blue channel (a vector of eight 8-bit + * values), d1 register for green, d2 for red and d3 for alpha. This + * simple conversion can be also done with a few NEON instructions: + * + * Packed to planar conversion: + * vuzp.8 d0, d1 + * vuzp.8 d2, d3 + * vuzp.8 d1, d3 + * vuzp.8 d0, d2 + * + * Planar to packed conversion: + * vzip.8 d0, d2 + * vzip.8 d1, d3 + * vzip.8 d2, d3 + * vzip.8 d0, d1 + * + * But pixel can be loaded directly in planar format using VLD4.8 NEON + * instruction. It is 1 cycle slower than VLD1.32, so this is not always + * desirable, that's why deinterleaving is optional. + * + * But anyway, here is the code: + */ +.macro pixman_composite_over_8888_0565_process_pixblock_head + /* convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format + and put data into d6 - red, d7 - green, d30 - blue */ + vshrn.u16 d6, q2, #8 + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vsri.u8 d6, d6, #5 + vmvn.8 d3, d3 /* invert source alpha */ + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + /* now do alpha blending, storing results in 8-bit planar format + into d16 - red, d19 - green, d18 - blue */ + vmull.u8 q10, d3, d6 + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + vrshr.u16 q13, q10, #8 + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 + vraddhn.u16 d22, q12, q15 +.endm + +.macro pixman_composite_over_8888_0565_process_pixblock_tail + /* ... continue alpha blending */ + vqadd.u8 d16, d2, d20 + vqadd.u8 q9, q0, q11 + /* convert the result to r5g6b5 and store it into {d28, d29} */ + vshll.u8 q14, d16, #8 + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +/* + * OK, now we got almost everything that we need. Using the above two + * macros, the work can be done right. But now we want to optimize + * it a bit. ARM Cortex-A8 is an in-order core, and benefits really + * a lot from good code scheduling and software pipelining. + * + * Let's construct some code, which will run in the core main loop. + * Some pseudo-code of the main loop will look like this: + * head + * while (...) { + * tail + * head + * } + * tail + * + * It may look a bit weird, but this setup allows to hide instruction + * latencies better and also utilize dual-issue capability more + * efficiently (make pairs of load-store and ALU instructions). + * + * So what we need now is a '*_tail_head' macro, which will be used + * in the core main loop. A trivial straightforward implementation + * of this macro would look like this: + * + * pixman_composite_over_8888_0565_process_pixblock_tail + * vst1.16 {d28, d29}, [DST_W, :128]! + * vld1.16 {d4, d5}, [DST_R, :128]! + * vld4.32 {d0, d1, d2, d3}, [SRC]! + * pixman_composite_over_8888_0565_process_pixblock_head + * cache_preload 8, 8 + * + * Now it also got some VLD/VST instructions. We simply can't move from + * processing one block of pixels to the other one with just arithmetics. + * The previously processed data needs to be written to memory and new + * data needs to be fetched. Fortunately, this main loop does not deal + * with partial leading/trailing pixels and can load/store a full block + * of pixels in a bulk. Additionally, destination buffer is already + * 16 bytes aligned here (which is good for performance). + * + * New things here are DST_R, DST_W, SRC and MASK identifiers. These + * are the aliases for ARM registers which are used as pointers for + * accessing data. We maintain separate pointers for reading and writing + * destination buffer (DST_R and DST_W). + * + * Another new thing is 'cache_preload' macro. It is used for prefetching + * data into CPU L2 cache and improve performance when dealing with large + * images which are far larger than cache size. It uses one argument + * (actually two, but they need to be the same here) - number of pixels + * in a block. Looking into 'pixman-arm-neon-asm.h' can provide some + * details about this macro. Moreover, if good performance is needed + * the code from this macro needs to be copied into '*_tail_head' macro + * and mixed with the rest of code for optimal instructions scheduling. + * We are actually doing it below. + * + * Now after all the explanations, here is the optimized code. + * Different instruction streams (originaling from '*_head', '*_tail' + * and 'cache_preload' macro) use different indentation levels for + * better readability. Actually taking the code from one of these + * indentation levels and ignoring a few VLD/VST instructions would + * result in exactly the code from '*_head', '*_tail' or 'cache_preload' + * macro! + */ + +#if 1 + +.macro pixman_composite_over_8888_0565_process_pixblock_tail_head + vqadd.u8 d16, d2, d20 + vld1.16 {d4, d5}, [DST_R, :128]! + vqadd.u8 q9, q0, q11 + vshrn.u16 d6, q2, #8 + fetch_src_pixblock + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vshll.u8 q14, d16, #8 + PF add, PF_X, PF_X, #8 + vshll.u8 q8, d19, #8 + PF tst, PF_CTL, #0xF + vsri.u8 d6, d6, #5 + PF addne, PF_X, PF_X, #8 + vmvn.8 d3, d3 + PF subne, PF_CTL, PF_CTL, #1 + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + vmull.u8 q10, d3, d6 + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vsri.u16 q14, q8, #5 + PF cmp, PF_X, ORIG_W + vshll.u8 q9, d18, #8 + vrshr.u16 q13, q10, #8 + PF subge, PF_X, PF_X, ORIG_W + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 + PF subges, PF_CTL, PF_CTL, #0x10 + vsri.u16 q14, q9, #11 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vraddhn.u16 d22, q12, q15 + vst1.16 {d28, d29}, [DST_W, :128]! +.endm + +#else + +/* If we did not care much about the performance, we would just use this... */ +.macro pixman_composite_over_8888_0565_process_pixblock_tail_head + pixman_composite_over_8888_0565_process_pixblock_tail + vst1.16 {d28, d29}, [DST_W, :128]! + vld1.16 {d4, d5}, [DST_R, :128]! + fetch_src_pixblock + pixman_composite_over_8888_0565_process_pixblock_head + cache_preload 8, 8 +.endm + +#endif + +/* + * And now the final part. We are using 'generate_composite_function' macro + * to put all the stuff together. We are specifying the name of the function + * which we want to get, number of bits per pixel for the source, mask and + * destination (0 if unused, like mask in this case). Next come some bit + * flags: + * FLAG_DST_READWRITE - tells that the destination buffer is both read + * and written, for write-only buffer we would use + * FLAG_DST_WRITEONLY flag instead + * FLAG_DEINTERLEAVE_32BPP - tells that we prefer to work with planar data + * and separate color channels for 32bpp format. + * The next things are: + * - the number of pixels processed per iteration (8 in this case, because + * that's the maximum what can fit into four 64-bit NEON registers). + * - prefetch distance, measured in pixel blocks. In this case it is 5 times + * by 8 pixels. That would be 40 pixels, or up to 160 bytes. Optimal + * prefetch distance can be selected by running some benchmarks. + * + * After that we specify some macros, these are 'default_init', + * 'default_cleanup' here which are empty (but it is possible to have custom + * init/cleanup macros to be able to save/restore some extra NEON registers + * like d8-d15 or do anything else) followed by + * 'pixman_composite_over_8888_0565_process_pixblock_head', + * 'pixman_composite_over_8888_0565_process_pixblock_tail' and + * 'pixman_composite_over_8888_0565_process_pixblock_tail_head' + * which we got implemented above. + * + * The last part is the NEON registers allocation scheme. + */ +generate_composite_function \ + pixman_composite_over_8888_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_0565_process_pixblock_head, \ + pixman_composite_over_8888_0565_process_pixblock_tail, \ + pixman_composite_over_8888_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_n_0565_process_pixblock_head + /* convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format + and put data into d6 - red, d7 - green, d30 - blue */ + vshrn.u16 d6, q2, #8 + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vsri.u8 d6, d6, #5 + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + /* now do alpha blending, storing results in 8-bit planar format + into d16 - red, d19 - green, d18 - blue */ + vmull.u8 q10, d3, d6 + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + vrshr.u16 q13, q10, #8 + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 + vraddhn.u16 d22, q12, q15 +.endm + +.macro pixman_composite_over_n_0565_process_pixblock_tail + /* ... continue alpha blending */ + vqadd.u8 d16, d2, d20 + vqadd.u8 q9, q0, q11 + /* convert the result to r5g6b5 and store it into {d28, d29} */ + vshll.u8 q14, d16, #8 + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_n_0565_process_pixblock_tail_head + pixman_composite_over_n_0565_process_pixblock_tail + vld1.16 {d4, d5}, [DST_R, :128]! + vst1.16 {d28, d29}, [DST_W, :128]! + pixman_composite_over_n_0565_process_pixblock_head + cache_preload 8, 8 +.endm + +.macro pixman_composite_over_n_0565_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d3[0]}, [DUMMY] + vdup.8 d0, d3[0] + vdup.8 d1, d3[1] + vdup.8 d2, d3[2] + vdup.8 d3, d3[3] + vmvn.8 d3, d3 /* invert source alpha */ +.endm + +generate_composite_function \ + pixman_composite_over_n_0565_asm_neon, 0, 0, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_0565_init, \ + default_cleanup, \ + pixman_composite_over_n_0565_process_pixblock_head, \ + pixman_composite_over_n_0565_process_pixblock_tail, \ + pixman_composite_over_n_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_8888_0565_process_pixblock_head + vshll.u8 q8, d1, #8 + vshll.u8 q14, d2, #8 + vshll.u8 q9, d0, #8 +.endm + +.macro pixman_composite_src_8888_0565_process_pixblock_tail + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +.macro pixman_composite_src_8888_0565_process_pixblock_tail_head + vsri.u16 q14, q8, #5 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + fetch_src_pixblock + PF addne, PF_X, PF_X, #8 + PF subne, PF_CTL, PF_CTL, #1 + vsri.u16 q14, q9, #11 + PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vshll.u8 q8, d1, #8 + vst1.16 {d28, d29}, [DST_W, :128]! + PF subge, PF_X, PF_X, ORIG_W + PF subges, PF_CTL, PF_CTL, #0x10 + vshll.u8 q14, d2, #8 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vshll.u8 q9, d0, #8 +.endm + +generate_composite_function \ + pixman_composite_src_8888_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_8888_0565_process_pixblock_head, \ + pixman_composite_src_8888_0565_process_pixblock_tail, \ + pixman_composite_src_8888_0565_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_src_0565_8888_process_pixblock_head + vshrn.u16 d30, q0, #8 + vshrn.u16 d29, q0, #3 + vsli.u16 q0, q0, #5 + vmov.u8 d31, #255 + vsri.u8 d30, d30, #5 + vsri.u8 d29, d29, #6 + vshrn.u16 d28, q0, #2 +.endm + +.macro pixman_composite_src_0565_8888_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_src_0565_8888_process_pixblock_tail_head + pixman_composite_src_0565_8888_process_pixblock_tail + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + fetch_src_pixblock + pixman_composite_src_0565_8888_process_pixblock_head + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_0565_8888_asm_neon, 16, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0565_8888_process_pixblock_head, \ + pixman_composite_src_0565_8888_process_pixblock_tail, \ + pixman_composite_src_0565_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8_8_process_pixblock_head + vqadd.u8 q14, q0, q2 + vqadd.u8 q15, q1, q3 +.endm + +.macro pixman_composite_add_8_8_process_pixblock_tail +.endm + +.macro pixman_composite_add_8_8_process_pixblock_tail_head + fetch_src_pixblock + PF add, PF_X, PF_X, #32 + PF tst, PF_CTL, #0xF + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + PF addne, PF_X, PF_X, #32 + PF subne, PF_CTL, PF_CTL, #1 + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + PF subge, PF_X, PF_X, ORIG_W + PF subges, PF_CTL, PF_CTL, #0x10 + vqadd.u8 q14, q0, q2 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vqadd.u8 q15, q1, q3 +.endm + +generate_composite_function \ + pixman_composite_add_8_8_asm_neon, 8, 0, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_process_pixblock_tail, \ + pixman_composite_add_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8888_8888_process_pixblock_tail_head + fetch_src_pixblock + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + vld1.32 {d4, d5, d6, d7}, [DST_R, :128]! + PF addne, PF_X, PF_X, #8 + PF subne, PF_CTL, PF_CTL, #1 + vst1.32 {d28, d29, d30, d31}, [DST_W, :128]! + PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + PF subge, PF_X, PF_X, ORIG_W + PF subges, PF_CTL, PF_CTL, #0x10 + vqadd.u8 q14, q0, q2 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vqadd.u8 q15, q1, q3 +.endm + +generate_composite_function \ + pixman_composite_add_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_process_pixblock_tail, \ + pixman_composite_add_8888_8888_process_pixblock_tail_head + +generate_composite_function_single_scanline \ + pixman_composite_scanline_add_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_process_pixblock_tail, \ + pixman_composite_add_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_out_reverse_8888_8888_process_pixblock_head + vmvn.8 d24, d3 /* get inverted alpha */ + /* do alpha blending */ + vmull.u8 q8, d24, d4 + vmull.u8 q9, d24, d5 + vmull.u8 q10, d24, d6 + vmull.u8 q11, d24, d7 +.endm + +.macro pixman_composite_out_reverse_8888_8888_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 +.endm + +.macro pixman_composite_out_reverse_8888_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q14, q8, #8 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + PF addne, PF_X, PF_X, #8 + PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + PF cmp, PF_X, ORIG_W + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + fetch_src_pixblock + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vmvn.8 d22, d3 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q8, d22, d4 + PF subsge, PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d22, d5 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vmull.u8 q10, d22, d6 + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vmull.u8 q11, d22, d7 +.endm + +generate_composite_function_single_scanline \ + pixman_composite_scanline_out_reverse_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_out_reverse_8888_8888_process_pixblock_head, \ + pixman_composite_out_reverse_8888_8888_process_pixblock_tail, \ + pixman_composite_out_reverse_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_8888_8888_process_pixblock_head + pixman_composite_out_reverse_8888_8888_process_pixblock_head +.endm + +.macro pixman_composite_over_8888_8888_process_pixblock_tail + pixman_composite_out_reverse_8888_8888_process_pixblock_tail + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +.macro pixman_composite_over_8888_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q14, q8, #8 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + PF addne, PF_X, PF_X, #8 + PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + PF cmp, PF_X, ORIG_W + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + fetch_src_pixblock + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vmvn.8 d22, d3 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q8, d22, d4 + PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d22, d5 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vmull.u8 q10, d22, d6 + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vmull.u8 q11, d22, d7 +.endm + +generate_composite_function \ + pixman_composite_over_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_process_pixblock_tail_head + +generate_composite_function_single_scanline \ + pixman_composite_scanline_over_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8888_process_pixblock_head + /* deinterleaved source pixels in {d0, d1, d2, d3} */ + /* inverted alpha in {d24} */ + /* destination pixels in {d4, d5, d6, d7} */ + vmull.u8 q8, d24, d4 + vmull.u8 q9, d24, d5 + vmull.u8 q10, d24, d6 + vmull.u8 q11, d24, d7 +.endm + +.macro pixman_composite_over_n_8888_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q2, q10, #8 + vrshr.u16 q3, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q2, q10 + vraddhn.u16 d31, q3, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +.macro pixman_composite_over_n_8888_process_pixblock_tail_head + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q2, q10, #8 + vrshr.u16 q3, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q2, q10 + vraddhn.u16 d31, q3, q11 + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vqadd.u8 q14, q0, q14 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0x0F + PF addne, PF_X, PF_X, #8 + PF subne, PF_CTL, PF_CTL, #1 + vqadd.u8 q15, q1, q15 + PF cmp, PF_X, ORIG_W + vmull.u8 q8, d24, d4 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vmull.u8 q9, d24, d5 + PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q10, d24, d6 + PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q11, d24, d7 + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +.macro pixman_composite_over_n_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d3[0]}, [DUMMY] + vdup.8 d0, d3[0] + vdup.8 d1, d3[1] + vdup.8 d2, d3[2] + vdup.8 d3, d3[3] + vmvn.8 d24, d3 /* get inverted alpha */ +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8888_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_n_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_reverse_n_8888_process_pixblock_tail_head + vrshr.u16 q14, q8, #8 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + PF addne, PF_X, PF_X, #8 + PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + PF cmp, PF_X, ORIG_W + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + vld4.8 {d0, d1, d2, d3}, [DST_R, :128]! + vmvn.8 d22, d3 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q8, d22, d4 + PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d22, d5 + vmull.u8 q10, d22, d6 + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vmull.u8 q11, d22, d7 +.endm + +.macro pixman_composite_over_reverse_n_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d7[0]}, [DUMMY] + vdup.8 d4, d7[0] + vdup.8 d5, d7[1] + vdup.8 d6, d7[2] + vdup.8 d7, d7[3] +.endm + +generate_composite_function \ + pixman_composite_over_reverse_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_reverse_n_8888_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_reverse_n_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 4, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_8888_8_0565_process_pixblock_head + vmull.u8 q0, d24, d8 /* IN for SRC pixels (part1) */ + vmull.u8 q1, d24, d9 + vmull.u8 q6, d24, d10 + vmull.u8 q7, d24, d11 + vshrn.u16 d6, q2, #8 /* convert DST_R data to 32-bpp (part1) */ + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vrshr.u16 q8, q0, #8 /* IN for SRC pixels (part2) */ + vrshr.u16 q9, q1, #8 + vrshr.u16 q10, q6, #8 + vrshr.u16 q11, q7, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q9 + vraddhn.u16 d2, q6, q10 + vraddhn.u16 d3, q7, q11 + vsri.u8 d6, d6, #5 /* convert DST_R data to 32-bpp (part2) */ + vsri.u8 d7, d7, #6 + vmvn.8 d3, d3 + vshrn.u16 d30, q2, #2 + vmull.u8 q8, d3, d6 /* now do alpha blending */ + vmull.u8 q9, d3, d7 + vmull.u8 q10, d3, d30 +.endm + +.macro pixman_composite_over_8888_8_0565_process_pixblock_tail + /* 3 cycle bubble (after vmull.u8) */ + vrshr.u16 q13, q8, #8 + vrshr.u16 q11, q9, #8 + vrshr.u16 q15, q10, #8 + vraddhn.u16 d16, q8, q13 + vraddhn.u16 d27, q9, q11 + vraddhn.u16 d26, q10, q15 + vqadd.u8 d16, d2, d16 + /* 1 cycle bubble */ + vqadd.u8 q9, q0, q13 + vshll.u8 q14, d16, #8 /* convert to 16bpp */ + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + /* 1 cycle bubble */ + vsri.u16 q14, q9, #11 +.endm + +.macro pixman_composite_over_8888_8_0565_process_pixblock_tail_head + vld1.16 {d4, d5}, [DST_R, :128]! + vshrn.u16 d6, q2, #8 + fetch_mask_pixblock + vshrn.u16 d7, q2, #3 + fetch_src_pixblock + vmull.u8 q6, d24, d10 + vrshr.u16 q13, q8, #8 + vrshr.u16 q11, q9, #8 + vrshr.u16 q15, q10, #8 + vraddhn.u16 d16, q8, q13 + vraddhn.u16 d27, q9, q11 + vraddhn.u16 d26, q10, q15 + vqadd.u8 d16, d2, d16 + vmull.u8 q1, d24, d9 + vqadd.u8 q9, q0, q13 + vshll.u8 q14, d16, #8 + vmull.u8 q0, d24, d8 + vshll.u8 q8, d19, #8 + vshll.u8 q9, d18, #8 + vsri.u16 q14, q8, #5 + vmull.u8 q7, d24, d11 + vsri.u16 q14, q9, #11 + + cache_preload 8, 8 + + vsli.u16 q2, q2, #5 + vrshr.u16 q8, q0, #8 + vrshr.u16 q9, q1, #8 + vrshr.u16 q10, q6, #8 + vrshr.u16 q11, q7, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q9 + vraddhn.u16 d2, q6, q10 + vraddhn.u16 d3, q7, q11 + vsri.u8 d6, d6, #5 + vsri.u8 d7, d7, #6 + vmvn.8 d3, d3 + vshrn.u16 d30, q2, #2 + vst1.16 {d28, d29}, [DST_W, :128]! + vmull.u8 q8, d3, d6 + vmull.u8 q9, d3, d7 + vmull.u8 q10, d3, d30 +.endm + +generate_composite_function \ + pixman_composite_over_8888_8_0565_asm_neon, 32, 8, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_8_0565_process_pixblock_head, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +/* + * This function needs a special initialization of solid mask. + * Solid source pixel data is fetched from stack at ARGS_STACK_OFFSET + * offset, split into color components and replicated in d8-d11 + * registers. Additionally, this function needs all the NEON registers, + * so it has to save d8-d15 registers which are callee saved according + * to ABI. These registers are restored from 'cleanup' macro. All the + * other NEON registers are caller saved, so can be clobbered freely + * without introducing any problems. + */ +.macro pixman_composite_over_n_8_0565_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d8, d11[0] + vdup.8 d9, d11[1] + vdup.8 d10, d11[2] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_over_n_8_0565_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_n_8_0565_asm_neon, 0, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8_0565_init, \ + pixman_composite_over_n_8_0565_cleanup, \ + pixman_composite_over_8888_8_0565_process_pixblock_head, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_8888_n_0565_init + add DUMMY, sp, #(ARGS_STACK_OFFSET + 8) + vpush {d8-d15} + vld1.32 {d24[0]}, [DUMMY] + vdup.8 d24, d24[3] +.endm + +.macro pixman_composite_over_8888_n_0565_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_8888_n_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_8888_n_0565_init, \ + pixman_composite_over_8888_n_0565_cleanup, \ + pixman_composite_over_8888_8_0565_process_pixblock_head, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0565_0565_process_pixblock_head +.endm + +.macro pixman_composite_src_0565_0565_process_pixblock_tail +.endm + +.macro pixman_composite_src_0565_0565_process_pixblock_tail_head + vst1.16 {d0, d1, d2, d3}, [DST_W, :128]! + fetch_src_pixblock + cache_preload 16, 16 +.endm + +generate_composite_function \ + pixman_composite_src_0565_0565_asm_neon, 16, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 16, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0565_0565_process_pixblock_head, \ + pixman_composite_src_0565_0565_process_pixblock_tail, \ + pixman_composite_src_0565_0565_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8_process_pixblock_head +.endm + +.macro pixman_composite_src_n_8_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_8_process_pixblock_tail_head + vst1.8 {d0, d1, d2, d3}, [DST_W, :128]! +.endm + +.macro pixman_composite_src_n_8_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d0[0]}, [DUMMY] + vsli.u64 d0, d0, #8 + vsli.u64 d0, d0, #16 + vsli.u64 d0, d0, #32 + vorr d1, d0, d0 + vorr q1, q0, q0 +.endm + +.macro pixman_composite_src_n_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8_asm_neon, 0, 0, 8, \ + FLAG_DST_WRITEONLY, \ + 32, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_8_init, \ + pixman_composite_src_n_8_cleanup, \ + pixman_composite_src_n_8_process_pixblock_head, \ + pixman_composite_src_n_8_process_pixblock_tail, \ + pixman_composite_src_n_8_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_0565_process_pixblock_head +.endm + +.macro pixman_composite_src_n_0565_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_0565_process_pixblock_tail_head + vst1.16 {d0, d1, d2, d3}, [DST_W, :128]! +.endm + +.macro pixman_composite_src_n_0565_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d0[0]}, [DUMMY] + vsli.u64 d0, d0, #16 + vsli.u64 d0, d0, #32 + vorr d1, d0, d0 + vorr q1, q0, q0 +.endm + +.macro pixman_composite_src_n_0565_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_0565_asm_neon, 0, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 16, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_0565_init, \ + pixman_composite_src_n_0565_cleanup, \ + pixman_composite_src_n_0565_process_pixblock_head, \ + pixman_composite_src_n_0565_process_pixblock_tail, \ + pixman_composite_src_n_0565_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8888_process_pixblock_head +.endm + +.macro pixman_composite_src_n_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_8888_process_pixblock_tail_head + vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! +.endm + +.macro pixman_composite_src_n_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d0[0]}, [DUMMY] + vsli.u64 d0, d0, #32 + vorr d1, d0, d0 + vorr q1, q0, q0 +.endm + +.macro pixman_composite_src_n_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_8888_init, \ + pixman_composite_src_n_8888_cleanup, \ + pixman_composite_src_n_8888_process_pixblock_head, \ + pixman_composite_src_n_8888_process_pixblock_tail, \ + pixman_composite_src_n_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_8888_8888_process_pixblock_head +.endm + +.macro pixman_composite_src_8888_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_8888_8888_process_pixblock_tail_head + vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! + fetch_src_pixblock + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_8888_8888_process_pixblock_head, \ + pixman_composite_src_8888_8888_process_pixblock_tail, \ + pixman_composite_src_8888_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_x888_8888_process_pixblock_head + vorr q0, q0, q2 + vorr q1, q1, q2 +.endm + +.macro pixman_composite_src_x888_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_x888_8888_process_pixblock_tail_head + vst1.32 {d0, d1, d2, d3}, [DST_W, :128]! + fetch_src_pixblock + vorr q0, q0, q2 + vorr q1, q1, q2 + cache_preload 8, 8 +.endm + +.macro pixman_composite_src_x888_8888_init + vmov.u8 q2, #0xFF + vshl.u32 q2, q2, #24 +.endm + +generate_composite_function \ + pixman_composite_src_x888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + pixman_composite_src_x888_8888_init, \ + default_cleanup, \ + pixman_composite_src_x888_8888_process_pixblock_head, \ + pixman_composite_src_x888_8888_process_pixblock_tail, \ + pixman_composite_src_x888_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8_8888_process_pixblock_head + /* expecting solid source in {d0, d1, d2, d3} */ + /* mask is in d24 (d25, d26, d27 are unused) */ + + /* in */ + vmull.u8 q8, d24, d0 + vmull.u8 q9, d24, d1 + vmull.u8 q10, d24, d2 + vmull.u8 q11, d24, d3 + vrsra.u16 q8, q8, #8 + vrsra.u16 q9, q9, #8 + vrsra.u16 q10, q10, #8 + vrsra.u16 q11, q11, #8 +.endm + +.macro pixman_composite_src_n_8_8888_process_pixblock_tail + vrshrn.u16 d28, q8, #8 + vrshrn.u16 d29, q9, #8 + vrshrn.u16 d30, q10, #8 + vrshrn.u16 d31, q11, #8 +.endm + +.macro pixman_composite_src_n_8_8888_process_pixblock_tail_head + fetch_mask_pixblock + PF add, PF_X, PF_X, #8 + vrshrn.u16 d28, q8, #8 + PF tst, PF_CTL, #0x0F + vrshrn.u16 d29, q9, #8 + PF addne, PF_X, PF_X, #8 + vrshrn.u16 d30, q10, #8 + PF subne, PF_CTL, PF_CTL, #1 + vrshrn.u16 d31, q11, #8 + PF cmp, PF_X, ORIG_W + vmull.u8 q8, d24, d0 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] + vmull.u8 q9, d24, d1 + PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q10, d24, d2 + PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q11, d24, d3 + PF ldrgeb, DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + vrsra.u16 q8, q8, #8 + vrsra.u16 q9, q9, #8 + vrsra.u16 q10, q10, #8 + vrsra.u16 q11, q11, #8 +.endm + +.macro pixman_composite_src_n_8_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d3[0]}, [DUMMY] + vdup.8 d0, d3[0] + vdup.8 d1, d3[1] + vdup.8 d2, d3[2] + vdup.8 d3, d3[3] +.endm + +.macro pixman_composite_src_n_8_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8_8888_asm_neon, 0, 8, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_src_n_8_8888_init, \ + pixman_composite_src_n_8_8888_cleanup, \ + pixman_composite_src_n_8_8888_process_pixblock_head, \ + pixman_composite_src_n_8_8888_process_pixblock_tail, \ + pixman_composite_src_n_8_8888_process_pixblock_tail_head, \ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8_8_process_pixblock_head + vmull.u8 q0, d24, d16 + vmull.u8 q1, d25, d16 + vmull.u8 q2, d26, d16 + vmull.u8 q3, d27, d16 + vrsra.u16 q0, q0, #8 + vrsra.u16 q1, q1, #8 + vrsra.u16 q2, q2, #8 + vrsra.u16 q3, q3, #8 +.endm + +.macro pixman_composite_src_n_8_8_process_pixblock_tail + vrshrn.u16 d28, q0, #8 + vrshrn.u16 d29, q1, #8 + vrshrn.u16 d30, q2, #8 + vrshrn.u16 d31, q3, #8 +.endm + +.macro pixman_composite_src_n_8_8_process_pixblock_tail_head + fetch_mask_pixblock + PF add, PF_X, PF_X, #8 + vrshrn.u16 d28, q0, #8 + PF tst, PF_CTL, #0x0F + vrshrn.u16 d29, q1, #8 + PF addne, PF_X, PF_X, #8 + vrshrn.u16 d30, q2, #8 + PF subne, PF_CTL, PF_CTL, #1 + vrshrn.u16 d31, q3, #8 + PF cmp, PF_X, ORIG_W + vmull.u8 q0, d24, d16 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] + vmull.u8 q1, d25, d16 + PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q2, d26, d16 + PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q3, d27, d16 + PF ldrgeb, DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + vrsra.u16 q0, q0, #8 + vrsra.u16 q1, q1, #8 + vrsra.u16 q2, q2, #8 + vrsra.u16 q3, q3, #8 +.endm + +.macro pixman_composite_src_n_8_8_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d16[0]}, [DUMMY] + vdup.8 d16, d16[3] +.endm + +.macro pixman_composite_src_n_8_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8_8_asm_neon, 0, 8, 8, \ + FLAG_DST_WRITEONLY, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_src_n_8_8_init, \ + pixman_composite_src_n_8_8_cleanup, \ + pixman_composite_src_n_8_8_process_pixblock_head, \ + pixman_composite_src_n_8_8_process_pixblock_tail, \ + pixman_composite_src_n_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8_8888_process_pixblock_head + /* expecting deinterleaved source data in {d8, d9, d10, d11} */ + /* d8 - blue, d9 - green, d10 - red, d11 - alpha */ + /* and destination data in {d4, d5, d6, d7} */ + /* mask is in d24 (d25, d26, d27 are unused) */ + + /* in */ + vmull.u8 q6, d24, d8 + vmull.u8 q7, d24, d9 + vmull.u8 q8, d24, d10 + vmull.u8 q9, d24, d11 + vrshr.u16 q10, q6, #8 + vrshr.u16 q11, q7, #8 + vrshr.u16 q12, q8, #8 + vrshr.u16 q13, q9, #8 + vraddhn.u16 d0, q6, q10 + vraddhn.u16 d1, q7, q11 + vraddhn.u16 d2, q8, q12 + vraddhn.u16 d3, q9, q13 + vmvn.8 d25, d3 /* get inverted alpha */ + /* source: d0 - blue, d1 - green, d2 - red, d3 - alpha */ + /* destination: d4 - blue, d5 - green, d6 - red, d7 - alpha */ + /* now do alpha blending */ + vmull.u8 q8, d25, d4 + vmull.u8 q9, d25, d5 + vmull.u8 q10, d25, d6 + vmull.u8 q11, d25, d7 +.endm + +.macro pixman_composite_over_n_8_8888_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q6, q10, #8 + vrshr.u16 q7, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q6, q10 + vraddhn.u16 d31, q7, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +.macro pixman_composite_over_n_8_8888_process_pixblock_tail_head + vrshr.u16 q14, q8, #8 + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q15, q9, #8 + fetch_mask_pixblock + vrshr.u16 q6, q10, #8 + PF add, PF_X, PF_X, #8 + vrshr.u16 q7, q11, #8 + PF tst, PF_CTL, #0x0F + vraddhn.u16 d28, q14, q8 + PF addne, PF_X, PF_X, #8 + vraddhn.u16 d29, q15, q9 + PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d30, q6, q10 + PF cmp, PF_X, ORIG_W + vraddhn.u16 d31, q7, q11 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vmull.u8 q6, d24, d8 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] + vmull.u8 q7, d24, d9 + PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q8, d24, d10 + PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d24, d11 + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vqadd.u8 q14, q0, q14 + PF ldrgeb, DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! + vqadd.u8 q15, q1, q15 + vrshr.u16 q10, q6, #8 + vrshr.u16 q11, q7, #8 + vrshr.u16 q12, q8, #8 + vrshr.u16 q13, q9, #8 + vraddhn.u16 d0, q6, q10 + vraddhn.u16 d1, q7, q11 + vraddhn.u16 d2, q8, q12 + vraddhn.u16 d3, q9, q13 + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + vmvn.8 d25, d3 + vmull.u8 q8, d25, d4 + vmull.u8 q9, d25, d5 + vmull.u8 q10, d25, d6 + vmull.u8 q11, d25, d7 +.endm + +.macro pixman_composite_over_n_8_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d8, d11[0] + vdup.8 d9, d11[1] + vdup.8 d10, d11[2] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_over_n_8_8888_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_n_8_8888_asm_neon, 0, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8_8888_init, \ + pixman_composite_over_n_8_8888_cleanup, \ + pixman_composite_over_n_8_8888_process_pixblock_head, \ + pixman_composite_over_n_8_8888_process_pixblock_tail, \ + pixman_composite_over_n_8_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8_8_process_pixblock_head + vmull.u8 q0, d24, d8 + vmull.u8 q1, d25, d8 + vmull.u8 q6, d26, d8 + vmull.u8 q7, d27, d8 + vrshr.u16 q10, q0, #8 + vrshr.u16 q11, q1, #8 + vrshr.u16 q12, q6, #8 + vrshr.u16 q13, q7, #8 + vraddhn.u16 d0, q0, q10 + vraddhn.u16 d1, q1, q11 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d3, q7, q13 + vmvn.8 q12, q0 + vmvn.8 q13, q1 + vmull.u8 q8, d24, d4 + vmull.u8 q9, d25, d5 + vmull.u8 q10, d26, d6 + vmull.u8 q11, d27, d7 +.endm + +.macro pixman_composite_over_n_8_8_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_n_8_8_process_pixblock_tail_head + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + pixman_composite_over_n_8_8_process_pixblock_tail + fetch_mask_pixblock + cache_preload 32, 32 + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + pixman_composite_over_n_8_8_process_pixblock_head +.endm + +.macro pixman_composite_over_n_8_8_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d8[0]}, [DUMMY] + vdup.8 d8, d8[3] +.endm + +.macro pixman_composite_over_n_8_8_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_n_8_8_asm_neon, 0, 8, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8_8_init, \ + pixman_composite_over_n_8_8_cleanup, \ + pixman_composite_over_n_8_8_process_pixblock_head, \ + pixman_composite_over_n_8_8_process_pixblock_tail, \ + pixman_composite_over_n_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_head + /* + * 'combine_mask_ca' replacement + * + * input: solid src (n) in {d8, d9, d10, d11} + * dest in {d4, d5, d6, d7 } + * mask in {d24, d25, d26, d27} + * output: updated src in {d0, d1, d2, d3 } + * updated mask in {d24, d25, d26, d3 } + */ + vmull.u8 q0, d24, d8 + vmull.u8 q1, d25, d9 + vmull.u8 q6, d26, d10 + vmull.u8 q7, d27, d11 + vmull.u8 q9, d11, d25 + vmull.u8 q12, d11, d24 + vmull.u8 q13, d11, d26 + vrshr.u16 q8, q0, #8 + vrshr.u16 q10, q1, #8 + vrshr.u16 q11, q6, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q10 + vraddhn.u16 d2, q6, q11 + vrshr.u16 q11, q12, #8 + vrshr.u16 q8, q9, #8 + vrshr.u16 q6, q13, #8 + vrshr.u16 q10, q7, #8 + vraddhn.u16 d24, q12, q11 + vraddhn.u16 d25, q9, q8 + vraddhn.u16 d26, q13, q6 + vraddhn.u16 d3, q7, q10 + /* + * 'combine_over_ca' replacement + * + * output: updated dest in {d28, d29, d30, d31} + */ + vmvn.8 q12, q12 + vmvn.8 d26, d26 + vmull.u8 q8, d24, d4 + vmull.u8 q9, d25, d5 + vmvn.8 d27, d3 + vmull.u8 q10, d26, d6 + vmull.u8 q11, d27, d7 +.endm + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_tail + /* ... continue 'combine_over_ca' replacement */ + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q6, q10, #8 + vrshr.u16 q7, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q6, q10 + vraddhn.u16 d31, q7, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_tail_head + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q6, q10, #8 + vrshr.u16 q7, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q6, q10 + vraddhn.u16 d31, q7, q11 + fetch_mask_pixblock + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + cache_preload 8, 8 + pixman_composite_over_n_8888_8888_ca_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +.macro pixman_composite_over_n_8888_8888_ca_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d8, d11[0] + vdup.8 d9, d11[1] + vdup.8 d10, d11[2] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_over_n_8888_8888_ca_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_8888_ca_asm_neon, 0, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8888_8888_ca_init, \ + pixman_composite_over_n_8888_8888_ca_cleanup, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_head, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_tail, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8888_0565_ca_process_pixblock_head + /* + * 'combine_mask_ca' replacement + * + * input: solid src (n) in {d8, d9, d10, d11} [B, G, R, A] + * mask in {d24, d25, d26} [B, G, R] + * output: updated src in {d0, d1, d2 } [B, G, R] + * updated mask in {d24, d25, d26} [B, G, R] + */ + vmull.u8 q0, d24, d8 + vmull.u8 q1, d25, d9 + vmull.u8 q6, d26, d10 + vmull.u8 q9, d11, d25 + vmull.u8 q12, d11, d24 + vmull.u8 q13, d11, d26 + vrshr.u16 q8, q0, #8 + vrshr.u16 q10, q1, #8 + vrshr.u16 q11, q6, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q10 + vraddhn.u16 d2, q6, q11 + vrshr.u16 q11, q12, #8 + vrshr.u16 q8, q9, #8 + vrshr.u16 q6, q13, #8 + vraddhn.u16 d24, q12, q11 + vraddhn.u16 d25, q9, q8 + /* + * convert 8 r5g6b5 pixel data from {d4, d5} to planar 8-bit format + * and put data into d16 - blue, d17 - green, d18 - red + */ + vshrn.u16 d17, q2, #3 + vshrn.u16 d18, q2, #8 + vraddhn.u16 d26, q13, q6 + vsli.u16 q2, q2, #5 + vsri.u8 d18, d18, #5 + vsri.u8 d17, d17, #6 + /* + * 'combine_over_ca' replacement + * + * output: updated dest in d16 - blue, d17 - green, d18 - red + */ + vmvn.8 q12, q12 + vshrn.u16 d16, q2, #2 + vmvn.8 d26, d26 + vmull.u8 q6, d16, d24 + vmull.u8 q7, d17, d25 + vmull.u8 q11, d18, d26 +.endm + +.macro pixman_composite_over_n_8888_0565_ca_process_pixblock_tail + /* ... continue 'combine_over_ca' replacement */ + vrshr.u16 q10, q6, #8 + vrshr.u16 q14, q7, #8 + vrshr.u16 q15, q11, #8 + vraddhn.u16 d16, q10, q6 + vraddhn.u16 d17, q14, q7 + vraddhn.u16 d18, q15, q11 + vqadd.u8 q8, q0, q8 + vqadd.u8 d18, d2, d18 + /* + * convert the results in d16, d17, d18 to r5g6b5 and store + * them into {d28, d29} + */ + vshll.u8 q14, d18, #8 + vshll.u8 q10, d17, #8 + vshll.u8 q15, d16, #8 + vsri.u16 q14, q10, #5 + vsri.u16 q14, q15, #11 +.endm + +.macro pixman_composite_over_n_8888_0565_ca_process_pixblock_tail_head + fetch_mask_pixblock + vrshr.u16 q10, q6, #8 + vrshr.u16 q14, q7, #8 + vld1.16 {d4, d5}, [DST_R, :128]! + vrshr.u16 q15, q11, #8 + vraddhn.u16 d16, q10, q6 + vraddhn.u16 d17, q14, q7 + vraddhn.u16 d22, q15, q11 + /* process_pixblock_head */ + /* + * 'combine_mask_ca' replacement + * + * input: solid src (n) in {d8, d9, d10, d11} [B, G, R, A] + * mask in {d24, d25, d26} [B, G, R] + * output: updated src in {d0, d1, d2 } [B, G, R] + * updated mask in {d24, d25, d26} [B, G, R] + */ + vmull.u8 q6, d26, d10 + vqadd.u8 q8, q0, q8 + vmull.u8 q0, d24, d8 + vqadd.u8 d22, d2, d22 + vmull.u8 q1, d25, d9 + /* + * convert the result in d16, d17, d22 to r5g6b5 and store + * it into {d28, d29} + */ + vshll.u8 q14, d22, #8 + vshll.u8 q10, d17, #8 + vshll.u8 q15, d16, #8 + vmull.u8 q9, d11, d25 + vsri.u16 q14, q10, #5 + vmull.u8 q12, d11, d24 + vmull.u8 q13, d11, d26 + vsri.u16 q14, q15, #11 + cache_preload 8, 8 + vrshr.u16 q8, q0, #8 + vrshr.u16 q10, q1, #8 + vrshr.u16 q11, q6, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q10 + vraddhn.u16 d2, q6, q11 + vrshr.u16 q11, q12, #8 + vrshr.u16 q8, q9, #8 + vrshr.u16 q6, q13, #8 + vraddhn.u16 d24, q12, q11 + vraddhn.u16 d25, q9, q8 + /* + * convert 8 r5g6b5 pixel data from {d4, d5} to planar + * 8-bit format and put data into d16 - blue, d17 - green, + * d18 - red + */ + vshrn.u16 d17, q2, #3 + vshrn.u16 d18, q2, #8 + vraddhn.u16 d26, q13, q6 + vsli.u16 q2, q2, #5 + vsri.u8 d17, d17, #6 + vsri.u8 d18, d18, #5 + /* + * 'combine_over_ca' replacement + * + * output: updated dest in d16 - blue, d17 - green, d18 - red + */ + vmvn.8 q12, q12 + vshrn.u16 d16, q2, #2 + vmvn.8 d26, d26 + vmull.u8 q7, d17, d25 + vmull.u8 q6, d16, d24 + vmull.u8 q11, d18, d26 + vst1.16 {d28, d29}, [DST_W, :128]! +.endm + +.macro pixman_composite_over_n_8888_0565_ca_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d8, d11[0] + vdup.8 d9, d11[1] + vdup.8 d10, d11[2] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_over_n_8888_0565_ca_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_0565_ca_asm_neon, 0, 32, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8888_0565_ca_init, \ + pixman_composite_over_n_8888_0565_ca_cleanup, \ + pixman_composite_over_n_8888_0565_ca_process_pixblock_head, \ + pixman_composite_over_n_8888_0565_ca_process_pixblock_tail, \ + pixman_composite_over_n_8888_0565_ca_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_in_n_8_process_pixblock_head + /* expecting source data in {d0, d1, d2, d3} */ + /* and destination data in {d4, d5, d6, d7} */ + vmull.u8 q8, d4, d3 + vmull.u8 q9, d5, d3 + vmull.u8 q10, d6, d3 + vmull.u8 q11, d7, d3 +.endm + +.macro pixman_composite_in_n_8_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d28, q8, q14 + vraddhn.u16 d29, q9, q15 + vraddhn.u16 d30, q10, q12 + vraddhn.u16 d31, q11, q13 +.endm + +.macro pixman_composite_in_n_8_process_pixblock_tail_head + pixman_composite_in_n_8_process_pixblock_tail + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + cache_preload 32, 32 + pixman_composite_in_n_8_process_pixblock_head + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +.macro pixman_composite_in_n_8_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d3[0]}, [DUMMY] + vdup.8 d3, d3[3] +.endm + +.macro pixman_composite_in_n_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_in_n_8_asm_neon, 0, 0, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_in_n_8_init, \ + pixman_composite_in_n_8_cleanup, \ + pixman_composite_in_n_8_process_pixblock_head, \ + pixman_composite_in_n_8_process_pixblock_tail, \ + pixman_composite_in_n_8_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +.macro pixman_composite_add_n_8_8_process_pixblock_head + /* expecting source data in {d8, d9, d10, d11} */ + /* d8 - blue, d9 - green, d10 - red, d11 - alpha */ + /* and destination data in {d4, d5, d6, d7} */ + /* mask is in d24, d25, d26, d27 */ + vmull.u8 q0, d24, d11 + vmull.u8 q1, d25, d11 + vmull.u8 q6, d26, d11 + vmull.u8 q7, d27, d11 + vrshr.u16 q10, q0, #8 + vrshr.u16 q11, q1, #8 + vrshr.u16 q12, q6, #8 + vrshr.u16 q13, q7, #8 + vraddhn.u16 d0, q0, q10 + vraddhn.u16 d1, q1, q11 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d3, q7, q13 + vqadd.u8 q14, q0, q2 + vqadd.u8 q15, q1, q3 +.endm + +.macro pixman_composite_add_n_8_8_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_n_8_8_process_pixblock_tail_head + pixman_composite_add_n_8_8_process_pixblock_tail + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + fetch_mask_pixblock + cache_preload 32, 32 + pixman_composite_add_n_8_8_process_pixblock_head +.endm + +.macro pixman_composite_add_n_8_8_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vpush {d8-d15} + vld1.32 {d11[0]}, [DUMMY] + vdup.8 d11, d11[3] +.endm + +.macro pixman_composite_add_n_8_8_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_add_n_8_8_asm_neon, 0, 8, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_n_8_8_init, \ + pixman_composite_add_n_8_8_cleanup, \ + pixman_composite_add_n_8_8_process_pixblock_head, \ + pixman_composite_add_n_8_8_process_pixblock_tail, \ + pixman_composite_add_n_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8_8_8_process_pixblock_head + /* expecting source data in {d0, d1, d2, d3} */ + /* destination data in {d4, d5, d6, d7} */ + /* mask in {d24, d25, d26, d27} */ + vmull.u8 q8, d24, d0 + vmull.u8 q9, d25, d1 + vmull.u8 q10, d26, d2 + vmull.u8 q11, d27, d3 + vrshr.u16 q0, q8, #8 + vrshr.u16 q1, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d0, q0, q8 + vraddhn.u16 d1, q1, q9 + vraddhn.u16 d2, q12, q10 + vraddhn.u16 d3, q13, q11 + vqadd.u8 q14, q0, q2 + vqadd.u8 q15, q1, q3 +.endm + +.macro pixman_composite_add_8_8_8_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_8_8_8_process_pixblock_tail_head + pixman_composite_add_8_8_8_process_pixblock_tail + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! + fetch_mask_pixblock + fetch_src_pixblock + cache_preload 32, 32 + pixman_composite_add_8_8_8_process_pixblock_head +.endm + +.macro pixman_composite_add_8_8_8_init +.endm + +.macro pixman_composite_add_8_8_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_add_8_8_8_asm_neon, 8, 8, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_8_8_8_init, \ + pixman_composite_add_8_8_8_cleanup, \ + pixman_composite_add_8_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_8_process_pixblock_tail, \ + pixman_composite_add_8_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8888_8888_8888_process_pixblock_head + /* expecting source data in {d0, d1, d2, d3} */ + /* destination data in {d4, d5, d6, d7} */ + /* mask in {d24, d25, d26, d27} */ + vmull.u8 q8, d27, d0 + vmull.u8 q9, d27, d1 + vmull.u8 q10, d27, d2 + vmull.u8 q11, d27, d3 + /* 1 cycle bubble */ + vrsra.u16 q8, q8, #8 + vrsra.u16 q9, q9, #8 + vrsra.u16 q10, q10, #8 + vrsra.u16 q11, q11, #8 +.endm + +.macro pixman_composite_add_8888_8888_8888_process_pixblock_tail + /* 2 cycle bubble */ + vrshrn.u16 d28, q8, #8 + vrshrn.u16 d29, q9, #8 + vrshrn.u16 d30, q10, #8 + vrshrn.u16 d31, q11, #8 + vqadd.u8 q14, q2, q14 + /* 1 cycle bubble */ + vqadd.u8 q15, q3, q15 +.endm + +.macro pixman_composite_add_8888_8888_8888_process_pixblock_tail_head + fetch_src_pixblock + vrshrn.u16 d28, q8, #8 + fetch_mask_pixblock + vrshrn.u16 d29, q9, #8 + vmull.u8 q8, d27, d0 + vrshrn.u16 d30, q10, #8 + vmull.u8 q9, d27, d1 + vrshrn.u16 d31, q11, #8 + vmull.u8 q10, d27, d2 + vqadd.u8 q14, q2, q14 + vmull.u8 q11, d27, d3 + vqadd.u8 q15, q3, q15 + vrsra.u16 q8, q8, #8 + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrsra.u16 q9, q9, #8 + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + vrsra.u16 q10, q10, #8 + + cache_preload 8, 8 + + vrsra.u16 q11, q11, #8 +.endm + +generate_composite_function \ + pixman_composite_add_8888_8888_8888_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head + +generate_composite_function_single_scanline \ + pixman_composite_scanline_add_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +generate_composite_function \ + pixman_composite_add_8888_8_8888_asm_neon, 32, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 27 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_add_n_8_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d3[0]}, [DUMMY] + vdup.8 d0, d3[0] + vdup.8 d1, d3[1] + vdup.8 d2, d3[2] + vdup.8 d3, d3[3] +.endm + +.macro pixman_composite_add_n_8_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_add_n_8_8888_asm_neon, 0, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_n_8_8888_init, \ + pixman_composite_add_n_8_8888_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 27 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_add_8888_n_8888_init + add DUMMY, sp, #(ARGS_STACK_OFFSET + 8) + vld1.32 {d27[0]}, [DUMMY] + vdup.8 d27, d27[3] +.endm + +.macro pixman_composite_add_8888_n_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_add_8888_n_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_8888_n_8888_init, \ + pixman_composite_add_8888_n_8888_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 27 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_out_reverse_8888_n_8888_process_pixblock_head + /* expecting source data in {d0, d1, d2, d3} */ + /* destination data in {d4, d5, d6, d7} */ + /* solid mask is in d15 */ + + /* 'in' */ + vmull.u8 q8, d15, d3 + vmull.u8 q6, d15, d2 + vmull.u8 q5, d15, d1 + vmull.u8 q4, d15, d0 + vrshr.u16 q13, q8, #8 + vrshr.u16 q12, q6, #8 + vrshr.u16 q11, q5, #8 + vrshr.u16 q10, q4, #8 + vraddhn.u16 d3, q8, q13 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d1, q5, q11 + vraddhn.u16 d0, q4, q10 + vmvn.8 d24, d3 /* get inverted alpha */ + /* now do alpha blending */ + vmull.u8 q8, d24, d4 + vmull.u8 q9, d24, d5 + vmull.u8 q10, d24, d6 + vmull.u8 q11, d24, d7 +.endm + +.macro pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_out_reverse_8888_8888_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail + fetch_src_pixblock + cache_preload 8, 8 + fetch_mask_pixblock + pixman_composite_out_reverse_8888_n_8888_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +generate_composite_function_single_scanline \ + pixman_composite_scanline_out_reverse_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_out_reverse_8888_n_8888_process_pixblock_head, \ + pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail, \ + pixman_composite_out_reverse_8888_8888_8888_process_pixblock_tail_head \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_8888_n_8888_process_pixblock_head + pixman_composite_out_reverse_8888_n_8888_process_pixblock_head +.endm + +.macro pixman_composite_over_8888_n_8888_process_pixblock_tail + pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_n_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + pixman_composite_over_8888_n_8888_process_pixblock_tail + fetch_src_pixblock + cache_preload 8, 8 + pixman_composite_over_8888_n_8888_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +.macro pixman_composite_over_8888_n_8888_init + add DUMMY, sp, #48 + vpush {d8-d15} + vld1.32 {d15[0]}, [DUMMY] + vdup.8 d15, d15[3] +.endm + +.macro pixman_composite_over_8888_n_8888_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_8888_n_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_8888_n_8888_init, \ + pixman_composite_over_8888_n_8888_cleanup, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail_head + +/******************************************************************************/ + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_8888_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + pixman_composite_over_8888_n_8888_process_pixblock_tail + fetch_src_pixblock + cache_preload 8, 8 + fetch_mask_pixblock + pixman_composite_over_8888_n_8888_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +generate_composite_function \ + pixman_composite_over_8888_8888_8888_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_8888_process_pixblock_tail_head \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +generate_composite_function_single_scanline \ + pixman_composite_scanline_over_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_8888_process_pixblock_tail_head \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +/******************************************************************************/ + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_8_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + pixman_composite_over_8888_n_8888_process_pixblock_tail + fetch_src_pixblock + cache_preload 8, 8 + fetch_mask_pixblock + pixman_composite_over_8888_n_8888_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +generate_composite_function \ + pixman_composite_over_8888_8_8888_asm_neon, 32, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8_8888_process_pixblock_tail_head \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_0888_process_pixblock_head +.endm + +.macro pixman_composite_src_0888_0888_process_pixblock_tail +.endm + +.macro pixman_composite_src_0888_0888_process_pixblock_tail_head + vst3.8 {d0, d1, d2}, [DST_W]! + fetch_src_pixblock + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_0888_0888_asm_neon, 24, 0, 24, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0888_0888_process_pixblock_head, \ + pixman_composite_src_0888_0888_process_pixblock_tail, \ + pixman_composite_src_0888_0888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_head + vswp d0, d2 +.endm + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_tail +.endm + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_tail_head + vst4.8 {d0, d1, d2, d3}, [DST_W]! + fetch_src_pixblock + vswp d0, d2 + cache_preload 8, 8 +.endm + +.macro pixman_composite_src_0888_8888_rev_init + veor d3, d3, d3 +.endm + +generate_composite_function \ + pixman_composite_src_0888_8888_rev_asm_neon, 24, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + pixman_composite_src_0888_8888_rev_init, \ + default_cleanup, \ + pixman_composite_src_0888_8888_rev_process_pixblock_head, \ + pixman_composite_src_0888_8888_rev_process_pixblock_tail, \ + pixman_composite_src_0888_8888_rev_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_head + vshll.u8 q8, d1, #8 + vshll.u8 q9, d2, #8 +.endm + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_tail + vshll.u8 q14, d0, #8 + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 +.endm + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_tail_head + vshll.u8 q14, d0, #8 + fetch_src_pixblock + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 + vshll.u8 q8, d1, #8 + vst1.16 {d28, d29}, [DST_W, :128]! + vshll.u8 q9, d2, #8 +.endm + +generate_composite_function \ + pixman_composite_src_0888_0565_rev_asm_neon, 24, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0888_0565_rev_process_pixblock_head, \ + pixman_composite_src_0888_0565_rev_process_pixblock_tail, \ + pixman_composite_src_0888_0565_rev_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_head + vmull.u8 q8, d3, d0 + vmull.u8 q9, d3, d1 + vmull.u8 q10, d3, d2 +.endm + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_tail + vrshr.u16 q11, q8, #8 + vswp d3, d31 + vrshr.u16 q12, q9, #8 + vrshr.u16 q13, q10, #8 + vraddhn.u16 d30, q11, q8 + vraddhn.u16 d29, q12, q9 + vraddhn.u16 d28, q13, q10 +.endm + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_tail_head + vrshr.u16 q11, q8, #8 + vswp d3, d31 + vrshr.u16 q12, q9, #8 + vrshr.u16 q13, q10, #8 + fetch_src_pixblock + vraddhn.u16 d30, q11, q8 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + PF addne, PF_X, PF_X, #8 + PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d29, q12, q9 + vraddhn.u16 d28, q13, q10 + vmull.u8 q8, d3, d0 + vmull.u8 q9, d3, d1 + vmull.u8 q10, d3, d2 + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF subge, PF_X, PF_X, ORIG_W + PF subges, PF_CTL, PF_CTL, #0x10 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! +.endm + +generate_composite_function \ + pixman_composite_src_pixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_pixbuf_8888_process_pixblock_head, \ + pixman_composite_src_pixbuf_8888_process_pixblock_tail, \ + pixman_composite_src_pixbuf_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_rpixbuf_8888_process_pixblock_head + vmull.u8 q8, d3, d0 + vmull.u8 q9, d3, d1 + vmull.u8 q10, d3, d2 +.endm + +.macro pixman_composite_src_rpixbuf_8888_process_pixblock_tail + vrshr.u16 q11, q8, #8 + vswp d3, d31 + vrshr.u16 q12, q9, #8 + vrshr.u16 q13, q10, #8 + vraddhn.u16 d28, q11, q8 + vraddhn.u16 d29, q12, q9 + vraddhn.u16 d30, q13, q10 +.endm + +.macro pixman_composite_src_rpixbuf_8888_process_pixblock_tail_head + vrshr.u16 q11, q8, #8 + vswp d3, d31 + vrshr.u16 q12, q9, #8 + vrshr.u16 q13, q10, #8 + fetch_src_pixblock + vraddhn.u16 d28, q11, q8 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + PF addne, PF_X, PF_X, #8 + PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d29, q12, q9 + vraddhn.u16 d30, q13, q10 + vmull.u8 q8, d3, d0 + vmull.u8 q9, d3, d1 + vmull.u8 q10, d3, d2 + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF subge, PF_X, PF_X, ORIG_W + PF subges, PF_CTL, PF_CTL, #0x10 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! +.endm + +generate_composite_function \ + pixman_composite_src_rpixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_rpixbuf_8888_process_pixblock_head, \ + pixman_composite_src_rpixbuf_8888_process_pixblock_tail, \ + pixman_composite_src_rpixbuf_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_0565_8_0565_process_pixblock_head + /* mask is in d15 */ + convert_0565_to_x888 q4, d2, d1, d0 + convert_0565_to_x888 q5, d6, d5, d4 + /* source pixel data is in {d0, d1, d2, XX} */ + /* destination pixel data is in {d4, d5, d6, XX} */ + vmvn.8 d7, d15 + vmull.u8 q6, d15, d2 + vmull.u8 q5, d15, d1 + vmull.u8 q4, d15, d0 + vmull.u8 q8, d7, d4 + vmull.u8 q9, d7, d5 + vmull.u8 q13, d7, d6 + vrshr.u16 q12, q6, #8 + vrshr.u16 q11, q5, #8 + vrshr.u16 q10, q4, #8 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d1, q5, q11 + vraddhn.u16 d0, q4, q10 +.endm + +.macro pixman_composite_over_0565_8_0565_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q13, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q13 + vqadd.u8 q0, q0, q14 + vqadd.u8 q1, q1, q15 + /* 32bpp result is in {d0, d1, d2, XX} */ + convert_8888_to_0565 d2, d1, d0, q14, q15, q3 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_0565_8_0565_process_pixblock_tail_head + fetch_mask_pixblock + pixman_composite_over_0565_8_0565_process_pixblock_tail + fetch_src_pixblock + vld1.16 {d10, d11}, [DST_R, :128]! + cache_preload 8, 8 + pixman_composite_over_0565_8_0565_process_pixblock_head + vst1.16 {d28, d29}, [DST_W, :128]! +.endm + +generate_composite_function \ + pixman_composite_over_0565_8_0565_asm_neon, 16, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_0565_8_0565_process_pixblock_head, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_0565_n_0565_init + add DUMMY, sp, #(ARGS_STACK_OFFSET + 8) + vpush {d8-d15} + vld1.32 {d15[0]}, [DUMMY] + vdup.8 d15, d15[3] +.endm + +.macro pixman_composite_over_0565_n_0565_cleanup + vpop {d8-d15} +.endm + +generate_composite_function \ + pixman_composite_over_0565_n_0565_asm_neon, 16, 0, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_0565_n_0565_init, \ + pixman_composite_over_0565_n_0565_cleanup, \ + pixman_composite_over_0565_8_0565_process_pixblock_head, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_add_0565_8_0565_process_pixblock_head + /* mask is in d15 */ + convert_0565_to_x888 q4, d2, d1, d0 + convert_0565_to_x888 q5, d6, d5, d4 + /* source pixel data is in {d0, d1, d2, XX} */ + /* destination pixel data is in {d4, d5, d6, XX} */ + vmull.u8 q6, d15, d2 + vmull.u8 q5, d15, d1 + vmull.u8 q4, d15, d0 + vrshr.u16 q12, q6, #8 + vrshr.u16 q11, q5, #8 + vrshr.u16 q10, q4, #8 + vraddhn.u16 d2, q6, q12 + vraddhn.u16 d1, q5, q11 + vraddhn.u16 d0, q4, q10 +.endm + +.macro pixman_composite_add_0565_8_0565_process_pixblock_tail + vqadd.u8 q0, q0, q2 + vqadd.u8 q1, q1, q3 + /* 32bpp result is in {d0, d1, d2, XX} */ + convert_8888_to_0565 d2, d1, d0, q14, q15, q3 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_0565_8_0565_process_pixblock_tail_head + fetch_mask_pixblock + pixman_composite_add_0565_8_0565_process_pixblock_tail + fetch_src_pixblock + vld1.16 {d10, d11}, [DST_R, :128]! + cache_preload 8, 8 + pixman_composite_add_0565_8_0565_process_pixblock_head + vst1.16 {d28, d29}, [DST_W, :128]! +.endm + +generate_composite_function \ + pixman_composite_add_0565_8_0565_asm_neon, 16, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_add_0565_8_0565_process_pixblock_head, \ + pixman_composite_add_0565_8_0565_process_pixblock_tail, \ + pixman_composite_add_0565_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_out_reverse_8_0565_process_pixblock_head + /* mask is in d15 */ + convert_0565_to_x888 q5, d6, d5, d4 + /* destination pixel data is in {d4, d5, d6, xx} */ + vmvn.8 d24, d15 /* get inverted alpha */ + /* now do alpha blending */ + vmull.u8 q8, d24, d4 + vmull.u8 q9, d24, d5 + vmull.u8 q10, d24, d6 +.endm + +.macro pixman_composite_out_reverse_8_0565_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vraddhn.u16 d0, q14, q8 + vraddhn.u16 d1, q15, q9 + vraddhn.u16 d2, q12, q10 + /* 32bpp result is in {d0, d1, d2, XX} */ + convert_8888_to_0565 d2, d1, d0, q14, q15, q3 +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_out_reverse_8_0565_process_pixblock_tail_head + fetch_src_pixblock + pixman_composite_out_reverse_8_0565_process_pixblock_tail + vld1.16 {d10, d11}, [DST_R, :128]! + cache_preload 8, 8 + pixman_composite_out_reverse_8_0565_process_pixblock_head + vst1.16 {d28, d29}, [DST_W, :128]! +.endm + +generate_composite_function \ + pixman_composite_out_reverse_8_0565_asm_neon, 8, 0, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_out_reverse_8_0565_process_pixblock_head, \ + pixman_composite_out_reverse_8_0565_process_pixblock_tail, \ + pixman_composite_out_reverse_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 15, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_out_reverse_8_8888_process_pixblock_head + /* src is in d0 */ + /* destination pixel data is in {d4, d5, d6, d7} */ + vmvn.8 d1, d0 /* get inverted alpha */ + /* now do alpha blending */ + vmull.u8 q8, d1, d4 + vmull.u8 q9, d1, d5 + vmull.u8 q10, d1, d6 + vmull.u8 q11, d1, d7 +.endm + +.macro pixman_composite_out_reverse_8_8888_process_pixblock_tail + vrshr.u16 q14, q8, #8 + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + /* 32bpp result is in {d28, d29, d30, d31} */ +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_out_reverse_8_8888_process_pixblock_tail_head + fetch_src_pixblock + pixman_composite_out_reverse_8_8888_process_pixblock_tail + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + cache_preload 8, 8 + pixman_composite_out_reverse_8_8888_process_pixblock_head + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +.endm + +generate_composite_function \ + pixman_composite_out_reverse_8_8888_asm_neon, 8, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_out_reverse_8_8888_process_pixblock_head, \ + pixman_composite_out_reverse_8_8888_process_pixblock_tail, \ + pixman_composite_out_reverse_8_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_8888_8888_OVER_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_process_pixblock_tail_head + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_8888_0565_OVER_asm_neon, 32, 0, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_0565_process_pixblock_head, \ + pixman_composite_over_8888_0565_process_pixblock_tail, \ + pixman_composite_over_8888_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_8888_0565_SRC_asm_neon, 32, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_8888_0565_process_pixblock_head, \ + pixman_composite_src_8888_0565_process_pixblock_tail, \ + pixman_composite_src_8888_0565_process_pixblock_tail_head + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_0565_8888_SRC_asm_neon, 16, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0565_8888_process_pixblock_head, \ + pixman_composite_src_0565_8888_process_pixblock_tail, \ + pixman_composite_src_0565_8888_process_pixblock_tail_head + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_8888_8_0565_OVER_asm_neon, 32, 8, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_8_0565_process_pixblock_head, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 24 /* mask_basereg */ + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_0565_8_0565_OVER_asm_neon, 16, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_0565_8_0565_process_pixblock_head, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +/* + * Bilinear scaling support code which tries to provide pixel fetching, color + * format conversion, and interpolation as separate macros which can be used + * as the basic building blocks for constructing bilinear scanline functions. + */ + +.macro bilinear_load_8888 reg1, reg2, tmp + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + vld1.32 {\reg1}, [TMP1], STRIDE + vld1.32 {\reg2}, [TMP1] +.endm + +.macro bilinear_load_0565 reg1, reg2, tmp + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + vld1.32 {\reg2[0]}, [TMP1], STRIDE + vld1.32 {\reg2[1]}, [TMP1] + convert_four_0565_to_x888_packed \reg2, \reg1, \reg2, \tmp +.endm + +.macro bilinear_load_and_vertical_interpolate_two_8888 \ + acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 + + bilinear_load_8888 \reg1, \reg2, \tmp1 + vmull.u8 \acc1, \reg1, d28 + vmlal.u8 \acc1, \reg2, d29 + bilinear_load_8888 \reg3, \reg4, \tmp2 + vmull.u8 \acc2, \reg3, d28 + vmlal.u8 \acc2, \reg4, d29 +.endm + +.macro bilinear_load_and_vertical_interpolate_four_8888 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + bilinear_load_and_vertical_interpolate_two_8888 \ + \xacc1, \xacc2, \xreg1, \xreg2, \xreg3, \xreg4, \xacc2lo, \xacc2hi + bilinear_load_and_vertical_interpolate_two_8888 \ + \yacc1, \yacc2, \yreg1, \yreg2, \yreg3, \yreg4, \yacc2lo, \yacc2hi +.endm + +.macro bilinear_load_and_vertical_interpolate_two_0565 \ + acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 + vld1.32 {\acc2lo[0]}, [TMP1], STRIDE + vld1.32 {\acc2hi[0]}, [TMP2], STRIDE + vld1.32 {\acc2lo[1]}, [TMP1] + vld1.32 {\acc2hi[1]}, [TMP2] + convert_0565_to_x888 \acc2, \reg3, \reg2, \reg1 + vzip.u8 \reg1, \reg3 + vzip.u8 \reg2, \reg4 + vzip.u8 \reg3, \reg4 + vzip.u8 \reg1, \reg2 + vmull.u8 \acc1, \reg1, d28 + vmlal.u8 \acc1, \reg2, d29 + vmull.u8 \acc2, \reg3, d28 + vmlal.u8 \acc2, \reg4, d29 +.endm + +.macro bilinear_load_and_vertical_interpolate_four_0565 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 + vld1.32 {\xacc2lo[0]}, [TMP1], STRIDE + vld1.32 {\xacc2hi[0]}, [TMP2], STRIDE + vld1.32 {\xacc2lo[1]}, [TMP1] + vld1.32 {\xacc2hi[1]}, [TMP2] + convert_0565_to_x888 \xacc2, \xreg3, \xreg2, \xreg1 + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 + vld1.32 {\yacc2lo[0]}, [TMP1], STRIDE + vzip.u8 \xreg1, \xreg3 + vld1.32 {\yacc2hi[0]}, [TMP2], STRIDE + vzip.u8 \xreg2, \xreg4 + vld1.32 {\yacc2lo[1]}, [TMP1] + vzip.u8 \xreg3, \xreg4 + vld1.32 {\yacc2hi[1]}, [TMP2] + vzip.u8 \xreg1, \xreg2 + convert_0565_to_x888 \yacc2, \yreg3, \yreg2, \yreg1 + vmull.u8 \xacc1, \xreg1, d28 + vzip.u8 \yreg1, \yreg3 + vmlal.u8 \xacc1, \xreg2, d29 + vzip.u8 \yreg2, \yreg4 + vmull.u8 \xacc2, \xreg3, d28 + vzip.u8 \yreg3, \yreg4 + vmlal.u8 \xacc2, \xreg4, d29 + vzip.u8 \yreg1, \yreg2 + vmull.u8 \yacc1, \yreg1, d28 + vmlal.u8 \yacc1, \yreg2, d29 + vmull.u8 \yacc2, \yreg3, d28 + vmlal.u8 \yacc2, \yreg4, d29 +.endm + +.macro bilinear_store_8888 numpix, tmp1, tmp2 +.if \numpix == 4 + vst1.32 {d0, d1}, [OUT, :128]! +.elseif \numpix == 2 + vst1.32 {d0}, [OUT, :64]! +.elseif \numpix == 1 + vst1.32 {d0[0]}, [OUT, :32]! +.else + .error bilinear_store_8888 \numpix is unsupported +.endif +.endm + +.macro bilinear_store_0565 numpix, tmp1, tmp2 + vuzp.u8 d0, d1 + vuzp.u8 d2, d3 + vuzp.u8 d1, d3 + vuzp.u8 d0, d2 + convert_8888_to_0565 d2, d1, d0, q1, \tmp1, \tmp2 +.if \numpix == 4 + vst1.16 {d2}, [OUT, :64]! +.elseif \numpix == 2 + vst1.32 {d2[0]}, [OUT, :32]! +.elseif \numpix == 1 + vst1.16 {d2[0]}, [OUT, :16]! +.else + .error bilinear_store_0565 \numpix is unsupported +.endif +.endm + +.macro bilinear_interpolate_last_pixel src_fmt, dst_fmt + bilinear_load_\()\src_fmt d0, d1, d2 + vmull.u8 q1, d0, d28 + vmlal.u8 q1, d1, d29 + /* 5 cycles bubble */ + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + /* 5 cycles bubble */ + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + /* 3 cycles bubble */ + vmovn.u16 d0, q0 + /* 1 cycle bubble */ + bilinear_store_\()\dst_fmt 1, q2, q3 +.endm + +.macro bilinear_interpolate_two_pixels src_fmt, dst_fmt + bilinear_load_and_vertical_interpolate_two_\()\src_fmt \ + q1, q11, d0, d1, d20, d21, d22, d23 + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q10, d22, d31 + vmlal.u16 q10, d23, d31 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vmovn.u16 d0, q0 + bilinear_store_\()\dst_fmt 2, q2, q3 +.endm + +.macro bilinear_interpolate_four_pixels src_fmt, dst_fmt + bilinear_load_and_vertical_interpolate_four_\()\src_fmt \ + q1, q11, d0, d1, d20, d21, d22, d23 \ + q3, q9, d4, d5, d16, d17, d18, d19 + pld [TMP1, PF_OFFS] + sub TMP1, TMP1, STRIDE + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q10, d22, d31 + vmlal.u16 q10, d23, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshll.u16 q2, d6, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d6, d30 + vmlal.u16 q2, d7, d30 + vshll.u16 q8, d18, #BILINEAR_INTERPOLATION_BITS + pld [TMP2, PF_OFFS] + vmlsl.u16 q8, d18, d31 + vmlal.u16 q8, d19, d31 + vadd.u16 q12, q12, q13 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d5, q8, #(2 * BILINEAR_INTERPOLATION_BITS) + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d0, q0 + vmovn.u16 d1, q2 + vadd.u16 q12, q12, q13 + bilinear_store_\()\dst_fmt 4, q2, q3 +.endm + +.macro bilinear_interpolate_four_pixels_head src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_head +.else + bilinear_interpolate_four_pixels \src_fmt, \dst_fmt +.endif +.endm + +.macro bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail +.endif +.endm + +.macro bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail_head +.else + bilinear_interpolate_four_pixels \src_fmt, \dst_fmt +.endif +.endm + +.macro bilinear_interpolate_eight_pixels_head src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_head +.else + bilinear_interpolate_four_pixels_head \src_fmt, \dst_fmt + bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt +.endif +.endm + +.macro bilinear_interpolate_eight_pixels_tail src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail +.else + bilinear_interpolate_four_pixels_tail \src_fmt, \dst_fmt +.endif +.endm + +.macro bilinear_interpolate_eight_pixels_tail_head src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail_head +.else + bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt +.endif +.endm + +.set BILINEAR_FLAG_UNROLL_4, 0 +.set BILINEAR_FLAG_UNROLL_8, 1 +.set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 + +/* + * Main template macro for generating NEON optimized bilinear scanline + * functions. + * + * Bilinear scanline scaler macro template uses the following arguments: + * fname - name of the function to generate + * src_fmt - source color format (8888 or 0565) + * dst_fmt - destination color format (8888 or 0565) + * bpp_shift - (1 << bpp_shift) is the size of source pixel in bytes + * prefetch_distance - prefetch in the source image by that many + * pixels ahead + */ + +.macro generate_bilinear_scanline_func fname, src_fmt, dst_fmt, \ + src_bpp_shift, dst_bpp_shift, \ + prefetch_distance, flags + +pixman_asm_function \fname + OUT .req r0 + TOP .req r1 + BOTTOM .req r2 + WT .req r3 + WB .req r4 + X .req r5 + UX .req r6 + WIDTH .req ip + TMP1 .req r3 + TMP2 .req r4 + PF_OFFS .req r7 + TMP3 .req r8 + TMP4 .req r9 + STRIDE .req r2 + + mov ip, sp + push {r4, r5, r6, r7, r8, r9} + mov PF_OFFS, #\prefetch_distance + ldmia ip, {WB, X, UX, WIDTH} + mul PF_OFFS, PF_OFFS, UX + +.if ((\flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 + vpush {d8-d15} +.endif + + sub STRIDE, BOTTOM, TOP + .unreq BOTTOM + + cmp WIDTH, #0 + ble 3f + + vdup.u16 q12, X + vdup.u16 q13, UX + vdup.u8 d28, WT + vdup.u8 d29, WB + vadd.u16 d25, d25, d26 + + /* ensure good destination alignment */ + cmp WIDTH, #1 + blt 0f + tst OUT, #(1 << dst_bpp_shift) + beq 0f + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + bilinear_interpolate_last_pixel \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #1 +0: + vadd.u16 q13, q13, q13 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + + cmp WIDTH, #2 + blt 0f + tst OUT, #(1 << (dst_bpp_shift + 1)) + beq 0f + bilinear_interpolate_two_pixels \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #2 +0: +.if ((\flags) & BILINEAR_FLAG_UNROLL_8) != 0 +/*********** 8 pixels per iteration *****************/ + cmp WIDTH, #4 + blt 0f + tst OUT, #(1 << (dst_bpp_shift + 2)) + beq 0f + bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #4 +0: + subs WIDTH, WIDTH, #8 + blt 1f + mov PF_OFFS, PF_OFFS, asr #(16 - src_bpp_shift) + bilinear_interpolate_eight_pixels_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #8 + blt 5f +0: + bilinear_interpolate_eight_pixels_tail_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #8 + bge 0b +5: + bilinear_interpolate_eight_pixels_tail \src_fmt, \dst_fmt +1: + tst WIDTH, #4 + beq 2f + bilinear_interpolate_four_pixels \src_fmt, \dst_fmt +2: +.else +/*********** 4 pixels per iteration *****************/ + subs WIDTH, WIDTH, #4 + blt 1f + mov PF_OFFS, PF_OFFS, asr #(16 - src_bpp_shift) + bilinear_interpolate_four_pixels_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #4 + blt 5f +0: + bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #4 + bge 0b +5: + bilinear_interpolate_four_pixels_tail \src_fmt, \dst_fmt +1: +/****************************************************/ +.endif + /* handle the remaining trailing pixels */ + tst WIDTH, #2 + beq 2f + bilinear_interpolate_two_pixels \src_fmt, \dst_fmt +2: + tst WIDTH, #1 + beq 3f + bilinear_interpolate_last_pixel \src_fmt, \dst_fmt +3: +.if ((\flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 + vpop {d8-d15} +.endif + pop {r4, r5, r6, r7, r8, r9} + bx lr + + .unreq OUT + .unreq TOP + .unreq WT + .unreq WB + .unreq X + .unreq UX + .unreq WIDTH + .unreq TMP1 + .unreq TMP2 + .unreq PF_OFFS + .unreq TMP3 + .unreq TMP4 + .unreq STRIDE +pixman_end_asm_function + +.endm + +/*****************************************************************************/ + +.set have_bilinear_interpolate_four_pixels_8888_8888, 1 + +.macro bilinear_interpolate_four_pixels_8888_8888_head + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + + vld1.32 {d22}, [TMP1], STRIDE + vld1.32 {d23}, [TMP1] + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + vmull.u8 q8, d22, d28 + vmlal.u8 q8, d23, d29 + + vld1.32 {d22}, [TMP2], STRIDE + vld1.32 {d23}, [TMP2] + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vmull.u8 q9, d22, d28 + vmlal.u8 q9, d23, d29 + + vld1.32 {d22}, [TMP3], STRIDE + vld1.32 {d23}, [TMP3] + vmull.u8 q10, d22, d28 + vmlal.u8 q10, d23, d29 + + vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d16, d30 + vmlal.u16 q0, d17, d30 + + pld [TMP4, PF_OFFS] + vld1.32 {d16}, [TMP4], STRIDE + vld1.32 {d17}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q11, d16, d28 + vmlal.u8 q11, d17, d29 + + vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q1, d18, d31 +.endm + +.macro bilinear_interpolate_four_pixels_8888_8888_tail + vmlal.u16 q1, d19, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d20, d30 + vmlal.u16 q2, d21, d30 + vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q3, d22, d31 + vmlal.u16 q3, d23, d31 + vadd.u16 q12, q12, q13 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d6, q0 + vmovn.u16 d7, q2 + vadd.u16 q12, q12, q13 + vst1.32 {d6, d7}, [OUT, :128]! +.endm + +.macro bilinear_interpolate_four_pixels_8888_8888_tail_head + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + vmlal.u16 q1, d19, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d20, d30 + vmlal.u16 q2, d21, d30 + vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS + vld1.32 {d20}, [TMP1], STRIDE + vmlsl.u16 q3, d22, d31 + vmlal.u16 q3, d23, d31 + vld1.32 {d21}, [TMP1] + vmull.u8 q8, d20, d28 + vmlal.u8 q8, d21, d29 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vld1.32 {d22}, [TMP2], STRIDE + vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vld1.32 {d23}, [TMP2] + vmull.u8 q9, d22, d28 + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vmlal.u8 q9, d23, d29 + vld1.32 {d22}, [TMP3], STRIDE + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vld1.32 {d23}, [TMP3] + vmull.u8 q10, d22, d28 + vmlal.u8 q10, d23, d29 + vmovn.u16 d6, q0 + vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS + vmovn.u16 d7, q2 + vmlsl.u16 q0, d16, d30 + vmlal.u16 q0, d17, d30 + pld [TMP4, PF_OFFS] + vld1.32 {d16}, [TMP4], STRIDE + vadd.u16 q12, q12, q13 + vld1.32 {d17}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q11, d16, d28 + vmlal.u8 q11, d17, d29 + vst1.32 {d6, d7}, [OUT, :128]! + vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q1, d18, d31 +.endm + +/*****************************************************************************/ + +.set have_bilinear_interpolate_eight_pixels_8888_0565, 1 + +.macro bilinear_interpolate_eight_pixels_8888_0565_head + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + vld1.32 {d20}, [TMP1], STRIDE + vld1.32 {d21}, [TMP1] + vmull.u8 q8, d20, d28 + vmlal.u8 q8, d21, d29 + vld1.32 {d22}, [TMP2], STRIDE + vld1.32 {d23}, [TMP2] + vmull.u8 q9, d22, d28 + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vmlal.u8 q9, d23, d29 + vld1.32 {d22}, [TMP3], STRIDE + vld1.32 {d23}, [TMP3] + vmull.u8 q10, d22, d28 + vmlal.u8 q10, d23, d29 + vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d16, d30 + vmlal.u16 q0, d17, d30 + pld [TMP4, PF_OFFS] + vld1.32 {d16}, [TMP4], STRIDE + vld1.32 {d17}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q11, d16, d28 + vmlal.u8 q11, d17, d29 + vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q1, d18, d31 + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + vmlal.u16 q1, d19, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d20, d30 + vmlal.u16 q2, d21, d30 + vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS + vld1.32 {d20}, [TMP1], STRIDE + vmlsl.u16 q3, d22, d31 + vmlal.u16 q3, d23, d31 + vld1.32 {d21}, [TMP1] + vmull.u8 q8, d20, d28 + vmlal.u8 q8, d21, d29 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vld1.32 {d22}, [TMP2], STRIDE + vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vld1.32 {d23}, [TMP2] + vmull.u8 q9, d22, d28 + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vmlal.u8 q9, d23, d29 + vld1.32 {d22}, [TMP3], STRIDE + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vld1.32 {d23}, [TMP3] + vmull.u8 q10, d22, d28 + vmlal.u8 q10, d23, d29 + vmovn.u16 d8, q0 + vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS + vmovn.u16 d9, q2 + vmlsl.u16 q0, d16, d30 + vmlal.u16 q0, d17, d30 + pld [TMP4, PF_OFFS] + vld1.32 {d16}, [TMP4], STRIDE + vadd.u16 q12, q12, q13 + vld1.32 {d17}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q11, d16, d28 + vmlal.u8 q11, d17, d29 + vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q1, d18, d31 +.endm + +.macro bilinear_interpolate_eight_pixels_8888_0565_tail + vmlal.u16 q1, d19, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d20, d30 + vmlal.u16 q2, d21, d30 + vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q3, d22, d31 + vmlal.u16 q3, d23, d31 + vadd.u16 q12, q12, q13 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d10, q0 + vmovn.u16 d11, q2 + vadd.u16 q12, q12, q13 + + vuzp.u8 d8, d9 + vuzp.u8 d10, d11 + vuzp.u8 d9, d11 + vuzp.u8 d8, d10 + vshll.u8 q6, d9, #8 + vshll.u8 q5, d10, #8 + vshll.u8 q7, d8, #8 + vsri.u16 q5, q6, #5 + vsri.u16 q5, q7, #11 + vst1.32 {d10, d11}, [OUT, :128]! +.endm + +.macro bilinear_interpolate_eight_pixels_8888_0565_tail_head + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + vmlal.u16 q1, d19, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vuzp.u8 d8, d9 + vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d20, d30 + vmlal.u16 q2, d21, d30 + vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS + vld1.32 {d20}, [TMP1], STRIDE + vmlsl.u16 q3, d22, d31 + vmlal.u16 q3, d23, d31 + vld1.32 {d21}, [TMP1] + vmull.u8 q8, d20, d28 + vmlal.u8 q8, d21, d29 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vld1.32 {d22}, [TMP2], STRIDE + vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vld1.32 {d23}, [TMP2] + vmull.u8 q9, d22, d28 + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vmlal.u8 q9, d23, d29 + vld1.32 {d22}, [TMP3], STRIDE + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vld1.32 {d23}, [TMP3] + vmull.u8 q10, d22, d28 + vmlal.u8 q10, d23, d29 + vmovn.u16 d10, q0 + vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS + vmovn.u16 d11, q2 + vmlsl.u16 q0, d16, d30 + vmlal.u16 q0, d17, d30 + pld [TMP4, PF_OFFS] + vld1.32 {d16}, [TMP4], STRIDE + vadd.u16 q12, q12, q13 + vld1.32 {d17}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q11, d16, d28 + vmlal.u8 q11, d17, d29 + vuzp.u8 d10, d11 + vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q1, d18, d31 + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #2 + vmlal.u16 q1, d19, d31 + vuzp.u8 d9, d11 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshll.u16 q2, d20, #BILINEAR_INTERPOLATION_BITS + vuzp.u8 d8, d10 + vmlsl.u16 q2, d20, d30 + vmlal.u16 q2, d21, d30 + vshll.u16 q3, d22, #BILINEAR_INTERPOLATION_BITS + vld1.32 {d20}, [TMP1], STRIDE + vmlsl.u16 q3, d22, d31 + vmlal.u16 q3, d23, d31 + vld1.32 {d21}, [TMP1] + vmull.u8 q8, d20, d28 + vmlal.u8 q8, d21, d29 + vshll.u8 q6, d9, #8 + vshll.u8 q5, d10, #8 + vshll.u8 q7, d8, #8 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vsri.u16 q5, q6, #5 + vshrn.u32 d1, q1, #(2 * BILINEAR_INTERPOLATION_BITS) + vsri.u16 q5, q7, #11 + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vld1.32 {d22}, [TMP2], STRIDE + vshrn.u32 d5, q3, #(2 * BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vld1.32 {d23}, [TMP2] + vmull.u8 q9, d22, d28 + mov TMP3, X, asr #16 + add X, X, UX + add TMP3, TOP, TMP3, asl #2 + mov TMP4, X, asr #16 + add X, X, UX + add TMP4, TOP, TMP4, asl #2 + vmlal.u8 q9, d23, d29 + vld1.32 {d22}, [TMP3], STRIDE + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vld1.32 {d23}, [TMP3] + vmull.u8 q10, d22, d28 + vmlal.u8 q10, d23, d29 + vmovn.u16 d8, q0 + vshll.u16 q0, d16, #BILINEAR_INTERPOLATION_BITS + vmovn.u16 d9, q2 + vmlsl.u16 q0, d16, d30 + vmlal.u16 q0, d17, d30 + pld [TMP4, PF_OFFS] + vld1.32 {d16}, [TMP4], STRIDE + vadd.u16 q12, q12, q13 + vld1.32 {d17}, [TMP4] + pld [TMP4, PF_OFFS] + vmull.u8 q11, d16, d28 + vmlal.u8 q11, d17, d29 + vshll.u16 q1, d18, #BILINEAR_INTERPOLATION_BITS + vst1.32 {d10, d11}, [OUT, :128]! + vmlsl.u16 q1, d18, d31 +.endm +/*****************************************************************************/ + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon, 8888, 8888, \ + 2, 2, 28, BILINEAR_FLAG_UNROLL_4 + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_0565_SRC_asm_neon, 8888, 0565, \ + 2, 1, 28, BILINEAR_FLAG_UNROLL_8 | BILINEAR_FLAG_USE_ALL_NEON_REGS + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_0565_x888_SRC_asm_neon, 0565, 8888, \ + 1, 2, 28, BILINEAR_FLAG_UNROLL_4 + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_0565_0565_SRC_asm_neon, 0565, 0565, \ + 1, 1, 28, BILINEAR_FLAG_UNROLL_4 diff --git a/gfx/cairo/libpixman/src/pixman-arm-neon-asm.h b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.h new file mode 100644 index 0000000000..07a136234e --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.h @@ -0,0 +1,1186 @@ +/* + * Copyright © 2009 Nokia 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. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + */ + +/* + * This file contains a macro ('generate_composite_function') which can + * construct 2D image processing functions, based on a common template. + * Any combinations of source, destination and mask images with 8bpp, + * 16bpp, 24bpp, 32bpp color formats are supported. + * + * This macro takes care of: + * - handling of leading and trailing unaligned pixels + * - doing most of the work related to L2 cache preload + * - encourages the use of software pipelining for better instructions + * scheduling + * + * The user of this macro has to provide some configuration parameters + * (bit depths for the images, prefetch distance, etc.) and a set of + * macros, which should implement basic code chunks responsible for + * pixels processing. See 'pixman-arm-neon-asm.S' file for the usage + * examples. + * + * TODO: + * - try overlapped pixel method (from Ian Rickards) when processing + * exactly two blocks of pixels + * - maybe add an option to do reverse scanline processing + */ + +/* + * Bit flags for 'generate_composite_function' macro which are used + * to tune generated functions behavior. + */ +.set FLAG_DST_WRITEONLY, 0 +.set FLAG_DST_READWRITE, 1 +.set FLAG_DEINTERLEAVE_32BPP, 2 + +/* + * Offset in stack where mask and source pointer/stride can be accessed + * from 'init' macro. This is useful for doing special handling for solid mask. + */ +.set ARGS_STACK_OFFSET, 40 + +/* + * Constants for selecting preferable prefetch type. + */ +.set PREFETCH_TYPE_NONE, 0 /* No prefetch at all */ +.set PREFETCH_TYPE_SIMPLE, 1 /* A simple, fixed-distance-ahead prefetch */ +.set PREFETCH_TYPE_ADVANCED, 2 /* Advanced fine-grained prefetch */ + +/* + * Definitions of supplementary pixld/pixst macros (for partial load/store of + * pixel data). + */ + +.macro pixldst1 op, elem_size, reg1, mem_operand, abits +.if \abits > 0 + \op\().\()\elem_size {d\()\reg1}, [\()\mem_operand\(), :\()\abits\()]! +.else + \op\().\()\elem_size {d\()\reg1}, [\()\mem_operand\()]! +.endif +.endm + +.macro pixldst2 op, elem_size, reg1, reg2, mem_operand, abits +.if \abits > 0 + \op\().\()\elem_size {d\()\reg1, d\()\reg2}, [\()\mem_operand\(), :\()\abits\()]! +.else + \op\().\()\elem_size {d\()\reg1, d\()\reg2}, [\()\mem_operand\()]! +.endif +.endm + +.macro pixldst4 op, elem_size, reg1, reg2, reg3, reg4, mem_operand, abits +.if \abits > 0 + \op\().\()\elem_size {d\()\reg1, d\()\reg2, d\()\reg3, d\()\reg4}, [\()\mem_operand\(), :\()\abits\()]! +.else + \op\().\()\elem_size {d\()\reg1, d\()\reg2, d\()\reg3, d\()\reg4}, [\()\mem_operand\()]! +.endif +.endm + +.macro pixldst0 op, elem_size, reg1, idx, mem_operand, abits + \op\().\()\elem_size {d\()\reg1[\idx]}, [\()\mem_operand\()]! +.endm + +.macro pixldst3 op, elem_size, reg1, reg2, reg3, mem_operand + \op\().\()\elem_size {d\()\reg1, d\()\reg2, d\()\reg3}, [\()\mem_operand\()]! +.endm + +.macro pixldst30 op, elem_size, reg1, reg2, reg3, idx, mem_operand + \op\().\()\elem_size {d\()\reg1[\idx], d\()\reg2[\idx], d\()\reg3[\idx]}, [\()\mem_operand\()]! +.endm + +.macro pixldst numbytes, op, elem_size, basereg, mem_operand, abits +.if \numbytes == 32 + pixldst4 \op, \elem_size, %(\basereg+4), %(\basereg+5), \ + %(\basereg+6), %(\basereg+7), \mem_operand, \abits +.elseif \numbytes == 16 + pixldst2 \op, \elem_size, %(\basereg+2), %(\basereg+3), \mem_operand, \abits +.elseif \numbytes == 8 + pixldst1 \op, \elem_size, %(\basereg+1), \mem_operand, \abits +.elseif \numbytes == 4 + .if !RESPECT_STRICT_ALIGNMENT || (\elem_size == 32) + pixldst0 \op, 32, %(\basereg+0), 1, \mem_operand, \abits + .elseif \elem_size == 16 + pixldst0 \op, 16, %(\basereg+0), 2, \mem_operand, \abits + pixldst0 \op, 16, %(\basereg+0), 3, \mem_operand, \abits + .else + pixldst0 \op, 8, %(\basereg+0), 4, \mem_operand, \abits + pixldst0 \op, 8, %(\basereg+0), 5, \mem_operand, \abits + pixldst0 \op, 8, %(\basereg+0), 6, \mem_operand, \abits + pixldst0 \op, 8, %(\basereg+0), 7, \mem_operand, \abits + .endif +.elseif \numbytes == 2 + .if !RESPECT_STRICT_ALIGNMENT || (\elem_size == 16) + pixldst0 \op, 16, %(\basereg+0), 1, \mem_operand, \abits + .else + pixldst0 \op, 8, %(\basereg+0), 2, \mem_operand, \abits + pixldst0 \op, 8, %(\basereg+0), 3, \mem_operand, \abits + .endif +.elseif \numbytes == 1 + pixldst0 \op, 8, %(\basereg+0), 1, \mem_operand, \abits +.else + .error "unsupported size: \numbytes" +.endif +.endm + +.macro pixld numpix, bpp, basereg, mem_operand, abits=0 +.if \bpp > 0 +.if (\bpp == 32) && (\numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) + pixldst4 vld4, 8, %(\basereg+4), %(\basereg+5), \ + %(\basereg+6), %(\basereg+7), \mem_operand, \abits +.elseif (\bpp == 24) && (\numpix == 8) + pixldst3 vld3, 8, %(\basereg+3), %(\basereg+4), %(\basereg+5), \mem_operand +.elseif (\bpp == 24) && (\numpix == 4) + pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 4, \mem_operand + pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 5, \mem_operand + pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 6, \mem_operand + pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 7, \mem_operand +.elseif (\bpp == 24) && (\numpix == 2) + pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 2, \mem_operand + pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 3, \mem_operand +.elseif (\bpp == 24) && (\numpix == 1) + pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 1, \mem_operand +.else + pixldst %(\numpix * \bpp / 8), vld1, %(\bpp), \basereg, \mem_operand, \abits +.endif +.endif +.endm + +.macro pixst numpix, bpp, basereg, mem_operand, abits=0 +.if \bpp > 0 +.if (\bpp == 32) && (\numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) + pixldst4 vst4, 8, %(\basereg+4), %(\basereg+5), \ + %(\basereg+6), %(\basereg+7), \mem_operand, \abits +.elseif (\bpp == 24) && (\numpix == 8) + pixldst3 vst3, 8, %(\basereg+3), %(\basereg+4), %(\basereg+5), \mem_operand +.elseif (\bpp == 24) && (\numpix == 4) + pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 4, \mem_operand + pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 5, \mem_operand + pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 6, \mem_operand + pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 7, \mem_operand +.elseif (\bpp == 24) && (\numpix == 2) + pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 2, \mem_operand + pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 3, \mem_operand +.elseif (\bpp == 24) && (\numpix == 1) + pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 1, \mem_operand +.else + pixldst %(\numpix * \bpp / 8), vst1, %(\bpp), \basereg, \mem_operand, \abits +.endif +.endif +.endm + +.macro pixld_a numpix, bpp, basereg, mem_operand +.if (\bpp * \numpix) <= 128 + pixld \numpix, \bpp, \basereg, \mem_operand, %(\bpp * \numpix) +.else + pixld \numpix, \bpp, \basereg, \mem_operand, 128 +.endif +.endm + +.macro pixst_a numpix, bpp, basereg, mem_operand +.if (\bpp * \numpix) <= 128 + pixst \numpix, \bpp, \basereg, \mem_operand, %(\bpp * \numpix) +.else + pixst \numpix, \bpp, \basereg, \mem_operand, 128 +.endif +.endm + +/* + * Pixel fetcher for nearest scaling (needs TMP1, TMP2, VX, UNIT_X register + * aliases to be defined) + */ +.macro pixld1_s elem_size, reg1, mem_operand +.if \elem_size == 16 + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X +5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b + add TMP1, \mem_operand, TMP1, asl #1 + mov TMP2, VX, asr #16 + adds VX, VX, UNIT_X +5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b + add TMP2, \mem_operand, TMP2, asl #1 + vld1.16 {d\()\reg1\()[0]}, [TMP1, :16] + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X +5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b + add TMP1, \mem_operand, TMP1, asl #1 + vld1.16 {d\()\reg1\()[1]}, [TMP2, :16] + mov TMP2, VX, asr #16 + adds VX, VX, UNIT_X +5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b + add TMP2, \mem_operand, TMP2, asl #1 + vld1.16 {d\()\reg1\()[2]}, [TMP1, :16] + vld1.16 {d\()\reg1\()[3]}, [TMP2, :16] +.elseif \elem_size == 32 + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X +5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b + add TMP1, \mem_operand, TMP1, asl #2 + mov TMP2, VX, asr #16 + adds VX, VX, UNIT_X +5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b + add TMP2, \mem_operand, TMP2, asl #2 + vld1.32 {d\()\reg1\()[0]}, [TMP1, :32] + vld1.32 {d\()\reg1\()[1]}, [TMP2, :32] +.else + .error "unsupported" +.endif +.endm + +.macro pixld2_s elem_size, reg1, reg2, mem_operand +.if 0 /* elem_size == 32 */ + mov TMP1, VX, asr #16 + add VX, VX, UNIT_X, asl #1 + add TMP1, \mem_operand, TMP1, asl #2 + mov TMP2, VX, asr #16 + sub VX, VX, UNIT_X + add TMP2, \mem_operand, TMP2, asl #2 + vld1.32 {d\()\reg1\()[0]}, [TMP1, :32] + mov TMP1, VX, asr #16 + add VX, VX, UNIT_X, asl #1 + add TMP1, \mem_operand, TMP1, asl #2 + vld1.32 {d\()\reg2\()[0]}, [TMP2, :32] + mov TMP2, VX, asr #16 + add VX, VX, UNIT_X + add TMP2, \mem_operand, TMP2, asl #2 + vld1.32 {d\()\reg1\()[1]}, [TMP1, :32] + vld1.32 {d\()\reg2\()[1]}, [TMP2, :32] +.else + pixld1_s \elem_size, \reg1, \mem_operand + pixld1_s \elem_size, \reg2, \mem_operand +.endif +.endm + +.macro pixld0_s elem_size, reg1, idx, mem_operand +.if \elem_size == 16 + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X +5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b + add TMP1, \mem_operand, TMP1, asl #1 + vld1.16 {d\()\reg1\()[\idx]}, [TMP1, :16] +.elseif \elem_size == 32 + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X +5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b + add TMP1, \mem_operand, TMP1, asl #2 + vld1.32 {d\()\reg1\()[\idx]}, [TMP1, :32] +.endif +.endm + +.macro pixld_s_internal numbytes, elem_size, basereg, mem_operand +.if \numbytes == 32 + pixld2_s \elem_size, %(\basereg+4), %(\basereg+5), \mem_operand + pixld2_s \elem_size, %(\basereg+6), %(\basereg+7), \mem_operand + pixdeinterleave \elem_size, %(\basereg+4) +.elseif \numbytes == 16 + pixld2_s \elem_size, %(\basereg+2), %(\basereg+3), \mem_operand +.elseif \numbytes == 8 + pixld1_s \elem_size, %(\basereg+1), \mem_operand +.elseif \numbytes == 4 + .if \elem_size == 32 + pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand + .elseif \elem_size == 16 + pixld0_s \elem_size, %(\basereg+0), 2, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 3, \mem_operand + .else + pixld0_s \elem_size, %(\basereg+0), 4, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 5, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 6, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 7, \mem_operand + .endif +.elseif \numbytes == 2 + .if \elem_size == 16 + pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand + .else + pixld0_s \elem_size, %(\basereg+0), 2, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 3, \mem_operand + .endif +.elseif \numbytes == 1 + pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand +.else + .error "unsupported size: \numbytes" +.endif +.endm + +.macro pixld_s numpix, bpp, basereg, mem_operand +.if \bpp > 0 + pixld_s_internal %(\numpix * \bpp / 8), %(\bpp), \basereg, \mem_operand +.endif +.endm + +.macro vuzp8 reg1, reg2 + vuzp.8 d\()\reg1, d\()\reg2 +.endm + +.macro vzip8 reg1, reg2 + vzip.8 d\()\reg1, d\()\reg2 +.endm + +/* deinterleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ +.macro pixdeinterleave bpp, basereg +.if (\bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) + vuzp8 %(\basereg+0), %(\basereg+1) + vuzp8 %(\basereg+2), %(\basereg+3) + vuzp8 %(\basereg+1), %(\basereg+3) + vuzp8 %(\basereg+0), %(\basereg+2) +.endif +.endm + +/* interleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ +.macro pixinterleave bpp, basereg +.if (\bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) + vzip8 %(\basereg+0), %(\basereg+2) + vzip8 %(\basereg+1), %(\basereg+3) + vzip8 %(\basereg+2), %(\basereg+3) + vzip8 %(\basereg+0), %(\basereg+1) +.endif +.endm + +/* + * This is a macro for implementing cache preload. The main idea is that + * cache preload logic is mostly independent from the rest of pixels + * processing code. It starts at the top left pixel and moves forward + * across pixels and can jump across scanlines. Prefetch distance is + * handled in an 'incremental' way: it starts from 0 and advances to the + * optimal distance over time. After reaching optimal prefetch distance, + * it is kept constant. There are some checks which prevent prefetching + * unneeded pixel lines below the image (but it still can prefetch a bit + * more data on the right side of the image - not a big issue and may + * be actually helpful when rendering text glyphs). Additional trick is + * the use of LDR instruction for prefetch instead of PLD when moving to + * the next line, the point is that we have a high chance of getting TLB + * miss in this case, and PLD would be useless. + * + * This sounds like it may introduce a noticeable overhead (when working with + * fully cached data). But in reality, due to having a separate pipeline and + * instruction queue for NEON unit in ARM Cortex-A8, normal ARM code can + * execute simultaneously with NEON and be completely shadowed by it. Thus + * we get no performance overhead at all (*). This looks like a very nice + * feature of Cortex-A8, if used wisely. We don't have a hardware prefetcher, + * but still can implement some rather advanced prefetch logic in software + * for almost zero cost! + * + * (*) The overhead of the prefetcher is visible when running some trivial + * pixels processing like simple copy. Anyway, having prefetch is a must + * when working with the graphics data. + */ +.macro PF a, x:vararg +.if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_ADVANCED) + \a \x +.endif +.endm + +.macro cache_preload std_increment, boost_increment +.if (src_bpp_shift >= 0) || (dst_r_bpp != 0) || (mask_bpp_shift >= 0) +.if regs_shortage + PF ldr, ORIG_W, [sp] /* If we are short on regs, ORIG_W is kept on stack */ +.endif +.if \std_increment != 0 + PF add, PF_X, PF_X, #\std_increment +.endif + PF tst, PF_CTL, #0xF + PF addne, PF_X, PF_X, #\boost_increment + PF subne, PF_CTL, PF_CTL, #1 + PF cmp, PF_X, ORIG_W +.if src_bpp_shift >= 0 + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] +.endif +.if dst_r_bpp != 0 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] +.endif +.if mask_bpp_shift >= 0 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] +.endif + PF subge, PF_X, PF_X, ORIG_W + PF subges, PF_CTL, PF_CTL, #0x10 +.if src_bpp_shift >= 0 + PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! +.endif +.if dst_r_bpp != 0 + PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! +.endif +.if mask_bpp_shift >= 0 + PF ldrgeb, DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! +.endif +.endif +.endm + +.macro cache_preload_simple +.if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_SIMPLE) +.if src_bpp > 0 + pld [SRC, #(PREFETCH_DISTANCE_SIMPLE * src_bpp / 8)] +.endif +.if dst_r_bpp > 0 + pld [DST_R, #(PREFETCH_DISTANCE_SIMPLE * dst_r_bpp / 8)] +.endif +.if mask_bpp > 0 + pld [MASK, #(PREFETCH_DISTANCE_SIMPLE * mask_bpp / 8)] +.endif +.endif +.endm + +.macro fetch_mask_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK +.endm + +/* + * Macro which is used to process leading pixels until destination + * pointer is properly aligned (at 16 bytes boundary). When destination + * buffer uses 16bpp format, this is unnecessary, or even pointless. + */ +.macro ensure_destination_ptr_alignment process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head +.if dst_w_bpp != 24 + tst DST_R, #0xF + beq 2f + +.irp lowbit, 1, 2, 4, 8, 16 +#ifndef __clang__ +local skip1 +#endif +.if (dst_w_bpp <= (\lowbit * 8)) && ((\lowbit * 8) < (pixblock_size * dst_w_bpp)) +.if \lowbit < 16 /* we don't need more than 16-byte alignment */ + tst DST_R, #\lowbit + beq 1f +.endif + pixld_src (\lowbit * 8 / dst_w_bpp), src_bpp, src_basereg, SRC + pixld (\lowbit * 8 / dst_w_bpp), mask_bpp, mask_basereg, MASK +.if dst_r_bpp > 0 + pixld_a (\lowbit * 8 / dst_r_bpp), dst_r_bpp, dst_r_basereg, DST_R +.else + add DST_R, DST_R, #\lowbit +.endif + PF add, PF_X, PF_X, #(\lowbit * 8 / dst_w_bpp) + sub W, W, #(\lowbit * 8 / dst_w_bpp) +1: +.endif +.endr + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + + \process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple + \process_pixblock_tail + + pixinterleave dst_w_bpp, dst_w_basereg +.irp lowbit, 1, 2, 4, 8, 16 +.if (dst_w_bpp <= (\lowbit * 8)) && ((\lowbit * 8) < (pixblock_size * dst_w_bpp)) +.if \lowbit < 16 /* we don't need more than 16-byte alignment */ + tst DST_W, #\lowbit + beq 1f +.endif + pixst_a (\lowbit * 8 / dst_w_bpp), dst_w_bpp, dst_w_basereg, DST_W +1: +.endif +.endr +.endif +2: +.endm + +/* + * Special code for processing up to (pixblock_size - 1) remaining + * trailing pixels. As SIMD processing performs operation on + * pixblock_size pixels, anything smaller than this has to be loaded + * and stored in a special way. Loading and storing of pixel data is + * performed in such a way that we fill some 'slots' in the NEON + * registers (some slots naturally are unused), then perform compositing + * operation as usual. In the end, the data is taken from these 'slots' + * and saved to memory. + * + * cache_preload_flag - allows to suppress prefetch if + * set to 0 + * dst_aligned_flag - selects whether destination buffer + * is aligned + */ +.macro process_trailing_pixels cache_preload_flag, \ + dst_aligned_flag, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + tst W, #(pixblock_size - 1) + beq 2f +.irp chunk_size, 16, 8, 4, 2, 1 +.if pixblock_size > \chunk_size + tst W, #\chunk_size + beq 1f + pixld_src \chunk_size, src_bpp, src_basereg, SRC + pixld \chunk_size, mask_bpp, mask_basereg, MASK +.if \dst_aligned_flag != 0 + pixld_a \chunk_size, dst_r_bpp, dst_r_basereg, DST_R +.else + pixld \chunk_size, dst_r_bpp, dst_r_basereg, DST_R +.endif +.if \cache_preload_flag != 0 + PF add, PF_X, PF_X, #\chunk_size +.endif +1: +.endif +.endr + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + + \process_pixblock_head +.if \cache_preload_flag != 0 + cache_preload 0, pixblock_size + cache_preload_simple +.endif + \process_pixblock_tail + pixinterleave dst_w_bpp, dst_w_basereg +.irp chunk_size, 16, 8, 4, 2, 1 +.if pixblock_size > \chunk_size + tst W, #\chunk_size + beq 1f +.if \dst_aligned_flag != 0 + pixst_a \chunk_size, dst_w_bpp, dst_w_basereg, DST_W +.else + pixst \chunk_size, dst_w_bpp, dst_w_basereg, DST_W +.endif +1: +.endif +.endr +2: +.endm + +/* + * Macro, which performs all the needed operations to switch to the next + * scanline and start the next loop iteration unless all the scanlines + * are already processed. + */ +.macro advance_to_next_scanline start_of_loop_label +.if regs_shortage + ldrd W, [sp] /* load W and H (width and height) from stack */ +.else + mov W, ORIG_W +.endif + add DST_W, DST_W, DST_STRIDE, lsl #dst_bpp_shift +.if src_bpp != 0 + add SRC, SRC, SRC_STRIDE, lsl #src_bpp_shift +.endif +.if mask_bpp != 0 + add MASK, MASK, MASK_STRIDE, lsl #mask_bpp_shift +.endif +.if (dst_w_bpp != 24) + sub DST_W, DST_W, W, lsl #dst_bpp_shift +.endif +.if (src_bpp != 24) && (src_bpp != 0) + sub SRC, SRC, W, lsl #src_bpp_shift +.endif +.if (mask_bpp != 24) && (mask_bpp != 0) + sub MASK, MASK, W, lsl #mask_bpp_shift +.endif + subs H, H, #1 + mov DST_R, DST_W +.if regs_shortage + str H, [sp, #4] /* save updated height to stack */ +.endif + bge \start_of_loop_label +.endm + +/* + * Registers are allocated in the following way by default: + * d0, d1, d2, d3 - reserved for loading source pixel data + * d4, d5, d6, d7 - reserved for loading destination pixel data + * d24, d25, d26, d27 - reserved for loading mask pixel data + * d28, d29, d30, d31 - final destination pixel data for writeback to memory + */ +.macro generate_composite_function fname, \ + src_bpp_, \ + mask_bpp_, \ + dst_w_bpp_, \ + flags, \ + pixblock_size_, \ + prefetch_distance, \ + init, \ + cleanup, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + + pixman_asm_function \fname + + push {r4-r12, lr} /* save all registers */ + +/* + * Select prefetch type for this function. If prefetch distance is + * set to 0 or one of the color formats is 24bpp, SIMPLE prefetch + * has to be used instead of ADVANCED. + */ + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_DEFAULT +.if \prefetch_distance == 0 + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE +.elseif (PREFETCH_TYPE_CURRENT > PREFETCH_TYPE_SIMPLE) && \ + ((\src_bpp_ == 24) || (\mask_bpp_ == 24) || (\dst_w_bpp_ == 24)) + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_SIMPLE +.endif + +/* + * Make some macro arguments globally visible and accessible + * from other macros + */ + .set src_bpp, \src_bpp_ + .set mask_bpp, \mask_bpp_ + .set dst_w_bpp, \dst_w_bpp_ + .set pixblock_size, \pixblock_size_ + .set dst_w_basereg, \dst_w_basereg_ + .set dst_r_basereg, \dst_r_basereg_ + .set src_basereg, \src_basereg_ + .set mask_basereg, \mask_basereg_ + + .macro pixld_src x:vararg + pixld \x + .endm + .macro fetch_src_pixblock + pixld_src pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + .endm +/* + * Assign symbolic names to registers + */ + W .req r0 /* width (is updated during processing) */ + H .req r1 /* height (is updated during processing) */ + DST_W .req r2 /* destination buffer pointer for writes */ + DST_STRIDE .req r3 /* destination image stride */ + SRC .req r4 /* source buffer pointer */ + SRC_STRIDE .req r5 /* source image stride */ + DST_R .req r6 /* destination buffer pointer for reads */ + + MASK .req r7 /* mask pointer */ + MASK_STRIDE .req r8 /* mask stride */ + + PF_CTL .req r9 /* combined lines counter and prefetch */ + /* distance increment counter */ + PF_X .req r10 /* pixel index in a scanline for current */ + /* pretetch position */ + PF_SRC .req r11 /* pointer to source scanline start */ + /* for prefetch purposes */ + PF_DST .req r12 /* pointer to destination scanline start */ + /* for prefetch purposes */ + PF_MASK .req r14 /* pointer to mask scanline start */ + /* for prefetch purposes */ +/* + * Check whether we have enough registers for all the local variables. + * If we don't have enough registers, original width and height are + * kept on top of stack (and 'regs_shortage' variable is set to indicate + * this for the rest of code). Even if there are enough registers, the + * allocation scheme may be a bit different depending on whether source + * or mask is not used. + */ +.if (PREFETCH_TYPE_CURRENT < PREFETCH_TYPE_ADVANCED) + ORIG_W .req r10 /* saved original width */ + DUMMY .req r12 /* temporary register */ + .set regs_shortage, 0 +.elseif mask_bpp == 0 + ORIG_W .req r7 /* saved original width */ + DUMMY .req r8 /* temporary register */ + .set regs_shortage, 0 +.elseif src_bpp == 0 + ORIG_W .req r4 /* saved original width */ + DUMMY .req r5 /* temporary register */ + .set regs_shortage, 0 +.else + ORIG_W .req r1 /* saved original width */ + DUMMY .req r1 /* temporary register */ + .set regs_shortage, 1 +.endif + + .set mask_bpp_shift, -1 +.if src_bpp == 32 + .set src_bpp_shift, 2 +.elseif src_bpp == 24 + .set src_bpp_shift, 0 +.elseif src_bpp == 16 + .set src_bpp_shift, 1 +.elseif src_bpp == 8 + .set src_bpp_shift, 0 +.elseif src_bpp == 0 + .set src_bpp_shift, -1 +.else + .error "requested src bpp (src_bpp) is not supported" +.endif +.if mask_bpp == 32 + .set mask_bpp_shift, 2 +.elseif mask_bpp == 24 + .set mask_bpp_shift, 0 +.elseif mask_bpp == 8 + .set mask_bpp_shift, 0 +.elseif mask_bpp == 0 + .set mask_bpp_shift, -1 +.else + .error "requested mask bpp (mask_bpp) is not supported" +.endif +.if dst_w_bpp == 32 + .set dst_bpp_shift, 2 +.elseif dst_w_bpp == 24 + .set dst_bpp_shift, 0 +.elseif dst_w_bpp == 16 + .set dst_bpp_shift, 1 +.elseif dst_w_bpp == 8 + .set dst_bpp_shift, 0 +.else + .error "requested dst bpp (dst_w_bpp) is not supported" +.endif + +.if (((\flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp +.else + .set dst_r_bpp, 0 +.endif +.if (((\flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 +.else + .set DEINTERLEAVE_32BPP_ENABLED, 0 +.endif + +.if \prefetch_distance < 0 || \prefetch_distance > 15 + .error "invalid prefetch distance (\prefetch_distance)" +.endif + +.if src_bpp > 0 + ldr SRC, [sp, #40] +.endif +.if mask_bpp > 0 + ldr MASK, [sp, #48] +.endif + PF mov, PF_X, #0 +.if src_bpp > 0 + ldr SRC_STRIDE, [sp, #44] +.endif +.if mask_bpp > 0 + ldr MASK_STRIDE, [sp, #52] +.endif + mov DST_R, DST_W + +.if src_bpp == 24 + sub SRC_STRIDE, SRC_STRIDE, W + sub SRC_STRIDE, SRC_STRIDE, W, lsl #1 +.endif +.if mask_bpp == 24 + sub MASK_STRIDE, MASK_STRIDE, W + sub MASK_STRIDE, MASK_STRIDE, W, lsl #1 +.endif +.if dst_w_bpp == 24 + sub DST_STRIDE, DST_STRIDE, W + sub DST_STRIDE, DST_STRIDE, W, lsl #1 +.endif + +/* + * Setup advanced prefetcher initial state + */ + PF mov, PF_SRC, SRC + PF mov, PF_DST, DST_R + PF mov, PF_MASK, MASK + /* PF_CTL = prefetch_distance | ((h - 1) << 4) */ + PF mov, PF_CTL, H, lsl #4 + PF add, PF_CTL, #(\prefetch_distance - 0x10) + + \init +.if regs_shortage + push {r0, r1} +.endif + subs H, H, #1 +.if regs_shortage + str H, [sp, #4] /* save updated height to stack */ +.else + mov ORIG_W, W +.endif + blt 9f + cmp W, #(pixblock_size * 2) + blt 8f +/* + * This is the start of the pipelined loop, which if optimized for + * long scanlines + */ +0: + ensure_destination_ptr_alignment \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + PF add, PF_X, PF_X, #pixblock_size + \process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple + subs W, W, #(pixblock_size * 2) + blt 2f +1: + \process_pixblock_tail_head + cache_preload_simple + subs W, W, #pixblock_size + bge 1b +2: + \process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 1, 1, \ + \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + advance_to_next_scanline 0b + +.if regs_shortage + pop {r0, r1} +.endif + \cleanup + pop {r4-r12, pc} /* exit */ +/* + * This is the start of the loop, designed to process images with small width + * (less than pixblock_size * 2 pixels). In this case neither pipelining + * nor prefetch are used. + */ +8: + /* Process exactly pixblock_size pixels if needed */ + tst W, #pixblock_size + beq 1f + pixld pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + \process_pixblock_head + \process_pixblock_tail + pixst pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W +1: + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 0, 0, \ + \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + advance_to_next_scanline 8b +9: +.if regs_shortage + pop {r0, r1} +.endif + \cleanup + pop {r4-r12, pc} /* exit */ + + .purgem fetch_src_pixblock + .purgem pixld_src + + .unreq SRC + .unreq MASK + .unreq DST_R + .unreq DST_W + .unreq ORIG_W + .unreq W + .unreq H + .unreq SRC_STRIDE + .unreq DST_STRIDE + .unreq MASK_STRIDE + .unreq PF_CTL + .unreq PF_X + .unreq PF_SRC + .unreq PF_DST + .unreq PF_MASK + .unreq DUMMY + pixman_end_asm_function +.endm + +/* + * A simplified variant of function generation template for a single + * scanline processing (for implementing pixman combine functions) + */ +.macro generate_composite_function_scanline use_nearest_scaling, \ + fname, \ + src_bpp_, \ + mask_bpp_, \ + dst_w_bpp_, \ + flags, \ + pixblock_size_, \ + init, \ + cleanup, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + + pixman_asm_function \fname + + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE +/* + * Make some macro arguments globally visible and accessible + * from other macros + */ + .set src_bpp, \src_bpp_ + .set mask_bpp, \mask_bpp_ + .set dst_w_bpp, \dst_w_bpp_ + .set pixblock_size, \pixblock_size_ + .set dst_w_basereg, \dst_w_basereg_ + .set dst_r_basereg, \dst_r_basereg_ + .set src_basereg, \src_basereg_ + .set mask_basereg, \mask_basereg_ + +.if \use_nearest_scaling != 0 + /* + * Assign symbolic names to registers for nearest scaling + */ + W .req r0 + DST_W .req r1 + SRC .req r2 + VX .req r3 + UNIT_X .req ip + MASK .req lr + TMP1 .req r4 + TMP2 .req r5 + DST_R .req r6 + SRC_WIDTH_FIXED .req r7 + + .macro pixld_src x:vararg + pixld_s \x + .endm + + ldr UNIT_X, [sp] + push {r4-r8, lr} + ldr SRC_WIDTH_FIXED, [sp, #(24 + 4)] + .if mask_bpp != 0 + ldr MASK, [sp, #(24 + 8)] + .endif +.else + /* + * Assign symbolic names to registers + */ + W .req r0 /* width (is updated during processing) */ + DST_W .req r1 /* destination buffer pointer for writes */ + SRC .req r2 /* source buffer pointer */ + DST_R .req ip /* destination buffer pointer for reads */ + MASK .req r3 /* mask pointer */ + + .macro pixld_src x:vararg + pixld \x + .endm +.endif + +.if (((\flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp +.else + .set dst_r_bpp, 0 +.endif +.if (((\flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 +.else + .set DEINTERLEAVE_32BPP_ENABLED, 0 +.endif + + .macro fetch_src_pixblock + pixld_src pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + .endm + + \init + mov DST_R, DST_W + + cmp W, #pixblock_size + blt 8f + + ensure_destination_ptr_alignment \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + + subs W, W, #pixblock_size + blt 7f + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + \process_pixblock_head + subs W, W, #pixblock_size + blt 2f +1: + \process_pixblock_tail_head + subs W, W, #pixblock_size + bge 1b +2: + \process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W +7: + /* Process the remaining trailing pixels in the scanline (dst aligned) */ + process_trailing_pixels 0, 1, \ + \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + + \cleanup +.if \use_nearest_scaling != 0 + pop {r4-r8, pc} /* exit */ +.else + bx lr /* exit */ +.endif +8: + /* Process the remaining trailing pixels in the scanline (dst unaligned) */ + process_trailing_pixels 0, 0, \ + \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + + \cleanup + +.if \use_nearest_scaling != 0 + pop {r4-r8, pc} /* exit */ + + .unreq DST_R + .unreq SRC + .unreq W + .unreq VX + .unreq UNIT_X + .unreq TMP1 + .unreq TMP2 + .unreq DST_W + .unreq MASK + .unreq SRC_WIDTH_FIXED + +.else + bx lr /* exit */ + + .unreq SRC + .unreq MASK + .unreq DST_R + .unreq DST_W + .unreq W +.endif + + .purgem fetch_src_pixblock + .purgem pixld_src + + pixman_end_asm_function +.endm + +.macro generate_composite_function_single_scanline x:vararg + generate_composite_function_scanline 0, \x +.endm + +.macro generate_composite_function_nearest_scanline x:vararg + generate_composite_function_scanline 1, \x +.endm + +/* Default prologue/epilogue, nothing special needs to be done */ + +.macro default_init +.endm + +.macro default_cleanup +.endm + +/* + * Prologue/epilogue variant which additionally saves/restores d8-d15 + * registers (they need to be saved/restored by callee according to ABI). + * This is required if the code needs to use all the NEON registers. + */ + +.macro default_init_need_all_regs + vpush {d8-d15} +.endm + +.macro default_cleanup_need_all_regs + vpop {d8-d15} +.endm + +/******************************************************************************/ + +/* + * Conversion of 8 r5g6b6 pixels packed in 128-bit register (in) + * into a planar a8r8g8b8 format (with a, r, g, b color components + * stored into 64-bit registers out_a, out_r, out_g, out_b respectively). + * + * Warning: the conversion is destructive and the original + * value (in) is lost. + */ +.macro convert_0565_to_8888 in, out_a, out_r, out_g, out_b + vshrn.u16 \out_r, \in, #8 + vshrn.u16 \out_g, \in, #3 + vsli.u16 \in, \in, #5 + vmov.u8 \out_a, #255 + vsri.u8 \out_r, \out_r, #5 + vsri.u8 \out_g, \out_g, #6 + vshrn.u16 \out_b, \in, #2 +.endm + +.macro convert_0565_to_x888 in, out_r, out_g, out_b + vshrn.u16 \out_r, \in, #8 + vshrn.u16 \out_g, \in, #3 + vsli.u16 \in, \in, #5 + vsri.u8 \out_r, \out_r, #5 + vsri.u8 \out_g, \out_g, #6 + vshrn.u16 \out_b, \in, #2 +.endm + +/* + * Conversion from planar a8r8g8b8 format (with a, r, g, b color components + * in 64-bit registers in_a, in_r, in_g, in_b respectively) into 8 r5g6b6 + * pixels packed in 128-bit register (out). Requires two temporary 128-bit + * registers (tmp1, tmp2) + */ +.macro convert_8888_to_0565 in_r, in_g, in_b, out, tmp1, tmp2 + vshll.u8 \tmp1, \in_g, #8 + vshll.u8 \out, \in_r, #8 + vshll.u8 \tmp2, \in_b, #8 + vsri.u16 \out, \tmp1, #5 + vsri.u16 \out, \tmp2, #11 +.endm + +/* + * Conversion of four r5g6b5 pixels (in) to four x8r8g8b8 pixels + * returned in (out0, out1) registers pair. Requires one temporary + * 64-bit register (tmp). 'out1' and 'in' may overlap, the original + * value from 'in' is lost + */ +.macro convert_four_0565_to_x888_packed in, out0, out1, tmp + vshl.u16 \out0, \in, #5 /* G top 6 bits */ + vshl.u16 \tmp, \in, #11 /* B top 5 bits */ + vsri.u16 \in, \in, #5 /* R is ready in top bits */ + vsri.u16 \out0, \out0, #6 /* G is ready in top bits */ + vsri.u16 \tmp, \tmp, #5 /* B is ready in top bits */ + vshr.u16 \out1, \in, #8 /* R is in place */ + vsri.u16 \out0, \tmp, #8 /* G & B is in place */ + vzip.u16 \out0, \out1 /* everything is in place */ +.endm diff --git a/gfx/cairo/libpixman/src/pixman-arm-neon.c b/gfx/cairo/libpixman/src/pixman-arm-neon.c new file mode 100644 index 0000000000..62c944222d --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-neon.c @@ -0,0 +1,493 @@ +/* + * Copyright © 2009 ARM Ltd, Movial Creative Technologies Oy + * + * 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 ARM Ltd not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. ARM Ltd makes 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. + * + * Author: Ian Rickards (ian.rickards@arm.com) + * Author: Jonathan Morton (jonathan.morton@movial.com) + * Author: Markku Vire (markku.vire@movial.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "pixman-private.h" +#include "pixman-arm-common.h" + +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_8888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_x888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0565_0565, + uint16_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0888_0888, + uint8_t, 3, uint8_t, 3) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_8888_0565, + uint32_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0565_8888, + uint16_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0888_8888_rev, + uint8_t, 3, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_0888_0565_rev, + uint8_t, 3, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_pixbuf_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, src_rpixbuf_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, add_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, add_8888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, over_8888_0565, + uint32_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, over_8888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, out_reverse_8_0565, + uint8_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (neon, out_reverse_8_8888, + uint8_t, 1, uint32_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, neon, over_n_0565, + uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, neon, over_n_8888, + uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, neon, over_reverse_n_8888, + uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_DST (0, neon, in_n_8, + uint8_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8_0565, + uint8_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8_8888, + uint8_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8888_8888_ca, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8888_0565_ca, + uint32_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, over_n_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, add_n_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, neon, add_n_8_8888, + uint8_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (0, neon, src_n_8_8888, + uint8_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (0, neon, src_n_8_8, + uint8_t, 1, uint8_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, neon, over_8888_n_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, neon, over_8888_n_0565, + uint32_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, neon, over_0565_n_0565, + uint16_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, neon, add_8888_n_8888, + uint32_t, 1, uint32_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, add_8_8_8, + uint8_t, 1, uint8_t, 1, uint8_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, add_0565_8_0565, + uint16_t, 1, uint8_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, add_8888_8_8888, + uint32_t, 1, uint8_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, add_8888_8888_8888, + uint32_t, 1, uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, over_8888_8_8888, + uint32_t, 1, uint8_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, over_8888_8888_8888, + uint32_t, 1, uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, over_8888_8_0565, + uint32_t, 1, uint8_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_MASK_DST (neon, over_0565_8_0565, + uint16_t, 1, uint8_t, 1, uint16_t, 1) + +PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (neon, 8888_8888, OVER, + uint32_t, uint32_t) +PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (neon, 8888_0565, OVER, + uint32_t, uint16_t) +PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (neon, 8888_0565, SRC, + uint32_t, uint16_t) +PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (neon, 0565_8888, SRC, + uint16_t, uint32_t) + +PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_A8_DST (SKIP_ZERO_SRC, neon, 8888_8_0565, + OVER, uint32_t, uint16_t) +PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_A8_DST (SKIP_ZERO_SRC, neon, 0565_8_0565, + OVER, uint16_t, uint16_t) + +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (0, neon, 8888_8888, SRC, + uint32_t, uint32_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (0, neon, 8888_0565, SRC, + uint32_t, uint16_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (0, neon, 0565_x888, SRC, + uint16_t, uint32_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (0, neon, 0565_0565, SRC, + uint16_t, uint16_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (SKIP_ZERO_SRC, neon, 8888_8888, OVER, + uint32_t, uint32_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST (SKIP_ZERO_SRC, neon, 8888_8888, ADD, + uint32_t, uint32_t) + +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (0, neon, 8888_8_8888, SRC, + uint32_t, uint32_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (0, neon, 8888_8_0565, SRC, + uint32_t, uint16_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (0, neon, 0565_8_x888, SRC, + uint16_t, uint32_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (0, neon, 0565_8_0565, SRC, + uint16_t, uint16_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (SKIP_ZERO_SRC, neon, 8888_8_8888, OVER, + uint32_t, uint32_t) +PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_A8_DST (SKIP_ZERO_SRC, neon, 8888_8_8888, ADD, + uint32_t, uint32_t) + +void +pixman_composite_src_n_8_asm_neon (int32_t w, + int32_t h, + uint8_t *dst, + int32_t dst_stride, + uint8_t src); + +void +pixman_composite_src_n_0565_asm_neon (int32_t w, + int32_t h, + uint16_t *dst, + int32_t dst_stride, + uint16_t src); + +void +pixman_composite_src_n_8888_asm_neon (int32_t w, + int32_t h, + uint32_t *dst, + int32_t dst_stride, + uint32_t src); + +static pixman_bool_t +arm_neon_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t _xor) +{ + /* stride is always multiple of 32bit units in pixman */ + int32_t byte_stride = stride * sizeof(uint32_t); + + switch (bpp) + { + case 8: + pixman_composite_src_n_8_asm_neon ( + width, + height, + (uint8_t *)(((char *) bits) + y * byte_stride + x), + byte_stride, + _xor & 0xff); + return TRUE; + case 16: + pixman_composite_src_n_0565_asm_neon ( + width, + height, + (uint16_t *)(((char *) bits) + y * byte_stride + x * 2), + byte_stride / 2, + _xor & 0xffff); + return TRUE; + case 32: + pixman_composite_src_n_8888_asm_neon ( + width, + height, + (uint32_t *)(((char *) bits) + y * byte_stride + x * 4), + byte_stride / 4, + _xor); + return TRUE; + default: + return FALSE; + } +} + +static pixman_bool_t +arm_neon_blt (pixman_implementation_t *imp, + uint32_t * src_bits, + uint32_t * dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height) +{ + if (src_bpp != dst_bpp) + return FALSE; + + switch (src_bpp) + { + case 16: + pixman_composite_src_0565_0565_asm_neon ( + width, height, + (uint16_t *)(((char *) dst_bits) + + dest_y * dst_stride * 4 + dest_x * 2), dst_stride * 2, + (uint16_t *)(((char *) src_bits) + + src_y * src_stride * 4 + src_x * 2), src_stride * 2); + return TRUE; + case 32: + pixman_composite_src_8888_8888_asm_neon ( + width, height, + (uint32_t *)(((char *) dst_bits) + + dest_y * dst_stride * 4 + dest_x * 4), dst_stride, + (uint32_t *)(((char *) src_bits) + + src_y * src_stride * 4 + src_x * 4), src_stride); + return TRUE; + default: + return FALSE; + } +} + +static const pixman_fast_path_t arm_neon_fast_paths[] = +{ + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, neon_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, neon_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, neon_composite_src_8888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, neon_composite_src_8888_0565), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, neon_composite_src_8888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, neon_composite_src_8888_0565), + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, a8r8g8b8, neon_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, x8r8g8b8, neon_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, a8b8g8r8, neon_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, x8b8g8r8, neon_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, neon_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, neon_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, neon_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, neon_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, neon_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, neon_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, neon_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, neon_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, r8g8b8, null, r8g8b8, neon_composite_src_0888_0888), + PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, x8r8g8b8, neon_composite_src_0888_8888_rev), + PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, r5g6b5, neon_composite_src_0888_0565_rev), + PIXMAN_STD_FAST_PATH (SRC, pixbuf, pixbuf, a8r8g8b8, neon_composite_src_pixbuf_8888), + PIXMAN_STD_FAST_PATH (SRC, pixbuf, pixbuf, a8b8g8r8, neon_composite_src_rpixbuf_8888), + PIXMAN_STD_FAST_PATH (SRC, rpixbuf, rpixbuf, a8r8g8b8, neon_composite_src_rpixbuf_8888), + PIXMAN_STD_FAST_PATH (SRC, rpixbuf, rpixbuf, a8b8g8r8, neon_composite_src_pixbuf_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8r8g8b8, neon_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, neon_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, neon_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, neon_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8, neon_composite_src_n_8_8), + + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8, neon_composite_over_n_8_8), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, neon_composite_over_n_8_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, neon_composite_over_n_8_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, neon_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, neon_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, neon_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, neon_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, r5g6b5, neon_composite_over_n_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, neon_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, neon_composite_over_n_8888), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, neon_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, neon_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, neon_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, neon_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, neon_composite_over_n_8888_0565_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, neon_composite_over_n_8888_0565_ca), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, neon_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, neon_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, r5g6b5, neon_composite_over_8888_n_0565), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, b5g6r5, neon_composite_over_8888_n_0565), + PIXMAN_STD_FAST_PATH (OVER, r5g6b5, solid, r5g6b5, neon_composite_over_0565_n_0565), + PIXMAN_STD_FAST_PATH (OVER, b5g6r5, solid, b5g6r5, neon_composite_over_0565_n_0565), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, a8r8g8b8, neon_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, x8r8g8b8, neon_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, a8b8g8r8, neon_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, x8b8g8r8, neon_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, r5g6b5, neon_composite_over_8888_8_0565), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, b5g6r5, neon_composite_over_8888_8_0565), + PIXMAN_STD_FAST_PATH (OVER, r5g6b5, a8, r5g6b5, neon_composite_over_0565_8_0565), + PIXMAN_STD_FAST_PATH (OVER, b5g6r5, a8, b5g6r5, neon_composite_over_0565_8_0565), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, x8r8g8b8, neon_composite_over_8888_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, a8r8g8b8, neon_composite_over_8888_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, neon_composite_over_8888_0565), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, neon_composite_over_8888_0565), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, neon_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, neon_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, neon_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, neon_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, null, a8r8g8b8, neon_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, null, a8b8g8r8, neon_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, neon_composite_add_n_8_8), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, x8r8g8b8, neon_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8r8g8b8, neon_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, x8b8g8r8, neon_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8b8g8r8, neon_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, a8, a8, a8, neon_composite_add_8_8_8), + PIXMAN_STD_FAST_PATH (ADD, r5g6b5, a8, r5g6b5, neon_composite_add_0565_8_0565), + PIXMAN_STD_FAST_PATH (ADD, b5g6r5, a8, b5g6r5, neon_composite_add_0565_8_0565), + PIXMAN_STD_FAST_PATH (ADD, x8r8g8b8, a8, x8r8g8b8, neon_composite_add_8888_8_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8, x8r8g8b8, neon_composite_add_8888_8_8888), + PIXMAN_STD_FAST_PATH (ADD, x8b8g8r8, a8, x8b8g8r8, neon_composite_add_8888_8_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, a8, x8b8g8r8, neon_composite_add_8888_8_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8, a8r8g8b8, neon_composite_add_8888_8_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, a8, a8b8g8r8, neon_composite_add_8888_8_8888), + PIXMAN_STD_FAST_PATH (ADD, x8r8g8b8, a8r8g8b8, x8r8g8b8, neon_composite_add_8888_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, x8r8g8b8, neon_composite_add_8888_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, a8r8g8b8, neon_composite_add_8888_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, x8r8g8b8, solid, x8r8g8b8, neon_composite_add_8888_n_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, solid, x8r8g8b8, neon_composite_add_8888_n_8888), + PIXMAN_STD_FAST_PATH (ADD, x8b8g8r8, solid, x8b8g8r8, neon_composite_add_8888_n_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, solid, x8b8g8r8, neon_composite_add_8888_n_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, solid, a8r8g8b8, neon_composite_add_8888_n_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, solid, a8b8g8r8, neon_composite_add_8888_n_8888), + PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, neon_composite_add_8_8), + PIXMAN_STD_FAST_PATH (ADD, x8r8g8b8, null, x8r8g8b8, neon_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, x8r8g8b8, neon_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, x8b8g8r8, null, x8b8g8r8, neon_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, x8b8g8r8, neon_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, neon_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, neon_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (IN, solid, null, a8, neon_composite_in_n_8), + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, neon_composite_over_reverse_n_8888), + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, neon_composite_over_reverse_n_8888), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, r5g6b5, neon_composite_out_reverse_8_0565), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, b5g6r5, neon_composite_out_reverse_8_0565), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, x8r8g8b8, neon_composite_out_reverse_8_8888), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8r8g8b8, neon_composite_out_reverse_8_8888), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, x8b8g8r8, neon_composite_out_reverse_8_8888), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8b8g8r8, neon_composite_out_reverse_8_8888), + + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, neon_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, neon_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, neon_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, neon_8888_8888), + + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, r5g6b5, neon_8888_0565), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, b5g6r5, neon_8888_0565), + + SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, r5g6b5, neon_8888_0565), + SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, r5g6b5, neon_8888_0565), + SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, b5g6r5, neon_8888_0565), + SIMPLE_NEAREST_FAST_PATH (SRC, x8b8g8r8, b5g6r5, neon_8888_0565), + + SIMPLE_NEAREST_FAST_PATH (SRC, b5g6r5, x8b8g8r8, neon_0565_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, x8r8g8b8, neon_0565_8888), + /* Note: NONE repeat is not supported yet */ + SIMPLE_NEAREST_FAST_PATH_COVER (SRC, r5g6b5, a8r8g8b8, neon_0565_8888), + SIMPLE_NEAREST_FAST_PATH_COVER (SRC, b5g6r5, a8b8g8r8, neon_0565_8888), + SIMPLE_NEAREST_FAST_PATH_PAD (SRC, r5g6b5, a8r8g8b8, neon_0565_8888), + SIMPLE_NEAREST_FAST_PATH_PAD (SRC, b5g6r5, a8b8g8r8, neon_0565_8888), + + PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8r8g8b8, r5g6b5, neon_8888_8_0565), + PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8b8g8r8, b5g6r5, neon_8888_8_0565), + + PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, r5g6b5, r5g6b5, neon_0565_8_0565), + PIXMAN_ARM_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, b5g6r5, b5g6r5, neon_0565_8_0565), + + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, neon_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, neon_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, neon_8888_8888), + + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, r5g6b5, neon_8888_0565), + SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, r5g6b5, neon_8888_0565), + + SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, x8r8g8b8, neon_0565_x888), + SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, r5g6b5, neon_0565_0565), + + SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, neon_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, neon_8888_8888), + + SIMPLE_BILINEAR_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, neon_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (ADD, a8r8g8b8, x8r8g8b8, neon_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (ADD, x8r8g8b8, x8r8g8b8, neon_8888_8888), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, neon_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, neon_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, neon_8888_8_8888), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, r5g6b5, neon_8888_8_0565), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, x8r8g8b8, r5g6b5, neon_8888_8_0565), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, r5g6b5, x8r8g8b8, neon_0565_8_x888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, r5g6b5, r5g6b5, neon_0565_8_0565), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, neon_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, neon_8888_8_8888), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, neon_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, a8r8g8b8, x8r8g8b8, neon_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, x8r8g8b8, x8r8g8b8, neon_8888_8_8888), + + { PIXMAN_OP_NONE }, +}; + +#define BIND_COMBINE_U(name) \ +void \ +pixman_composite_scanline_##name##_mask_asm_neon (int32_t w, \ + const uint32_t *dst, \ + const uint32_t *src, \ + const uint32_t *mask); \ + \ +void \ +pixman_composite_scanline_##name##_asm_neon (int32_t w, \ + const uint32_t *dst, \ + const uint32_t *src); \ + \ +static void \ +neon_combine_##name##_u (pixman_implementation_t *imp, \ + pixman_op_t op, \ + uint32_t * dest, \ + const uint32_t * src, \ + const uint32_t * mask, \ + int width) \ +{ \ + if (mask) \ + pixman_composite_scanline_##name##_mask_asm_neon (width, dest, \ + src, mask); \ + else \ + pixman_composite_scanline_##name##_asm_neon (width, dest, src); \ +} + +BIND_COMBINE_U (over) +BIND_COMBINE_U (add) +BIND_COMBINE_U (out_reverse) + +pixman_implementation_t * +_pixman_implementation_create_arm_neon (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = + _pixman_implementation_create (fallback, arm_neon_fast_paths); + + imp->combine_32[PIXMAN_OP_OVER] = neon_combine_over_u; + imp->combine_32[PIXMAN_OP_ADD] = neon_combine_add_u; + imp->combine_32[PIXMAN_OP_OUT_REVERSE] = neon_combine_out_reverse_u; + + imp->blt = arm_neon_blt; + imp->fill = arm_neon_fill; + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S b/gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S new file mode 100644 index 0000000000..a06b5964ef --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S @@ -0,0 +1,165 @@ +/* + * Copyright © 2008 Mozilla Corporation + * Copyright © 2010 Nokia Corporation + * + * 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 Mozilla Corporation not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Mozilla Corporation makes 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. + * + * Author: Jeff Muizelaar (jeff@infidigm.net) + * + */ + +#ifdef __clang__ +#define subpls subspl +#endif + +/* Prevent the stack from becoming executable */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + + .text + .arch armv6 + .object_arch armv4 + .arm + .altmacro + .p2align 2 + +#include "pixman-arm-asm.h" + +/* + * Note: This code is only using armv5te instructions (not even armv6), + * but is scheduled for ARM Cortex-A8 pipeline. So it might need to + * be split into a few variants, tuned for each microarchitecture. + * + * TODO: In order to get good performance on ARM9/ARM11 cores (which don't + * have efficient write combining), it needs to be changed to use 16-byte + * aligned writes using STM instruction. + * + * Nearest scanline scaler macro template uses the following arguments: + * fname - name of the function to generate + * bpp_shift - (1 << bpp_shift) is the size of pixel in bytes + * t - type suffix for LDR/STR instructions + * prefetch_distance - prefetch in the source image by that many + * pixels ahead + * prefetch_braking_distance - stop prefetching when that many pixels are + * remaining before the end of scanline + */ + +.macro generate_nearest_scanline_func fname, bpp_shift, t, \ + prefetch_distance, \ + prefetch_braking_distance + +pixman_asm_function \fname + W .req r0 + DST .req r1 + SRC .req r2 + VX .req r3 + UNIT_X .req ip + TMP1 .req r4 + TMP2 .req r5 + VXMASK .req r6 + PF_OFFS .req r7 + SRC_WIDTH_FIXED .req r8 + + ldr UNIT_X, [sp] + push {r4, r5, r6, r7, r8, r10} + mvn VXMASK, #((1 << \bpp_shift) - 1) + ldr SRC_WIDTH_FIXED, [sp, #28] + + /* define helper macro */ + .macro scale_2_pixels + ldr\()\t TMP1, [SRC, TMP1] + and TMP2, VXMASK, VX, asr #(16 - \bpp_shift) + adds VX, VX, UNIT_X + str\()\t TMP1, [DST], #(1 << \bpp_shift) +9: subpls VX, VX, SRC_WIDTH_FIXED + bpl 9b + + ldr\()\t TMP2, [SRC, TMP2] + and TMP1, VXMASK, VX, asr #(16 - \bpp_shift) + adds VX, VX, UNIT_X + str\()\t TMP2, [DST], #(1 << \bpp_shift) +9: subpls VX, VX, SRC_WIDTH_FIXED + bpl 9b + .endm + + /* now do the scaling */ + and TMP1, VXMASK, VX, asr #(16 - \bpp_shift) + adds VX, VX, UNIT_X +9: subpls VX, VX, SRC_WIDTH_FIXED + bpl 9b + subs W, W, #(8 + \prefetch_braking_distance) + blt 2f + /* calculate prefetch offset */ + mov PF_OFFS, #\prefetch_distance + mla PF_OFFS, UNIT_X, PF_OFFS, VX +1: /* main loop, process 8 pixels per iteration with prefetch */ + pld [SRC, PF_OFFS, asr #(16 - \bpp_shift)] + add PF_OFFS, UNIT_X, lsl #3 + scale_2_pixels + scale_2_pixels + scale_2_pixels + scale_2_pixels + subs W, W, #8 + bge 1b +2: + subs W, W, #(4 - 8 - \prefetch_braking_distance) + blt 2f +1: /* process the remaining pixels */ + scale_2_pixels + scale_2_pixels + subs W, W, #4 + bge 1b +2: + tst W, #2 + beq 2f + scale_2_pixels +2: + tst W, #1 +#ifdef __clang__ + ldr\()\t\()ne TMP1, [SRC, TMP1] + str\()\t\()ne TMP1, [DST] +#else + ldrne\()\t TMP1, [SRC, TMP1] + strne\()\t TMP1, [DST] +#endif + /* cleanup helper macro */ + .purgem scale_2_pixels + .unreq DST + .unreq SRC + .unreq W + .unreq VX + .unreq UNIT_X + .unreq TMP1 + .unreq TMP2 + .unreq VXMASK + .unreq PF_OFFS + .unreq SRC_WIDTH_FIXED + /* return */ + pop {r4, r5, r6, r7, r8, r10} + bx lr +pixman_end_asm_function +.endm + +generate_nearest_scanline_func \ + pixman_scaled_nearest_scanline_0565_0565_SRC_asm_armv6, 1, h, 80, 32 + +generate_nearest_scanline_func \ + pixman_scaled_nearest_scanline_8888_8888_SRC_asm_armv6, 2, , 48, 32 diff --git a/gfx/cairo/libpixman/src/pixman-arm-simd-asm.S b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.S new file mode 100644 index 0000000000..48032183a3 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.S @@ -0,0 +1,1184 @@ +/* + * Copyright © 2012 Raspberry Pi Foundation + * Copyright © 2012 RISC OS Open Ltd + * + * 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. + * + * Author: Ben Avison (bavison@riscosopen.org) + * + */ + +#ifdef __clang__ +#define adceqs adcseq +#define ldmnedb ldmdbne +#endif + +/* Prevent the stack from becoming executable */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + + .text + .arch armv6 + .object_arch armv4 + .arm + .altmacro + .p2align 2 + +#include "pixman-arm-asm.h" +#include "pixman-arm-simd-asm.h" + +/* A head macro should do all processing which results in an output of up to + * 16 bytes, as far as the final load instruction. The corresponding tail macro + * should complete the processing of the up-to-16 bytes. The calling macro will + * sometimes choose to insert a preload or a decrement of X between them. + * cond ARM condition code for code block + * numbytes Number of output bytes that should be generated this time + * firstreg First WK register in which to place output + * unaligned_src Whether to use non-wordaligned loads of source image + * unaligned_mask Whether to use non-wordaligned loads of mask image + * preload If outputting 16 bytes causes 64 bytes to be read, whether an extra preload should be output + */ + +.macro blit_init + line_saved_regs STRIDE_D, STRIDE_S +.endm + +.macro blit_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + pixld \cond, \numbytes, \firstreg, SRC, \unaligned_src +.endm + +.macro blit_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, dst_alignment + WK4 .req STRIDE_D + WK5 .req STRIDE_S + WK6 .req MASK + WK7 .req STRIDE_M +110: pixld , 16, 0, SRC, \unaligned_src + pixld , 16, 4, SRC, \unaligned_src + pld [SRC, SCRATCH] + pixst , 16, 0, DST + pixst , 16, 4, DST + subs X, X, #32*8/src_bpp + bhs 110b + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 +.endm + +generate_composite_function \ + pixman_composite_src_8888_8888_asm_armv6, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_SPILL_LINE_VARS_WIDE | FLAG_PROCESS_PRESERVES_SCRATCH, \ + 4, /* prefetch distance */ \ + blit_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + blit_process_head, \ + nop_macro, /* process tail */ \ + blit_inner_loop + +generate_composite_function \ + pixman_composite_src_0565_0565_asm_armv6, 16, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_SPILL_LINE_VARS_WIDE | FLAG_PROCESS_PRESERVES_SCRATCH, \ + 4, /* prefetch distance */ \ + blit_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + blit_process_head, \ + nop_macro, /* process tail */ \ + blit_inner_loop + +generate_composite_function \ + pixman_composite_src_8_8_asm_armv6, 8, 0, 8, \ + FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_SPILL_LINE_VARS_WIDE | FLAG_PROCESS_PRESERVES_SCRATCH, \ + 3, /* prefetch distance */ \ + blit_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + blit_process_head, \ + nop_macro, /* process tail */ \ + blit_inner_loop + +/******************************************************************************/ + +.macro src_n_8888_init + ldr SRC, [sp, #ARGS_STACK_OFFSET] + mov STRIDE_S, SRC + mov MASK, SRC + mov STRIDE_M, SRC +.endm + +.macro src_n_0565_init + ldrh SRC, [sp, #ARGS_STACK_OFFSET] + orr SRC, SRC, lsl #16 + mov STRIDE_S, SRC + mov MASK, SRC + mov STRIDE_M, SRC +.endm + +.macro src_n_8_init + ldrb SRC, [sp, #ARGS_STACK_OFFSET] + orr SRC, SRC, lsl #8 + orr SRC, SRC, lsl #16 + mov STRIDE_S, SRC + mov MASK, SRC + mov STRIDE_M, SRC +.endm + +.macro fill_process_tail cond, numbytes, firstreg + WK4 .req SRC + WK5 .req STRIDE_S + WK6 .req MASK + WK7 .req STRIDE_M + pixst \cond, \numbytes, 4, DST + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 +.endm + +generate_composite_function \ + pixman_composite_src_n_8888_asm_armv6, 0, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_PSR | FLAG_PROCESS_DOES_STORE | FLAG_PROCESS_PRESERVES_SCRATCH \ + 0, /* prefetch distance doesn't apply */ \ + src_n_8888_init \ + nop_macro, /* newline */ \ + nop_macro /* cleanup */ \ + nop_macro /* process head */ \ + fill_process_tail + +generate_composite_function \ + pixman_composite_src_n_0565_asm_armv6, 0, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_PSR | FLAG_PROCESS_DOES_STORE | FLAG_PROCESS_PRESERVES_SCRATCH \ + 0, /* prefetch distance doesn't apply */ \ + src_n_0565_init \ + nop_macro, /* newline */ \ + nop_macro /* cleanup */ \ + nop_macro /* process head */ \ + fill_process_tail + +generate_composite_function \ + pixman_composite_src_n_8_asm_armv6, 0, 0, 8, \ + FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_PSR | FLAG_PROCESS_DOES_STORE | FLAG_PROCESS_PRESERVES_SCRATCH \ + 0, /* prefetch distance doesn't apply */ \ + src_n_8_init \ + nop_macro, /* newline */ \ + nop_macro /* cleanup */ \ + nop_macro /* process head */ \ + fill_process_tail + +/******************************************************************************/ + +.macro src_x888_8888_pixel, cond, reg + orr\()\cond WK\()\reg, WK\()\reg, #0xFF000000 +.endm + +.macro pixman_composite_src_x888_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + pixld \cond, \numbytes, \firstreg, SRC, \unaligned_src +.endm + +.macro pixman_composite_src_x888_8888_process_tail cond, numbytes, firstreg + src_x888_8888_pixel \cond, %(\firstreg+0) + .if \numbytes >= 8 + src_x888_8888_pixel \cond, %(\firstreg+1) + .if \numbytes == 16 + src_x888_8888_pixel \cond, %(\firstreg+2) + src_x888_8888_pixel \cond, %(\firstreg+3) + .endif + .endif +.endm + +generate_composite_function \ + pixman_composite_src_x888_8888_asm_armv6, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_SCRATCH, \ + 3, /* prefetch distance */ \ + nop_macro, /* init */ \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + pixman_composite_src_x888_8888_process_head, \ + pixman_composite_src_x888_8888_process_tail + +/******************************************************************************/ + +.macro src_0565_8888_init + /* Hold loop invariants in MASK and STRIDE_M */ + ldr MASK, =0x07E007E0 + mov STRIDE_M, #0xFF000000 + /* Set GE[3:0] to 1010 so SEL instructions do what we want */ + ldr SCRATCH, =0x80008000 + uadd8 SCRATCH, SCRATCH, SCRATCH +.endm + +.macro src_0565_8888_2pixels, reg1, reg2 + and SCRATCH, WK\()\reg1, MASK @ 00000GGGGGG0000000000gggggg00000 + bic WK\()\reg2, WK\()\reg1, MASK @ RRRRR000000BBBBBrrrrr000000bbbbb + orr SCRATCH, SCRATCH, SCRATCH, lsr #6 @ 00000GGGGGGGGGGGG0000ggggggggggg + mov WK\()\reg1, WK\()\reg2, lsl #16 @ rrrrr000000bbbbb0000000000000000 + mov SCRATCH, SCRATCH, ror #19 @ GGGG0000ggggggggggg00000GGGGGGGG + bic WK\()\reg2, WK\()\reg2, WK\()\reg1, lsr #16 @ RRRRR000000BBBBB0000000000000000 + orr WK\()\reg1, WK\()\reg1, WK\()\reg1, lsr #5 @ rrrrrrrrrr0bbbbbbbbbb00000000000 + orr WK\()\reg2, WK\()\reg2, WK\()\reg2, lsr #5 @ RRRRRRRRRR0BBBBBBBBBB00000000000 + pkhtb WK\()\reg1, WK\()\reg1, WK\()\reg1, asr #5 @ rrrrrrrr--------bbbbbbbb-------- + sel WK\()\reg1, WK\()\reg1, SCRATCH @ rrrrrrrrggggggggbbbbbbbb-------- + mov SCRATCH, SCRATCH, ror #16 @ ggg00000GGGGGGGGGGGG0000gggggggg + pkhtb WK\()\reg2, WK\()\reg2, WK\()\reg2, asr #5 @ RRRRRRRR--------BBBBBBBB-------- + sel WK\()\reg2, WK\()\reg2, SCRATCH @ RRRRRRRRGGGGGGGGBBBBBBBB-------- + orr WK\()\reg1, STRIDE_M, WK\()\reg1, lsr #8 @ 11111111rrrrrrrrggggggggbbbbbbbb + orr WK\()\reg2, STRIDE_M, WK\()\reg2, lsr #8 @ 11111111RRRRRRRRGGGGGGGGBBBBBBBB +.endm + +/* This version doesn't need STRIDE_M, but is one instruction longer. + It would however be preferable for an XRGB target, since we could knock off the last 2 instructions, but is that a common case? + and SCRATCH, WK\()\reg1, MASK @ 00000GGGGGG0000000000gggggg00000 + bic WK\()\reg1, WK\()\reg1, MASK @ RRRRR000000BBBBBrrrrr000000bbbbb + orr SCRATCH, SCRATCH, SCRATCH, lsr #6 @ 00000GGGGGGGGGGGG0000ggggggggggg + mov WK\()\reg2, WK\()\reg1, lsr #16 @ 0000000000000000RRRRR000000BBBBB + mov SCRATCH, SCRATCH, ror #27 @ GGGGGGGGGGGG0000ggggggggggg00000 + bic WK\()\reg1, WK\()\reg1, WK\()\reg2, lsl #16 @ 0000000000000000rrrrr000000bbbbb + mov WK\()\reg2, WK\()\reg2, lsl #3 @ 0000000000000RRRRR000000BBBBB000 + mov WK\()\reg1, WK\()\reg1, lsl #3 @ 0000000000000rrrrr000000bbbbb000 + orr WK\()\reg2, WK\()\reg2, WK\()\reg2, lsr #5 @ 0000000000000RRRRRRRRRR0BBBBBBBB + orr WK\()\reg1, WK\()\reg1, WK\()\reg1, lsr #5 @ 0000000000000rrrrrrrrrr0bbbbbbbb + pkhbt WK\()\reg2, WK\()\reg2, WK\()\reg2, lsl #5 @ --------RRRRRRRR--------BBBBBBBB + pkhbt WK\()\reg1, WK\()\reg1, WK\()\reg1, lsl #5 @ --------rrrrrrrr--------bbbbbbbb + sel WK\()\reg2, SCRATCH, WK\()\reg2 @ --------RRRRRRRRGGGGGGGGBBBBBBBB + sel WK\()\reg1, SCRATCH, WK\()\reg1 @ --------rrrrrrrrggggggggbbbbbbbb + orr WK\()\reg2, WK\()\reg2, #0xFF000000 @ 11111111RRRRRRRRGGGGGGGGBBBBBBBB + orr WK\()\reg1, WK\()\reg1, #0xFF000000 @ 11111111rrrrrrrrggggggggbbbbbbbb +*/ + +.macro src_0565_8888_1pixel, reg + bic SCRATCH, WK\()\reg, MASK @ 0000000000000000rrrrr000000bbbbb + and WK\()\reg, WK\()\reg, MASK @ 000000000000000000000gggggg00000 + mov SCRATCH, SCRATCH, lsl #3 @ 0000000000000rrrrr000000bbbbb000 + mov WK\()\reg, WK\()\reg, lsl #5 @ 0000000000000000gggggg0000000000 + orr SCRATCH, SCRATCH, SCRATCH, lsr #5 @ 0000000000000rrrrrrrrrr0bbbbbbbb + orr WK\()\reg, WK\()\reg, WK\()\reg, lsr #6 @ 000000000000000gggggggggggg00000 + pkhbt SCRATCH, SCRATCH, SCRATCH, lsl #5 @ --------rrrrrrrr--------bbbbbbbb + sel WK\()\reg, WK\()\reg, SCRATCH @ --------rrrrrrrrggggggggbbbbbbbb + orr WK\()\reg, WK\()\reg, #0xFF000000 @ 11111111rrrrrrrrggggggggbbbbbbbb +.endm + +.macro src_0565_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + .if \numbytes == 16 + pixldst ld,, 8, \firstreg, %(\firstreg+2),,, SRC, \unaligned_src + .elseif \numbytes == 8 + pixld , 4, \firstreg, SRC, \unaligned_src + .elseif \numbytes == 4 + pixld , 2, \firstreg, SRC, \unaligned_src + .endif +.endm + +.macro src_0565_8888_process_tail cond, numbytes, firstreg + .if \numbytes == 16 + src_0565_8888_2pixels \firstreg, %(\firstreg+1) + src_0565_8888_2pixels %(\firstreg+2), %(\firstreg+3) + .elseif \numbytes == 8 + src_0565_8888_2pixels \firstreg, %(\firstreg+1) + .else + src_0565_8888_1pixel \firstreg + .endif +.endm + +generate_composite_function \ + pixman_composite_src_0565_8888_asm_armv6, 16, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_BRANCH_OVER, \ + 3, /* prefetch distance */ \ + src_0565_8888_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + src_0565_8888_process_head, \ + src_0565_8888_process_tail + +/******************************************************************************/ + +.macro src_x888_0565_init + /* Hold loop invariant in MASK */ + ldr MASK, =0x001F001F + line_saved_regs STRIDE_S, ORIG_W +.endm + +.macro src_x888_0565_1pixel s, d + and WK\()\d, MASK, WK\()\s, lsr #3 @ 00000000000rrrrr00000000000bbbbb + and STRIDE_S, WK\()\s, #0xFC00 @ 0000000000000000gggggg0000000000 + orr WK\()\d, WK\()\d, WK\()\d, lsr #5 @ 00000000000-----rrrrr000000bbbbb + orr WK\()\d, WK\()\d, STRIDE_S, lsr #5 @ 00000000000-----rrrrrggggggbbbbb + /* Top 16 bits are discarded during the following STRH */ +.endm + +.macro src_x888_0565_2pixels slo, shi, d, tmp + and SCRATCH, WK\()\shi, #0xFC00 @ 0000000000000000GGGGGG0000000000 + and WK\()\tmp, MASK, WK\()\shi, lsr #3 @ 00000000000RRRRR00000000000BBBBB + and WK\()\shi, MASK, WK\()\slo, lsr #3 @ 00000000000rrrrr00000000000bbbbb + orr WK\()\tmp, WK\()\tmp, WK\()\tmp, lsr #5 @ 00000000000-----RRRRR000000BBBBB + orr WK\()\tmp, WK\()\tmp, SCRATCH, lsr #5 @ 00000000000-----RRRRRGGGGGGBBBBB + and SCRATCH, WK\()\slo, #0xFC00 @ 0000000000000000gggggg0000000000 + orr WK\()\shi, WK\()\shi, WK\()\shi, lsr #5 @ 00000000000-----rrrrr000000bbbbb + orr WK\()\shi, WK\()\shi, SCRATCH, lsr #5 @ 00000000000-----rrrrrggggggbbbbb + pkhbt WK\()\d, WK\()\shi, WK\()\tmp, lsl #16 @ RRRRRGGGGGGBBBBBrrrrrggggggbbbbb +.endm + +.macro src_x888_0565_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req STRIDE_S + WK5 .req STRIDE_M + WK6 .req WK3 + WK7 .req ORIG_W + .if \numbytes == 16 + pixld , 16, 4, SRC, 0 + src_x888_0565_2pixels 4, 5, 0, 0 + pixld , 8, 4, SRC, 0 + src_x888_0565_2pixels 6, 7, 1, 1 + pixld , 8, 6, SRC, 0 + .else + pixld , \numbytes*2, 4, SRC, 0 + .endif +.endm + +.macro src_x888_0565_process_tail cond, numbytes, firstreg + .if \numbytes == 16 + src_x888_0565_2pixels 4, 5, 2, 2 + src_x888_0565_2pixels 6, 7, 3, 4 + .elseif \numbytes == 8 + src_x888_0565_2pixels 4, 5, 1, 1 + src_x888_0565_2pixels 6, 7, 2, 2 + .elseif \numbytes == 4 + src_x888_0565_2pixels 4, 5, 1, 1 + .else + src_x888_0565_1pixel 4, 1 + .endif + .if \numbytes == 16 + pixst , \numbytes, 0, DST + .else + pixst , \numbytes, 1, DST + .endif + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 +.endm + +generate_composite_function \ + pixman_composite_src_x888_0565_asm_armv6, 32, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_BRANCH_OVER | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS | FLAG_PROCESS_CORRUPTS_SCRATCH, \ + 3, /* prefetch distance */ \ + src_x888_0565_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + src_x888_0565_process_head, \ + src_x888_0565_process_tail + +/******************************************************************************/ + +.macro add_8_8_8pixels cond, dst1, dst2 + uqadd8\()\cond WK\()\dst1, WK\()\dst1, MASK + uqadd8\()\cond WK\()\dst2, WK\()\dst2, STRIDE_M +.endm + +.macro add_8_8_4pixels cond, dst + uqadd8\()\cond WK\()\dst, WK\()\dst, MASK +.endm + +.macro add_8_8_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req MASK + WK5 .req STRIDE_M + .if \numbytes == 16 + pixld \cond, 8, 4, SRC, \unaligned_src + pixld \cond, 16, \firstreg, DST, 0 + add_8_8_8pixels \cond, \firstreg, %(\firstreg+1) + pixld \cond, 8, 4, SRC, \unaligned_src + .else + pixld \cond, \numbytes, 4, SRC, \unaligned_src + pixld \cond, \numbytes, \firstreg, DST, 0 + .endif + .unreq WK4 + .unreq WK5 +.endm + +.macro add_8_8_process_tail cond, numbytes, firstreg + .if \numbytes == 16 + add_8_8_8pixels \cond, %(\firstreg+2), %(\firstreg+3) + .elseif \numbytes == 8 + add_8_8_8pixels \cond, \firstreg, %(\firstreg+1) + .else + add_8_8_4pixels \cond, \firstreg + .endif +.endm + +generate_composite_function \ + pixman_composite_add_8_8_asm_armv6, 8, 0, 8, \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_PRESERVES_SCRATCH, \ + 2, /* prefetch distance */ \ + nop_macro, /* init */ \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + add_8_8_process_head, \ + add_8_8_process_tail + +/******************************************************************************/ + +.macro over_8888_8888_init + /* Hold loop invariant in MASK */ + ldr MASK, =0x00800080 + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, MASK, MASK + line_saved_regs STRIDE_D, STRIDE_S, ORIG_W +.endm + +.macro over_8888_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req STRIDE_D + WK5 .req STRIDE_S + WK6 .req STRIDE_M + WK7 .req ORIG_W + pixld , \numbytes, %(4+\firstreg), SRC, \unaligned_src + pixld , \numbytes, \firstreg, DST, 0 + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 +.endm + +.macro over_8888_8888_check_transparent numbytes, reg0, reg1, reg2, reg3 + /* Since these colours a premultiplied by alpha, only 0 indicates transparent (any other colour with 0 in the alpha byte is luminous) */ + teq WK\()\reg0, #0 + .if \numbytes > 4 + teqeq WK\()\reg1, #0 + .if \numbytes > 8 + teqeq WK\()\reg2, #0 + teqeq WK\()\reg3, #0 + .endif + .endif +.endm + +.macro over_8888_8888_prepare next + mov WK\()\next, WK\()\next, lsr #24 +.endm + +.macro over_8888_8888_1pixel src, dst, offset, next + /* src = destination component multiplier */ + rsb WK\()\src, WK\()\src, #255 + /* Split even/odd bytes of dst into SCRATCH/dst */ + uxtb16 SCRATCH, WK\()\dst + uxtb16 WK\()\dst, WK\()\dst, ror #8 + /* Multiply through, adding 0.5 to the upper byte of result for rounding */ + mla SCRATCH, SCRATCH, WK\()\src, MASK + mla WK\()\dst, WK\()\dst, WK\()\src, MASK + /* Where we would have had a stall between the result of the first MLA and the shifter input, + * reload the complete source pixel */ + ldr WK\()\src, [SRC, #\offset] + /* Multiply by 257/256 to approximate 256/255 */ + uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 + /* In this stall, start processing the next pixel */ + .if \offset < -4 + mov WK\()\next, WK\()\next, lsr #24 + .endif + uxtab16 WK\()\dst, WK\()\dst, WK\()\dst, ror #8 + /* Recombine even/odd bytes of multiplied destination */ + mov SCRATCH, SCRATCH, ror #8 + sel WK\()\dst, SCRATCH, WK\()\dst + /* Saturated add of source to multiplied destination */ + uqadd8 WK\()\dst, WK\()\dst, WK\()\src +.endm + +.macro over_8888_8888_process_tail cond, numbytes, firstreg + WK4 .req STRIDE_D + WK5 .req STRIDE_S + WK6 .req STRIDE_M + WK7 .req ORIG_W + over_8888_8888_check_transparent \numbytes, %(4+\firstreg), %(5+\firstreg), %(6+\firstreg), %(7+\firstreg) + beq 10f + over_8888_8888_prepare %(4+\firstreg) + .set PROCESS_REG, \firstreg + .set PROCESS_OFF, -\numbytes + .rept \numbytes / 4 + over_8888_8888_1pixel %(4+PROCESS_REG), %(0+PROCESS_REG), PROCESS_OFF, %(5+PROCESS_REG) + .set PROCESS_REG, PROCESS_REG+1 + .set PROCESS_OFF, PROCESS_OFF+4 + .endr + pixst , \numbytes, \firstreg, DST +10: + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 +.endm + +generate_composite_function \ + pixman_composite_over_8888_8888_asm_armv6, 32, 0, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS \ + 2, /* prefetch distance */ \ + over_8888_8888_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + over_8888_8888_process_head, \ + over_8888_8888_process_tail + +/******************************************************************************/ + +/* Multiply each byte of a word by a byte. + * Useful when there aren't any obvious ways to fill the stalls with other instructions. + * word Register containing 4 bytes + * byte Register containing byte multiplier (bits 8-31 must be 0) + * tmp Scratch register + * half Register containing the constant 0x00800080 + * GE[3:0] bits must contain 0101 + */ +.macro mul_8888_8 word, byte, tmp, half + /* Split even/odd bytes of word apart */ + uxtb16 \tmp, \word + uxtb16 \word, \word, ror #8 + /* Multiply bytes together with rounding, then by 257/256 */ + mla \tmp, \tmp, \byte, \half + mla \word, \word, \byte, \half /* 1 stall follows */ + uxtab16 \tmp, \tmp, \tmp, ror #8 /* 1 stall follows */ + uxtab16 \word, \word, \word, ror #8 + /* Recombine bytes */ + mov \tmp, \tmp, ror #8 + sel \word, \tmp, \word +.endm + +/******************************************************************************/ + +.macro over_8888_n_8888_init + /* Mask is constant */ + ldr MASK, [sp, #ARGS_STACK_OFFSET+8] + /* Hold loop invariant in STRIDE_M */ + ldr STRIDE_M, =0x00800080 + /* We only want the alpha bits of the constant mask */ + mov MASK, MASK, lsr #24 + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, STRIDE_M, STRIDE_M + line_saved_regs Y, STRIDE_D, STRIDE_S, ORIG_W +.endm + +.macro over_8888_n_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req Y + WK5 .req STRIDE_D + WK6 .req STRIDE_S + WK7 .req ORIG_W + pixld , \numbytes, %(4+(\firstreg%2)), SRC, \unaligned_src + pixld , \numbytes, \firstreg, DST, 0 + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 +.endm + +.macro over_8888_n_8888_1pixel src, dst + mul_8888_8 WK\()\src, MASK, SCRATCH, STRIDE_M + sub WK7, WK6, WK\()\src, lsr #24 + mul_8888_8 WK\()\dst, WK7, SCRATCH, STRIDE_M + uqadd8 WK\()\dst, WK\()\dst, WK\()\src +.endm + +.macro over_8888_n_8888_process_tail cond, numbytes, firstreg + WK4 .req Y + WK5 .req STRIDE_D + WK6 .req STRIDE_S + WK7 .req ORIG_W + over_8888_8888_check_transparent \numbytes, %(4+(\firstreg%2)), %(5+(\firstreg%2)), %(6+\firstreg), %(7+\firstreg) + beq 10f + mov WK6, #255 + .set PROCESS_REG, \firstreg + .rept \numbytes / 4 + .if \numbytes == 16 && PROCESS_REG == 2 + /* We're using WK6 and WK7 as temporaries, so half way through + * 4 pixels, reload the second two source pixels but this time + * into WK4 and WK5 */ + ldmdb SRC, {WK4, WK5} + .endif + over_8888_n_8888_1pixel %(4+(PROCESS_REG%2)), %(PROCESS_REG) + .set PROCESS_REG, PROCESS_REG+1 + .endr + pixst , \numbytes, \firstreg, DST +10: + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 +.endm + +generate_composite_function \ + pixman_composite_over_8888_n_8888_asm_armv6, 32, 0, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS \ + 2, /* prefetch distance */ \ + over_8888_n_8888_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + over_8888_n_8888_process_head, \ + over_8888_n_8888_process_tail + +/******************************************************************************/ + +.macro over_n_8_8888_init + /* Source is constant, but splitting it into even/odd bytes is a loop invariant */ + ldr SRC, [sp, #ARGS_STACK_OFFSET] + /* Not enough registers to hold this constant, but we still use it here to set GE[3:0] */ + ldr SCRATCH, =0x00800080 + uxtb16 STRIDE_S, SRC + uxtb16 SRC, SRC, ror #8 + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, SCRATCH, SCRATCH + line_saved_regs Y, STRIDE_D, STRIDE_M, ORIG_W +.endm + +.macro over_n_8_8888_newline + ldr STRIDE_D, =0x00800080 + b 1f + .ltorg +1: +.endm + +.macro over_n_8_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req STRIDE_M + pixld , \numbytes/4, 4, MASK, \unaligned_mask + pixld , \numbytes, \firstreg, DST, 0 + .unreq WK4 +.endm + +.macro over_n_8_8888_1pixel src, dst + uxtb Y, WK4, ror #\src*8 + /* Trailing part of multiplication of source */ + mla SCRATCH, STRIDE_S, Y, STRIDE_D + mla Y, SRC, Y, STRIDE_D + mov ORIG_W, #255 + uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 + uxtab16 Y, Y, Y, ror #8 + mov SCRATCH, SCRATCH, ror #8 + sub ORIG_W, ORIG_W, Y, lsr #24 + sel Y, SCRATCH, Y + /* Then multiply the destination */ + mul_8888_8 WK\()\dst, ORIG_W, SCRATCH, STRIDE_D + uqadd8 WK\()\dst, WK\()\dst, Y +.endm + +.macro over_n_8_8888_process_tail cond, numbytes, firstreg + WK4 .req STRIDE_M + teq WK4, #0 + beq 10f + .set PROCESS_REG, \firstreg + .rept \numbytes / 4 + over_n_8_8888_1pixel %(PROCESS_REG-\firstreg), %(PROCESS_REG) + .set PROCESS_REG, PROCESS_REG+1 + .endr + pixst , \numbytes, \firstreg, DST +10: + .unreq WK4 +.endm + +generate_composite_function \ + pixman_composite_over_n_8_8888_asm_armv6, 0, 8, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS \ + 2, /* prefetch distance */ \ + over_n_8_8888_init, \ + over_n_8_8888_newline, \ + nop_macro, /* cleanup */ \ + over_n_8_8888_process_head, \ + over_n_8_8888_process_tail + +/******************************************************************************/ + +.macro over_reverse_n_8888_init + ldr SRC, [sp, #ARGS_STACK_OFFSET] + ldr MASK, =0x00800080 + /* Split source pixel into RB/AG parts */ + uxtb16 STRIDE_S, SRC + uxtb16 STRIDE_M, SRC, ror #8 + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, MASK, MASK + line_saved_regs STRIDE_D, ORIG_W +.endm + +.macro over_reverse_n_8888_newline + mov STRIDE_D, #0xFF +.endm + +.macro over_reverse_n_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + pixld , \numbytes, \firstreg, DST, 0 +.endm + +.macro over_reverse_n_8888_1pixel d, is_only + teq WK\()\d, #0 + beq 8f /* replace with source */ + bics ORIG_W, STRIDE_D, WK\()\d, lsr #24 + .if \is_only == 1 + beq 49f /* skip store */ + .else + beq 9f /* write same value back */ + .endif + mla SCRATCH, STRIDE_S, ORIG_W, MASK /* red/blue */ + mla ORIG_W, STRIDE_M, ORIG_W, MASK /* alpha/green */ + uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 + uxtab16 ORIG_W, ORIG_W, ORIG_W, ror #8 + mov SCRATCH, SCRATCH, ror #8 + sel ORIG_W, SCRATCH, ORIG_W + uqadd8 WK\()\d, WK\()\d, ORIG_W + b 9f +8: mov WK\()\d, SRC +9: +.endm + +.macro over_reverse_n_8888_tail numbytes, reg1, reg2, reg3, reg4 + .if \numbytes == 4 + over_reverse_n_8888_1pixel \reg1, 1 + .else + and SCRATCH, WK\()\reg1, WK\()\reg2 + .if \numbytes == 16 + and SCRATCH, SCRATCH, WK\()\reg3 + and SCRATCH, SCRATCH, WK\()\reg4 + .endif + mvns SCRATCH, SCRATCH, asr #24 + beq 49f /* skip store if all opaque */ + over_reverse_n_8888_1pixel \reg1, 0 + over_reverse_n_8888_1pixel \reg2, 0 + .if \numbytes == 16 + over_reverse_n_8888_1pixel \reg3, 0 + over_reverse_n_8888_1pixel \reg4, 0 + .endif + .endif + pixst , \numbytes, \reg1, DST +49: +.endm + +.macro over_reverse_n_8888_process_tail cond, numbytes, firstreg + over_reverse_n_8888_tail \numbytes, \firstreg, %(\firstreg+1), %(\firstreg+2), %(\firstreg+3) +.endm + +generate_composite_function \ + pixman_composite_over_reverse_n_8888_asm_armv6, 0, 0, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS | FLAG_PROCESS_CORRUPTS_SCRATCH, \ + 3, /* prefetch distance */ \ + over_reverse_n_8888_init, \ + over_reverse_n_8888_newline, \ + nop_macro, /* cleanup */ \ + over_reverse_n_8888_process_head, \ + over_reverse_n_8888_process_tail + +/******************************************************************************/ + +.macro over_white_8888_8888_ca_init + HALF .req SRC + TMP0 .req STRIDE_D + TMP1 .req STRIDE_S + TMP2 .req STRIDE_M + TMP3 .req ORIG_W + WK4 .req SCRATCH + line_saved_regs STRIDE_D, STRIDE_M, ORIG_W + ldr SCRATCH, =0x800080 + mov HALF, #0x80 + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, SCRATCH, SCRATCH + .set DST_PRELOAD_BIAS, 8 +.endm + +.macro over_white_8888_8888_ca_cleanup + .set DST_PRELOAD_BIAS, 0 + .unreq HALF + .unreq TMP0 + .unreq TMP1 + .unreq TMP2 + .unreq TMP3 + .unreq WK4 +.endm + +.macro over_white_8888_8888_ca_combine m, d + uxtb16 TMP1, TMP0 /* rb_notmask */ + uxtb16 TMP2, \d /* rb_dest; 1 stall follows */ + smlatt TMP3, TMP2, TMP1, HALF /* red */ + smlabb TMP2, TMP2, TMP1, HALF /* blue */ + uxtb16 TMP0, TMP0, ror #8 /* ag_notmask */ + uxtb16 TMP1, \d, ror #8 /* ag_dest; 1 stall follows */ + smlatt \d, TMP1, TMP0, HALF /* alpha */ + smlabb TMP1, TMP1, TMP0, HALF /* green */ + pkhbt TMP0, TMP2, TMP3, lsl #16 /* rb; 1 stall follows */ + pkhbt TMP1, TMP1, \d, lsl #16 /* ag */ + uxtab16 TMP0, TMP0, TMP0, ror #8 + uxtab16 TMP1, TMP1, TMP1, ror #8 + mov TMP0, TMP0, ror #8 + sel \d, TMP0, TMP1 + uqadd8 \d, \d, \m /* d is a late result */ +.endm + +.macro over_white_8888_8888_ca_1pixel_head + pixld , 4, 1, MASK, 0 + pixld , 4, 3, DST, 0 +.endm + +.macro over_white_8888_8888_ca_1pixel_tail + mvn TMP0, WK1 + teq WK1, WK1, asr #32 + bne 01f + bcc 03f + mov WK3, WK1 + b 02f +01: over_white_8888_8888_ca_combine WK1, WK3 +02: pixst , 4, 3, DST +03: +.endm + +.macro over_white_8888_8888_ca_2pixels_head + pixld , 8, 1, MASK, 0 +.endm + +.macro over_white_8888_8888_ca_2pixels_tail + pixld , 8, 3, DST + mvn TMP0, WK1 + teq WK1, WK1, asr #32 + bne 01f + movcs WK3, WK1 + bcs 02f + teq WK2, #0 + beq 05f + b 02f +01: over_white_8888_8888_ca_combine WK1, WK3 +02: mvn TMP0, WK2 + teq WK2, WK2, asr #32 + bne 03f + movcs WK4, WK2 + b 04f +03: over_white_8888_8888_ca_combine WK2, WK4 +04: pixst , 8, 3, DST +05: +.endm + +.macro over_white_8888_8888_ca_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + .if \numbytes == 4 + over_white_8888_8888_ca_1pixel_head + .else + .if \numbytes == 16 + over_white_8888_8888_ca_2pixels_head + over_white_8888_8888_ca_2pixels_tail + .endif + over_white_8888_8888_ca_2pixels_head + .endif +.endm + +.macro over_white_8888_8888_ca_process_tail cond, numbytes, firstreg + .if \numbytes == 4 + over_white_8888_8888_ca_1pixel_tail + .else + over_white_8888_8888_ca_2pixels_tail + .endif +.endm + +generate_composite_function \ + pixman_composite_over_white_8888_8888_ca_asm_armv6, 0, 32, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS | FLAG_PROCESS_CORRUPTS_SCRATCH \ + 2, /* prefetch distance */ \ + over_white_8888_8888_ca_init, \ + nop_macro, /* newline */ \ + over_white_8888_8888_ca_cleanup, \ + over_white_8888_8888_ca_process_head, \ + over_white_8888_8888_ca_process_tail + + +.macro over_n_8888_8888_ca_init + /* Set up constants. RB_SRC and AG_SRC are in registers; + * RB_FLDS, A_SRC, and the two HALF values need to go on the + * stack (and the ful SRC value is already there) */ + ldr SCRATCH, [sp, #ARGS_STACK_OFFSET] + mov WK0, #0x00FF0000 + orr WK0, WK0, #0xFF /* RB_FLDS (0x00FF00FF) */ + mov WK1, #0x80 /* HALF default value */ + mov WK2, SCRATCH, lsr #24 /* A_SRC */ + orr WK3, WK1, WK1, lsl #16 /* HALF alternate value (0x00800080) */ + push {WK0-WK3} + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET+16 + uxtb16 SRC, SCRATCH + uxtb16 STRIDE_S, SCRATCH, ror #8 + + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, WK3, WK3 + + .unreq WK0 + .unreq WK1 + .unreq WK2 + .unreq WK3 + WK0 .req Y + WK1 .req STRIDE_D + RB_SRC .req SRC + AG_SRC .req STRIDE_S + WK2 .req STRIDE_M + RB_FLDS .req r8 /* the reloaded constants have to be at consecutive registers starting at an even one */ + A_SRC .req r8 + HALF .req r9 + WK3 .req r10 + WK4 .req r11 + WK5 .req SCRATCH + WK6 .req ORIG_W + + line_saved_regs Y, STRIDE_D, STRIDE_M, ORIG_W +.endm + +.macro over_n_8888_8888_ca_cleanup + add sp, sp, #16 + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET-16 + + .unreq WK0 + .unreq WK1 + .unreq RB_SRC + .unreq AG_SRC + .unreq WK2 + .unreq RB_FLDS + .unreq A_SRC + .unreq HALF + .unreq WK3 + .unreq WK4 + .unreq WK5 + .unreq WK6 + WK0 .req r8 + WK1 .req r9 + WK2 .req r10 + WK3 .req r11 +.endm + +.macro over_n_8888_8888_ca_1pixel_head + pixld , 4, 6, MASK, 0 + pixld , 4, 0, DST, 0 +.endm + +.macro over_n_8888_8888_ca_1pixel_tail + ldrd A_SRC, HALF, [sp, #LOCALS_STACK_OFFSET+8] + uxtb16 WK1, WK6 /* rb_mask (first step of hard case placed in what would otherwise be a stall) */ + teq WK6, WK6, asr #32 /* Zc if transparent, ZC if opaque */ + bne 20f + bcc 40f + /* Mask is fully opaque (all channels) */ + ldr WK6, [sp, #ARGS_STACK_OFFSET] /* get SRC back */ + eors A_SRC, A_SRC, #0xFF + bne 10f + /* Source is also opaque - same as src_8888_8888 */ + mov WK0, WK6 + b 30f +10: /* Same as over_8888_8888 */ + mul_8888_8 WK0, A_SRC, WK5, HALF + uqadd8 WK0, WK0, WK6 + b 30f +20: /* No simplifications possible - do it the hard way */ + uxtb16 WK2, WK6, ror #8 /* ag_mask */ + mla WK3, WK1, A_SRC, HALF /* rb_mul; 2 cycles */ + mla WK4, WK2, A_SRC, HALF /* ag_mul; 2 cycles */ + ldrd RB_FLDS, HALF, [sp, #LOCALS_STACK_OFFSET] + uxtb16 WK5, WK0 /* rb_dest */ + uxtab16 WK3, WK3, WK3, ror #8 + uxtb16 WK6, WK0, ror #8 /* ag_dest */ + uxtab16 WK4, WK4, WK4, ror #8 + smlatt WK0, RB_SRC, WK1, HALF /* red1 */ + smlabb WK1, RB_SRC, WK1, HALF /* blue1 */ + bic WK3, RB_FLDS, WK3, lsr #8 + bic WK4, RB_FLDS, WK4, lsr #8 + pkhbt WK1, WK1, WK0, lsl #16 /* rb1 */ + smlatt WK0, WK5, WK3, HALF /* red2 */ + smlabb WK3, WK5, WK3, HALF /* blue2 */ + uxtab16 WK1, WK1, WK1, ror #8 + smlatt WK5, AG_SRC, WK2, HALF /* alpha1 */ + pkhbt WK3, WK3, WK0, lsl #16 /* rb2 */ + smlabb WK0, AG_SRC, WK2, HALF /* green1 */ + smlatt WK2, WK6, WK4, HALF /* alpha2 */ + smlabb WK4, WK6, WK4, HALF /* green2 */ + pkhbt WK0, WK0, WK5, lsl #16 /* ag1 */ + uxtab16 WK3, WK3, WK3, ror #8 + pkhbt WK4, WK4, WK2, lsl #16 /* ag2 */ + uxtab16 WK0, WK0, WK0, ror #8 + uxtab16 WK4, WK4, WK4, ror #8 + mov WK1, WK1, ror #8 + mov WK3, WK3, ror #8 + sel WK2, WK1, WK0 /* recombine source*mask */ + sel WK1, WK3, WK4 /* recombine dest*(1-source_alpha*mask) */ + uqadd8 WK0, WK1, WK2 /* followed by 1 stall */ +30: /* The destination buffer is already in the L1 cache, so + * there's little point in amalgamating writes */ + pixst , 4, 0, DST +40: +.endm + +.macro over_n_8888_8888_ca_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + .rept (\numbytes / 4) - 1 + over_n_8888_8888_ca_1pixel_head + over_n_8888_8888_ca_1pixel_tail + .endr + over_n_8888_8888_ca_1pixel_head +.endm + +.macro over_n_8888_8888_ca_process_tail cond, numbytes, firstreg + over_n_8888_8888_ca_1pixel_tail +.endm + +pixman_asm_function pixman_composite_over_n_8888_8888_ca_asm_armv6 + ldr ip, [sp] + cmp ip, #-1 + beq pixman_composite_over_white_8888_8888_ca_asm_armv6 + /* else drop through... */ + pixman_end_asm_function +generate_composite_function \ + pixman_composite_over_n_8888_8888_ca_asm_armv6_helper, 0, 32, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS | FLAG_PROCESS_CORRUPTS_SCRATCH | FLAG_PROCESS_CORRUPTS_WK0 \ + 2, /* prefetch distance */ \ + over_n_8888_8888_ca_init, \ + nop_macro, /* newline */ \ + over_n_8888_8888_ca_cleanup, \ + over_n_8888_8888_ca_process_head, \ + over_n_8888_8888_ca_process_tail + +/******************************************************************************/ + +.macro in_reverse_8888_8888_init + /* Hold loop invariant in MASK */ + ldr MASK, =0x00800080 + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, MASK, MASK + /* Offset the source pointer: we only need the alpha bytes */ + add SRC, SRC, #3 + line_saved_regs ORIG_W +.endm + +.macro in_reverse_8888_8888_head numbytes, reg1, reg2, reg3 + ldrb ORIG_W, [SRC], #4 + .if \numbytes >= 8 + ldrb WK\()\reg1, [SRC], #4 + .if \numbytes == 16 + ldrb WK\()\reg2, [SRC], #4 + ldrb WK\()\reg3, [SRC], #4 + .endif + .endif + add DST, DST, #\numbytes +.endm + +.macro in_reverse_8888_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + in_reverse_8888_8888_head \numbytes, \firstreg, %(\firstreg+1), %(\firstreg+2) +.endm + +.macro in_reverse_8888_8888_1pixel s, d, offset, is_only + .if \is_only != 1 + movs \s, ORIG_W + .if \offset != 0 + ldrb ORIG_W, [SRC, #\offset] + .endif + beq 01f + teq STRIDE_M, #0xFF + beq 02f + .endif + uxtb16 SCRATCH, \d /* rb_dest */ + uxtb16 \d, \d, ror #8 /* ag_dest */ + mla SCRATCH, SCRATCH, \s, MASK + mla \d, \d, \s, MASK + uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 + uxtab16 \d, \d, \d, ror #8 + mov SCRATCH, SCRATCH, ror #8 + sel \d, SCRATCH, \d + b 02f + .if \offset == 0 +48: /* Last mov d,#0 of the set - used as part of shortcut for + * source values all 0 */ + .endif +01: mov \d, #0 +02: +.endm + +.macro in_reverse_8888_8888_tail numbytes, reg1, reg2, reg3, reg4 + .if \numbytes == 4 + teq ORIG_W, ORIG_W, asr #32 + ldrne WK\()\reg1, [DST, #-4] + .elseif \numbytes == 8 + teq ORIG_W, WK\()\reg1 + teqeq ORIG_W, ORIG_W, asr #32 /* all 0 or all -1? */ + ldmnedb DST, {WK\()\reg1-WK\()\reg2} + .else + teq ORIG_W, WK\()\reg1 + teqeq ORIG_W, WK\()\reg2 + teqeq ORIG_W, WK\()\reg3 + teqeq ORIG_W, ORIG_W, asr #32 /* all 0 or all -1? */ + ldmnedb DST, {WK\()\reg1-WK\()\reg4} + .endif + cmnne DST, #0 /* clear C if NE */ + bcs 49f /* no writes to dest if source all -1 */ + beq 48f /* set dest to all 0 if source all 0 */ + .if \numbytes == 4 + in_reverse_8888_8888_1pixel ORIG_W, WK\()\reg1, 0, 1 + str WK\()\reg1, [DST, #-4] + .elseif \numbytes == 8 + in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg1, -4, 0 + in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg2, 0, 0 + stmdb DST, {WK\()\reg1-WK\()\reg2} + .else + in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg1, -12, 0 + in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg2, -8, 0 + in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg3, -4, 0 + in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg4, 0, 0 + stmdb DST, {WK\()\reg1-WK\()\reg4} + .endif +49: +.endm + +.macro in_reverse_8888_8888_process_tail cond, numbytes, firstreg + in_reverse_8888_8888_tail \numbytes, \firstreg, %(\firstreg+1), %(\firstreg+2), %(\firstreg+3) +.endm + +generate_composite_function \ + pixman_composite_in_reverse_8888_8888_asm_armv6, 32, 0, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS | FLAG_PROCESS_CORRUPTS_SCRATCH | FLAG_NO_PRELOAD_DST \ + 2, /* prefetch distance */ \ + in_reverse_8888_8888_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + in_reverse_8888_8888_process_head, \ + in_reverse_8888_8888_process_tail + +/******************************************************************************/ + +.macro over_n_8888_init + ldr SRC, [sp, #ARGS_STACK_OFFSET] + /* Hold loop invariant in MASK */ + ldr MASK, =0x00800080 + /* Hold multiplier for destination in STRIDE_M */ + mov STRIDE_M, #255 + sub STRIDE_M, STRIDE_M, SRC, lsr #24 + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, MASK, MASK +.endm + +.macro over_n_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + pixld , \numbytes, \firstreg, DST, 0 +.endm + +.macro over_n_8888_1pixel dst + mul_8888_8 WK\()\dst, STRIDE_M, SCRATCH, MASK + uqadd8 WK\()\dst, WK\()\dst, SRC +.endm + +.macro over_n_8888_process_tail cond, numbytes, firstreg + .set PROCESS_REG, \firstreg + .rept \numbytes / 4 + over_n_8888_1pixel %(PROCESS_REG) + .set PROCESS_REG, PROCESS_REG+1 + .endr + pixst , \numbytes, \firstreg, DST +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_asm_armv6, 0, 0, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_DOES_STORE \ + 2, /* prefetch distance */ \ + over_n_8888_init, \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + over_n_8888_process_head, \ + over_n_8888_process_tail + +/******************************************************************************/ diff --git a/gfx/cairo/libpixman/src/pixman-arm-simd-asm.h b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.h new file mode 100644 index 0000000000..3e78e8a468 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.h @@ -0,0 +1,998 @@ +/* + * Copyright © 2012 Raspberry Pi Foundation + * Copyright © 2012 RISC OS Open Ltd + * + * 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. + * + * Author: Ben Avison (bavison@riscosopen.org) + * + */ + +/* + * Because the alignment of pixel data to cachelines, and even the number of + * cachelines per row can vary from row to row, and because of the need to + * preload each scanline once and only once, this prefetch strategy treats + * each row of pixels independently. When a pixel row is long enough, there + * are three distinct phases of prefetch: + * * an inner loop section, where each time a cacheline of data is + * processed, another cacheline is preloaded (the exact distance ahead is + * determined empirically using profiling results from lowlevel-blt-bench) + * * a leading section, where enough cachelines are preloaded to ensure no + * cachelines escape being preloaded when the inner loop starts + * * a trailing section, where a limited number (0 or more) of cachelines + * are preloaded to deal with data (if any) that hangs off the end of the + * last iteration of the inner loop, plus any trailing bytes that were not + * enough to make up one whole iteration of the inner loop + * + * There are (in general) three distinct code paths, selected between + * depending upon how long the pixel row is. If it is long enough that there + * is at least one iteration of the inner loop (as described above) then + * this is described as the "wide" case. If it is shorter than that, but + * there are still enough bytes output that there is at least one 16-byte- + * long, 16-byte-aligned write to the destination (the optimum type of + * write), then this is the "medium" case. If it is not even this long, then + * this is the "narrow" case, and there is no attempt to align writes to + * 16-byte boundaries. In the "medium" and "narrow" cases, all the + * cachelines containing data from the pixel row are prefetched up-front. + */ + +/* + * Determine whether we put the arguments on the stack for debugging. + */ +#undef DEBUG_PARAMS + +/* + * Bit flags for 'generate_composite_function' macro which are used + * to tune generated functions behavior. + */ +.set FLAG_DST_WRITEONLY, 0 +.set FLAG_DST_READWRITE, 1 +.set FLAG_COND_EXEC, 0 +.set FLAG_BRANCH_OVER, 2 +.set FLAG_PROCESS_PRESERVES_PSR, 0 +.set FLAG_PROCESS_CORRUPTS_PSR, 4 +.set FLAG_PROCESS_DOESNT_STORE, 0 +.set FLAG_PROCESS_DOES_STORE, 8 /* usually because it needs to conditionally skip it */ +.set FLAG_NO_SPILL_LINE_VARS, 0 +.set FLAG_SPILL_LINE_VARS_WIDE, 16 +.set FLAG_SPILL_LINE_VARS_NON_WIDE, 32 +.set FLAG_SPILL_LINE_VARS, 48 +.set FLAG_PROCESS_CORRUPTS_SCRATCH, 0 +.set FLAG_PROCESS_PRESERVES_SCRATCH, 64 +.set FLAG_PROCESS_PRESERVES_WK0, 0 +.set FLAG_PROCESS_CORRUPTS_WK0, 128 /* if possible, use the specified register(s) instead so WK0 can hold number of leading pixels */ +.set FLAG_PRELOAD_DST, 0 +.set FLAG_NO_PRELOAD_DST, 256 + +/* + * Number of bytes by which to adjust preload offset of destination + * buffer (allows preload instruction to be moved before the load(s)) + */ +.set DST_PRELOAD_BIAS, 0 + +/* + * Offset into stack where mask and source pointer/stride can be accessed. + */ +#ifdef DEBUG_PARAMS +.set ARGS_STACK_OFFSET, (9*4+9*4) +#else +.set ARGS_STACK_OFFSET, (9*4) +#endif + +/* + * Offset into stack where space allocated during init macro can be accessed. + */ +.set LOCALS_STACK_OFFSET, 0 + +/* + * Constants for selecting preferable prefetch type. + */ +.set PREFETCH_TYPE_NONE, 0 +.set PREFETCH_TYPE_STANDARD, 1 + +/* + * Definitions of macros for load/store of pixel data. + */ + +.macro pixldst op, cond=al, numbytes, reg0, reg1, reg2, reg3, base, unaligned=0 + .if \numbytes == 16 + .if \unaligned == 1 + \op\()r\()\cond WK\()\reg0, [\base], #4 + \op\()r\()\cond WK\()\reg1, [\base], #4 + \op\()r\()\cond WK\()\reg2, [\base], #4 + \op\()r\()\cond WK\()\reg3, [\base], #4 + .else +#ifdef __clang__ + \op\()mia\()\cond \base!, {WK\()\reg0,WK\()\reg1,WK\()\reg2,WK\()\reg3} +#else + \op\()m\()\cond\()ia \base!, {WK\()\reg0,WK\()\reg1,WK\()\reg2,WK\()\reg3} +#endif + .endif + .elseif \numbytes == 8 + .if \unaligned == 1 + \op\()r\()\cond WK\()\reg0, [\base], #4 + \op\()r\()\cond WK\()\reg1, [\base], #4 + .else +#ifdef __clang__ + \op\()mia\()\cond \base!, {WK\()\reg0,WK\()\reg1} +#else + \op\()m\()\cond\()ia \base!, {WK\()\reg0,WK\()\reg1} +#endif + .endif + .elseif \numbytes == 4 + \op\()r\()\cond WK\()\reg0, [\base], #4 + .elseif \numbytes == 2 +#ifdef __clang__ + \op\()rh\()\cond WK\()\reg0, [\base], #2 +#else + \op\()r\()\cond\()h WK\()\reg0, [\base], #2 +#endif + .elseif \numbytes == 1 +#ifdef __clang__ + \op\()rb\()\cond WK\()\reg0, [\base], #1 +#else + \op\()r\()\cond\()b WK\()\reg0, [\base], #1 +#endif + .else + .error "unsupported size: \numbytes" + .endif +.endm + +.macro pixst_baseupdated cond, numbytes, reg0, reg1, reg2, reg3, base + .if \numbytes == 16 +#ifdef __clang__ + stm\()\cond\()db \base, {WK\()\reg0,WK\()\reg1,WK\()\reg2,WK\()\reg3} +#else + stmdb\()\cond \base, {WK\()\reg0,WK\()\reg1,WK\()\reg2,WK\()\reg3} +#endif + .elseif \numbytes == 8 +#ifdef __clang__ + stmdb\()\cond \base, {WK\()\reg0,WK\()\reg1} +#else + stm\()\cond\()db \base, {WK\()\reg0,WK\()\reg1} +#endif + .elseif \numbytes == 4 + str\()\cond WK\()\reg0, [\base, #-4] + .elseif \numbytes == 2 +#ifdef __clang__ + strh\()\cond WK\()\reg0, [\base, #-2] +#else + str\()\cond\()h WK\()\reg0, [\base, #-2] +#endif + .elseif \numbytes == 1 +#ifdef __clang__ + strb\()\cond WK\()\reg0, [\base, #-1] +#else + str\()\cond\()b WK\()\reg0, [\base, #-1] +#endif + .else + .error "unsupported size: \numbytes" + .endif +.endm + +.macro pixld cond, numbytes, firstreg, base, unaligned + pixldst ld, \cond, \numbytes, %(\firstreg+0), %(\firstreg+1), %(\firstreg+2), %(\firstreg+3), \base, \unaligned +.endm + +.macro pixst cond, numbytes, firstreg, base + .if (flags) & FLAG_DST_READWRITE + pixst_baseupdated \cond, \numbytes, %(\firstreg+0), %(\firstreg+1), %(\firstreg+2), %(\firstreg+3), \base + .else + pixldst st, \cond, \numbytes, %(\firstreg+0), %(\firstreg+1), %(\firstreg+2), %(\firstreg+3), \base + .endif +.endm + +.macro PF a, x:vararg + .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_STANDARD) + \a \x + .endif +.endm + + +.macro preload_leading_step1 bpp, ptr, base +/* If the destination is already 16-byte aligned, then we need to preload + * between 0 and prefetch_distance (inclusive) cache lines ahead so there + * are no gaps when the inner loop starts. + */ + .if \bpp > 0 + PF bic, \ptr, \base, #31 + .set OFFSET, 0 + .rept prefetch_distance+1 + PF pld, [\ptr, #OFFSET] + .set OFFSET, OFFSET+32 + .endr + .endif +.endm + +.macro preload_leading_step2 bpp, bpp_shift, ptr, base +/* However, if the destination is not 16-byte aligned, we may need to + * preload more cache lines than that. The question we need to ask is: + * are the bytes corresponding to the leading pixels more than the amount + * by which the source pointer will be rounded down for preloading, and if + * so, by how many cache lines? Effectively, we want to calculate + * leading_bytes = ((-dst)&15)*src_bpp/dst_bpp + * inner_loop_offset = (src+leading_bytes)&31 + * extra_needed = leading_bytes - inner_loop_offset + * and test if extra_needed is <= 0, <= 32, or > 32 (where > 32 is only + * possible when there are 4 src bytes for every 1 dst byte). + */ + .if \bpp > 0 + .ifc \base,DST + /* The test can be simplified further when preloading the destination */ + PF tst, \base, #16 + PF beq, 61f + .else + .if \bpp/dst_w_bpp == 4 + PF add, SCRATCH, \base, WK0, lsl #\bpp_shift-dst_bpp_shift + PF and, SCRATCH, SCRATCH, #31 + PF rsb, SCRATCH, SCRATCH, WK0, lsl #\bpp_shift-dst_bpp_shift + PF sub, SCRATCH, SCRATCH, #1 /* so now ranges are -16..-1 / 0..31 / 32..63 */ + PF movs, SCRATCH, SCRATCH, lsl #32-6 /* so this sets NC / nc / Nc */ + PF bcs, 61f + PF bpl, 60f + PF pld, [ptr, #32*(prefetch_distance+2)] + .else + PF mov, SCRATCH, \base, lsl #32-5 + PF add, SCRATCH, SCRATCH, WK0, lsl #32-5+\bpp_shift-dst_bpp_shift + PF rsbs, SCRATCH, SCRATCH, WK0, lsl #32-5+\bpp_shift-dst_bpp_shift + PF bls, 61f + .endif + .endif +60: PF pld, [\ptr, #32*(prefetch_distance+1)] +61: + .endif +.endm + +#define IS_END_OF_GROUP(INDEX,SIZE) ((SIZE) < 2 || ((INDEX) & ~((INDEX)+1)) & ((SIZE)/2)) +.macro preload_middle bpp, base, scratch_holds_offset + .if \bpp > 0 + /* prefetch distance = 256/bpp, stm distance = 128/dst_w_bpp */ + .if IS_END_OF_GROUP(SUBBLOCK,256/128*dst_w_bpp/\bpp) + .if \scratch_holds_offset + PF pld, [\base, SCRATCH] + .else + PF bic, SCRATCH, \base, #31 + PF pld, [SCRATCH, #32*prefetch_distance] + .endif + .endif + .endif +.endm + +.macro preload_trailing bpp, bpp_shift, base + .if \bpp > 0 + .if \bpp*pix_per_block > 256 + /* Calculations are more complex if more than one fetch per block */ + PF and, WK1, \base, #31 + PF add, WK1, WK1, WK0, lsl #\bpp_shift + PF add, WK1, WK1, #32*(\bpp*pix_per_block/256-1)*(prefetch_distance+1) + PF bic, SCRATCH, \base, #31 +80: PF pld, [SCRATCH, #32*(prefetch_distance+1)] + PF add, SCRATCH, SCRATCH, #32 + PF subs, WK1, WK1, #32 + PF bhi, 80b + .else + /* If exactly one fetch per block, then we need either 0, 1 or 2 extra preloads */ + PF mov, SCRATCH, \base, lsl #32-5 + PF adds, SCRATCH, SCRATCH, X, lsl #32-5+\bpp_shift + PF adceqs, SCRATCH, SCRATCH, #0 + /* The instruction above has two effects: ensures Z is only + * set if C was clear (so Z indicates that both shifted quantities + * were 0), and clears C if Z was set (so C indicates that the sum + * of the shifted quantities was greater and not equal to 32) */ + PF beq, 82f + PF bic, SCRATCH, \base, #31 + PF bcc, 81f + PF pld, [SCRATCH, #32*(prefetch_distance+2)] +81: PF pld, [SCRATCH, #32*(prefetch_distance+1)] +82: + .endif + .endif +.endm + + +.macro preload_line narrow_case, bpp, bpp_shift, base +/* "narrow_case" - just means that the macro was invoked from the "narrow" + * code path rather than the "medium" one - because in the narrow case, + * the row of pixels is known to output no more than 30 bytes, then + * (assuming the source pixels are no wider than the the destination + * pixels) they cannot possibly straddle more than 2 32-byte cachelines, + * meaning there's no need for a loop. + * "bpp" - number of bits per pixel in the channel (source, mask or + * destination) that's being preloaded, or 0 if this channel is not used + * for reading + * "bpp_shift" - log2 of ("bpp"/8) (except if "bpp"=0 of course) + * "base" - base address register of channel to preload (SRC, MASK or DST) + */ + .if \bpp > 0 + .if \narrow_case && (\bpp <= dst_w_bpp) + /* In these cases, each line for each channel is in either 1 or 2 cache lines */ + PF bic, WK0, \base, #31 + PF pld, [WK0] + PF add, WK1, \base, X, LSL #\bpp_shift + PF sub, WK1, WK1, #1 + PF bic, WK1, WK1, #31 + PF cmp, WK1, WK0 + PF beq, 90f + PF pld, [WK1] +90: + .else + PF bic, WK0, \base, #31 + PF pld, [WK0] + PF add, WK1, \base, X, lsl #\bpp_shift + PF sub, WK1, WK1, #1 + PF bic, WK1, WK1, #31 + PF cmp, WK1, WK0 + PF beq, 92f +91: PF add, WK0, WK0, #32 + PF cmp, WK0, WK1 + PF pld, [WK0] + PF bne, 91b +92: + .endif + .endif +.endm + + +.macro conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx + \process_head \cond, \numbytes, \firstreg, \unaligned_src, \unaligned_mask, 0 + .if \decrementx + sub\()\cond X, X, #8*\numbytes/dst_w_bpp + .endif + \process_tail \cond, \numbytes, \firstreg + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst \cond, \numbytes, \firstreg, DST + .endif +.endm + +.macro conditional_process1 cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx + .if (flags) & FLAG_BRANCH_OVER + .ifc \cond,mi + bpl 100f + .endif + .ifc \cond,cs + bcc 100f + .endif + .ifc \cond,ne + beq 100f + .endif + conditional_process1_helper , \process_head, \process_tail, \numbytes, \firstreg, \unaligned_src, \unaligned_mask, \decrementx +100: + .else + conditional_process1_helper \cond, \process_head, \process_tail, \numbytes, \firstreg, \unaligned_src, \unaligned_mask, \decrementx + .endif +.endm + +.macro conditional_process2 test, cond1, cond2, process_head, process_tail, numbytes1, numbytes2, firstreg1, firstreg2, unaligned_src, unaligned_mask, decrementx + .if (flags) & (FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE) + /* Can't interleave reads and writes */ + \test + conditional_process1 \cond1, \process_head, \process_tail, \numbytes1, \firstreg1, \unaligned_src, \unaligned_mask, \decrementx + .if (flags) & FLAG_PROCESS_CORRUPTS_PSR + \test + .endif + conditional_process1 \cond2, \process_head, \process_tail, \numbytes2, \firstreg2, \unaligned_src, \unaligned_mask, \decrementx + .else + /* Can interleave reads and writes for better scheduling */ + \test + \process_head \cond1, \numbytes1, \firstreg1, \unaligned_src, \unaligned_mask, 0 + \process_head \cond2, \numbytes2, \firstreg2, \unaligned_src, \unaligned_mask, 0 + .if \decrementx + sub\()\cond1 X, X, #8*\numbytes1/dst_w_bpp + sub\()\cond2 X, X, #8*\numbytes2/dst_w_bpp + .endif + \process_tail \cond1, \numbytes1, \firstreg1 + \process_tail \cond2, \numbytes2, \firstreg2 + pixst \cond1, \numbytes1, \firstreg1, DST + pixst \cond2, \numbytes2, \firstreg2, DST + .endif +.endm + + +.macro test_bits_1_0_ptr + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + movs SCRATCH, X, lsl #32-1 /* C,N = bits 1,0 of DST */ + .else + movs SCRATCH, WK0, lsl #32-1 /* C,N = bits 1,0 of DST */ + .endif +.endm + +.macro test_bits_3_2_ptr + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + movs SCRATCH, X, lsl #32-3 /* C,N = bits 3, 2 of DST */ + .else + movs SCRATCH, WK0, lsl #32-3 /* C,N = bits 3, 2 of DST */ + .endif +.endm + +.macro leading_15bytes process_head, process_tail + /* On entry, WK0 bits 0-3 = number of bytes until destination is 16-byte aligned */ + .set DECREMENT_X, 1 + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + .set DECREMENT_X, 0 + sub X, X, WK0, lsr #dst_bpp_shift + str X, [sp, #LINE_SAVED_REG_COUNT*4] + mov X, WK0 + .endif + /* Use unaligned loads in all cases for simplicity */ + .if dst_w_bpp == 8 + conditional_process2 test_bits_1_0_ptr, mi, cs, \process_head, \process_tail, 1, 2, 1, 2, 1, 1, DECREMENT_X + .elseif dst_w_bpp == 16 + test_bits_1_0_ptr + conditional_process1 cs, \process_head, \process_tail, 2, 2, 1, 1, DECREMENT_X + .endif + conditional_process2 test_bits_3_2_ptr, mi, cs, \process_head, \process_tail, 4, 8, 1, 2, 1, 1, DECREMENT_X + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + ldr X, [sp, #LINE_SAVED_REG_COUNT*4] + .endif +.endm + +.macro test_bits_3_2_pix + movs SCRATCH, X, lsl #dst_bpp_shift+32-3 +.endm + +.macro test_bits_1_0_pix + .if dst_w_bpp == 8 + movs SCRATCH, X, lsl #dst_bpp_shift+32-1 + .else + movs SCRATCH, X, lsr #1 + .endif +.endm + +.macro trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask + conditional_process2 test_bits_3_2_pix, cs, mi, \process_head, \process_tail, 8, 4, 0, 2, \unaligned_src, \unaligned_mask, 0 + .if dst_w_bpp == 16 + test_bits_1_0_pix + conditional_process1 cs, \process_head, \process_tail, 2, 0, \unaligned_src, \unaligned_mask, 0 + .elseif dst_w_bpp == 8 + conditional_process2 test_bits_1_0_pix, cs, mi, \process_head, \process_tail, 2, 1, 0, 1, \unaligned_src, \unaligned_mask, 0 + .endif +.endm + + +.macro wide_case_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, dst_alignment +110: + .set SUBBLOCK, 0 /* this is a count of STMs; there can be up to 8 STMs per block */ + .rept pix_per_block*dst_w_bpp/128 + \process_head , 16, 0, \unaligned_src, \unaligned_mask, 1 + .if (src_bpp > 0) && (mask_bpp == 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) + preload_middle src_bpp, SRC, 1 + .elseif (src_bpp == 0) && (mask_bpp > 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) + preload_middle mask_bpp, MASK, 1 + .else + preload_middle src_bpp, SRC, 0 + preload_middle mask_bpp, MASK, 0 + .endif + .if (dst_r_bpp > 0) && ((SUBBLOCK % 2) == 0) && (((flags) & FLAG_NO_PRELOAD_DST) == 0) + /* Because we know that writes are 16-byte aligned, it's relatively easy to ensure that + * destination prefetches are 32-byte aligned. It's also the easiest channel to offset + * preloads for, to achieve staggered prefetches for multiple channels, because there are + * always two STMs per prefetch, so there is always an opposite STM on which to put the + * preload. Note, no need to BIC the base register here */ + PF pld, [DST, #32*prefetch_distance - \dst_alignment] + .endif + \process_tail , 16, 0 + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst , 16, 0, DST + .endif + .set SUBBLOCK, SUBBLOCK+1 + .endr + subs X, X, #pix_per_block + bhs 110b +.endm + +.macro wide_case_inner_loop_and_trailing_pixels process_head, process_tail, process_inner_loop, exit_label, unaligned_src, unaligned_mask + /* Destination now 16-byte aligned; we have at least one block before we have to stop preloading */ + .if dst_r_bpp > 0 + tst DST, #16 + bne 111f + \process_inner_loop \process_head, \process_tail, \unaligned_src, \unaligned_mask, 16 + DST_PRELOAD_BIAS + b 112f +111: + .endif + \process_inner_loop \process_head, \process_tail, \unaligned_src, \unaligned_mask, 0 + DST_PRELOAD_BIAS +112: + /* Just before the final (prefetch_distance+1) 32-byte blocks, deal with final preloads */ + .if (src_bpp*pix_per_block > 256) || (mask_bpp*pix_per_block > 256) || (dst_r_bpp*pix_per_block > 256) + PF and, WK0, X, #pix_per_block-1 + .endif + preload_trailing src_bpp, src_bpp_shift, SRC + preload_trailing mask_bpp, mask_bpp_shift, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_trailing dst_r_bpp, dst_bpp_shift, DST + .endif + add X, X, #(prefetch_distance+2)*pix_per_block - 128/dst_w_bpp + /* The remainder of the line is handled identically to the medium case */ + medium_case_inner_loop_and_trailing_pixels \process_head, \process_tail,, \exit_label, \unaligned_src, \unaligned_mask +.endm + +.macro medium_case_inner_loop_and_trailing_pixels process_head, process_tail, unused, exit_label, unaligned_src, unaligned_mask +120: + \process_head , 16, 0, \unaligned_src, \unaligned_mask, 0 + \process_tail , 16, 0 + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst , 16, 0, DST + .endif + subs X, X, #128/dst_w_bpp + bhs 120b + /* Trailing pixels */ + tst X, #128/dst_w_bpp - 1 + beq \exit_label + trailing_15bytes \process_head, \process_tail, \unaligned_src, \unaligned_mask +.endm + +.macro narrow_case_inner_loop_and_trailing_pixels process_head, process_tail, unused, exit_label, unaligned_src, unaligned_mask + tst X, #16*8/dst_w_bpp + conditional_process1 ne, \process_head, \process_tail, 16, 0, \unaligned_src, \unaligned_mask, 0 + /* Trailing pixels */ + /* In narrow case, it's relatively unlikely to be aligned, so let's do without a branch here */ + trailing_15bytes \process_head, \process_tail, \unaligned_src, \unaligned_mask +.endm + +.macro switch_on_alignment action, process_head, process_tail, process_inner_loop, exit_label + /* Note that if we're reading the destination, it's already guaranteed to be aligned at this point */ + .if mask_bpp == 8 || mask_bpp == 16 + tst MASK, #3 + bne 141f + .endif + .if src_bpp == 8 || src_bpp == 16 + tst SRC, #3 + bne 140f + .endif + \action \process_head, \process_tail, \process_inner_loop, \exit_label, 0, 0 + .if src_bpp == 8 || src_bpp == 16 + b \exit_label +140: + \action \process_head, \process_tail, \process_inner_loop, \exit_label, 1, 0 + .endif + .if mask_bpp == 8 || mask_bpp == 16 + b \exit_label +141: + .if src_bpp == 8 || src_bpp == 16 + tst SRC, #3 + bne 142f + .endif + \action \process_head, \process_tail, \process_inner_loop, \exit_label, 0, 1 + .if src_bpp == 8 || src_bpp == 16 + b \exit_label +142: + \action \process_head, \process_tail, \process_inner_loop, \exit_label, 1, 1 + .endif + .endif +.endm + + +.macro end_of_line restore_x, vars_spilled, loop_label, last_one + .if \vars_spilled + /* Sadly, GAS doesn't seem have an equivalent of the DCI directive? */ + /* This is ldmia sp,{} */ + .word 0xE89D0000 | LINE_SAVED_REGS + .endif + subs Y, Y, #1 + .if \vars_spilled + .if (LINE_SAVED_REGS) & (1<<1) + str Y, [sp] + .endif + .endif + add DST, DST, STRIDE_D + .if src_bpp > 0 + add SRC, SRC, STRIDE_S + .endif + .if mask_bpp > 0 + add MASK, MASK, STRIDE_M + .endif + .if \restore_x + mov X, ORIG_W + .endif + bhs \loop_label + .ifc "\last_one","" + .if \vars_spilled + b 197f + .else + b 198f + .endif + .else + .if (!\vars_spilled) && ((flags) & FLAG_SPILL_LINE_VARS) + b 198f + .endif + .endif +.endm + + +.macro generate_composite_function fname, \ + src_bpp_, \ + mask_bpp_, \ + dst_w_bpp_, \ + flags_, \ + prefetch_distance_, \ + init, \ + newline, \ + cleanup, \ + process_head, \ + process_tail, \ + process_inner_loop + + pixman_asm_function \fname + +/* + * Make some macro arguments globally visible and accessible + * from other macros + */ + .set src_bpp, \src_bpp_ + .set mask_bpp, \mask_bpp_ + .set dst_w_bpp, \dst_w_bpp_ + .set flags, \flags_ + .set prefetch_distance, \prefetch_distance_ + +/* + * Select prefetch type for this function. + */ + .if prefetch_distance == 0 + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE + .else + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_STANDARD + .endif + + .if src_bpp == 32 + .set src_bpp_shift, 2 + .elseif src_bpp == 24 + .set src_bpp_shift, 0 + .elseif src_bpp == 16 + .set src_bpp_shift, 1 + .elseif src_bpp == 8 + .set src_bpp_shift, 0 + .elseif src_bpp == 0 + .set src_bpp_shift, -1 + .else + .error "requested src bpp (src_bpp) is not supported" + .endif + + .if mask_bpp == 32 + .set mask_bpp_shift, 2 + .elseif mask_bpp == 24 + .set mask_bpp_shift, 0 + .elseif mask_bpp == 8 + .set mask_bpp_shift, 0 + .elseif mask_bpp == 0 + .set mask_bpp_shift, -1 + .else + .error "requested mask bpp (mask_bpp) is not supported" + .endif + + .if dst_w_bpp == 32 + .set dst_bpp_shift, 2 + .elseif dst_w_bpp == 24 + .set dst_bpp_shift, 0 + .elseif dst_w_bpp == 16 + .set dst_bpp_shift, 1 + .elseif dst_w_bpp == 8 + .set dst_bpp_shift, 0 + .else + .error "requested dst bpp (dst_w_bpp) is not supported" + .endif + + .if (((flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp + .else + .set dst_r_bpp, 0 + .endif + + .set pix_per_block, 16*8/dst_w_bpp + .if src_bpp != 0 + .if 32*8/src_bpp > pix_per_block + .set pix_per_block, 32*8/src_bpp + .endif + .endif + .if mask_bpp != 0 + .if 32*8/mask_bpp > pix_per_block + .set pix_per_block, 32*8/mask_bpp + .endif + .endif + .if dst_r_bpp != 0 + .if 32*8/dst_r_bpp > pix_per_block + .set pix_per_block, 32*8/dst_r_bpp + .endif + .endif + +/* The standard entry conditions set up by pixman-arm-common.h are: + * r0 = width (pixels) + * r1 = height (rows) + * r2 = pointer to top-left pixel of destination + * r3 = destination stride (pixels) + * [sp] = source pixel value, or pointer to top-left pixel of source + * [sp,#4] = 0 or source stride (pixels) + * The following arguments are unused for non-mask operations + * [sp,#8] = mask pixel value, or pointer to top-left pixel of mask + * [sp,#12] = 0 or mask stride (pixels) + */ + +/* + * Assign symbolic names to registers + */ + X .req r0 /* pixels to go on this line */ + Y .req r1 /* lines to go */ + DST .req r2 /* destination pixel pointer */ + STRIDE_D .req r3 /* destination stride (bytes, minus width) */ + SRC .req r4 /* source pixel pointer */ + STRIDE_S .req r5 /* source stride (bytes, minus width) */ + MASK .req r6 /* mask pixel pointer (if applicable) */ + STRIDE_M .req r7 /* mask stride (bytes, minus width) */ + WK0 .req r8 /* pixel data registers */ + WK1 .req r9 + WK2 .req r10 + WK3 .req r11 + SCRATCH .req r12 + ORIG_W .req r14 /* width (pixels) */ + + push {r4-r11, lr} /* save all registers */ + + subs Y, Y, #1 + blo 199f + +#ifdef DEBUG_PARAMS + sub sp, sp, #9*4 +#endif + + .if src_bpp > 0 + ldr SRC, [sp, #ARGS_STACK_OFFSET] + ldr STRIDE_S, [sp, #ARGS_STACK_OFFSET+4] + .endif + .if mask_bpp > 0 + ldr MASK, [sp, #ARGS_STACK_OFFSET+8] + ldr STRIDE_M, [sp, #ARGS_STACK_OFFSET+12] + .endif + +#ifdef DEBUG_PARAMS + add Y, Y, #1 + stmia sp, {r0-r7,pc} + sub Y, Y, #1 +#endif + + \init + + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + /* Reserve a word in which to store X during leading pixels */ + sub sp, sp, #4 + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET+4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET+4 + .endif + + lsl STRIDE_D, #dst_bpp_shift /* stride in bytes */ + sub STRIDE_D, STRIDE_D, X, lsl #dst_bpp_shift + .if src_bpp > 0 + lsl STRIDE_S, #src_bpp_shift + sub STRIDE_S, STRIDE_S, X, lsl #src_bpp_shift + .endif + .if mask_bpp > 0 + lsl STRIDE_M, #mask_bpp_shift + sub STRIDE_M, STRIDE_M, X, lsl #mask_bpp_shift + .endif + + /* Are we not even wide enough to have one 16-byte aligned 16-byte block write? */ + cmp X, #2*16*8/dst_w_bpp - 1 + blo 170f + .if src_bpp || mask_bpp || dst_r_bpp /* Wide and medium cases are the same for fill */ + /* To preload ahead on the current line, we need at least (prefetch_distance+2) 32-byte blocks on all prefetch channels */ + cmp X, #(prefetch_distance+3)*pix_per_block - 1 + blo 160f + + /* Wide case */ + /* Adjust X so that the decrement instruction can also test for + * inner loop termination. We want it to stop when there are + * (prefetch_distance+1) complete blocks to go. */ + sub X, X, #(prefetch_distance+2)*pix_per_block + mov ORIG_W, X + .if (flags) & FLAG_SPILL_LINE_VARS_WIDE + /* This is stmdb sp!,{} */ + .word 0xE92D0000 | LINE_SAVED_REGS + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET + LINE_SAVED_REG_COUNT*4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET + LINE_SAVED_REG_COUNT*4 + .endif +151: /* New line */ + \newline + preload_leading_step1 src_bpp, WK1, SRC + preload_leading_step1 mask_bpp, WK2, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_leading_step1 dst_r_bpp, WK3, DST + .endif + + ands WK0, DST, #15 + beq 154f + rsb WK0, WK0, #16 /* number of leading bytes until destination aligned */ + + preload_leading_step2 src_bpp, src_bpp_shift, WK1, SRC + preload_leading_step2 mask_bpp, mask_bpp_shift, WK2, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_leading_step2 dst_r_bpp, dst_bpp_shift, WK3, DST + .endif + + leading_15bytes \process_head, \process_tail + +154: /* Destination now 16-byte aligned; we have at least one prefetch on each channel as well as at least one 16-byte output block */ + .if (src_bpp > 0) && (mask_bpp == 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) + and SCRATCH, SRC, #31 + rsb SCRATCH, SCRATCH, #32*prefetch_distance + .elseif (src_bpp == 0) && (mask_bpp > 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) + and SCRATCH, MASK, #31 + rsb SCRATCH, SCRATCH, #32*prefetch_distance + .endif + .ifc "\process_inner_loop","" + switch_on_alignment wide_case_inner_loop_and_trailing_pixels, \process_head, \process_tail, wide_case_inner_loop, 157f + .else + switch_on_alignment wide_case_inner_loop_and_trailing_pixels, \process_head, \process_tail, \process_inner_loop, 157f + .endif + +157: /* Check for another line */ + end_of_line 1, %((flags) & FLAG_SPILL_LINE_VARS_WIDE), 151b + .if (flags) & FLAG_SPILL_LINE_VARS_WIDE + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET - LINE_SAVED_REG_COUNT*4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET - LINE_SAVED_REG_COUNT*4 + .endif + .endif + + .ltorg + +160: /* Medium case */ + mov ORIG_W, X + .if (flags) & FLAG_SPILL_LINE_VARS_NON_WIDE + /* This is stmdb sp!,{} */ + .word 0xE92D0000 | LINE_SAVED_REGS + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET + LINE_SAVED_REG_COUNT*4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET + LINE_SAVED_REG_COUNT*4 + .endif +161: /* New line */ + \newline + preload_line 0, src_bpp, src_bpp_shift, SRC /* in: X, corrupts: WK0-WK1 */ + preload_line 0, mask_bpp, mask_bpp_shift, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_line 0, dst_r_bpp, dst_bpp_shift, DST + .endif + + sub X, X, #128/dst_w_bpp /* simplifies inner loop termination */ + ands WK0, DST, #15 + beq 164f + rsb WK0, WK0, #16 /* number of leading bytes until destination aligned */ + + leading_15bytes \process_head, \process_tail + +164: /* Destination now 16-byte aligned; we have at least one 16-byte output block */ + switch_on_alignment medium_case_inner_loop_and_trailing_pixels, \process_head, \process_tail,, 167f + +167: /* Check for another line */ + end_of_line 1, %((flags) & FLAG_SPILL_LINE_VARS_NON_WIDE), 161b + + .ltorg + +170: /* Narrow case, less than 31 bytes, so no guarantee of at least one 16-byte block */ + .if dst_w_bpp < 32 + mov ORIG_W, X + .endif + .if (flags) & FLAG_SPILL_LINE_VARS_NON_WIDE + /* This is stmdb sp!,{} */ + .word 0xE92D0000 | LINE_SAVED_REGS + .endif +171: /* New line */ + \newline + preload_line 1, src_bpp, src_bpp_shift, SRC /* in: X, corrupts: WK0-WK1 */ + preload_line 1, mask_bpp, mask_bpp_shift, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_line 1, dst_r_bpp, dst_bpp_shift, DST + .endif + + .if dst_w_bpp == 8 + tst DST, #3 + beq 174f +172: subs X, X, #1 + blo 177f + \process_head , 1, 0, 1, 1, 0 + \process_tail , 1, 0 + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst , 1, 0, DST + .endif + tst DST, #3 + bne 172b + .elseif dst_w_bpp == 16 + tst DST, #2 + beq 174f + subs X, X, #1 + blo 177f + \process_head , 2, 0, 1, 1, 0 + \process_tail , 2, 0 + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst , 2, 0, DST + .endif + .endif + +174: /* Destination now 4-byte aligned; we have 0 or more output bytes to go */ + switch_on_alignment narrow_case_inner_loop_and_trailing_pixels, \process_head, \process_tail,, 177f + +177: /* Check for another line */ + end_of_line %(dst_w_bpp < 32), %((flags) & FLAG_SPILL_LINE_VARS_NON_WIDE), 171b, last_one + .if (flags) & FLAG_SPILL_LINE_VARS_NON_WIDE + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET - LINE_SAVED_REG_COUNT*4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET - LINE_SAVED_REG_COUNT*4 + .endif + +197: + .if (flags) & FLAG_SPILL_LINE_VARS + add sp, sp, #LINE_SAVED_REG_COUNT*4 + .endif +198: + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET-4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET-4 + add sp, sp, #4 + .endif + + \cleanup + +#ifdef DEBUG_PARAMS + add sp, sp, #9*4 /* junk the debug copy of arguments */ +#endif +199: + pop {r4-r11, pc} /* exit */ + + .ltorg + + .unreq X + .unreq Y + .unreq DST + .unreq STRIDE_D + .unreq SRC + .unreq STRIDE_S + .unreq MASK + .unreq STRIDE_M + .unreq WK0 + .unreq WK1 + .unreq WK2 + .unreq WK3 + .unreq SCRATCH + .unreq ORIG_W + pixman_end_asm_function +.endm + +.macro line_saved_regs x:vararg + .set LINE_SAVED_REGS, 0 + .set LINE_SAVED_REG_COUNT, 0 + .irp SAVED_REG,\x + .ifc "SAVED_REG","Y" + .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<1) + .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 + .endif + .ifc "SAVED_REG","STRIDE_D" + .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<3) + .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 + .endif + .ifc "SAVED_REG","STRIDE_S" + .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<5) + .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 + .endif + .ifc "SAVED_REG","STRIDE_M" + .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<7) + .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 + .endif + .ifc "SAVED_REG","ORIG_W" + .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<14) + .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 + .endif + .endr +.endm + +.macro nop_macro x:vararg +.endm diff --git a/gfx/cairo/libpixman/src/pixman-arm-simd.c b/gfx/cairo/libpixman/src/pixman-arm-simd.c new file mode 100644 index 0000000000..f0d14540bc --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm-simd.c @@ -0,0 +1,291 @@ +/* + * Copyright © 2008 Mozilla Corporation + * + * 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 Mozilla Corporation not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Mozilla Corporation makes 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. + * + * Author: Jeff Muizelaar (jeff@infidigm.net) + * + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pixman-private.h" +#include "pixman-arm-common.h" +#include "pixman-inlines.h" + +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_8888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_x888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_0565_0565, + uint16_t, 1, uint16_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_0565_8888, + uint16_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, src_x888_0565, + uint32_t, 1, uint16_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, add_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, over_8888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_SRC_DST (armv6, in_reverse_8888_8888, + uint32_t, 1, uint32_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, armv6, over_n_8888, + uint32_t, 1) +PIXMAN_ARM_BIND_FAST_PATH_N_DST (0, armv6, over_reverse_n_8888, + uint32_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, armv6, over_8888_n_8888, + uint32_t, 1, uint32_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, armv6, over_n_8_8888, + uint8_t, 1, uint32_t, 1) + +PIXMAN_ARM_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, armv6, over_n_8888_8888_ca, + uint32_t, 1, uint32_t, 1) + +PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (armv6, 0565_0565, SRC, + uint16_t, uint16_t) +PIXMAN_ARM_BIND_SCALED_NEAREST_SRC_DST (armv6, 8888_8888, SRC, + uint32_t, uint32_t) + +void +pixman_composite_src_n_8888_asm_armv6 (int32_t w, + int32_t h, + uint32_t *dst, + int32_t dst_stride, + uint32_t src); + +void +pixman_composite_src_n_0565_asm_armv6 (int32_t w, + int32_t h, + uint16_t *dst, + int32_t dst_stride, + uint16_t src); + +void +pixman_composite_src_n_8_asm_armv6 (int32_t w, + int32_t h, + uint8_t *dst, + int32_t dst_stride, + uint8_t src); + +static pixman_bool_t +arm_simd_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, /* in 32-bit words */ + int bpp, + int x, + int y, + int width, + int height, + uint32_t _xor) +{ + /* stride is always multiple of 32bit units in pixman */ + uint32_t byte_stride = stride * sizeof(uint32_t); + + switch (bpp) + { + case 8: + pixman_composite_src_n_8_asm_armv6 ( + width, + height, + (uint8_t *)(((char *) bits) + y * byte_stride + x), + byte_stride, + _xor & 0xff); + return TRUE; + case 16: + pixman_composite_src_n_0565_asm_armv6 ( + width, + height, + (uint16_t *)(((char *) bits) + y * byte_stride + x * 2), + byte_stride / 2, + _xor & 0xffff); + return TRUE; + case 32: + pixman_composite_src_n_8888_asm_armv6 ( + width, + height, + (uint32_t *)(((char *) bits) + y * byte_stride + x * 4), + byte_stride / 4, + _xor); + return TRUE; + default: + return FALSE; + } +} + +static pixman_bool_t +arm_simd_blt (pixman_implementation_t *imp, + uint32_t * src_bits, + uint32_t * dst_bits, + int src_stride, /* in 32-bit words */ + int dst_stride, /* in 32-bit words */ + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height) +{ + if (src_bpp != dst_bpp) + return FALSE; + + switch (src_bpp) + { + case 8: + pixman_composite_src_8_8_asm_armv6 ( + width, height, + (uint8_t *)(((char *) dst_bits) + + dest_y * dst_stride * 4 + dest_x * 1), dst_stride * 4, + (uint8_t *)(((char *) src_bits) + + src_y * src_stride * 4 + src_x * 1), src_stride * 4); + return TRUE; + case 16: + pixman_composite_src_0565_0565_asm_armv6 ( + width, height, + (uint16_t *)(((char *) dst_bits) + + dest_y * dst_stride * 4 + dest_x * 2), dst_stride * 2, + (uint16_t *)(((char *) src_bits) + + src_y * src_stride * 4 + src_x * 2), src_stride * 2); + return TRUE; + case 32: + pixman_composite_src_8888_8888_asm_armv6 ( + width, height, + (uint32_t *)(((char *) dst_bits) + + dest_y * dst_stride * 4 + dest_x * 4), dst_stride, + (uint32_t *)(((char *) src_bits) + + src_y * src_stride * 4 + src_x * 4), src_stride); + return TRUE; + default: + return FALSE; + } +} + +static const pixman_fast_path_t arm_simd_fast_paths[] = +{ + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, armv6_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, armv6_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, armv6_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, armv6_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, armv6_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, armv6_composite_src_8888_8888), + + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, armv6_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, armv6_composite_src_x888_8888), + + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a1r5g5b5, null, a1r5g5b5, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a1b5g5r5, null, a1b5g5r5, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a1r5g5b5, null, x1r5g5b5, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a1b5g5r5, null, x1b5g5r5, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, x1r5g5b5, null, x1r5g5b5, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, x1b5g5r5, null, x1b5g5r5, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a4r4g4b4, null, a4r4g4b4, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a4b4g4r4, null, a4b4g4r4, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a4r4g4b4, null, x4r4g4b4, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a4b4g4r4, null, x4b4g4r4, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, x4r4g4b4, null, x4r4g4b4, armv6_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, x4b4g4r4, null, x4b4g4r4, armv6_composite_src_0565_0565), + + PIXMAN_STD_FAST_PATH (SRC, a8, null, a8, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, r3g3b2, null, r3g3b2, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, b2g3r3, null, b2g3r3, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, a2r2g2b2, null, a2r2g2b2, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, a2b2g2r2, null, a2b2g2r2, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, c8, null, c8, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, g8, null, g8, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, x4a4, null, x4a4, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, x4c4, null, x4c4, armv6_composite_src_8_8), + PIXMAN_STD_FAST_PATH (SRC, x4g4, null, x4g4, armv6_composite_src_8_8), + + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, a8r8g8b8, armv6_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, x8r8g8b8, armv6_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, a8b8g8r8, armv6_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, x8b8g8r8, armv6_composite_src_0565_8888), + + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, armv6_composite_src_x888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, armv6_composite_src_x888_0565), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, armv6_composite_src_x888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, armv6_composite_src_x888_0565), + + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, armv6_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, armv6_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, armv6_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, armv6_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, armv6_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, armv6_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, a8b8g8r8, armv6_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, x8b8g8r8, armv6_composite_over_8888_n_8888), + + PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, armv6_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, armv6_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, a8b8g8r8, armv6_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, x8b8g8r8, armv6_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, armv6_composite_over_reverse_n_8888), + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, armv6_composite_over_reverse_n_8888), + + PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, armv6_composite_add_8_8), + + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, armv6_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, armv6_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, armv6_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, armv6_composite_over_n_8_8888), + + PIXMAN_STD_FAST_PATH (IN_REVERSE, a8r8g8b8, null, a8r8g8b8, armv6_composite_in_reverse_8888_8888), + PIXMAN_STD_FAST_PATH (IN_REVERSE, a8r8g8b8, null, x8r8g8b8, armv6_composite_in_reverse_8888_8888), + PIXMAN_STD_FAST_PATH (IN_REVERSE, a8b8g8r8, null, a8b8g8r8, armv6_composite_in_reverse_8888_8888), + PIXMAN_STD_FAST_PATH (IN_REVERSE, a8b8g8r8, null, x8b8g8r8, armv6_composite_in_reverse_8888_8888), + + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, armv6_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, armv6_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, armv6_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, armv6_composite_over_n_8888_8888_ca), + + SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, r5g6b5, armv6_0565_0565), + SIMPLE_NEAREST_FAST_PATH (SRC, b5g6r5, b5g6r5, armv6_0565_0565), + + SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, armv6_8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, armv6_8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, armv6_8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, armv6_8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, armv6_8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8, armv6_8888_8888), + + { PIXMAN_OP_NONE }, +}; + +pixman_implementation_t * +_pixman_implementation_create_arm_simd (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = _pixman_implementation_create (fallback, arm_simd_fast_paths); + + imp->blt = arm_simd_blt; + imp->fill = arm_simd_fill; + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-arm.c b/gfx/cairo/libpixman/src/pixman-arm.c new file mode 100644 index 0000000000..a164b7f6ca --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arm.c @@ -0,0 +1,256 @@ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pixman-private.h" + +typedef enum +{ + ARM_V7 = (1 << 0), + ARM_V6 = (1 << 1), + ARM_VFP = (1 << 2), + ARM_NEON = (1 << 3), + ARM_IWMMXT = (1 << 4) +} arm_cpu_features_t; + +#if defined(USE_ARM_SIMD) || defined(USE_ARM_NEON) || defined(USE_ARM_IWMMXT) + +#if defined(_MSC_VER) + +/* Needed for EXCEPTION_ILLEGAL_INSTRUCTION */ +#include + +extern int pixman_msvc_try_arm_neon_op (); +extern int pixman_msvc_try_arm_simd_op (); + +static arm_cpu_features_t +detect_cpu_features (void) +{ + arm_cpu_features_t features = 0; + + __try + { + pixman_msvc_try_arm_simd_op (); + features |= ARM_V6; + } + __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) + { + } + + __try + { + pixman_msvc_try_arm_neon_op (); + features |= ARM_NEON; + } + __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) + { + } + + return features; +} + +#elif defined(__APPLE__) && defined(TARGET_OS_IPHONE) /* iOS */ + +#include "TargetConditionals.h" + +static arm_cpu_features_t +detect_cpu_features (void) +{ + arm_cpu_features_t features = 0; + + features |= ARM_V6; + + /* Detection of ARM NEON on iOS is fairly simple because iOS binaries + * contain separate executable images for each processor architecture. + * So all we have to do is detect the armv7 architecture build. The + * operating system automatically runs the armv7 binary for armv7 devices + * and the armv6 binary for armv6 devices. + */ +#if defined(__ARM_NEON__) + features |= ARM_NEON; +#endif + + return features; +} + +#elif defined(__ANDROID__) || defined(ANDROID) /* Android */ + +#include + +static arm_cpu_features_t +detect_cpu_features (void) +{ + arm_cpu_features_t features = 0; + AndroidCpuFamily cpu_family; + uint64_t cpu_features; + + cpu_family = android_getCpuFamily(); + cpu_features = android_getCpuFeatures(); + + if (cpu_family == ANDROID_CPU_FAMILY_ARM) + { + if (cpu_features & ANDROID_CPU_ARM_FEATURE_ARMv7) + features |= ARM_V7; + + if (cpu_features & ANDROID_CPU_ARM_FEATURE_VFPv3) + features |= ARM_VFP; + + if (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON) + features |= ARM_NEON; + } + + return features; +} + +#elif defined (__linux__) /* linux ELF */ + +#include +#include +#include +#include +#include +#include +#include + +static arm_cpu_features_t +detect_cpu_features (void) +{ + arm_cpu_features_t features = 0; + Elf32_auxv_t aux; + int fd; + + fd = open ("/proc/self/auxv", O_RDONLY); + if (fd >= 0) + { + while (read (fd, &aux, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t)) + { + if (aux.a_type == AT_HWCAP) + { + uint32_t hwcap = aux.a_un.a_val; + + /* hardcode these values to avoid depending on specific + * versions of the hwcap header, e.g. HWCAP_NEON + */ + if ((hwcap & 64) != 0) + features |= ARM_VFP; + if ((hwcap & 512) != 0) + features |= ARM_IWMMXT; + /* this flag is only present on kernel 2.6.29 */ + if ((hwcap & 4096) != 0) + features |= ARM_NEON; + } + else if (aux.a_type == AT_PLATFORM) + { + const char *plat = (const char*) aux.a_un.a_val; + + if (strncmp (plat, "v7l", 3) == 0) + features |= (ARM_V7 | ARM_V6); + else if (strncmp (plat, "v6l", 3) == 0) + features |= ARM_V6; + } + } + close (fd); + } + + return features; +} + +#elif defined (_3DS) /* 3DS homebrew (devkitARM) */ + +static arm_cpu_features_t +detect_cpu_features (void) +{ + arm_cpu_features_t features = 0; + + features |= ARM_V6; + + return features; +} + +#elif defined (PSP2) || defined (__SWITCH__) +/* Vita (VitaSDK) or Switch (devkitA64) homebrew */ + +static arm_cpu_features_t +detect_cpu_features (void) +{ + arm_cpu_features_t features = 0; + + features |= ARM_NEON; + + return features; +} + +#else /* Unknown */ + +static arm_cpu_features_t +detect_cpu_features (void) +{ + return 0; +} + +#endif /* Linux elf */ + +static pixman_bool_t +have_feature (arm_cpu_features_t feature) +{ + static pixman_bool_t initialized; + static arm_cpu_features_t features; + + if (!initialized) + { + features = detect_cpu_features(); + initialized = TRUE; + } + + return (features & feature) == feature; +} + +#endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */ + +pixman_implementation_t * +_pixman_arm_get_implementations (pixman_implementation_t *imp) +{ +#ifdef USE_ARM_SIMD + if (!_pixman_disabled ("arm-simd") && have_feature (ARM_V6)) + imp = _pixman_implementation_create_arm_simd (imp); +#endif + +#ifdef USE_ARM_IWMMXT + if (!_pixman_disabled ("arm-iwmmxt") && have_feature (ARM_IWMMXT)) + imp = _pixman_implementation_create_mmx (imp); +#endif + +#ifdef USE_ARM_NEON + if (!_pixman_disabled ("arm-neon") && have_feature (ARM_NEON)) + imp = _pixman_implementation_create_arm_neon (imp); +#endif + +#ifdef USE_ARM_A64_NEON + /* neon is a part of aarch64 */ + if (!_pixman_disabled ("arm-neon")) + imp = _pixman_implementation_create_arm_neon (imp); +#endif + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm-bilinear.S b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm-bilinear.S new file mode 100644 index 0000000000..7303bdcdd3 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm-bilinear.S @@ -0,0 +1,1276 @@ +/* + * Copyright © 2011 SCore 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. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + * Author: Taekyun Kim (tkq.kim@samsung.com) + */ + +/* + * This file contains scaled bilinear scanline functions implemented + * using older siarhei's bilinear macro template. + * + * << General scanline function procedures >> + * 1. bilinear interpolate source pixels + * 2. load mask pixels + * 3. load destination pixels + * 4. duplicate mask to fill whole register + * 5. interleave source & destination pixels + * 6. apply mask to source pixels + * 7. combine source & destination pixels + * 8, Deinterleave final result + * 9. store destination pixels + * + * All registers with single number (i.e. src0, tmp0) are 64-bits registers. + * Registers with double numbers(src01, dst01) are 128-bits registers. + * All temp registers can be used freely outside the code block. + * Assume that symbol(register .req) OUT and MASK are defined at caller of these macro blocks. + * + * Remarks + * There can be lots of pipeline stalls inside code block and between code blocks. + * Further optimizations will be done by new macro templates using head/tail_head/tail scheme. + */ + +/* Prevent the stack from becoming executable for no reason... */ +#if defined(__linux__) && defined (__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +.text +.arch armv8-a +.altmacro +.p2align 2 + +#include "pixman-private.h" +#include "pixman-arm-asm.h" +#include "pixman-arma64-neon-asm.h" + +/* + * Bilinear macros from pixman-arm-neon-asm.S + */ + +/* + * Bilinear scaling support code which tries to provide pixel fetching, color + * format conversion, and interpolation as separate macros which can be used + * as the basic building blocks for constructing bilinear scanline functions. + */ + +.macro bilinear_load_8888 reg1, reg2, tmp + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #2 + ld1 {\()\reg1\().2s}, [TMP1], STRIDE + ld1 {\()\reg2\().2s}, [TMP1] +.endm + +.macro bilinear_load_0565 reg1, reg2, tmp + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + ld1 {\()\reg2\().s}[0], [TMP1], STRIDE + ld1 {\()\reg2\().s}[1], [TMP1] + convert_four_0565_to_x888_packed \reg2, \reg1, \reg2, \tmp +.endm + +.macro bilinear_load_and_vertical_interpolate_two_8888 \ + acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 + + bilinear_load_8888 \reg1, \reg2, \tmp1 + umull \()\acc1\().8h, \()\reg1\().8b, v28.8b + umlal \()\acc1\().8h, \()\reg2\().8b, v29.8b + bilinear_load_8888 \reg3, \reg4, \tmp2 + umull \()\acc2\().8h, \()\reg3\().8b, v28.8b + umlal \()\acc2\().8h, \()\reg4\().8b, v29.8b +.endm + +.macro bilinear_load_and_vertical_interpolate_four_8888 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi, \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + bilinear_load_and_vertical_interpolate_two_8888 \ + \xacc1, \xacc2, \xreg1, \xreg2, \xreg3, \xreg4, \xacc2lo, xacc2hi + bilinear_load_and_vertical_interpolate_two_8888 \ + \yacc1, \yacc2, \yreg1, \yreg2, \yreg3, \yreg4, \yacc2lo, \yacc2hi +.endm + +.macro vzip reg1, reg2 + zip1 v24.8b, \reg1, \reg2 + zip2 \reg2, \reg1, \reg2 + mov \reg1, v24.8b +.endm + +.macro vuzp reg1, reg2 + uzp1 v24.8b, \reg1, \reg2 + uzp2 \reg2, \reg1, \reg2 + mov \reg1, v24.8b +.endm + +.macro bilinear_load_and_vertical_interpolate_two_0565 \ + acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr WTMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 + ld1 {\()\acc2\().s}[0], [TMP1], STRIDE + ld1 {\()\acc2\().s}[2], [TMP2], STRIDE + ld1 {\()\acc2\().s}[1], [TMP1] + ld1 {\()\acc2\().s}[3], [TMP2] + convert_0565_to_x888 \acc2, \reg3, \reg2, \reg1 + vzip \()\reg1\().8b, \()\reg3\().8b + vzip \()\reg2\().8b, \()\reg4\().8b + vzip \()\reg3\().8b, \()\reg4\().8b + vzip \()\reg1\().8b, \()\reg2\().8b + umull \()\acc1\().8h, \()\reg1\().8b, v28.8b + umlal \()\acc1\().8h, \()\reg2\().8b, v29.8b + umull \()\acc2\().8h, \()\reg3\().8b, v28.8b + umlal \()\acc2\().8h, \()\reg4\().8b, v29.8b +.endm + +.macro bilinear_load_and_vertical_interpolate_four_0565 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi, \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr WTMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 + ld1 {\()\xacc2\().s}[0], [TMP1], STRIDE + ld1 {\()\xacc2\().s}[2], [TMP2], STRIDE + ld1 {\()\xacc2\().s}[1], [TMP1] + ld1 {\()\xacc2\().s}[3], [TMP2] + convert_0565_to_x888 \xacc2, \xreg3, \xreg2, \xreg1 + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr WTMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 + ld1 {\()\yacc2\().s}[0], [TMP1], STRIDE + vzip \()\xreg1\().8b, \()\xreg3\().8b + ld1 {\()\yacc2\().s}[2], [TMP2], STRIDE + vzip \()\xreg2\().8b, \()\xreg4\().8b + ld1 {\()\yacc2\().s}[1], [TMP1] + vzip \()\xreg3\().8b, \()\xreg4\().8b + ld1 {\()\yacc2\().s}[3], [TMP2] + vzip \()\xreg1\().8b, \()\xreg2\().8b + convert_0565_to_x888 \yacc2, \yreg3, \yreg2, \yreg1 + umull \()\xacc1\().8h, \()\xreg1\().8b, v28.8b + vzip \()\yreg1\().8b, \()\yreg3\().8b + umlal \()\xacc1\().8h, \()\xreg2\().8b, v29.8b + vzip \()\yreg2\().8b, \()\yreg4\().8b + umull \()\xacc2\().8h, \()\xreg3\().8b, v28.8b + vzip \()\yreg3\().8b, \()\yreg4\().8b + umlal \()\xacc2\().8h, \()\xreg4\().8b, v29.8b + vzip \()\yreg1\().8b, \()\yreg2\().8b + umull \()\yacc1\().8h, \()\yreg1\().8b, v28.8b + umlal \()\yacc1\().8h, \()\yreg2\().8b, v29.8b + umull \()\yacc2\().8h, \()\yreg3\().8b, v28.8b + umlal \()\yacc2\().8h, \()\yreg4\().8b, v29.8b +.endm + +.macro bilinear_store_8888 numpix, tmp1, tmp2 +.if \numpix == 4 + st1 {v0.2s, v1.2s}, [OUT], #16 +.elseif \numpix == 2 + st1 {v0.2s}, [OUT], #8 +.elseif \numpix == 1 + st1 {v0.s}[0], [OUT], #4 +.else + .error bilinear_store_8888 \numpix is unsupported +.endif +.endm + +.macro bilinear_store_0565 numpix, tmp1, tmp2 + vuzp v0.8b, v1.8b + vuzp v2.8b, v3.8b + vuzp v1.8b, v3.8b + vuzp v0.8b, v2.8b + convert_8888_to_0565 v2, v1, v0, v1, \tmp1, \tmp2 +.if \numpix == 4 + st1 {v1.4h}, [OUT], #8 +.elseif \numpix == 2 + st1 {v1.s}[0], [OUT], #4 +.elseif \numpix == 1 + st1 {v1.h}[0], [OUT], #2 +.else + .error bilinear_store_0565 \numpix is unsupported +.endif +.endm + + +/* + * Macros for loading mask pixels into register 'mask'. + * dup must be done in somewhere else. + */ +.macro bilinear_load_mask_x numpix, mask +.endm + +.macro bilinear_load_mask_8 numpix, mask +.if \numpix == 4 + ld1 {\()\mask\().s}[0], [MASK], #4 +.elseif \numpix == 2 + ld1 {\()\mask\().h}[0], [MASK], #2 +.elseif \numpix == 1 + ld1 {\()\mask\().b}[0], [MASK], #1 +.else + .error bilinear_load_mask_8 \numpix is unsupported +.endif + prfum PREFETCH_MODE, [MASK, #(prefetch_offset)] +.endm + +.macro bilinear_load_mask mask_fmt, numpix, mask + bilinear_load_mask_\mask_fmt \numpix, \mask +.endm + + +/* + * Macros for loading destination pixels into register 'dst0' and 'dst1'. + * Interleave should be done somewhere else. + */ +.macro bilinear_load_dst_0565_src numpix, dst0, dst1, dst01 +.endm + +.macro bilinear_load_dst_8888_src numpix, dst0, dst1, dst01 +.endm + +.macro bilinear_load_dst_8888 numpix, dst0, dst1, dst01 +.if \numpix == 4 + ld1 {\()\dst0\().2s, \()\dst1\().2s}, [OUT] +.elseif \numpix == 2 + ld1 {\()\dst0\().2s}, [OUT] +.elseif \numpix == 1 + ld1 {\()\dst0\().s}[0], [OUT] +.else + .error bilinear_load_dst_8888 \numpix is unsupported +.endif + mov \()\dst01\().d[0], \()\dst0\().d[0] + mov \()\dst01\().d[1], \()\dst1\().d[0] + prfm PREFETCH_MODE, [OUT, #(prefetch_offset * 4)] +.endm + +.macro bilinear_load_dst_8888_over numpix, dst0, dst1, dst01 + bilinear_load_dst_8888 \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_load_dst_8888_add numpix, dst0, dst1, dst01 + bilinear_load_dst_8888 \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_load_dst dst_fmt, op, numpix, dst0, dst1, dst01 + bilinear_load_dst_\()\dst_fmt\()_\()\op \numpix, \dst0, \dst1, \dst01 +.endm + +/* + * Macros for duplicating partially loaded mask to fill entire register. + * We will apply mask to interleaved source pixels, that is + * (r0, r1, r2, r3, g0, g1, g2, g3) x (m0, m1, m2, m3, m0, m1, m2, m3) + * (b0, b1, b2, b3, a0, a1, a2, a3) x (m0, m1, m2, m3, m0, m1, m2, m3) + * So, we need to duplicate loaded mask into whole register. + * + * For two pixel case + * (r0, r1, x, x, g0, g1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) + * (b0, b1, x, x, a0, a1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) + * We can do some optimizations for this including last pixel cases. + */ +.macro bilinear_duplicate_mask_x numpix, mask +.endm + +.macro bilinear_duplicate_mask_8 numpix, mask +.if \numpix == 4 + dup \()\mask\().2s, \()\mask\().s[0] +.elseif \numpix == 2 + dup \()\mask\().4h, \()\mask\().h[0] +.elseif \numpix == 1 + dup \()\mask\().8b, \()\mask\().b[0] +.else + .error bilinear_duplicate_\mask_8 is unsupported +.endif +.endm + +.macro bilinear_duplicate_mask mask_fmt, numpix, mask + bilinear_duplicate_mask_\()\mask_fmt \numpix, \mask +.endm + +/* + * Macros for interleaving src and dst pixels to rrrr gggg bbbb aaaa form. + * Interleave should be done when maks is enabled or operator is 'over'. + */ +.macro bilinear_interleave src0, src1, src01, dst0, dst1, dst01 + vuzp \()\src0\().8b, \()\src1\().8b + vuzp \()\dst0\().8b, \()\dst1\().8b + vuzp \()\src0\().8b, \()\src1\().8b + vuzp \()\dst0\().8b, \()\dst1\().8b + mov \()\src01\().d[1], \()\src1\().d[0] + mov \()\src01\().d[0], \()\src0\().d[0] + mov \()\dst01\().d[1], \()\dst1\().d[0] + mov \()\dst01\().d[0], \()\dst0\().d[0] +.endm + +.macro bilinear_interleave_src_dst_x_src \ + numpix, src0, src1, src01, dst0, dst1, dst01 +.endm + +.macro bilinear_interleave_src_dst_x_over \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_interleave_src_dst_x_add \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_interleave_src_dst_8_src \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_interleave_src_dst_8_over \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_interleave_src_dst_8_add \ + numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_interleave_src_dst \ + mask_fmt, op, numpix, src0, src1, src01, dst0, dst1, dst01 + + bilinear_interleave_src_dst_\()\mask_fmt\()_\()\op \ + \numpix, \src0, \src1, \src01, \dst0, \dst1, \dst01 +.endm + + +/* + * Macros for applying masks to src pixels. (see combine_mask_u() function) + * src, dst should be in interleaved form. + * mask register should be in form (m0, m1, m2, m3). + */ +.macro bilinear_apply_mask_to_src_x \ + numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 +.endm + +.macro bilinear_apply_mask_to_src_8 \ + numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + + umull \()\tmp01\().8h, \()\src0\().8b, \()\mask\().8b + umull \()\tmp23\().8h, \()\src1\().8b, \()\mask\().8b + /* bubbles */ + urshr \()\tmp45\().8h, \()\tmp01\().8h, #8 + urshr \()\tmp67\().8h, \()\tmp23\().8h, #8 + /* bubbles */ + raddhn \()\src0\().8b, \()\tmp45\().8h, \()\tmp01\().8h + raddhn \()\src1\().8b, \()\tmp67\().8h, \()\tmp23\().8h + mov \()\src01\().d[0], \()\src0\().d[0] + mov \()\src01\().d[1], \()\src1\().d[0] +.endm + +.macro bilinear_apply_mask_to_src \ + mask_fmt, numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + + bilinear_apply_mask_to_src_\()\mask_fmt \ + \numpix, \src0, \src1, \src01, \mask, \ + \tmp01, \tmp23, \tmp45, \tmp67 +.endm + + +/* + * Macros for combining src and destination pixels. + * Interleave or not is depending on operator 'op'. + */ +.macro bilinear_combine_src \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 +.endm + +.macro bilinear_combine_over \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + + dup \()\tmp8\().2s, \()\src1\().s[1] + /* bubbles */ + mvn \()\tmp8\().8b, \()\tmp8\().8b + /* bubbles */ + umull \()\tmp01\().8h, \()\dst0\().8b, \()\tmp8\().8b + /* bubbles */ + umull \()\tmp23\().8h, \()\dst1\().8b, \()\tmp8\().8b + /* bubbles */ + urshr \()\tmp45\().8h, \()\tmp01\().8h, #8 + urshr \()\tmp67\().8h, \()\tmp23\().8h, #8 + /* bubbles */ + raddhn \()\dst0\().8b, \()\tmp45\().8h, \()\tmp01\().8h + raddhn \()\dst1\().8b, \()\tmp67\().8h, \()\tmp23\().8h + mov \()\dst01\().d[0], \()\dst0\().d[0] + mov \()\dst01\().d[1], \()\dst1\().d[0] + /* bubbles */ + uqadd \()\src0\().8b, \()\dst0\().8b, \()\src0\().8b + uqadd \()\src1\().8b, \()\dst1\().8b, \()\src1\().8b + mov \()\src01\().d[0], \()\src0\().d[0] + mov \()\src01\().d[1], \()\src1\().d[0] +.endm + +.macro bilinear_combine_add \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + + uqadd \()\src0\().8b, \()\dst0\().8b, \()\src0\().8b + uqadd \()\src1\().8b, \()\dst1\().8b, \()\src1\().8b + mov \()\src01\().d[0], \()\src0\().d[0] + mov \()\src01\().d[1], \()\src1\().d[0] +.endm + +.macro bilinear_combine \ + op, numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + + bilinear_combine_\()\op \ + \numpix, \src0, \src1, \src01, \dst0, \dst1, \dst01, \ + \tmp01, \tmp23, \tmp45, \tmp67, \tmp8 +.endm + +/* + * Macros for final deinterleaving of destination pixels if needed. + */ +.macro bilinear_deinterleave numpix, dst0, dst1, dst01 + vuzp \()\dst0\().8b, \()\dst1\().8b + /* bubbles */ + vuzp \()\dst0\().8b, \()\dst1\().8b + mov \()\dst01\().d[0], \()\dst0\().d[0] + mov \()\dst01\().d[1], \()\dst1\().d[0] +.endm + +.macro bilinear_deinterleave_dst_x_src numpix, dst0, dst1, dst01 +.endm + +.macro bilinear_deinterleave_dst_x_over numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst_x_add numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst_8_src numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst_8_over numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst_8_add numpix, dst0, dst1, dst01 + bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 +.endm + +.macro bilinear_deinterleave_dst mask_fmt, op, numpix, dst0, dst1, dst01 + bilinear_deinterleave_dst_\()\mask_fmt\()_\()\op \numpix, \dst0, \dst1, \dst01 +.endm + + +.macro bilinear_interpolate_last_pixel src_fmt, mask_fmt, dst_fmt, op + bilinear_load_\()\src_fmt v0, v1, v2 + bilinear_load_mask \mask_fmt, 1, v4 + bilinear_load_dst \dst_fmt, \op, 1, v18, v19, v9 + umull v2.8h, v0.8b, v28.8b + umlal v2.8h, v1.8b, v29.8b + /* 5 cycles bubble */ + ushll v0.4s, v2.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v2.4h, v15.h[0] + umlal2 v0.4s, v2.8h, v15.h[0] + /* 5 cycles bubble */ + bilinear_duplicate_mask \mask_fmt, 1, v4 + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + /* 3 cycles bubble */ + xtn v0.8b, v0.8h + /* 1 cycle bubble */ + bilinear_interleave_src_dst \ + \mask_fmt, \op, 1, v0, v1, v0, v18, v19, v9 + bilinear_apply_mask_to_src \ + \mask_fmt, 1, v0, v1, v0, v4, \ + v3, v8, v10, v11 + bilinear_combine \ + \op, 1, v0, v1, v0, v18, v19, v9, \ + v3, v8, v10, v11, v5 + bilinear_deinterleave_dst \mask_fmt, \op, 1, v0, v1, v0 + bilinear_store_\()\dst_fmt 1, v17, v18 +.endm + +.macro bilinear_interpolate_two_pixels src_fmt, mask_fmt, dst_fmt, op + bilinear_load_and_vertical_interpolate_two_\()\src_fmt \ + v1, v11, v18, v19, v20, v21, v22, v23 + bilinear_load_mask \mask_fmt, 2, v4 + bilinear_load_dst \dst_fmt, \op, 2, v18, v19, v9 + ushll v0.4s, v1.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v1.4h, v15.h[0] + umlal2 v0.4s, v1.8h, v15.h[0] + ushll v10.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v10.4s, v11.4h, v15.h[4] + umlal2 v10.4s, v11.8h, v15.h[4] + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v10.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + bilinear_duplicate_mask \mask_fmt, 2, v4 + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + xtn v0.8b, v0.8h + bilinear_interleave_src_dst \ + \mask_fmt, \op, 2, v0, v1, v0, v18, v19, v9 + bilinear_apply_mask_to_src \ + \mask_fmt, 2, v0, v1, v0, v4, \ + v3, v8, v10, v11 + bilinear_combine \ + \op, 2, v0, v1, v0, v18, v19, v9, \ + v3, v8, v10, v11, v5 + bilinear_deinterleave_dst \mask_fmt, \op, 2, v0, v1, v0 + bilinear_store_\()\dst_fmt 2, v16, v17 +.endm + +.macro bilinear_interpolate_four_pixels src_fmt, mask_fmt, dst_fmt, op + bilinear_load_and_vertical_interpolate_four_\()\src_fmt \ + v1, v11, v4, v5, v6, v7, v22, v23, \ + v3, v9, v16, v17, v20, v21, v18, v19 + prfm PREFETCH_MODE, [TMP1, PF_OFFS] + sub TMP1, TMP1, STRIDE + prfm PREFETCH_MODE, [TMP1, PF_OFFS] + ushll v0.4s, v1.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v1.4h, v15.h[0] + umlal2 v0.4s, v1.8h, v15.h[0] + ushll v10.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v10.4s, v11.4h, v15.h[4] + umlal2 v10.4s, v11.8h, v15.h[4] + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + ushll v2.4s, v3.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v2.4s, v3.4h, v15.h[0] + umlal2 v2.4s, v3.8h, v15.h[0] + ushll v8.4s, v9.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v8.4s, v9.4h, v15.h[4] + umlal2 v8.4s, v9.8h, v15.h[4] + add v12.8h, v12.8h, v13.8h + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v10.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn v2.4h, v2.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v2.8h, v8.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + bilinear_load_mask \mask_fmt, 4, v4 + bilinear_duplicate_mask \mask_fmt, 4, v4 + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + xtn v0.8b, v0.8h + xtn v1.8b, v2.8h + add v12.8h, v12.8h, v13.8h + bilinear_load_dst \dst_fmt, \op, 4, v2, v3, v21 + bilinear_interleave_src_dst \ + \mask_fmt, \op, 4, v0, v1, v0, v2, v3, v11 + bilinear_apply_mask_to_src \ + \mask_fmt, 4, v0, v1, v0, v4, \ + v6, v8, v9, v10 + bilinear_combine \ + \op, 4, v0, v1, v0, v2, v3, v1, \ + v6, v8, v9, v10, v23 + bilinear_deinterleave_dst \mask_fmt, \op, 4, v0, v1, v0 + bilinear_store_\()\dst_fmt 4, v6, v7 +.endm + +.set BILINEAR_FLAG_USE_MASK, 1 +.set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 + +/* + * Main template macro for generating NEON optimized bilinear scanline functions. + * + * Bilinear scanline generator macro take folling arguments: + * fname - name of the function to generate + * src_fmt - source color format (8888 or 0565) + * dst_fmt - destination color format (8888 or 0565) + * src/dst_bpp_shift - (1 << bpp_shift) is the size of src/dst pixel in bytes + * process_last_pixel - code block that interpolate one pixel and does not + * update horizontal weight + * process_two_pixels - code block that interpolate two pixels and update + * horizontal weight + * process_four_pixels - code block that interpolate four pixels and update + * horizontal weight + * process_pixblock_head - head part of middle loop + * process_pixblock_tail - tail part of middle loop + * process_pixblock_tail_head - tail_head of middle loop + * pixblock_size - number of pixels processed in a single middle loop + * prefetch_distance - prefetch in the source image by that many pixels ahead + */ + +.macro generate_bilinear_scanline_func \ + fname, \ + src_fmt, dst_fmt, src_bpp_shift, dst_bpp_shift, \ + bilinear_process_last_pixel, \ + bilinear_process_two_pixels, \ + bilinear_process_four_pixels, \ + bilinear_process_pixblock_head, \ + bilinear_process_pixblock_tail, \ + bilinear_process_pixblock_tail_head, \ + pixblock_size, \ + prefetch_distance, \ + flags + +pixman_asm_function \fname +.if \pixblock_size == 8 +.elseif \pixblock_size == 4 +.else + .error unsupported pixblock size +.endif + +.if ((\flags) & BILINEAR_FLAG_USE_MASK) == 0 + OUT .req x0 + TOP .req x1 + BOTTOM .req x2 + WT .req x3 + WWT .req w3 + WB .req x4 + WWB .req w4 + X .req w5 + UX .req w6 + WIDTH .req x7 + TMP1 .req x10 + WTMP1 .req w10 + TMP2 .req x11 + WTMP2 .req w11 + PF_OFFS .req x12 + TMP3 .req x13 + WTMP3 .req w13 + TMP4 .req x14 + WTMP4 .req w14 + STRIDE .req x15 + DUMMY .req x30 + + stp x29, x30, [sp, -16]! + mov x29, sp + sub sp, sp, 112 + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + stp x10, x11, [x29, -80] + stp x12, x13, [x29, -96] + stp x14, x15, [x29, -112] +.else + OUT .req x0 + MASK .req x1 + TOP .req x2 + BOTTOM .req x3 + WT .req x4 + WWT .req w4 + WB .req x5 + WWB .req w5 + X .req w6 + UX .req w7 + WIDTH .req x8 + TMP1 .req x10 + WTMP1 .req w10 + TMP2 .req x11 + WTMP2 .req w11 + PF_OFFS .req x12 + TMP3 .req x13 + WTMP3 .req w13 + TMP4 .req x14 + WTMP4 .req w14 + STRIDE .req x15 + DUMMY .req x30 + + .set prefetch_offset, \prefetch_distance + + stp x29, x30, [sp, -16]! + mov x29, sp + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + stp x10, x11, [x29, -80] + stp x12, x13, [x29, -96] + stp x14, x15, [x29, -112] + str x8, [x29, -120] + ldr w8, [x29, 16] + sub sp, sp, 120 +.endif + + mov WTMP1, #\prefetch_distance + umull PF_OFFS, WTMP1, UX + + sub STRIDE, BOTTOM, TOP + .unreq BOTTOM + + cmp WIDTH, #0 + ble 300f + + dup v12.8h, X + dup v13.8h, UX + dup v28.8b, WWT + dup v29.8b, WWB + mov v25.d[0], v12.d[1] + mov v26.d[0], v13.d[0] + add v25.4h, v25.4h, v26.4h + mov v12.d[1], v25.d[0] + + /* ensure good destination alignment */ + cmp WIDTH, #1 + blt 100f + tst OUT, #(1 << \dst_bpp_shift) + beq 100f + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + \bilinear_process_last_pixel + sub WIDTH, WIDTH, #1 +100: + add v13.8h, v13.8h, v13.8h + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + + cmp WIDTH, #2 + blt 100f + tst OUT, #(1 << (\dst_bpp_shift + 1)) + beq 100f + \bilinear_process_two_pixels + sub WIDTH, WIDTH, #2 +100: +.if \pixblock_size == 8 + cmp WIDTH, #4 + blt 100f + tst OUT, #(1 << (\dst_bpp_shift + 2)) + beq 100f + \bilinear_process_four_pixels + sub WIDTH, WIDTH, #4 +100: +.endif + subs WIDTH, WIDTH, #\pixblock_size + blt 100f + asr PF_OFFS, PF_OFFS, #(16 - \src_bpp_shift) + \bilinear_process_pixblock_head + subs WIDTH, WIDTH, #\pixblock_size + blt 500f +0: + \bilinear_process_pixblock_tail_head + subs WIDTH, WIDTH, #\pixblock_size + bge 0b +500: + \bilinear_process_pixblock_tail +100: +.if \pixblock_size == 8 + tst WIDTH, #4 + beq 200f + \bilinear_process_four_pixels +200: +.endif + /* handle the remaining trailing pixels */ + tst WIDTH, #2 + beq 200f + \bilinear_process_two_pixels +200: + tst WIDTH, #1 + beq 300f + \bilinear_process_last_pixel +300: + +.if ((\flags) & BILINEAR_FLAG_USE_MASK) == 0 + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x10, x11, [x29, -80] + ldp x12, x13, [x29, -96] + ldp x14, x15, [x29, -112] + mov sp, x29 + ldp x29, x30, [sp], 16 +.else + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x10, x11, [x29, -80] + ldp x12, x13, [x29, -96] + ldp x14, x15, [x29, -112] + ldr x8, [x29, -120] + mov sp, x29 + ldp x29, x30, [sp], 16 +.endif + ret + + .unreq OUT + .unreq TOP + .unreq WT + .unreq WWT + .unreq WB + .unreq WWB + .unreq X + .unreq UX + .unreq WIDTH + .unreq TMP1 + .unreq WTMP1 + .unreq TMP2 + .unreq PF_OFFS + .unreq TMP3 + .unreq TMP4 + .unreq STRIDE +.if ((\flags) & BILINEAR_FLAG_USE_MASK) != 0 + .unreq MASK +.endif + +pixman_end_asm_function + +.endm + +/* src_8888_8_8888 */ +.macro bilinear_src_8888_8_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 8888, src +.endm + +.macro bilinear_src_8888_8_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, 8, 8888, src +.endm + +.macro bilinear_src_8888_8_8888_process_four_pixels + bilinear_interpolate_four_pixels 8888, 8, 8888, src +.endm + +.macro bilinear_src_8888_8_8888_process_pixblock_head + bilinear_src_8888_8_8888_process_four_pixels +.endm + +.macro bilinear_src_8888_8_8888_process_pixblock_tail +.endm + +.macro bilinear_src_8888_8_8888_process_pixblock_tail_head + bilinear_src_8888_8_8888_process_pixblock_tail + bilinear_src_8888_8_8888_process_pixblock_head +.endm + +/* src_8888_8_0565 */ +.macro bilinear_src_8888_8_0565_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 0565, src +.endm + +.macro bilinear_src_8888_8_0565_process_two_pixels + bilinear_interpolate_two_pixels 8888, 8, 0565, src +.endm + +.macro bilinear_src_8888_8_0565_process_four_pixels + bilinear_interpolate_four_pixels 8888, 8, 0565, src +.endm + +.macro bilinear_src_8888_8_0565_process_pixblock_head + bilinear_src_8888_8_0565_process_four_pixels +.endm + +.macro bilinear_src_8888_8_0565_process_pixblock_tail +.endm + +.macro bilinear_src_8888_8_0565_process_pixblock_tail_head + bilinear_src_8888_8_0565_process_pixblock_tail + bilinear_src_8888_8_0565_process_pixblock_head +.endm + +/* src_0565_8_x888 */ +.macro bilinear_src_0565_8_x888_process_last_pixel + bilinear_interpolate_last_pixel 0565, 8, 8888, src +.endm + +.macro bilinear_src_0565_8_x888_process_two_pixels + bilinear_interpolate_two_pixels 0565, 8, 8888, src +.endm + +.macro bilinear_src_0565_8_x888_process_four_pixels + bilinear_interpolate_four_pixels 0565, 8, 8888, src +.endm + +.macro bilinear_src_0565_8_x888_process_pixblock_head + bilinear_src_0565_8_x888_process_four_pixels +.endm + +.macro bilinear_src_0565_8_x888_process_pixblock_tail +.endm + +.macro bilinear_src_0565_8_x888_process_pixblock_tail_head + bilinear_src_0565_8_x888_process_pixblock_tail + bilinear_src_0565_8_x888_process_pixblock_head +.endm + +/* src_0565_8_0565 */ +.macro bilinear_src_0565_8_0565_process_last_pixel + bilinear_interpolate_last_pixel 0565, 8, 0565, src +.endm + +.macro bilinear_src_0565_8_0565_process_two_pixels + bilinear_interpolate_two_pixels 0565, 8, 0565, src +.endm + +.macro bilinear_src_0565_8_0565_process_four_pixels + bilinear_interpolate_four_pixels 0565, 8, 0565, src +.endm + +.macro bilinear_src_0565_8_0565_process_pixblock_head + bilinear_src_0565_8_0565_process_four_pixels +.endm + +.macro bilinear_src_0565_8_0565_process_pixblock_tail +.endm + +.macro bilinear_src_0565_8_0565_process_pixblock_tail_head + bilinear_src_0565_8_0565_process_pixblock_tail + bilinear_src_0565_8_0565_process_pixblock_head +.endm + +/* over_8888_8888 */ +.macro bilinear_over_8888_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, x, 8888, over +.endm + +.macro bilinear_over_8888_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, x, 8888, over +.endm + +.macro bilinear_over_8888_8888_process_four_pixels + bilinear_interpolate_four_pixels 8888, x, 8888, over +.endm + +.macro bilinear_over_8888_8888_process_pixblock_head + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #2 + asr WTMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #2 + + ld1 {v22.2s}, [TMP1], STRIDE + ld1 {v23.2s}, [TMP1] + asr WTMP3, X, #16 + add X, X, UX + add TMP3, TOP, TMP3, lsl #2 + umull v8.8h, v22.8b, v28.8b + umlal v8.8h, v23.8b, v29.8b + + ld1 {v22.2s}, [TMP2], STRIDE + ld1 {v23.2s}, [TMP2] + asr WTMP4, X, #16 + add X, X, UX + add TMP4, TOP, TMP4, lsl #2 + umull v9.8h, v22.8b, v28.8b + umlal v9.8h, v23.8b, v29.8b + + ld1 {v22.2s}, [TMP3], STRIDE + ld1 {v23.2s}, [TMP3] + umull v10.8h, v22.8b, v28.8b + umlal v10.8h, v23.8b, v29.8b + + ushll v0.4s, v8.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v8.4h, v15.h[0] + umlal2 v0.4s, v8.8h, v15.h[0] + + prfm PREFETCH_MODE, [TMP4, PF_OFFS] + ld1 {v16.2s}, [TMP4], STRIDE + ld1 {v17.2s}, [TMP4] + prfm PREFETCH_MODE, [TMP4, PF_OFFS] + umull v11.8h, v16.8b, v28.8b + umlal v11.8h, v17.8b, v29.8b + + ushll v1.4s, v9.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v1.4s, v9.4h, v15.h[4] + umlal2 v1.4s, v9.8h, v15.h[4] + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h +.endm + +.macro bilinear_over_8888_8888_process_pixblock_tail + ushll v2.4s, v10.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v2.4s, v10.4h, v15.h[0] + umlal2 v2.4s, v10.8h, v15.h[0] + ushll v3.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v3.4s, v11.4h, v15.h[4] + umlal2 v3.4s, v11.8h, v15.h[4] + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v1.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn v2.4h, v2.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + shrn2 v2.8h, v3.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + xtn v6.8b, v0.8h + xtn v7.8b, v2.8h + ld1 {v2.2s, v3.2s}, [OUT] + prfm PREFETCH_MODE, [OUT, #(prefetch_offset * 4)] + vuzp v6.8b, v7.8b + vuzp v2.8b, v3.8b + vuzp v6.8b, v7.8b + vuzp v2.8b, v3.8b + dup v4.2s, v7.s[1] + mvn v4.8b, v4.8b + umull v11.8h, v2.8b, v4.8b + umull v2.8h, v3.8b, v4.8b + urshr v1.8h, v11.8h, #8 + urshr v10.8h, v2.8h, #8 + raddhn v3.8b, v10.8h, v2.8h + raddhn v2.8b, v1.8h, v11.8h + uqadd v6.8b, v2.8b, v6.8b + uqadd v7.8b, v3.8b, v7.8b + vuzp v6.8b, v7.8b + vuzp v6.8b, v7.8b + add v12.8h, v12.8h, v13.8h + st1 {v6.2s, v7.2s}, [OUT], #16 +.endm + +.macro bilinear_over_8888_8888_process_pixblock_tail_head + ushll v2.4s, v10.4h, #BILINEAR_INTERPOLATION_BITS + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #2 + umlsl v2.4s, v10.4h, v15.h[0] + asr WTMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #2 + umlal2 v2.4s, v10.8h, v15.h[0] + ushll v3.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + ld1 {v20.2s}, [TMP1], STRIDE + umlsl v3.4s, v11.4h, v15.h[4] + umlal2 v3.4s, v11.8h, v15.h[4] + ld1 {v21.2s}, [TMP1] + umull v8.8h, v20.8b, v28.8b + umlal v8.8h, v21.8b, v29.8b + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v1.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn v2.4h, v2.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + ld1 {v22.2s}, [TMP2], STRIDE + shrn2 v2.8h, v3.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + xtn v6.8b, v0.8h + ld1 {v23.2s}, [TMP2] + umull v9.8h, v22.8b, v28.8b + asr WTMP3, X, #16 + add X, X, UX + add TMP3, TOP, TMP3, lsl #2 + asr WTMP4, X, #16 + add X, X, UX + add TMP4, TOP, TMP4, lsl #2 + umlal v9.8h, v23.8b, v29.8b + xtn v7.8b, v2.8h + ld1 {v2.2s, v3.2s}, [OUT] + prfm PREFETCH_MODE, [OUT, PF_OFFS] + ld1 {v22.2s}, [TMP3], STRIDE + vuzp v6.8b, v7.8b + vuzp v2.8b, v3.8b + vuzp v6.8b, v7.8b + vuzp v2.8b, v3.8b + dup v4.2s, v7.s[1] + ld1 {v23.2s}, [TMP3] + mvn v4.8b, v4.8b + umull v10.8h, v22.8b, v28.8b + umlal v10.8h, v23.8b, v29.8b + umull v11.8h, v2.8b, v4.8b + umull v2.8h, v3.8b, v4.8b + ushll v0.4s, v8.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v8.4h, v15.h[0] + urshr v1.8h, v11.8h, #8 + umlal2 v0.4s, v8.8h, v15.h[0] + urshr v8.8h, v2.8h, #8 + raddhn v3.8b, v8.8h, v2.8h + raddhn v2.8b, v1.8h, v11.8h + prfm PREFETCH_MODE, [TMP4, PF_OFFS] + ld1 {v16.2s}, [TMP4], STRIDE + uqadd v6.8b, v2.8b, v6.8b + uqadd v7.8b, v3.8b, v7.8b + ld1 {v17.2s}, [TMP4] + prfm PREFETCH_MODE, [TMP4, PF_OFFS] + umull v11.8h, v16.8b, v28.8b + umlal v11.8h, v17.8b, v29.8b + vuzp v6.8b, v7.8b + ushll v1.4s, v9.4h, #BILINEAR_INTERPOLATION_BITS + vuzp v6.8b, v7.8b + umlsl v1.4s, v9.4h, v15.h[4] + add v12.8h, v12.8h, v13.8h + umlal2 v1.4s, v9.8h, v15.h[4] + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + st1 {v6.2s, v7.2s}, [OUT], #16 +.endm + +/* over_8888_8_8888 */ +.macro bilinear_over_8888_8_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 8888, over +.endm + +.macro bilinear_over_8888_8_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, 8, 8888, over +.endm + +.macro bilinear_over_8888_8_8888_process_four_pixels + bilinear_interpolate_two_pixels 8888, 8, 8888, over + bilinear_interpolate_two_pixels 8888, 8, 8888, over +.endm + +.macro bilinear_over_8888_8_8888_process_pixblock_head + bilinear_over_8888_8_8888_process_four_pixels +.endm + +.macro bilinear_over_8888_8_8888_process_pixblock_tail +.endm + +.macro bilinear_over_8888_8_8888_process_pixblock_tail_head + bilinear_over_8888_8_8888_process_pixblock_tail + bilinear_over_8888_8_8888_process_pixblock_head +.endm + +/* add_8888_8888 */ +.macro bilinear_add_8888_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, x, 8888, add +.endm + +.macro bilinear_add_8888_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, x, 8888, add +.endm + +.macro bilinear_add_8888_8888_process_four_pixels + bilinear_interpolate_two_pixels 8888, x, 8888, add + bilinear_interpolate_two_pixels 8888, x, 8888, add +.endm + +.macro bilinear_add_8888_8888_process_pixblock_head + bilinear_add_8888_8888_process_four_pixels +.endm + +.macro bilinear_add_8888_8888_process_pixblock_tail +.endm + +.macro bilinear_add_8888_8888_process_pixblock_tail_head + bilinear_add_8888_8888_process_pixblock_tail + bilinear_add_8888_8888_process_pixblock_head +.endm + +/* add_8888_8_8888 */ +.macro bilinear_add_8888_8_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 8888, add +.endm + +.macro bilinear_add_8888_8_8888_process_two_pixels + bilinear_interpolate_two_pixels 8888, 8, 8888, add +.endm + +.macro bilinear_add_8888_8_8888_process_four_pixels + bilinear_interpolate_four_pixels 8888, 8, 8888, add +.endm + +.macro bilinear_add_8888_8_8888_process_pixblock_head + bilinear_add_8888_8_8888_process_four_pixels +.endm + +.macro bilinear_add_8888_8_8888_process_pixblock_tail +.endm + +.macro bilinear_add_8888_8_8888_process_pixblock_tail_head + bilinear_add_8888_8_8888_process_pixblock_tail + bilinear_add_8888_8_8888_process_pixblock_head +.endm + + +/* Bilinear scanline functions */ +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8_8888_SRC_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_src_8888_8_8888_process_last_pixel, \ + bilinear_src_8888_8_8888_process_two_pixels, \ + bilinear_src_8888_8_8888_process_four_pixels, \ + bilinear_src_8888_8_8888_process_pixblock_head, \ + bilinear_src_8888_8_8888_process_pixblock_tail, \ + bilinear_src_8888_8_8888_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8_0565_SRC_asm_neon, \ + 8888, 0565, 2, 1, \ + bilinear_src_8888_8_0565_process_last_pixel, \ + bilinear_src_8888_8_0565_process_two_pixels, \ + bilinear_src_8888_8_0565_process_four_pixels, \ + bilinear_src_8888_8_0565_process_pixblock_head, \ + bilinear_src_8888_8_0565_process_pixblock_tail, \ + bilinear_src_8888_8_0565_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_0565_8_x888_SRC_asm_neon, \ + 0565, 8888, 1, 2, \ + bilinear_src_0565_8_x888_process_last_pixel, \ + bilinear_src_0565_8_x888_process_two_pixels, \ + bilinear_src_0565_8_x888_process_four_pixels, \ + bilinear_src_0565_8_x888_process_pixblock_head, \ + bilinear_src_0565_8_x888_process_pixblock_tail, \ + bilinear_src_0565_8_x888_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_0565_8_0565_SRC_asm_neon, \ + 0565, 0565, 1, 1, \ + bilinear_src_0565_8_0565_process_last_pixel, \ + bilinear_src_0565_8_0565_process_two_pixels, \ + bilinear_src_0565_8_0565_process_four_pixels, \ + bilinear_src_0565_8_0565_process_pixblock_head, \ + bilinear_src_0565_8_0565_process_pixblock_tail, \ + bilinear_src_0565_8_0565_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8888_OVER_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_over_8888_8888_process_last_pixel, \ + bilinear_over_8888_8888_process_two_pixels, \ + bilinear_over_8888_8888_process_four_pixels, \ + bilinear_over_8888_8888_process_pixblock_head, \ + bilinear_over_8888_8888_process_pixblock_tail, \ + bilinear_over_8888_8888_process_pixblock_tail_head, \ + 4, 28, 0 + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8_8888_OVER_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_over_8888_8_8888_process_last_pixel, \ + bilinear_over_8888_8_8888_process_two_pixels, \ + bilinear_over_8888_8_8888_process_four_pixels, \ + bilinear_over_8888_8_8888_process_pixblock_head, \ + bilinear_over_8888_8_8888_process_pixblock_tail, \ + bilinear_over_8888_8_8888_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8888_ADD_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_add_8888_8888_process_last_pixel, \ + bilinear_add_8888_8888_process_two_pixels, \ + bilinear_add_8888_8888_process_four_pixels, \ + bilinear_add_8888_8888_process_pixblock_head, \ + bilinear_add_8888_8888_process_pixblock_tail, \ + bilinear_add_8888_8888_process_pixblock_tail_head, \ + 4, 28, 0 + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8_8888_ADD_asm_neon, \ + 8888, 8888, 2, 2, \ + bilinear_add_8888_8_8888_process_last_pixel, \ + bilinear_add_8888_8_8888_process_two_pixels, \ + bilinear_add_8888_8_8888_process_four_pixels, \ + bilinear_add_8888_8_8888_process_pixblock_head, \ + bilinear_add_8888_8_8888_process_pixblock_tail, \ + bilinear_add_8888_8_8888_process_pixblock_tail_head, \ + 4, 28, BILINEAR_FLAG_USE_MASK diff --git a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S new file mode 100644 index 0000000000..107c13328a --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S @@ -0,0 +1,3704 @@ +/* + * Copyright © 2009 Nokia 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. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + */ + +/* + * This file contains implementations of NEON optimized pixel processing + * functions. There is no full and detailed tutorial, but some functions + * (those which are exposing some new or interesting features) are + * extensively commented and can be used as examples. + * + * You may want to have a look at the comments for following functions: + * - pixman_composite_over_8888_0565_asm_neon + * - pixman_composite_over_n_8_0565_asm_neon + */ + +/* Prevent the stack from becoming executable for no reason... */ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +.text +.arch armv8-a + +.altmacro +.p2align 2 + +#include "pixman-private.h" +#include "pixman-arm-asm.h" +#include "pixman-arma64-neon-asm.h" + +/* Global configuration options and preferences */ + +/* + * The code can optionally make use of unaligned memory accesses to improve + * performance of handling leading/trailing pixels for each scanline. + * Configuration variable RESPECT_STRICT_ALIGNMENT can be set to 0 for + * example in linux if unaligned memory accesses are not configured to + * generate.exceptions. + */ +.set RESPECT_STRICT_ALIGNMENT, 1 + +/* + * Set default prefetch type. There is a choice between the following options: + * + * PREFETCH_TYPE_NONE (may be useful for the ARM cores where PLD is set to work + * as NOP to workaround some HW bugs or for whatever other reason) + * + * PREFETCH_TYPE_SIMPLE (may be useful for simple single-issue ARM cores where + * advanced prefetch intruduces heavy overhead) + * + * PREFETCH_TYPE_ADVANCED (useful for superscalar cores such as ARM Cortex-A8 + * which can run ARM and NEON instructions simultaneously so that extra ARM + * instructions do not add (many) extra cycles, but improve prefetch efficiency) + * + * Note: some types of function can't support advanced prefetch and fallback + * to simple one (those which handle 24bpp pixels) + */ +.set PREFETCH_TYPE_DEFAULT, PREFETCH_TYPE_ADVANCED + +/* Prefetch distance in pixels for simple prefetch */ +.set PREFETCH_DISTANCE_SIMPLE, 64 + +/* + * Implementation of pixman_composite_over_8888_0565_asm_neon + * + * This function takes a8r8g8b8 source buffer, r5g6b5 destination buffer and + * performs OVER compositing operation. Function fast_composite_over_8888_0565 + * from pixman-fast-path.c does the same in C and can be used as a reference. + * + * First we need to have some NEON assembly code which can do the actual + * operation on the pixels and provide it to the template macro. + * + * Template macro quite conveniently takes care of emitting all the necessary + * code for memory reading and writing (including quite tricky cases of + * handling unaligned leading/trailing pixels), so we only need to deal with + * the data in NEON registers. + * + * NEON registers allocation in general is recommented to be the following: + * v0, v1, v2, v3 - contain loaded source pixel data + * v4, v5, v6, v7 - contain loaded destination pixels (if they are needed) + * v24, v25, v26, v27 - contain loading mask pixel data (if mask is used) + * v28, v29, v30, v31 - place for storing the result (destination pixels) + * + * As can be seen above, four 64-bit NEON registers are used for keeping + * intermediate pixel data and up to 8 pixels can be processed in one step + * for 32bpp formats (16 pixels for 16bpp, 32 pixels for 8bpp). + * + * This particular function uses the following registers allocation: + * v0, v1, v2, v3 - contain loaded source pixel data + * v4, v5 - contain loaded destination pixels (they are needed) + * v28, v29 - place for storing the result (destination pixels) + */ + +/* + * Step one. We need to have some code to do some arithmetics on pixel data. + * This is implemented as a pair of macros: '*_head' and '*_tail'. When used + * back-to-back, they take pixel data from {v0, v1, v2, v3} and {v4, v5}, + * perform all the needed calculations and write the result to {v28, v29}. + * The rationale for having two macros and not just one will be explained + * later. In practice, any single monolitic function which does the work can + * be split into two parts in any arbitrary way without affecting correctness. + * + * There is one special trick here too. Common template macro can optionally + * make our life a bit easier by doing R, G, B, A color components + * deinterleaving for 32bpp pixel formats (and this feature is used in + * 'pixman_composite_over_8888_0565_asm_neon' function). So it means that + * instead of having 8 packed pixels in {v0, v1, v2, v3} registers, we + * actually use v0 register for blue channel (a vector of eight 8-bit + * values), v1 register for green, v2 for red and v3 for alpha. This + * simple conversion can be also done with a few NEON instructions: + * + * Packed to planar conversion: // vuzp8 is a wrapper macro + * vuzp8 v0, v1 + * vuzp8 v2, v3 + * vuzp8 v1, v3 + * vuzp8 v0, v2 + * + * Planar to packed conversion: // vzip8 is a wrapper macro + * vzip8 v0, v2 + * vzip8 v1, v3 + * vzip8 v2, v3 + * vzip8 v0, v1 + * + * But pixel can be loaded directly in planar format using LD4 / b NEON + * instruction. It is 1 cycle slower than LD1 / s, so this is not always + * desirable, that's why deinterleaving is optional. + * + * But anyway, here is the code: + */ + +.macro pixman_composite_over_8888_0565_process_pixblock_head + /* convert 8 r5g6b5 pixel data from {v4} to planar 8-bit format + and put data into v6 - red, v7 - green, v30 - blue */ + mov v4.d[1], v5.d[0] + shrn v6.8b, v4.8h, #8 + shrn v7.8b, v4.8h, #3 + sli v4.8h, v4.8h, #5 + sri v6.8b, v6.8b, #5 + mvn v3.8b, v3.8b /* invert source alpha */ + sri v7.8b, v7.8b, #6 + shrn v30.8b, v4.8h, #2 + /* now do alpha blending, storing results in 8-bit planar format + into v20 - red, v23 - green, v22 - blue */ + umull v10.8h, v3.8b, v6.8b + umull v11.8h, v3.8b, v7.8b + umull v12.8h, v3.8b, v30.8b + urshr v17.8h, v10.8h, #8 + urshr v18.8h, v11.8h, #8 + urshr v19.8h, v12.8h, #8 + raddhn v20.8b, v10.8h, v17.8h + raddhn v23.8b, v11.8h, v18.8h + raddhn v22.8b, v12.8h, v19.8h +.endm + +.macro pixman_composite_over_8888_0565_process_pixblock_tail + /* ... continue alpha blending */ + uqadd v17.8b, v2.8b, v20.8b + uqadd v18.8b, v0.8b, v22.8b + uqadd v19.8b, v1.8b, v23.8b + /* convert the result to r5g6b5 and store it into {v14} */ + ushll v14.8h, v17.8b, #7 + sli v14.8h, v14.8h, #1 + ushll v8.8h, v19.8b, #7 + sli v8.8h, v8.8h, #1 + ushll v9.8h, v18.8b, #7 + sli v9.8h, v9.8h, #1 + sri v14.8h, v8.8h, #5 + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +/* + * OK, now we got almost everything that we need. Using the above two + * macros, the work can be done right. But now we want to optimize + * it a bit. ARM Cortex-A8 is an in-order core, and benefits really + * a lot from good code scheduling and software pipelining. + * + * Let's construct some code, which will run in the core main loop. + * Some pseudo-code of the main loop will look like this: + * head + * while (...) { + * tail + * head + * } + * tail + * + * It may look a bit weird, but this setup allows to hide instruction + * latencies better and also utilize dual-issue capability more + * efficiently (make pairs of load-store and ALU instructions). + * + * So what we need now is a '*_tail_head' macro, which will be used + * in the core main loop. A trivial straightforward implementation + * of this macro would look like this: + * + * pixman_composite_over_8888_0565_process_pixblock_tail + * st1 {v28.4h, v29.4h}, [DST_W], #32 + * ld1 {v4.4h, v5.4h}, [DST_R], #16 + * ld4 {v0.2s, v1.2s, v2.2s, v3.2s}, [SRC], #32 + * pixman_composite_over_8888_0565_process_pixblock_head + * cache_preload 8, 8 + * + * Now it also got some VLD/VST instructions. We simply can't move from + * processing one block of pixels to the other one with just arithmetics. + * The previously processed data needs to be written to memory and new + * data needs to be fetched. Fortunately, this main loop does not deal + * with partial leading/trailing pixels and can load/store a full block + * of pixels in a bulk. Additionally, destination buffer is already + * 16 bytes aligned here (which is good for performance). + * + * New things here are DST_R, DST_W, SRC and MASK identifiers. These + * are the aliases for ARM registers which are used as pointers for + * accessing data. We maintain separate pointers for reading and writing + * destination buffer (DST_R and DST_W). + * + * Another new thing is 'cache_preload' macro. It is used for prefetching + * data into CPU L2 cache and improve performance when dealing with large + * images which are far larger than cache size. It uses one argument + * (actually two, but they need to be the same here) - number of pixels + * in a block. Looking into 'pixman-arm-neon-asm.h' can provide some + * details about this macro. Moreover, if good performance is needed + * the code from this macro needs to be copied into '*_tail_head' macro + * and mixed with the rest of code for optimal instructions scheduling. + * We are actually doing it below. + * + * Now after all the explanations, here is the optimized code. + * Different instruction streams (originaling from '*_head', '*_tail' + * and 'cache_preload' macro) use different indentation levels for + * better readability. Actually taking the code from one of these + * indentation levels and ignoring a few LD/ST instructions would + * result in exactly the code from '*_head', '*_tail' or 'cache_preload' + * macro! + */ + +#if 1 + +.macro pixman_composite_over_8888_0565_process_pixblock_tail_head + uqadd v17.8b, v2.8b, v20.8b + ld1 {v4.4h, v5.4h}, [DST_R], #16 + mov v4.d[1], v5.d[0] + uqadd v18.8b, v0.8b, v22.8b + uqadd v19.8b, v1.8b, v23.8b + shrn v6.8b, v4.8h, #8 + fetch_src_pixblock + shrn v7.8b, v4.8h, #3 + sli v4.8h, v4.8h, #5 + ushll v14.8h, v17.8b, #7 + sli v14.8h, v14.8h, #1 + PF add, PF_X, PF_X, #8 + ushll v8.8h, v19.8b, #7 + sli v8.8h, v8.8h, #1 + PF tst, PF_CTL, #0xF + sri v6.8b, v6.8b, #5 + PF beq, 10f + PF add, PF_X, PF_X, #8 +10: + mvn v3.8b, v3.8b + PF beq, 10f + PF sub, PF_CTL, PF_CTL, #1 +10: + sri v7.8b, v7.8b, #6 + shrn v30.8b, v4.8h, #2 + umull v10.8h, v3.8b, v6.8b + PF lsl, DUMMY, PF_X, #src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + umull v11.8h, v3.8b, v7.8b + umull v12.8h, v3.8b, v30.8b + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + sri v14.8h, v8.8h, #5 + PF cmp, PF_X, ORIG_W + ushll v9.8h, v18.8b, #7 + sli v9.8h, v9.8h, #1 + urshr v17.8h, v10.8h, #8 + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W +10: + urshr v19.8h, v11.8h, #8 + urshr v18.8h, v12.8h, #8 + PF ble, 10f + PF subs, PF_CTL, PF_CTL, #0x10 +10: + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] + PF ble, 10f + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 +10: + raddhn v20.8b, v10.8h, v17.8h + raddhn v23.8b, v11.8h, v19.8h + PF ble, 10f + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_SRC, #1 +10: + raddhn v22.8b, v12.8h, v18.8h + st1 {v14.8h}, [DST_W], #16 +.endm + +#else + +/* If we did not care much about the performance, we would just use this... */ +.macro pixman_composite_over_8888_0565_process_pixblock_tail_head + pixman_composite_over_8888_0565_process_pixblock_tail + st1 {v14.8h}, [DST_W], #16 + ld1 {v4.4h, v4.5h}, [DST_R], #16 + fetch_src_pixblock + pixman_composite_over_8888_0565_process_pixblock_head + cache_preload 8, 8 +.endm + +#endif + +/* + * And now the final part. We are using 'generate_composite_function' macro + * to put all the stuff together. We are specifying the name of the function + * which we want to get, number of bits per pixel for the source, mask and + * destination (0 if unused, like mask in this case). Next come some bit + * flags: + * FLAG_DST_READWRITE - tells that the destination buffer is both read + * and written, for write-only buffer we would use + * FLAG_DST_WRITEONLY flag instead + * FLAG_DEINTERLEAVE_32BPP - tells that we prefer to work with planar data + * and separate color channels for 32bpp format. + * The next things are: + * - the number of pixels processed per iteration (8 in this case, because + * that's the maximum what can fit into four 64-bit NEON registers). + * - prefetch distance, measured in pixel blocks. In this case it is 5 times + * by 8 pixels. That would be 40 pixels, or up to 160 bytes. Optimal + * prefetch distance can be selected by running some benchmarks. + * + * After that we specify some macros, these are 'default_init', + * 'default_cleanup' here which are empty (but it is possible to have custom + * init/cleanup macros to be able to save/restore some extra NEON registers + * like d8-d15 or do anything else) followed by + * 'pixman_composite_over_8888_0565_process_pixblock_head', + * 'pixman_composite_over_8888_0565_process_pixblock_tail' and + * 'pixman_composite_over_8888_0565_process_pixblock_tail_head' + * which we got implemented above. + * + * The last part is the NEON registers allocation scheme. + */ +generate_composite_function \ + pixman_composite_over_8888_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_0565_process_pixblock_head, \ + pixman_composite_over_8888_0565_process_pixblock_tail, \ + pixman_composite_over_8888_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_n_0565_process_pixblock_head + /* convert 8 r5g6b5 pixel data from {v4} to planar 8-bit format + and put data into v6 - red, v7 - green, v30 - blue */ + mov v4.d[1], v5.d[0] + shrn v6.8b, v4.8h, #8 + shrn v7.8b, v4.8h, #3 + sli v4.8h, v4.8h, #5 + sri v6.8b, v6.8b, #5 + sri v7.8b, v7.8b, #6 + shrn v30.8b, v4.8h, #2 + /* now do alpha blending, storing results in 8-bit planar format + into v20 - red, v23 - green, v22 - blue */ + umull v10.8h, v3.8b, v6.8b + umull v11.8h, v3.8b, v7.8b + umull v12.8h, v3.8b, v30.8b + urshr v13.8h, v10.8h, #8 + urshr v14.8h, v11.8h, #8 + urshr v15.8h, v12.8h, #8 + raddhn v20.8b, v10.8h, v13.8h + raddhn v23.8b, v11.8h, v14.8h + raddhn v22.8b, v12.8h, v15.8h +.endm + +.macro pixman_composite_over_n_0565_process_pixblock_tail + /* ... continue alpha blending */ + uqadd v17.8b, v2.8b, v20.8b + uqadd v18.8b, v0.8b, v22.8b + uqadd v19.8b, v1.8b, v23.8b + /* convert the result to r5g6b5 and store it into {v14} */ + ushll v14.8h, v17.8b, #7 + sli v14.8h, v14.8h, #1 + ushll v8.8h, v19.8b, #7 + sli v8.8h, v8.8h, #1 + ushll v9.8h, v18.8b, #7 + sli v9.8h, v9.8h, #1 + sri v14.8h, v8.8h, #5 + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_n_0565_process_pixblock_tail_head + pixman_composite_over_n_0565_process_pixblock_tail + ld1 {v4.4h, v5.4h}, [DST_R], #16 + st1 {v14.8h}, [DST_W], #16 + pixman_composite_over_n_0565_process_pixblock_head + cache_preload 8, 8 +.endm + +.macro pixman_composite_over_n_0565_init + mov v3.s[0], w4 + dup v0.8b, v3.b[0] + dup v1.8b, v3.b[1] + dup v2.8b, v3.b[2] + dup v3.8b, v3.b[3] + mvn v3.8b, v3.8b /* invert source alpha */ +.endm + +generate_composite_function \ + pixman_composite_over_n_0565_asm_neon, 0, 0, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_0565_init, \ + default_cleanup, \ + pixman_composite_over_n_0565_process_pixblock_head, \ + pixman_composite_over_n_0565_process_pixblock_tail, \ + pixman_composite_over_n_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_8888_0565_process_pixblock_head + ushll v8.8h, v1.8b, #7 + sli v8.8h, v8.8h, #1 + ushll v14.8h, v2.8b, #7 + sli v14.8h, v14.8h, #1 + ushll v9.8h, v0.8b, #7 + sli v9.8h, v9.8h, #1 +.endm + +.macro pixman_composite_src_8888_0565_process_pixblock_tail + sri v14.8h, v8.8h, #5 + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +.macro pixman_composite_src_8888_0565_process_pixblock_tail_head + sri v14.8h, v8.8h, #5 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + fetch_src_pixblock + PF beq, 10f + PF add, PF_X, PF_X, #8 + PF sub, PF_CTL, PF_CTL, #1 +10: + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] + PF cmp, PF_X, ORIG_W + PF lsl, DUMMY, PF_X, #src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + ushll v8.8h, v1.8b, #7 + sli v8.8h, v8.8h, #1 + st1 {v14.8h}, [DST_W], #16 + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W + PF subs, PF_CTL, PF_CTL, #0x10 +10: + ushll v14.8h, v2.8b, #7 + sli v14.8h, v14.8h, #1 + PF ble, 10f + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 +10: + ushll v9.8h, v0.8b, #7 + sli v9.8h, v9.8h, #1 +.endm + +generate_composite_function \ + pixman_composite_src_8888_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_8888_0565_process_pixblock_head, \ + pixman_composite_src_8888_0565_process_pixblock_tail, \ + pixman_composite_src_8888_0565_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_src_0565_8888_process_pixblock_head + mov v0.d[1], v1.d[0] + shrn v30.8b, v0.8h, #8 + shrn v29.8b, v0.8h, #3 + sli v0.8h, v0.8h, #5 + movi v31.8b, #255 + sri v30.8b, v30.8b, #5 + sri v29.8b, v29.8b, #6 + shrn v28.8b, v0.8h, #2 +.endm + +.macro pixman_composite_src_0565_8888_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_src_0565_8888_process_pixblock_tail_head + pixman_composite_src_0565_8888_process_pixblock_tail + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + fetch_src_pixblock + pixman_composite_src_0565_8888_process_pixblock_head + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_0565_8888_asm_neon, 16, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0565_8888_process_pixblock_head, \ + pixman_composite_src_0565_8888_process_pixblock_tail, \ + pixman_composite_src_0565_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8_8_process_pixblock_head + uqadd v28.8b, v0.8b, v4.8b + uqadd v29.8b, v1.8b, v5.8b + uqadd v30.8b, v2.8b, v6.8b + uqadd v31.8b, v3.8b, v7.8b +.endm + +.macro pixman_composite_add_8_8_process_pixblock_tail +.endm + +.macro pixman_composite_add_8_8_process_pixblock_tail_head + fetch_src_pixblock + PF add, PF_X, PF_X, #32 + PF tst, PF_CTL, #0xF + ld1 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + PF beq, 10f + PF add, PF_X, PF_X, #32 + PF sub, PF_CTL, PF_CTL, #1 +10: + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + PF cmp, PF_X, ORIG_W + PF lsl, DUMMY, PF_X, #src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W + PF subs, PF_CTL, PF_CTL, #0x10 +10: + uqadd v28.8b, v0.8b, v4.8b + PF ble, 10f + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_DST, #1 +10: + uqadd v29.8b, v1.8b, v5.8b + uqadd v30.8b, v2.8b, v6.8b + uqadd v31.8b, v3.8b, v7.8b +.endm + +generate_composite_function \ + pixman_composite_add_8_8_asm_neon, 8, 0, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_process_pixblock_tail, \ + pixman_composite_add_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8888_8888_process_pixblock_tail_head + fetch_src_pixblock + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + ld1 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + PF beq, 10f + PF add, PF_X, PF_X, #8 + PF sub, PF_CTL, PF_CTL, #1 +10: + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + PF cmp, PF_X, ORIG_W + PF lsl, DUMMY, PF_X, #src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W + PF subs, PF_CTL, PF_CTL, #0x10 +10: + uqadd v28.8b, v0.8b, v4.8b + PF ble, 10f + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_DST, #1 +10: + uqadd v29.8b, v1.8b, v5.8b + uqadd v30.8b, v2.8b, v6.8b + uqadd v31.8b, v3.8b, v7.8b +.endm + +generate_composite_function \ + pixman_composite_add_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_process_pixblock_tail, \ + pixman_composite_add_8888_8888_process_pixblock_tail_head + +generate_composite_function_single_scanline \ + pixman_composite_scanline_add_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_process_pixblock_tail, \ + pixman_composite_add_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_out_reverse_8888_8888_process_pixblock_head + mvn v24.8b, v3.8b /* get inverted alpha */ + /* do alpha blending */ + umull v8.8h, v24.8b, v4.8b + umull v9.8h, v24.8b, v5.8b + umull v10.8h, v24.8b, v6.8b + umull v11.8h, v24.8b, v7.8b +.endm + +.macro pixman_composite_out_reverse_8888_8888_process_pixblock_tail + urshr v14.8h, v8.8h, #8 + urshr v15.8h, v9.8h, #8 + urshr v16.8h, v10.8h, #8 + urshr v17.8h, v11.8h, #8 + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h +.endm + +.macro pixman_composite_out_reverse_8888_8888_process_pixblock_tail_head + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + urshr v14.8h, v8.8h, #8 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + urshr v15.8h, v9.8h, #8 + urshr v16.8h, v10.8h, #8 + urshr v17.8h, v11.8h, #8 + PF beq, 10f + PF add, PF_X, PF_X, #8 + PF sub, PF_CTL, PF_CTL, #1 +10: + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h + PF cmp, PF_X, ORIG_W + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h + fetch_src_pixblock + PF lsl, DUMMY, PF_X, #src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + mvn v22.8b, v3.8b + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W +10: + umull v8.8h, v22.8b, v4.8b + PF ble, 10f + PF subs, PF_CTL, PF_CTL, #0x10 +10: + umull v9.8h, v22.8b, v5.8b + PF ble, 10f + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 +10: + umull v10.8h, v22.8b, v6.8b + PF ble, 10f + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_DST, #1 +10: + umull v11.8h, v22.8b, v7.8b +.endm + +generate_composite_function_single_scanline \ + pixman_composite_scanline_out_reverse_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_out_reverse_8888_8888_process_pixblock_head, \ + pixman_composite_out_reverse_8888_8888_process_pixblock_tail, \ + pixman_composite_out_reverse_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_8888_8888_process_pixblock_head + pixman_composite_out_reverse_8888_8888_process_pixblock_head +.endm + +.macro pixman_composite_over_8888_8888_process_pixblock_tail + pixman_composite_out_reverse_8888_8888_process_pixblock_tail + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b +.endm + +.macro pixman_composite_over_8888_8888_process_pixblock_tail_head + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + urshr v14.8h, v8.8h, #8 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + urshr v15.8h, v9.8h, #8 + urshr v16.8h, v10.8h, #8 + urshr v17.8h, v11.8h, #8 + PF beq, 10f + PF add, PF_X, PF_X, #8 + PF sub, PF_CTL, PF_CTL, #1 +10: + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h + PF cmp, PF_X, ORIG_W + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + fetch_src_pixblock + PF lsl, DUMMY, PF_X, #src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + mvn v22.8b, v3.8b + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W +10: + umull v8.8h, v22.8b, v4.8b + PF ble, 10f + PF subs, PF_CTL, PF_CTL, #0x10 +10: + umull v9.8h, v22.8b, v5.8b + PF ble, 10f + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 +10: + umull v10.8h, v22.8b, v6.8b + PF ble, 10f + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_DST, #1 +10: + umull v11.8h, v22.8b, v7.8b +.endm + +generate_composite_function \ + pixman_composite_over_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_process_pixblock_tail_head + +generate_composite_function_single_scanline \ + pixman_composite_scanline_over_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8888_process_pixblock_head + /* deinterleaved source pixels in {v0, v1, v2, v3} */ + /* inverted alpha in {v24} */ + /* destination pixels in {v4, v5, v6, v7} */ + umull v8.8h, v24.8b, v4.8b + umull v9.8h, v24.8b, v5.8b + umull v10.8h, v24.8b, v6.8b + umull v11.8h, v24.8b, v7.8b +.endm + +.macro pixman_composite_over_n_8888_process_pixblock_tail + urshr v14.8h, v8.8h, #8 + urshr v15.8h, v9.8h, #8 + urshr v16.8h, v10.8h, #8 + urshr v17.8h, v11.8h, #8 + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b +.endm + +.macro pixman_composite_over_n_8888_process_pixblock_tail_head + urshr v14.8h, v8.8h, #8 + urshr v15.8h, v9.8h, #8 + urshr v16.8h, v10.8h, #8 + urshr v17.8h, v11.8h, #8 + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + uqadd v28.8b, v0.8b, v28.8b + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0x0F + PF beq, 10f + PF add, PF_X, PF_X, #8 + PF sub, PF_CTL, PF_CTL, #1 +10: + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + PF cmp, PF_X, ORIG_W + umull v8.8h, v24.8b, v4.8b + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + umull v9.8h, v24.8b, v5.8b + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W +10: + umull v10.8h, v24.8b, v6.8b + PF subs, PF_CTL, PF_CTL, #0x10 + umull v11.8h, v24.8b, v7.8b + PF ble, 10f + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_DST, #1 +10: + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +.endm + +.macro pixman_composite_over_n_8888_init + mov v3.s[0], w4 + dup v0.8b, v3.b[0] + dup v1.8b, v3.b[1] + dup v2.8b, v3.b[2] + dup v3.8b, v3.b[3] + mvn v24.8b, v3.8b /* get inverted alpha */ +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8888_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_n_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_reverse_n_8888_process_pixblock_tail_head + urshr v14.8h, v8.8h, #8 + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + urshr v15.8h, v9.8h, #8 + urshr v12.8h, v10.8h, #8 + urshr v13.8h, v11.8h, #8 + PF beq, 10f + PF add, PF_X, PF_X, #8 + PF sub, PF_CTL, PF_CTL, #1 +10: + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h + PF cmp, PF_X, ORIG_W + raddhn v30.8b, v12.8h, v10.8h + raddhn v31.8b, v13.8h, v11.8h + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + ld4 {v0.8b, v1.8b, v2.8b, v3.8b}, [DST_R], #32 + mvn v22.8b, v3.8b + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + PF blt, 10f + PF sub, PF_X, PF_X, ORIG_W +10: + umull v8.8h, v22.8b, v4.8b + PF blt, 10f + PF subs, PF_CTL, PF_CTL, #0x10 +10: + umull v9.8h, v22.8b, v5.8b + umull v10.8h, v22.8b, v6.8b + PF blt, 10f + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_DST, #1 +10: + umull v11.8h, v22.8b, v7.8b +.endm + +.macro pixman_composite_over_reverse_n_8888_init + mov v7.s[0], w4 + dup v4.8b, v7.b[0] + dup v5.8b, v7.b[1] + dup v6.8b, v7.b[2] + dup v7.8b, v7.b[3] +.endm + +generate_composite_function \ + pixman_composite_over_reverse_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_reverse_n_8888_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_reverse_n_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 4, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_8888_8_0565_process_pixblock_head + umull v0.8h, v24.8b, v8.8b /* IN for SRC pixels (part1) */ + umull v1.8h, v24.8b, v9.8b + umull v2.8h, v24.8b, v10.8b + umull v3.8h, v24.8b, v11.8b + mov v4.d[1], v5.d[0] + shrn v25.8b, v4.8h, #8 /* convert DST_R data to 32-bpp (part1) */ + shrn v26.8b, v4.8h, #3 + sli v4.8h, v4.8h, #5 + urshr v17.8h, v0.8h, #8 /* IN for SRC pixels (part2) */ + urshr v18.8h, v1.8h, #8 + urshr v19.8h, v2.8h, #8 + urshr v20.8h, v3.8h, #8 + raddhn v0.8b, v0.8h, v17.8h + raddhn v1.8b, v1.8h, v18.8h + raddhn v2.8b, v2.8h, v19.8h + raddhn v3.8b, v3.8h, v20.8h + sri v25.8b, v25.8b, #5 /* convert DST_R data to 32-bpp (part2) */ + sri v26.8b, v26.8b, #6 + mvn v3.8b, v3.8b + shrn v30.8b, v4.8h, #2 + umull v18.8h, v3.8b, v25.8b /* now do alpha blending */ + umull v19.8h, v3.8b, v26.8b + umull v20.8h, v3.8b, v30.8b +.endm + +.macro pixman_composite_over_8888_8_0565_process_pixblock_tail + /* 3 cycle bubble (after vmull.u8) */ + urshr v5.8h, v18.8h, #8 + urshr v6.8h, v19.8h, #8 + urshr v7.8h, v20.8h, #8 + raddhn v17.8b, v18.8h, v5.8h + raddhn v19.8b, v19.8h, v6.8h + raddhn v18.8b, v20.8h, v7.8h + uqadd v5.8b, v2.8b, v17.8b + /* 1 cycle bubble */ + uqadd v6.8b, v0.8b, v18.8b + uqadd v7.8b, v1.8b, v19.8b + ushll v14.8h, v5.8b, #7 /* convert to 16bpp */ + sli v14.8h, v14.8h, #1 + ushll v18.8h, v7.8b, #7 + sli v18.8h, v18.8h, #1 + ushll v19.8h, v6.8b, #7 + sli v19.8h, v19.8h, #1 + sri v14.8h, v18.8h, #5 + /* 1 cycle bubble */ + sri v14.8h, v19.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +.macro pixman_composite_over_8888_8_0565_process_pixblock_tail_head +#if 0 + ld1 {v4.8h}, [DST_R], #16 + shrn v25.8b, v4.8h, #8 + fetch_mask_pixblock + shrn v26.8b, v4.8h, #3 + fetch_src_pixblock + umull v22.8h, v24.8b, v10.8b + urshr v13.8h, v18.8h, #8 + urshr v11.8h, v19.8h, #8 + urshr v15.8h, v20.8h, #8 + raddhn v17.8b, v18.8h, v13.8h + raddhn v19.8b, v19.8h, v11.8h + raddhn v18.8b, v20.8h, v15.8h + uqadd v17.8b, v2.8b, v17.8b + umull v21.8h, v24.8b, v9.8b + uqadd v18.8b, v0.8b, v18.8b + uqadd v19.8b, v1.8b, v19.8b + ushll v14.8h, v17.8b, #7 + sli v14.8h, v14.8h, #1 + umull v20.8h, v24.8b, v8.8b + ushll v18.8h, v18.8b, #7 + sli v18.8h, v18.8h, #1 + ushll v19.8h, v19.8b, #7 + sli v19.8h, v19.8h, #1 + sri v14.8h, v18.8h, #5 + umull v23.8h, v24.8b, v11.8b + sri v14.8h, v19.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] + + cache_preload 8, 8 + + sli v4.8h, v4.8h, #5 + urshr v16.8h, v20.8h, #8 + urshr v17.8h, v21.8h, #8 + urshr v18.8h, v22.8h, #8 + urshr v19.8h, v23.8h, #8 + raddhn v0.8b, v20.8h, v16.8h + raddhn v1.8b, v21.8h, v17.8h + raddhn v2.8b, v22.8h, v18.8h + raddhn v3.8b, v23.8h, v19.8h + sri v25.8b, v25.8b, #5 + sri v26.8b, v26.8b, #6 + mvn v3.8b, v3.8b + shrn v30.8b, v4.8h, #2 + st1 {v14.8h}, [DST_W], #16 + umull v18.8h, v3.8b, v25.8b + umull v19.8h, v3.8b, v26.8b + umull v20.8h, v3.8b, v30.8b +#else + pixman_composite_over_8888_8_0565_process_pixblock_tail + st1 {v28.4h, v29.4h}, [DST_W], #16 + ld1 {v4.4h, v5.4h}, [DST_R], #16 + fetch_mask_pixblock + fetch_src_pixblock + pixman_composite_over_8888_8_0565_process_pixblock_head +#endif +.endm + +generate_composite_function \ + pixman_composite_over_8888_8_0565_asm_neon, 32, 8, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_8_0565_process_pixblock_head, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +/* + * This function needs a special initialization of solid mask. + * Solid source pixel data is fetched from stack at ARGS_STACK_OFFSET + * offset, split into color components and replicated in d8-d11 + * registers. Additionally, this function needs all the NEON registers, + * so it has to save d8-d15 registers which are callee saved according + * to ABI. These registers are restored from 'cleanup' macro. All the + * other NEON registers are caller saved, so can be clobbered freely + * without introducing any problems. + */ +.macro pixman_composite_over_n_8_0565_init + mov v11.s[0], w4 + dup v8.8b, v11.b[0] + dup v9.8b, v11.b[1] + dup v10.8b, v11.b[2] + dup v11.8b, v11.b[3] +.endm + +.macro pixman_composite_over_n_8_0565_cleanup +.endm + +generate_composite_function \ + pixman_composite_over_n_8_0565_asm_neon, 0, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8_0565_init, \ + pixman_composite_over_n_8_0565_cleanup, \ + pixman_composite_over_8888_8_0565_process_pixblock_head, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_8888_n_0565_init + mov v24.s[0], w6 + dup v24.8b, v24.b[3] +.endm + +.macro pixman_composite_over_8888_n_0565_cleanup +.endm + +generate_composite_function \ + pixman_composite_over_8888_n_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_8888_n_0565_init, \ + pixman_composite_over_8888_n_0565_cleanup, \ + pixman_composite_over_8888_8_0565_process_pixblock_head, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0565_0565_process_pixblock_head +.endm + +.macro pixman_composite_src_0565_0565_process_pixblock_tail +.endm + +.macro pixman_composite_src_0565_0565_process_pixblock_tail_head + st1 {v0.4h, v1.4h, v2.4h, v3.4h}, [DST_W], #32 + fetch_src_pixblock + cache_preload 16, 16 +.endm + +generate_composite_function \ + pixman_composite_src_0565_0565_asm_neon, 16, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 16, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0565_0565_process_pixblock_head, \ + pixman_composite_src_0565_0565_process_pixblock_tail, \ + pixman_composite_src_0565_0565_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8_process_pixblock_head +.endm + +.macro pixman_composite_src_n_8_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_8_process_pixblock_tail_head + st1 {v0.8b, v1.8b, v2.8b, v3.8b}, [DST_W], 32 +.endm + +.macro pixman_composite_src_n_8_init + mov v0.s[0], w4 + dup v3.8b, v0.b[0] + dup v2.8b, v0.b[0] + dup v1.8b, v0.b[0] + dup v0.8b, v0.b[0] +.endm + +.macro pixman_composite_src_n_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8_asm_neon, 0, 0, 8, \ + FLAG_DST_WRITEONLY, \ + 32, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_8_init, \ + pixman_composite_src_n_8_cleanup, \ + pixman_composite_src_n_8_process_pixblock_head, \ + pixman_composite_src_n_8_process_pixblock_tail, \ + pixman_composite_src_n_8_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_0565_process_pixblock_head +.endm + +.macro pixman_composite_src_n_0565_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_0565_process_pixblock_tail_head + st1 {v0.4h, v1.4h, v2.4h, v3.4h}, [DST_W], #32 +.endm + +.macro pixman_composite_src_n_0565_init + mov v0.s[0], w4 + dup v3.4h, v0.h[0] + dup v2.4h, v0.h[0] + dup v1.4h, v0.h[0] + dup v0.4h, v0.h[0] +.endm + +.macro pixman_composite_src_n_0565_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_0565_asm_neon, 0, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 16, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_0565_init, \ + pixman_composite_src_n_0565_cleanup, \ + pixman_composite_src_n_0565_process_pixblock_head, \ + pixman_composite_src_n_0565_process_pixblock_tail, \ + pixman_composite_src_n_0565_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8888_process_pixblock_head +.endm + +.macro pixman_composite_src_n_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_n_8888_process_pixblock_tail_head + st1 {v0.2s, v1.2s, v2.2s, v3.2s}, [DST_W], #32 +.endm + +.macro pixman_composite_src_n_8888_init + mov v0.s[0], w4 + dup v3.2s, v0.s[0] + dup v2.2s, v0.s[0] + dup v1.2s, v0.s[0] + dup v0.2s, v0.s[0] +.endm + +.macro pixman_composite_src_n_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8888_asm_neon, 0, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 0, /* prefetch distance */ \ + pixman_composite_src_n_8888_init, \ + pixman_composite_src_n_8888_cleanup, \ + pixman_composite_src_n_8888_process_pixblock_head, \ + pixman_composite_src_n_8888_process_pixblock_tail, \ + pixman_composite_src_n_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_8888_8888_process_pixblock_head +.endm + +.macro pixman_composite_src_8888_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_8888_8888_process_pixblock_tail_head + st1 {v0.2s, v1.2s, v2.2s, v3.2s}, [DST_W], #32 + fetch_src_pixblock + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_8888_8888_process_pixblock_head, \ + pixman_composite_src_8888_8888_process_pixblock_tail, \ + pixman_composite_src_8888_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_x888_8888_process_pixblock_head + orr v0.8b, v0.8b, v4.8b + orr v1.8b, v1.8b, v4.8b + orr v2.8b, v2.8b, v4.8b + orr v3.8b, v3.8b, v4.8b +.endm + +.macro pixman_composite_src_x888_8888_process_pixblock_tail +.endm + +.macro pixman_composite_src_x888_8888_process_pixblock_tail_head + st1 {v0.2s, v1.2s, v2.2s, v3.2s}, [DST_W], #32 + fetch_src_pixblock + orr v0.8b, v0.8b, v4.8b + orr v1.8b, v1.8b, v4.8b + orr v2.8b, v2.8b, v4.8b + orr v3.8b, v3.8b, v4.8b + cache_preload 8, 8 +.endm + +.macro pixman_composite_src_x888_8888_init + movi v4.2s, #0xff, lsl 24 +.endm + +generate_composite_function \ + pixman_composite_src_x888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + pixman_composite_src_x888_8888_init, \ + default_cleanup, \ + pixman_composite_src_x888_8888_process_pixblock_head, \ + pixman_composite_src_x888_8888_process_pixblock_tail, \ + pixman_composite_src_x888_8888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8_8888_process_pixblock_head + /* expecting solid source in {v0, v1, v2, v3} */ + /* mask is in v24 (v25, v26, v27 are unused) */ + + /* in */ + umull v8.8h, v24.8b, v0.8b + umull v9.8h, v24.8b, v1.8b + umull v10.8h, v24.8b, v2.8b + umull v11.8h, v24.8b, v3.8b + ursra v8.8h, v8.8h, #8 + ursra v9.8h, v9.8h, #8 + ursra v10.8h, v10.8h, #8 + ursra v11.8h, v11.8h, #8 +.endm + +.macro pixman_composite_src_n_8_8888_process_pixblock_tail + rshrn v28.8b, v8.8h, #8 + rshrn v29.8b, v9.8h, #8 + rshrn v30.8b, v10.8h, #8 + rshrn v31.8b, v11.8h, #8 +.endm + +.macro pixman_composite_src_n_8_8888_process_pixblock_tail_head + fetch_mask_pixblock + PF add, PF_X, PF_X, #8 + rshrn v28.8b, v8.8h, #8 + PF tst, PF_CTL, #0x0F + rshrn v29.8b, v9.8h, #8 + PF beq, 10f + PF add, PF_X, PF_X, #8 +10: + rshrn v30.8b, v10.8h, #8 + PF beq, 10f + PF sub, PF_CTL, PF_CTL, #1 +10: + rshrn v31.8b, v11.8h, #8 + PF cmp, PF_X, ORIG_W + umull v8.8h, v24.8b, v0.8b + PF lsl, DUMMY, PF_X, #mask_bpp_shift + PF prfm, PREFETCH_MODE, [PF_MASK, DUMMY] + umull v9.8h, v24.8b, v1.8b + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W +10: + umull v10.8h, v24.8b, v2.8b + PF ble, 10f + PF subs, PF_CTL, PF_CTL, #0x10 +10: + umull v11.8h, v24.8b, v3.8b + PF ble, 10f + PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift + PF ldrsb, DUMMY, [PF_MASK, DUMMY] + PF add, PF_MASK, PF_MASK, #1 +10: + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + ursra v8.8h, v8.8h, #8 + ursra v9.8h, v9.8h, #8 + ursra v10.8h, v10.8h, #8 + ursra v11.8h, v11.8h, #8 +.endm + +.macro pixman_composite_src_n_8_8888_init + mov v3.s[0], w4 + dup v0.8b, v3.b[0] + dup v1.8b, v3.b[1] + dup v2.8b, v3.b[2] + dup v3.8b, v3.b[3] +.endm + +.macro pixman_composite_src_n_8_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8_8888_asm_neon, 0, 8, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_src_n_8_8888_init, \ + pixman_composite_src_n_8_8888_cleanup, \ + pixman_composite_src_n_8_8888_process_pixblock_head, \ + pixman_composite_src_n_8_8888_process_pixblock_tail, \ + pixman_composite_src_n_8_8888_process_pixblock_tail_head, \ + +/******************************************************************************/ + +.macro pixman_composite_src_n_8_8_process_pixblock_head + umull v0.8h, v24.8b, v16.8b + umull v1.8h, v25.8b, v16.8b + umull v2.8h, v26.8b, v16.8b + umull v3.8h, v27.8b, v16.8b + ursra v0.8h, v0.8h, #8 + ursra v1.8h, v1.8h, #8 + ursra v2.8h, v2.8h, #8 + ursra v3.8h, v3.8h, #8 +.endm + +.macro pixman_composite_src_n_8_8_process_pixblock_tail + rshrn v28.8b, v0.8h, #8 + rshrn v29.8b, v1.8h, #8 + rshrn v30.8b, v2.8h, #8 + rshrn v31.8b, v3.8h, #8 +.endm + +.macro pixman_composite_src_n_8_8_process_pixblock_tail_head + fetch_mask_pixblock + PF add, PF_X, PF_X, #8 + rshrn v28.8b, v0.8h, #8 + PF tst, PF_CTL, #0x0F + rshrn v29.8b, v1.8h, #8 + PF beq, 10f + PF add, PF_X, PF_X, #8 +10: + rshrn v30.8b, v2.8h, #8 + PF beq, 10f + PF sub, PF_CTL, PF_CTL, #1 +10: + rshrn v31.8b, v3.8h, #8 + PF cmp, PF_X, ORIG_W + umull v0.8h, v24.8b, v16.8b + PF lsl, DUMMY, PF_X, mask_bpp_shift + PF prfm, PREFETCH_MODE, [PF_MASK, DUMMY] + umull v1.8h, v25.8b, v16.8b + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W +10: + umull v2.8h, v26.8b, v16.8b + PF ble, 10f + PF subs, PF_CTL, PF_CTL, #0x10 +10: + umull v3.8h, v27.8b, v16.8b + PF ble, 10f + PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift + PF ldrsb, DUMMY, [PF_MASK, DUMMY] + PF add, PF_MASK, PF_MASK, #1 +10: + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + ursra v0.8h, v0.8h, #8 + ursra v1.8h, v1.8h, #8 + ursra v2.8h, v2.8h, #8 + ursra v3.8h, v3.8h, #8 +.endm + +.macro pixman_composite_src_n_8_8_init + mov v16.s[0], w4 + dup v16.8b, v16.b[3] +.endm + +.macro pixman_composite_src_n_8_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_src_n_8_8_asm_neon, 0, 8, 8, \ + FLAG_DST_WRITEONLY, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_src_n_8_8_init, \ + pixman_composite_src_n_8_8_cleanup, \ + pixman_composite_src_n_8_8_process_pixblock_head, \ + pixman_composite_src_n_8_8_process_pixblock_tail, \ + pixman_composite_src_n_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8_8888_process_pixblock_head + /* expecting deinterleaved source data in {v8, v9, v10, v11} */ + /* v8 - blue, v9 - green, v10 - red, v11 - alpha */ + /* and destination data in {v4, v5, v6, v7} */ + /* mask is in v24 (v25, v26, v27 are unused) */ + + /* in */ + umull v12.8h, v24.8b, v8.8b + umull v13.8h, v24.8b, v9.8b + umull v14.8h, v24.8b, v10.8b + umull v15.8h, v24.8b, v11.8b + urshr v16.8h, v12.8h, #8 + urshr v17.8h, v13.8h, #8 + urshr v18.8h, v14.8h, #8 + urshr v19.8h, v15.8h, #8 + raddhn v0.8b, v12.8h, v16.8h + raddhn v1.8b, v13.8h, v17.8h + raddhn v2.8b, v14.8h, v18.8h + raddhn v3.8b, v15.8h, v19.8h + mvn v25.8b, v3.8b /* get inverted alpha */ + /* source: v0 - blue, v1 - green, v2 - red, v3 - alpha */ + /* destination: v4 - blue, v5 - green, v6 - red, v7 - alpha */ + /* now do alpha blending */ + umull v12.8h, v25.8b, v4.8b + umull v13.8h, v25.8b, v5.8b + umull v14.8h, v25.8b, v6.8b + umull v15.8h, v25.8b, v7.8b +.endm + +.macro pixman_composite_over_n_8_8888_process_pixblock_tail + urshr v16.8h, v12.8h, #8 + urshr v17.8h, v13.8h, #8 + urshr v18.8h, v14.8h, #8 + urshr v19.8h, v15.8h, #8 + raddhn v28.8b, v16.8h, v12.8h + raddhn v29.8b, v17.8h, v13.8h + raddhn v30.8b, v18.8h, v14.8h + raddhn v31.8b, v19.8h, v15.8h + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b +.endm + +.macro pixman_composite_over_n_8_8888_process_pixblock_tail_head + urshr v16.8h, v12.8h, #8 + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + urshr v17.8h, v13.8h, #8 + fetch_mask_pixblock + urshr v18.8h, v14.8h, #8 + PF add, PF_X, PF_X, #8 + urshr v19.8h, v15.8h, #8 + PF tst, PF_CTL, #0x0F + raddhn v28.8b, v16.8h, v12.8h + PF beq, 10f + PF add, PF_X, PF_X, #8 +10: + raddhn v29.8b, v17.8h, v13.8h + PF beq, 10f + PF sub, PF_CTL, PF_CTL, #1 +10: + raddhn v30.8b, v18.8h, v14.8h + PF cmp, PF_X, ORIG_W + raddhn v31.8b, v19.8h, v15.8h + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + umull v16.8h, v24.8b, v8.8b + PF lsl, DUMMY, PF_X, #mask_bpp_shift + PF prfm, PREFETCH_MODE, [PF_MASK, DUMMY] + umull v17.8h, v24.8b, v9.8b + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W +10: + umull v18.8h, v24.8b, v10.8b + PF ble, 10f + PF subs, PF_CTL, PF_CTL, #0x10 +10: + umull v19.8h, v24.8b, v11.8b + PF ble, 10f + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_DST, #1 +10: + uqadd v28.8b, v0.8b, v28.8b + PF ble, 10f + PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift + PF ldrsb, DUMMY, [PF_MASK, DUMMY] + PF add, PF_MASK, PF_MASK, #1 +10: + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + urshr v12.8h, v16.8h, #8 + urshr v13.8h, v17.8h, #8 + urshr v14.8h, v18.8h, #8 + urshr v15.8h, v19.8h, #8 + raddhn v0.8b, v16.8h, v12.8h + raddhn v1.8b, v17.8h, v13.8h + raddhn v2.8b, v18.8h, v14.8h + raddhn v3.8b, v19.8h, v15.8h + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + mvn v25.8b, v3.8b + umull v12.8h, v25.8b, v4.8b + umull v13.8h, v25.8b, v5.8b + umull v14.8h, v25.8b, v6.8b + umull v15.8h, v25.8b, v7.8b +.endm + +.macro pixman_composite_over_n_8_8888_init + mov v11.s[0], w4 + dup v8.8b, v11.b[0] + dup v9.8b, v11.b[1] + dup v10.8b, v11.b[2] + dup v11.8b, v11.b[3] +.endm + +.macro pixman_composite_over_n_8_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_over_n_8_8888_asm_neon, 0, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8_8888_init, \ + pixman_composite_over_n_8_8888_cleanup, \ + pixman_composite_over_n_8_8888_process_pixblock_head, \ + pixman_composite_over_n_8_8888_process_pixblock_tail, \ + pixman_composite_over_n_8_8888_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8_8_process_pixblock_head + umull v0.8h, v24.8b, v8.8b + umull v1.8h, v25.8b, v8.8b + umull v2.8h, v26.8b, v8.8b + umull v3.8h, v27.8b, v8.8b + urshr v10.8h, v0.8h, #8 + urshr v11.8h, v1.8h, #8 + urshr v12.8h, v2.8h, #8 + urshr v13.8h, v3.8h, #8 + raddhn v0.8b, v0.8h, v10.8h + raddhn v1.8b, v1.8h, v11.8h + raddhn v2.8b, v2.8h, v12.8h + raddhn v3.8b, v3.8h, v13.8h + mvn v24.8b, v0.8b + mvn v25.8b, v1.8b + mvn v26.8b, v2.8b + mvn v27.8b, v3.8b + umull v10.8h, v24.8b, v4.8b + umull v11.8h, v25.8b, v5.8b + umull v12.8h, v26.8b, v6.8b + umull v13.8h, v27.8b, v7.8b +.endm + +.macro pixman_composite_over_n_8_8_process_pixblock_tail + urshr v14.8h, v10.8h, #8 + urshr v15.8h, v11.8h, #8 + urshr v16.8h, v12.8h, #8 + urshr v17.8h, v13.8h, #8 + raddhn v28.8b, v14.8h, v10.8h + raddhn v29.8b, v15.8h, v11.8h + raddhn v30.8b, v16.8h, v12.8h + raddhn v31.8b, v17.8h, v13.8h + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_n_8_8_process_pixblock_tail_head + ld1 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + pixman_composite_over_n_8_8_process_pixblock_tail + fetch_mask_pixblock + cache_preload 32, 32 + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + pixman_composite_over_n_8_8_process_pixblock_head +.endm + +.macro pixman_composite_over_n_8_8_init + mov v8.s[0], w4 + dup v8.8b, v8.b[3] +.endm + +.macro pixman_composite_over_n_8_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_over_n_8_8_asm_neon, 0, 8, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8_8_init, \ + pixman_composite_over_n_8_8_cleanup, \ + pixman_composite_over_n_8_8_process_pixblock_head, \ + pixman_composite_over_n_8_8_process_pixblock_tail, \ + pixman_composite_over_n_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_head + /* + * 'combine_mask_ca' replacement + * + * input: solid src (n) in {v8, v9, v10, v11} + * dest in {v4, v5, v6, v7 } + * mask in {v24, v25, v26, v27} + * output: updated src in {v0, v1, v2, v3 } + * updated mask in {v24, v25, v26, v3 } + */ + umull v0.8h, v24.8b, v8.8b + umull v1.8h, v25.8b, v9.8b + umull v2.8h, v26.8b, v10.8b + umull v3.8h, v27.8b, v11.8b + umull v12.8h, v11.8b, v25.8b + umull v13.8h, v11.8b, v24.8b + umull v14.8h, v11.8b, v26.8b + urshr v15.8h, v0.8h, #8 + urshr v16.8h, v1.8h, #8 + urshr v17.8h, v2.8h, #8 + raddhn v0.8b, v0.8h, v15.8h + raddhn v1.8b, v1.8h, v16.8h + raddhn v2.8b, v2.8h, v17.8h + urshr v15.8h, v13.8h, #8 + urshr v16.8h, v12.8h, #8 + urshr v17.8h, v14.8h, #8 + urshr v18.8h, v3.8h, #8 + raddhn v24.8b, v13.8h, v15.8h + raddhn v25.8b, v12.8h, v16.8h + raddhn v26.8b, v14.8h, v17.8h + raddhn v3.8b, v3.8h, v18.8h + /* + * 'combine_over_ca' replacement + * + * output: updated dest in {v28, v29, v30, v31} + */ + mvn v24.8b, v24.8b + mvn v25.8b, v25.8b + mvn v26.8b, v26.8b + mvn v27.8b, v3.8b + umull v12.8h, v24.8b, v4.8b + umull v13.8h, v25.8b, v5.8b + umull v14.8h, v26.8b, v6.8b + umull v15.8h, v27.8b, v7.8b +.endm + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_tail + /* ... continue 'combine_over_ca' replacement */ + urshr v16.8h, v12.8h, #8 + urshr v17.8h, v13.8h, #8 + urshr v18.8h, v14.8h, #8 + urshr v19.8h, v15.8h, #8 + raddhn v28.8b, v16.8h, v12.8h + raddhn v29.8b, v17.8h, v13.8h + raddhn v30.8b, v18.8h, v14.8h + raddhn v31.8b, v19.8h, v15.8h + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b +.endm + +.macro pixman_composite_over_n_8888_8888_ca_process_pixblock_tail_head + urshr v16.8h, v12.8h, #8 + urshr v17.8h, v13.8h, #8 + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + urshr v18.8h, v14.8h, #8 + urshr v19.8h, v15.8h, #8 + raddhn v28.8b, v16.8h, v12.8h + raddhn v29.8b, v17.8h, v13.8h + raddhn v30.8b, v18.8h, v14.8h + raddhn v31.8b, v19.8h, v15.8h + fetch_mask_pixblock + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + cache_preload 8, 8 + pixman_composite_over_n_8888_8888_ca_process_pixblock_head + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +.endm + +.macro pixman_composite_over_n_8888_8888_ca_init + mov v13.s[0], w4 + dup v8.8b, v13.b[0] + dup v9.8b, v13.b[1] + dup v10.8b, v13.b[2] + dup v11.8b, v13.b[3] +.endm + +.macro pixman_composite_over_n_8888_8888_ca_cleanup +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_8888_ca_asm_neon, 0, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8888_8888_ca_init, \ + pixman_composite_over_n_8888_8888_ca_cleanup, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_head, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_tail, \ + pixman_composite_over_n_8888_8888_ca_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_over_n_8888_0565_ca_process_pixblock_head + /* + * 'combine_mask_ca' replacement + * + * input: solid src (n) in {v8, v9, v10, v11} [B, G, R, A] + * mask in {v24, v25, v26} [B, G, R] + * output: updated src in {v0, v1, v2 } [B, G, R] + * updated mask in {v24, v25, v26} [B, G, R] + */ + umull v0.8h, v24.8b, v8.8b + umull v1.8h, v25.8b, v9.8b + umull v2.8h, v26.8b, v10.8b + umull v12.8h, v11.8b, v24.8b + umull v13.8h, v11.8b, v25.8b + umull v14.8h, v11.8b, v26.8b + urshr v15.8h, v0.8h, #8 + urshr v16.8h, v1.8h, #8 + urshr v17.8h, v2.8h, #8 + raddhn v0.8b, v0.8h, v15.8h + raddhn v1.8b, v1.8h, v16.8h + raddhn v2.8b, v2.8h, v17.8h + urshr v19.8h, v12.8h, #8 + urshr v20.8h, v13.8h, #8 + urshr v21.8h, v14.8h, #8 + raddhn v24.8b, v12.8h, v19.8h + raddhn v25.8b, v13.8h, v20.8h + /* + * convert 8 r5g6b5 pixel data from {v4} to planar 8-bit format + * and put data into v16 - blue, v17 - green, v18 - red + */ + mov v4.d[1], v5.d[0] + shrn v17.8b, v4.8h, #3 + shrn v18.8b, v4.8h, #8 + raddhn v26.8b, v14.8h, v21.8h + sli v4.8h, v4.8h, #5 + sri v18.8b, v18.8b, #5 + sri v17.8b, v17.8b, #6 + /* + * 'combine_over_ca' replacement + * + * output: updated dest in v16 - blue, v17 - green, v18 - red + */ + mvn v24.8b, v24.8b + mvn v25.8b, v25.8b + shrn v16.8b, v4.8h, #2 + mvn v26.8b, v26.8b + umull v5.8h, v16.8b, v24.8b + umull v6.8h, v17.8b, v25.8b + umull v7.8h, v18.8b, v26.8b +.endm + +.macro pixman_composite_over_n_8888_0565_ca_process_pixblock_tail + /* ... continue 'combine_over_ca' replacement */ + urshr v13.8h, v5.8h, #8 + urshr v14.8h, v6.8h, #8 + urshr v15.8h, v7.8h, #8 + raddhn v16.8b, v13.8h, v5.8h + raddhn v17.8b, v14.8h, v6.8h + raddhn v18.8b, v15.8h, v7.8h + uqadd v16.8b, v0.8b, v16.8b + uqadd v17.8b, v1.8b, v17.8b + uqadd v18.8b, v2.8b, v18.8b + /* + * convert the results in v16, v17, v18 to r5g6b5 and store + * them into {v14} + */ + ushll v14.8h, v18.8b, #7 + sli v14.8h, v14.8h, #1 + ushll v12.8h, v17.8b, #7 + sli v12.8h, v12.8h, #1 + ushll v13.8h, v16.8b, #7 + sli v13.8h, v13.8h, #1 + sri v14.8h, v12.8h, #5 + sri v14.8h, v13.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +.macro pixman_composite_over_n_8888_0565_ca_process_pixblock_tail_head + fetch_mask_pixblock + urshr v13.8h, v5.8h, #8 + urshr v14.8h, v6.8h, #8 + ld1 {v4.8h}, [DST_R], #16 + urshr v15.8h, v7.8h, #8 + raddhn v16.8b, v13.8h, v5.8h + raddhn v17.8b, v14.8h, v6.8h + raddhn v18.8b, v15.8h, v7.8h + mov v5.d[0], v4.d[1] + /* process_pixblock_head */ + /* + * 'combine_mask_ca' replacement + * + * input: solid src (n) in {v8, v9, v10, v11} [B, G, R, A] + * mask in {v24, v25, v26} [B, G, R] + * output: updated src in {v0, v1, v2 } [B, G, R] + * updated mask in {v24, v25, v26} [B, G, R] + */ + uqadd v16.8b, v0.8b, v16.8b + uqadd v17.8b, v1.8b, v17.8b + uqadd v18.8b, v2.8b, v18.8b + umull v0.8h, v24.8b, v8.8b + umull v1.8h, v25.8b, v9.8b + umull v2.8h, v26.8b, v10.8b + /* + * convert the result in v16, v17, v18 to r5g6b5 and store + * it into {v14} + */ + ushll v14.8h, v18.8b, #7 + sli v14.8h, v14.8h, #1 + ushll v18.8h, v16.8b, #7 + sli v18.8h, v18.8h, #1 + ushll v19.8h, v17.8b, #7 + sli v19.8h, v19.8h, #1 + umull v12.8h, v11.8b, v24.8b + sri v14.8h, v19.8h, #5 + umull v13.8h, v11.8b, v25.8b + umull v15.8h, v11.8b, v26.8b + sri v14.8h, v18.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] + cache_preload 8, 8 + urshr v16.8h, v0.8h, #8 + urshr v17.8h, v1.8h, #8 + urshr v18.8h, v2.8h, #8 + raddhn v0.8b, v0.8h, v16.8h + raddhn v1.8b, v1.8h, v17.8h + raddhn v2.8b, v2.8h, v18.8h + urshr v19.8h, v12.8h, #8 + urshr v20.8h, v13.8h, #8 + urshr v21.8h, v15.8h, #8 + raddhn v24.8b, v12.8h, v19.8h + raddhn v25.8b, v13.8h, v20.8h + /* + * convert 8 r5g6b5 pixel data from {v4, v5} to planar + * 8-bit format and put data into v16 - blue, v17 - green, + * v18 - red + */ + mov v4.d[1], v5.d[0] + shrn v17.8b, v4.8h, #3 + shrn v18.8b, v4.8h, #8 + raddhn v26.8b, v15.8h, v21.8h + sli v4.8h, v4.8h, #5 + sri v17.8b, v17.8b, #6 + sri v18.8b, v18.8b, #5 + /* + * 'combine_over_ca' replacement + * + * output: updated dest in v16 - blue, v17 - green, v18 - red + */ + mvn v24.8b, v24.8b + mvn v25.8b, v25.8b + shrn v16.8b, v4.8h, #2 + mvn v26.8b, v26.8b + umull v5.8h, v16.8b, v24.8b + umull v6.8h, v17.8b, v25.8b + umull v7.8h, v18.8b, v26.8b + st1 {v14.8h}, [DST_W], #16 +.endm + +.macro pixman_composite_over_n_8888_0565_ca_init + mov v13.s[0], w4 + dup v8.8b, v13.b[0] + dup v9.8b, v13.b[1] + dup v10.8b, v13.b[2] + dup v11.8b, v13.b[3] +.endm + +.macro pixman_composite_over_n_8888_0565_ca_cleanup +.endm + +generate_composite_function \ + pixman_composite_over_n_8888_0565_ca_asm_neon, 0, 32, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_n_8888_0565_ca_init, \ + pixman_composite_over_n_8888_0565_ca_cleanup, \ + pixman_composite_over_n_8888_0565_ca_process_pixblock_head, \ + pixman_composite_over_n_8888_0565_ca_process_pixblock_tail, \ + pixman_composite_over_n_8888_0565_ca_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_in_n_8_process_pixblock_head + /* expecting source data in {v0, v1, v2, v3} */ + /* and destination data in {v4, v5, v6, v7} */ + umull v8.8h, v4.8b, v3.8b + umull v9.8h, v5.8b, v3.8b + umull v10.8h, v6.8b, v3.8b + umull v11.8h, v7.8b, v3.8b +.endm + +.macro pixman_composite_in_n_8_process_pixblock_tail + urshr v14.8h, v8.8h, #8 + urshr v15.8h, v9.8h, #8 + urshr v12.8h, v10.8h, #8 + urshr v13.8h, v11.8h, #8 + raddhn v28.8b, v8.8h, v14.8h + raddhn v29.8b, v9.8h, v15.8h + raddhn v30.8b, v10.8h, v12.8h + raddhn v31.8b, v11.8h, v13.8h +.endm + +.macro pixman_composite_in_n_8_process_pixblock_tail_head + pixman_composite_in_n_8_process_pixblock_tail + ld1 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + cache_preload 32, 32 + pixman_composite_in_n_8_process_pixblock_head + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +.endm + +.macro pixman_composite_in_n_8_init + mov v3.s[0], w4 + dup v3.8b, v3.b[3] +.endm + +.macro pixman_composite_in_n_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_in_n_8_asm_neon, 0, 0, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_in_n_8_init, \ + pixman_composite_in_n_8_cleanup, \ + pixman_composite_in_n_8_process_pixblock_head, \ + pixman_composite_in_n_8_process_pixblock_tail, \ + pixman_composite_in_n_8_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +.macro pixman_composite_add_n_8_8_process_pixblock_head + /* expecting source data in {v8, v9, v10, v11} */ + /* v8 - blue, v9 - green, v10 - red, v11 - alpha */ + /* and destination data in {v4, v5, v6, v7} */ + /* mask is in v24, v25, v26, v27 */ + umull v0.8h, v24.8b, v11.8b + umull v1.8h, v25.8b, v11.8b + umull v2.8h, v26.8b, v11.8b + umull v3.8h, v27.8b, v11.8b + urshr v12.8h, v0.8h, #8 + urshr v13.8h, v1.8h, #8 + urshr v14.8h, v2.8h, #8 + urshr v15.8h, v3.8h, #8 + raddhn v0.8b, v0.8h, v12.8h + raddhn v1.8b, v1.8h, v13.8h + raddhn v2.8b, v2.8h, v14.8h + raddhn v3.8b, v3.8h, v15.8h + uqadd v28.8b, v0.8b, v4.8b + uqadd v29.8b, v1.8b, v5.8b + uqadd v30.8b, v2.8b, v6.8b + uqadd v31.8b, v3.8b, v7.8b +.endm + +.macro pixman_composite_add_n_8_8_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_n_8_8_process_pixblock_tail_head + pixman_composite_add_n_8_8_process_pixblock_tail + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + ld1 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + fetch_mask_pixblock + cache_preload 32, 32 + pixman_composite_add_n_8_8_process_pixblock_head +.endm + +.macro pixman_composite_add_n_8_8_init + mov v11.s[0], w4 + dup v11.8b, v11.b[3] +.endm + +.macro pixman_composite_add_n_8_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_add_n_8_8_asm_neon, 0, 8, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_n_8_8_init, \ + pixman_composite_add_n_8_8_cleanup, \ + pixman_composite_add_n_8_8_process_pixblock_head, \ + pixman_composite_add_n_8_8_process_pixblock_tail, \ + pixman_composite_add_n_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8_8_8_process_pixblock_head + /* expecting source data in {v0, v1, v2, v3} */ + /* destination data in {v4, v5, v6, v7} */ + /* mask in {v24, v25, v26, v27} */ + umull v8.8h, v24.8b, v0.8b + umull v9.8h, v25.8b, v1.8b + umull v10.8h, v26.8b, v2.8b + umull v11.8h, v27.8b, v3.8b + urshr v0.8h, v8.8h, #8 + urshr v1.8h, v9.8h, #8 + urshr v12.8h, v10.8h, #8 + urshr v13.8h, v11.8h, #8 + raddhn v0.8b, v0.8h, v8.8h + raddhn v1.8b, v1.8h, v9.8h + raddhn v2.8b, v12.8h, v10.8h + raddhn v3.8b, v13.8h, v11.8h + uqadd v28.8b, v0.8b, v4.8b + uqadd v29.8b, v1.8b, v5.8b + uqadd v30.8b, v2.8b, v6.8b + uqadd v31.8b, v3.8b, v7.8b +.endm + +.macro pixman_composite_add_8_8_8_process_pixblock_tail +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_8_8_8_process_pixblock_tail_head + pixman_composite_add_8_8_8_process_pixblock_tail + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + ld1 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + fetch_mask_pixblock + fetch_src_pixblock + cache_preload 32, 32 + pixman_composite_add_8_8_8_process_pixblock_head +.endm + +.macro pixman_composite_add_8_8_8_init +.endm + +.macro pixman_composite_add_8_8_8_cleanup +.endm + +generate_composite_function \ + pixman_composite_add_8_8_8_asm_neon, 8, 8, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_8_8_8_init, \ + pixman_composite_add_8_8_8_cleanup, \ + pixman_composite_add_8_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_8_process_pixblock_tail, \ + pixman_composite_add_8_8_8_process_pixblock_tail_head + +/******************************************************************************/ + +.macro pixman_composite_add_8888_8888_8888_process_pixblock_head + /* expecting source data in {v0, v1, v2, v3} */ + /* destination data in {v4, v5, v6, v7} */ + /* mask in {v24, v25, v26, v27} */ + umull v8.8h, v27.8b, v0.8b + umull v9.8h, v27.8b, v1.8b + umull v10.8h, v27.8b, v2.8b + umull v11.8h, v27.8b, v3.8b + /* 1 cycle bubble */ + ursra v8.8h, v8.8h, #8 + ursra v9.8h, v9.8h, #8 + ursra v10.8h, v10.8h, #8 + ursra v11.8h, v11.8h, #8 +.endm + +.macro pixman_composite_add_8888_8888_8888_process_pixblock_tail + /* 2 cycle bubble */ + rshrn v28.8b, v8.8h, #8 + rshrn v29.8b, v9.8h, #8 + rshrn v30.8b, v10.8h, #8 + rshrn v31.8b, v11.8h, #8 + uqadd v28.8b, v4.8b, v28.8b + uqadd v29.8b, v5.8b, v29.8b + uqadd v30.8b, v6.8b, v30.8b + uqadd v31.8b, v7.8b, v31.8b +.endm + +.macro pixman_composite_add_8888_8888_8888_process_pixblock_tail_head + fetch_src_pixblock + rshrn v28.8b, v8.8h, #8 + fetch_mask_pixblock + rshrn v29.8b, v9.8h, #8 + umull v8.8h, v27.8b, v0.8b + rshrn v30.8b, v10.8h, #8 + umull v9.8h, v27.8b, v1.8b + rshrn v31.8b, v11.8h, #8 + umull v10.8h, v27.8b, v2.8b + umull v11.8h, v27.8b, v3.8b + uqadd v28.8b, v4.8b, v28.8b + uqadd v29.8b, v5.8b, v29.8b + uqadd v30.8b, v6.8b, v30.8b + uqadd v31.8b, v7.8b, v31.8b + ursra v8.8h, v8.8h, #8 + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + ursra v9.8h, v9.8h, #8 + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + ursra v10.8h, v10.8h, #8 + + cache_preload 8, 8 + + ursra v11.8h, v11.8h, #8 +.endm + +generate_composite_function \ + pixman_composite_add_8888_8888_8888_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +generate_composite_function_single_scanline \ + pixman_composite_scanline_add_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +/******************************************************************************/ + +generate_composite_function \ + pixman_composite_add_8888_8_8888_asm_neon, 32, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 27 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_add_n_8_8888_init + mov v3.s[0], w4 + dup v0.8b, v3.b[0] + dup v1.8b, v3.b[1] + dup v2.8b, v3.b[2] + dup v3.8b, v3.b[3] +.endm + +.macro pixman_composite_add_n_8_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_add_n_8_8888_asm_neon, 0, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_n_8_8888_init, \ + pixman_composite_add_n_8_8888_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 27 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_add_8888_n_8888_init + mov v27.s[0], w6 + dup v27.8b, v27.b[3] +.endm + +.macro pixman_composite_add_8888_n_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_add_8888_n_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_add_8888_n_8888_init, \ + pixman_composite_add_8888_n_8888_cleanup, \ + pixman_composite_add_8888_8888_8888_process_pixblock_head, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail, \ + pixman_composite_add_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 27 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_out_reverse_8888_n_8888_process_pixblock_head + /* expecting source data in {v0, v1, v2, v3} */ + /* destination data in {v4, v5, v6, v7} */ + /* solid mask is in v15 */ + + /* 'in' */ + umull v11.8h, v15.8b, v3.8b + umull v10.8h, v15.8b, v2.8b + umull v9.8h, v15.8b, v1.8b + umull v8.8h, v15.8b, v0.8b + urshr v16.8h, v11.8h, #8 + urshr v14.8h, v10.8h, #8 + urshr v13.8h, v9.8h, #8 + urshr v12.8h, v8.8h, #8 + raddhn v3.8b, v11.8h, v16.8h + raddhn v2.8b, v10.8h, v14.8h + raddhn v1.8b, v9.8h, v13.8h + raddhn v0.8b, v8.8h, v12.8h + mvn v24.8b, v3.8b /* get inverted alpha */ + /* now do alpha blending */ + umull v8.8h, v24.8b, v4.8b + umull v9.8h, v24.8b, v5.8b + umull v10.8h, v24.8b, v6.8b + umull v11.8h, v24.8b, v7.8b +.endm + +.macro pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail + urshr v16.8h, v8.8h, #8 + urshr v17.8h, v9.8h, #8 + urshr v18.8h, v10.8h, #8 + urshr v19.8h, v11.8h, #8 + raddhn v28.8b, v16.8h, v8.8h + raddhn v29.8b, v17.8h, v9.8h + raddhn v30.8b, v18.8h, v10.8h + raddhn v31.8b, v19.8h, v11.8h +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_out_reverse_8888_8888_8888_process_pixblock_tail_head + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail + fetch_src_pixblock + cache_preload 8, 8 + fetch_mask_pixblock + pixman_composite_out_reverse_8888_n_8888_process_pixblock_head + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +.endm + +generate_composite_function_single_scanline \ + pixman_composite_scanline_out_reverse_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_out_reverse_8888_n_8888_process_pixblock_head, \ + pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail, \ + pixman_composite_out_reverse_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_8888_n_8888_process_pixblock_head + pixman_composite_out_reverse_8888_n_8888_process_pixblock_head +.endm + +.macro pixman_composite_over_8888_n_8888_process_pixblock_tail + pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_n_8888_process_pixblock_tail_head + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + pixman_composite_over_8888_n_8888_process_pixblock_tail + fetch_src_pixblock + cache_preload 8, 8 + pixman_composite_over_8888_n_8888_process_pixblock_head + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +.endm + +.macro pixman_composite_over_8888_n_8888_init + mov v15.s[0], w6 + dup v15.8b, v15.b[3] +.endm + +.macro pixman_composite_over_8888_n_8888_cleanup +.endm + +generate_composite_function \ + pixman_composite_over_8888_n_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_8888_n_8888_init, \ + pixman_composite_over_8888_n_8888_cleanup, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +/******************************************************************************/ + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_8888_8888_process_pixblock_tail_head + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + pixman_composite_over_8888_n_8888_process_pixblock_tail + fetch_src_pixblock + cache_preload 8, 8 + fetch_mask_pixblock + pixman_composite_over_8888_n_8888_process_pixblock_head + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +.endm + +generate_composite_function \ + pixman_composite_over_8888_8888_8888_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +generate_composite_function_single_scanline \ + pixman_composite_scanline_over_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + +/******************************************************************************/ + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_8888_8_8888_process_pixblock_tail_head + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + pixman_composite_over_8888_n_8888_process_pixblock_tail + fetch_src_pixblock + cache_preload 8, 8 + fetch_mask_pixblock + pixman_composite_over_8888_n_8888_process_pixblock_head + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +.endm + +generate_composite_function \ + pixman_composite_over_8888_8_8888_asm_neon, 32, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_0888_process_pixblock_head +.endm + +.macro pixman_composite_src_0888_0888_process_pixblock_tail +.endm + +.macro pixman_composite_src_0888_0888_process_pixblock_tail_head + st3 {v0.8b, v1.8b, v2.8b}, [DST_W], #24 + fetch_src_pixblock + cache_preload 8, 8 +.endm + +generate_composite_function \ + pixman_composite_src_0888_0888_asm_neon, 24, 0, 24, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0888_0888_process_pixblock_head, \ + pixman_composite_src_0888_0888_process_pixblock_tail, \ + pixman_composite_src_0888_0888_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_head + mov v31.8b, v2.8b + mov v2.8b, v0.8b + mov v0.8b, v31.8b +.endm + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_tail +.endm + +.macro pixman_composite_src_0888_8888_rev_process_pixblock_tail_head + st4 {v0.8b, v1.8b, v2.8b, v3.8b}, [DST_W], #32 + fetch_src_pixblock + mov v31.8b, v2.8b + mov v2.8b, v0.8b + mov v0.8b, v31.8b + cache_preload 8, 8 +.endm + +.macro pixman_composite_src_0888_8888_rev_init + eor v3.8b, v3.8b, v3.8b +.endm + +generate_composite_function \ + pixman_composite_src_0888_8888_rev_asm_neon, 24, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + pixman_composite_src_0888_8888_rev_init, \ + default_cleanup, \ + pixman_composite_src_0888_8888_rev_process_pixblock_head, \ + pixman_composite_src_0888_8888_rev_process_pixblock_tail, \ + pixman_composite_src_0888_8888_rev_process_pixblock_tail_head, \ + 0, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_head + ushll v8.8h, v1.8b, #7 + sli v8.8h, v8.8h, #1 + ushll v9.8h, v2.8b, #7 + sli v9.8h, v9.8h, #1 +.endm + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_tail + ushll v14.8h, v0.8b, #7 + sli v14.8h, v14.8h, #1 + sri v14.8h, v8.8h, #5 + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +.macro pixman_composite_src_0888_0565_rev_process_pixblock_tail_head + ushll v14.8h, v0.8b, #7 + sli v14.8h, v14.8h, #1 + fetch_src_pixblock + sri v14.8h, v8.8h, #5 + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] + ushll v8.8h, v1.8b, #7 + sli v8.8h, v8.8h, #1 + st1 {v14.8h}, [DST_W], #16 + ushll v9.8h, v2.8b, #7 + sli v9.8h, v9.8h, #1 +.endm + +generate_composite_function \ + pixman_composite_src_0888_0565_rev_asm_neon, 24, 0, 16, \ + FLAG_DST_WRITEONLY, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0888_0565_rev_process_pixblock_head, \ + pixman_composite_src_0888_0565_rev_process_pixblock_tail, \ + pixman_composite_src_0888_0565_rev_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_head + umull v8.8h, v3.8b, v0.8b + umull v9.8h, v3.8b, v1.8b + umull v10.8h, v3.8b, v2.8b +.endm + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_tail + urshr v11.8h, v8.8h, #8 + mov v30.8b, v31.8b + mov v31.8b, v3.8b + mov v3.8b, v30.8b + urshr v12.8h, v9.8h, #8 + urshr v13.8h, v10.8h, #8 + raddhn v30.8b, v11.8h, v8.8h + raddhn v29.8b, v12.8h, v9.8h + raddhn v28.8b, v13.8h, v10.8h +.endm + +.macro pixman_composite_src_pixbuf_8888_process_pixblock_tail_head + urshr v11.8h, v8.8h, #8 + mov v30.8b, v31.8b + mov v31.8b, v3.8b + mov v3.8b, v31.8b + urshr v12.8h, v9.8h, #8 + urshr v13.8h, v10.8h, #8 + fetch_src_pixblock + raddhn v30.8b, v11.8h, v8.8h + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + PF beq, 10f + PF add, PF_X, PF_X, #8 + PF sub, PF_CTL, PF_CTL, #1 +10: + raddhn v29.8b, v12.8h, v9.8h + raddhn v28.8b, v13.8h, v10.8h + umull v8.8h, v3.8b, v0.8b + umull v9.8h, v3.8b, v1.8b + umull v10.8h, v3.8b, v2.8b + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + PF cmp, PF_X, ORIG_W + PF lsl, DUMMY, PF_X, src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W + PF subs, PF_CTL, PF_CTL, #0x10 + PF ble, 10f + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 +10: +.endm + +generate_composite_function \ + pixman_composite_src_pixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_pixbuf_8888_process_pixblock_head, \ + pixman_composite_src_pixbuf_8888_process_pixblock_tail, \ + pixman_composite_src_pixbuf_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_src_rpixbuf_8888_process_pixblock_head + umull v8.8h, v3.8b, v0.8b + umull v9.8h, v3.8b, v1.8b + umull v10.8h, v3.8b, v2.8b +.endm + +.macro pixman_composite_src_rpixbuf_8888_process_pixblock_tail + urshr v11.8h, v8.8h, #8 + mov v30.8b, v31.8b + mov v31.8b, v3.8b + mov v3.8b, v30.8b + urshr v12.8h, v9.8h, #8 + urshr v13.8h, v10.8h, #8 + raddhn v28.8b, v11.8h, v8.8h + raddhn v29.8b, v12.8h, v9.8h + raddhn v30.8b, v13.8h, v10.8h +.endm + +.macro pixman_composite_src_rpixbuf_8888_process_pixblock_tail_head + urshr v11.8h, v8.8h, #8 + mov v30.8b, v31.8b + mov v31.8b, v3.8b + mov v3.8b, v30.8b + urshr v12.8h, v9.8h, #8 + urshr v13.8h, v10.8h, #8 + fetch_src_pixblock + raddhn v28.8b, v11.8h, v8.8h + PF add, PF_X, PF_X, #8 + PF tst, PF_CTL, #0xF + PF beq, 10f + PF add, PF_X, PF_X, #8 + PF sub, PF_CTL, PF_CTL, #1 +10: + raddhn v29.8b, v12.8h, v9.8h + raddhn v30.8b, v13.8h, v10.8h + umull v8.8h, v3.8b, v0.8b + umull v9.8h, v3.8b, v1.8b + umull v10.8h, v3.8b, v2.8b + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + PF cmp, PF_X, ORIG_W + PF lsl, DUMMY, PF_X, src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + PF ble, 10f + PF sub, PF_X, PF_X, ORIG_W + PF subs, PF_CTL, PF_CTL, #0x10 + PF ble, 10f + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 +10: +.endm + +generate_composite_function \ + pixman_composite_src_rpixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_rpixbuf_8888_process_pixblock_head, \ + pixman_composite_src_rpixbuf_8888_process_pixblock_tail, \ + pixman_composite_src_rpixbuf_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 0, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_0565_8_0565_process_pixblock_head + /* mask is in v15 */ + mov v4.d[0], v8.d[0] + mov v4.d[1], v9.d[0] + mov v13.d[0], v10.d[0] + mov v13.d[1], v11.d[0] + convert_0565_to_x888 v4, v2, v1, v0 + convert_0565_to_x888 v13, v6, v5, v4 + /* source pixel data is in {v0, v1, v2, XX} */ + /* destination pixel data is in {v4, v5, v6, XX} */ + mvn v7.8b, v15.8b + umull v10.8h, v15.8b, v2.8b + umull v9.8h, v15.8b, v1.8b + umull v8.8h, v15.8b, v0.8b + umull v11.8h, v7.8b, v4.8b + umull v12.8h, v7.8b, v5.8b + umull v13.8h, v7.8b, v6.8b + urshr v19.8h, v10.8h, #8 + urshr v18.8h, v9.8h, #8 + urshr v17.8h, v8.8h, #8 + raddhn v2.8b, v10.8h, v19.8h + raddhn v1.8b, v9.8h, v18.8h + raddhn v0.8b, v8.8h, v17.8h +.endm + +.macro pixman_composite_over_0565_8_0565_process_pixblock_tail + urshr v17.8h, v11.8h, #8 + urshr v18.8h, v12.8h, #8 + urshr v19.8h, v13.8h, #8 + raddhn v28.8b, v17.8h, v11.8h + raddhn v29.8b, v18.8h, v12.8h + raddhn v30.8b, v19.8h, v13.8h + uqadd v0.8b, v0.8b, v28.8b + uqadd v1.8b, v1.8b, v29.8b + uqadd v2.8b, v2.8b, v30.8b + /* 32bpp result is in {v0, v1, v2, XX} */ + convert_8888_to_0565 v2, v1, v0, v14, v30, v13 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_over_0565_8_0565_process_pixblock_tail_head + fetch_mask_pixblock + pixman_composite_over_0565_8_0565_process_pixblock_tail + fetch_src_pixblock + ld1 {v10.4h, v11.4h}, [DST_R], #16 + cache_preload 8, 8 + pixman_composite_over_0565_8_0565_process_pixblock_head + st1 {v14.8h}, [DST_W], #16 +.endm + +generate_composite_function \ + pixman_composite_over_0565_8_0565_asm_neon, 16, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_0565_8_0565_process_pixblock_head, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_over_0565_n_0565_init + mov v15.s[0], w6 + dup v15.8b, v15.b[3] +.endm + +.macro pixman_composite_over_0565_n_0565_cleanup +.endm + +generate_composite_function \ + pixman_composite_over_0565_n_0565_asm_neon, 16, 0, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + pixman_composite_over_0565_n_0565_init, \ + pixman_composite_over_0565_n_0565_cleanup, \ + pixman_composite_over_0565_8_0565_process_pixblock_head, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_add_0565_8_0565_process_pixblock_head + /* mask is in v15 */ + mov v4.d[0], v8.d[0] + mov v4.d[1], v9.d[0] + mov v13.d[0], v10.d[0] + mov v13.d[1], v11.d[0] + convert_0565_to_x888 v4, v2, v1, v0 + convert_0565_to_x888 v13, v6, v5, v4 + /* source pixel data is in {v0, v1, v2, XX} */ + /* destination pixel data is in {v4, v5, v6, XX} */ + umull v9.8h, v15.8b, v2.8b + umull v8.8h, v15.8b, v1.8b + umull v7.8h, v15.8b, v0.8b + urshr v12.8h, v9.8h, #8 + urshr v11.8h, v8.8h, #8 + urshr v10.8h, v7.8h, #8 + raddhn v2.8b, v9.8h, v12.8h + raddhn v1.8b, v8.8h, v11.8h + raddhn v0.8b, v7.8h, v10.8h +.endm + +.macro pixman_composite_add_0565_8_0565_process_pixblock_tail + uqadd v0.8b, v0.8b, v4.8b + uqadd v1.8b, v1.8b, v5.8b + uqadd v2.8b, v2.8b, v6.8b + /* 32bpp result is in {v0, v1, v2, XX} */ + convert_8888_to_0565 v2, v1, v0, v14, v30, v13 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_add_0565_8_0565_process_pixblock_tail_head + fetch_mask_pixblock + pixman_composite_add_0565_8_0565_process_pixblock_tail + fetch_src_pixblock + ld1 {v10.4h, v11.4h}, [DST_R], #16 + cache_preload 8, 8 + pixman_composite_add_0565_8_0565_process_pixblock_head + st1 {v14.8h}, [DST_W], #16 +.endm + +generate_composite_function \ + pixman_composite_add_0565_8_0565_asm_neon, 16, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_add_0565_8_0565_process_pixblock_head, \ + pixman_composite_add_0565_8_0565_process_pixblock_tail, \ + pixman_composite_add_0565_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_out_reverse_8_0565_process_pixblock_head + /* mask is in v15 */ + mov v12.d[0], v10.d[0] + mov v12.d[1], v11.d[0] + convert_0565_to_x888 v12, v6, v5, v4 + /* destination pixel data is in {v4, v5, v6, xx} */ + mvn v24.8b, v15.8b /* get inverted alpha */ + /* now do alpha blending */ + umull v8.8h, v24.8b, v4.8b + umull v9.8h, v24.8b, v5.8b + umull v10.8h, v24.8b, v6.8b +.endm + +.macro pixman_composite_out_reverse_8_0565_process_pixblock_tail + urshr v11.8h, v8.8h, #8 + urshr v12.8h, v9.8h, #8 + urshr v13.8h, v10.8h, #8 + raddhn v0.8b, v11.8h, v8.8h + raddhn v1.8b, v12.8h, v9.8h + raddhn v2.8b, v13.8h, v10.8h + /* 32bpp result is in {v0, v1, v2, XX} */ + convert_8888_to_0565 v2, v1, v0, v14, v12, v3 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_out_reverse_8_0565_process_pixblock_tail_head + fetch_src_pixblock + pixman_composite_out_reverse_8_0565_process_pixblock_tail + ld1 {v10.4h, v11.4h}, [DST_R], #16 + cache_preload 8, 8 + pixman_composite_out_reverse_8_0565_process_pixblock_head + st1 {v14.8h}, [DST_W], #16 +.endm + +generate_composite_function \ + pixman_composite_out_reverse_8_0565_asm_neon, 8, 0, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_out_reverse_8_0565_process_pixblock_head, \ + pixman_composite_out_reverse_8_0565_process_pixblock_tail, \ + pixman_composite_out_reverse_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 15, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +.macro pixman_composite_out_reverse_8_8888_process_pixblock_head + /* src is in v0 */ + /* destination pixel data is in {v4, v5, v6, v7} */ + mvn v1.8b, v0.8b /* get inverted alpha */ + /* now do alpha blending */ + umull v8.8h, v1.8b, v4.8b + umull v9.8h, v1.8b, v5.8b + umull v10.8h, v1.8b, v6.8b + umull v11.8h, v1.8b, v7.8b +.endm + +.macro pixman_composite_out_reverse_8_8888_process_pixblock_tail + urshr v14.8h, v8.8h, #8 + urshr v15.8h, v9.8h, #8 + urshr v12.8h, v10.8h, #8 + urshr v13.8h, v11.8h, #8 + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h + raddhn v30.8b, v12.8h, v10.8h + raddhn v31.8b, v13.8h, v11.8h + /* 32bpp result is in {v28, v29, v30, v31} */ +.endm + +/* TODO: expand macros and do better instructions scheduling */ +.macro pixman_composite_out_reverse_8_8888_process_pixblock_tail_head + fetch_src_pixblock + pixman_composite_out_reverse_8_8888_process_pixblock_tail + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + cache_preload 8, 8 + pixman_composite_out_reverse_8_8888_process_pixblock_head + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +.endm + +generate_composite_function \ + pixman_composite_out_reverse_8_8888_asm_neon, 8, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init, \ + default_cleanup, \ + pixman_composite_out_reverse_8_8888_process_pixblock_head, \ + pixman_composite_out_reverse_8_8888_process_pixblock_tail, \ + pixman_composite_out_reverse_8_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 0 /* mask_basereg */ + +/******************************************************************************/ + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_8888_8888_OVER_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_8888_8888_process_pixblock_tail_head + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_8888_0565_OVER_asm_neon, 32, 0, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_over_8888_0565_process_pixblock_head, \ + pixman_composite_over_8888_0565_process_pixblock_tail, \ + pixman_composite_over_8888_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 24 /* mask_basereg */ + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_8888_0565_SRC_asm_neon, 32, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_8888_0565_process_pixblock_head, \ + pixman_composite_src_8888_0565_process_pixblock_tail, \ + pixman_composite_src_8888_0565_process_pixblock_tail_head + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_0565_8888_SRC_asm_neon, 16, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ + default_cleanup, \ + pixman_composite_src_0565_8888_process_pixblock_head, \ + pixman_composite_src_0565_8888_process_pixblock_tail, \ + pixman_composite_src_0565_8888_process_pixblock_tail_head + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_8888_8_0565_OVER_asm_neon, 32, 8, 16, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_8_0565_process_pixblock_head, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail, \ + pixman_composite_over_8888_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 24 /* mask_basereg */ + +generate_composite_function_nearest_scanline \ + pixman_scaled_nearest_scanline_0565_8_0565_OVER_asm_neon, 16, 8, 16, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_0565_8_0565_process_pixblock_head, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail, \ + pixman_composite_over_0565_8_0565_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 10, /* dst_r_basereg */ \ + 8, /* src_basereg */ \ + 15 /* mask_basereg */ + +/******************************************************************************/ + +/* + * Bilinear scaling support code which tries to provide pixel fetching, color + * format conversion, and interpolation as separate macros which can be used + * as the basic building blocks for constructing bilinear scanline functions. + */ + +.macro bilinear_load_8888 reg1, reg2, tmp + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #2 + ld1 {\()\reg1\().2s}, [TMP1], STRIDE + ld1 {\()\reg2\().2s}, [TMP1] +.endm + +.macro bilinear_load_0565 reg1, reg2, tmp + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + ld1 {\()\reg2\().s}[0], [TMP1], STRIDE + ld1 {\()\reg2\().s}[1], [TMP1] + convert_four_0565_to_x888_packed \reg2, \reg1, \reg2, \tmp +.endm + +.macro bilinear_load_and_vertical_interpolate_two_8888 \ + acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 + + bilinear_load_8888 \reg1, \reg2, \tmp1 + umull \()\acc1\().8h, \()\reg1\().8b, v28.8b + umlal \()\acc1\().8h, \()\reg2\().8b, v29.8b + bilinear_load_8888 \reg3, \reg4, \tmp2 + umull \()\acc2\().8h, \()\reg3\().8b, v28.8b + umlal \()\acc2\().8h, \()\reg4\().8b, v29.8b +.endm + +.macro bilinear_load_and_vertical_interpolate_four_8888 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi, \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + bilinear_load_and_vertical_interpolate_two_8888 \ + \xacc1, \xacc2, \xreg1, \xreg2, \xreg3, \xreg4, \xacc2lo, \xacc2hi + bilinear_load_and_vertical_interpolate_two_8888 \ + \yacc1, \yacc2, \yreg1, \yreg2, \yreg3, \yreg4, \yacc2lo, \yacc2hi +.endm + +.macro vzip reg1, reg2 + umov TMP4, v31.d[0] + zip1 v31.8b, \reg1, \reg2 + zip2 \reg2, \reg1, \reg2 + mov \reg1, v31.8b + mov v31.d[0], TMP4 +.endm + +.macro vuzp reg1, reg2 + umov TMP4, v31.d[0] + uzp1 v31.8b, \reg1, \reg2 + uzp2 \reg2, \reg1, \reg2 + mov \reg1, v31.8b + mov v31.d[0], TMP4 +.endm + +.macro bilinear_load_and_vertical_interpolate_two_0565 \ + acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr TMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 + ld1 {\()\acc2\().s}[0], [TMP1], STRIDE + ld1 {\()\acc2\().s}[2], [TMP2], STRIDE + ld1 {\()\acc2\().s}[1], [TMP1] + ld1 {\()\acc2\().s}[3], [TMP2] + convert_0565_to_x888 \acc2, \reg3, \reg2, \reg1 + vzip \()\reg1\().8b, \()\reg3\().8b + vzip \()\reg2\().8b, \()\reg4\().8b + vzip \()\reg3\().8b, \()\reg4\().8b + vzip \()\reg1\().8b, \()\reg2\().8b + umull \()\acc1\().8h, \()\reg1\().8b, v28.8b + umlal \()\acc1\().8h, \()\reg2\().8b, v29.8b + umull \()\acc2\().8h, \()\reg3\().8b, v28.8b + umlal \()\acc2\().8h, \()\reg4\().8b, v29.8b +.endm + +.macro bilinear_load_and_vertical_interpolate_four_0565 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi, \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr TMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 + ld1 {\()\xacc2\().s}[0], [TMP1], STRIDE + ld1 {\()\xacc2\().s}[2], [TMP2], STRIDE + ld1 {\()\xacc2\().s}[1], [TMP1] + ld1 {\()\xacc2\().s}[3], [TMP2] + convert_0565_to_x888 \xacc2, \xreg3, \xreg2, \xreg1 + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr TMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 + ld1 {\()\yacc2\().s}[0], [TMP1], STRIDE + vzip \()\xreg1\().8b, \()\xreg3\().8b + ld1 {\()\yacc2\().s}[2], [TMP2], STRIDE + vzip \()\xreg2\().8b, \()\xreg4\().8b + ld1 {\()\yacc2\().s}[1], [TMP1] + vzip \()\xreg3\().8b, \()\xreg4\().8b + ld1 {\()\yacc2\().s}[3], [TMP2] + vzip \()\xreg1\().8b, \()\xreg2\().8b + convert_0565_to_x888 \yacc2, \yreg3, \yreg2, \yreg1 + umull \()\xacc1\().8h, \()\xreg1\().8b, v28.8b + vzip \()\yreg1\().8b, \()\yreg3\().8b + umlal \()\xacc1\().8h, \()\xreg2\().8b, v29.8b + vzip \()\yreg2\().8b, \()\yreg4\().8b + umull \()\xacc2\().8h, \()\xreg3\().8b, v28.8b + vzip \()\yreg3\().8b, \()\yreg4\().8b + umlal \()\xacc2\().8h, \()\xreg4\().8b, v29.8b + vzip \()\yreg1\().8b, \()\yreg2\().8b + umull \()\yacc1\().8h, \()\yreg1\().8b, v28.8b + umlal \()\yacc1\().8h, \()\yreg2\().8b, v29.8b + umull \()\yacc2\().8h, \()\yreg3\().8b, v28.8b + umlal \()\yacc2\().8h, \()\yreg4\().8b, v29.8b +.endm + +.macro bilinear_store_8888 numpix, tmp1, tmp2 +.if \numpix == 4 + st1 {v0.2s, v1.2s}, [OUT], #16 +.elseif \numpix == 2 + st1 {v0.2s}, [OUT], #8 +.elseif \numpix == 1 + st1 {v0.s}[0], [OUT], #4 +.else + .error bilinear_store_8888 \numpix is unsupported +.endif +.endm + +.macro bilinear_store_0565 numpix, tmp1, tmp2 + vuzp v0.8b, v1.8b + vuzp v2.8b, v3.8b + vuzp v1.8b, v3.8b + vuzp v0.8b, v2.8b + convert_8888_to_0565 v2, v1, v0, v1, \tmp1, \tmp2 +.if \numpix == 4 + st1 {v1.4h}, [OUT], #8 +.elseif \numpix == 2 + st1 {v1.s}[0], [OUT], #4 +.elseif \numpix == 1 + st1 {v1.h}[0], [OUT], #2 +.else + .error bilinear_store_0565 \numpix is unsupported +.endif +.endm + +.macro bilinear_interpolate_last_pixel src_fmt, dst_fmt + bilinear_load_\()\src_fmt v0, v1, v2 + umull v2.8h, v0.8b, v28.8b + umlal v2.8h, v1.8b, v29.8b + /* 5 cycles bubble */ + ushll v0.4s, v2.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v2.4h, v15.h[0] + umlal2 v0.4s, v2.8h, v15.h[0] + /* 5 cycles bubble */ + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + /* 3 cycles bubble */ + xtn v0.8b, v0.8h + /* 1 cycle bubble */ + bilinear_store_\()\dst_fmt 1, v3, v4 +.endm + +.macro bilinear_interpolate_two_pixels src_fmt, dst_fmt + bilinear_load_and_vertical_interpolate_two_\()\src_fmt \ + v1, v11, v2, v3, v20, v21, v22, v23 + ushll v0.4s, v1.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v1.4h, v15.h[0] + umlal2 v0.4s, v1.8h, v15.h[0] + ushll v10.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v10.4s, v11.4h, v15.h[4] + umlal2 v10.4s, v11.8h, v15.h[4] + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v10.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + xtn v0.8b, v0.8h + bilinear_store_\()\dst_fmt 2, v3, v4 +.endm + +.macro bilinear_interpolate_four_pixels src_fmt, dst_fmt + bilinear_load_and_vertical_interpolate_four_\()\src_fmt \ + v1, v11, v14, v20, v16, v17, v22, v23, \ + v3, v9, v24, v25, v26, v27, v18, v19 + prfm PREFETCH_MODE, [TMP1, PF_OFFS] + sub TMP1, TMP1, STRIDE + ushll v0.4s, v1.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v1.4h, v15.h[0] + umlal2 v0.4s, v1.8h, v15.h[0] + ushll v10.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v10.4s, v11.4h, v15.h[4] + umlal2 v10.4s, v11.8h, v15.h[4] + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + ushll v2.4s, v3.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v2.4s, v3.4h, v15.h[0] + umlal2 v2.4s, v3.8h, v15.h[0] + ushll v8.4s, v9.4h, #BILINEAR_INTERPOLATION_BITS + prfm PREFETCH_MODE, [TMP2, PF_OFFS] + umlsl v8.4s, v9.4h, v15.h[4] + umlal2 v8.4s, v9.8h, v15.h[4] + add v12.8h, v12.8h, v13.8h + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v10.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn v2.4h, v2.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v2.8h, v8.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + xtn v0.8b, v0.8h + xtn v1.8b, v2.8h + add v12.8h, v12.8h, v13.8h + bilinear_store_\()\dst_fmt 4, v3, v4 +.endm + +.macro bilinear_interpolate_four_pixels_head src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_head +.else + bilinear_interpolate_four_pixels \src_fmt, \dst_fmt +.endif +.endm + +.macro bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail +.endif +.endm + +.macro bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail_head +.else + bilinear_interpolate_four_pixels \src_fmt, \dst_fmt +.endif +.endm + +.macro bilinear_interpolate_eight_pixels_head src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_head +.else + bilinear_interpolate_four_pixels_head \src_fmt, \dst_fmt + bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt +.endif +.endm + +.macro bilinear_interpolate_eight_pixels_tail src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail +.else + bilinear_interpolate_four_pixels_tail \src_fmt, \dst_fmt +.endif +.endm + +.macro bilinear_interpolate_eight_pixels_tail_head src_fmt, dst_fmt +.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt + bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail_head +.else + bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt +.endif +.endm + +.set BILINEAR_FLAG_UNROLL_4, 0 +.set BILINEAR_FLAG_UNROLL_8, 1 +.set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 + +/* + * Main template macro for generating NEON optimized bilinear scanline + * functions. + * + * Bilinear scanline scaler macro template uses the following arguments: + * fname - name of the function to generate + * src_fmt - source color format (8888 or 0565) + * dst_fmt - destination color format (8888 or 0565) + * bpp_shift - (1 << bpp_shift) is the size of source pixel in bytes + * prefetch_distance - prefetch in the source image by that many + * pixels ahead + */ + +.macro generate_bilinear_scanline_func fname, src_fmt, dst_fmt, \ + src_bpp_shift, dst_bpp_shift, \ + prefetch_distance, flags + +pixman_asm_function \fname + OUT .req x0 + TOP .req x1 + BOTTOM .req x2 + WT .req x3 + WB .req x4 + X .req x5 + UX .req x6 + WIDTH .req x7 + TMP1 .req x8 + TMP2 .req x9 + PF_OFFS .req x10 + TMP3 .req x11 + TMP4 .req x12 + STRIDE .req x13 + + sxtw x3, w3 + sxtw x4, w4 + sxtw x5, w5 + sxtw x6, w6 + sxtw x7, w7 + + stp x29, x30, [sp, -16]! + mov x29, sp + sub sp, sp, 112 /* push all registers */ + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], #32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], #32 + stp x8, x9, [x29, -80] + stp x10, x11, [x29, -96] + stp x12, x13, [x29, -112] + + mov PF_OFFS, #\prefetch_distance + mul PF_OFFS, PF_OFFS, UX + + subs STRIDE, BOTTOM, TOP + .unreq BOTTOM + + cmp WIDTH, #0 + ble 300f + + dup v12.8h, w5 + dup v13.8h, w6 + dup v28.8b, w3 + dup v29.8b, w4 + mov v25.d[0], v12.d[1] + mov v26.d[0], v13.d[0] + add v25.4h, v25.4h, v26.4h + mov v12.d[1], v25.d[0] + + /* ensure good destination alignment */ + cmp WIDTH, #1 + blt 100f + tst OUT, #(1 << \dst_bpp_shift) + beq 100f + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + bilinear_interpolate_last_pixel \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #1 +100: + add v13.8h, v13.8h, v13.8h + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + + cmp WIDTH, #2 + blt 100f + tst OUT, #(1 << (\dst_bpp_shift + 1)) + beq 100f + bilinear_interpolate_two_pixels \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #2 +100: +.if ((\flags) & BILINEAR_FLAG_UNROLL_8) != 0 +/*********** 8 pixels per iteration *****************/ + cmp WIDTH, #4 + blt 100f + tst OUT, #(1 << (\dst_bpp_shift + 2)) + beq 100f + bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #4 +100: + subs WIDTH, WIDTH, #8 + blt 100f + asr PF_OFFS, PF_OFFS, #(16 - \src_bpp_shift) + bilinear_interpolate_eight_pixels_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #8 + blt 500f +1000: + bilinear_interpolate_eight_pixels_tail_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #8 + bge 1000b +500: + bilinear_interpolate_eight_pixels_tail \src_fmt, \dst_fmt +100: + tst WIDTH, #4 + beq 200f + bilinear_interpolate_four_pixels \src_fmt, \dst_fmt +200: +.else +/*********** 4 pixels per iteration *****************/ + subs WIDTH, WIDTH, #4 + blt 100f + asr PF_OFFS, PF_OFFS, #(16 - \src_bpp_shift) + bilinear_interpolate_four_pixels_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #4 + blt 500f +1000: + bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #4 + bge 1000b +500: + bilinear_interpolate_four_pixels_tail \src_fmt, \dst_fmt +100: +/****************************************************/ +.endif + /* handle the remaining trailing pixels */ + tst WIDTH, #2 + beq 200f + bilinear_interpolate_two_pixels \src_fmt, \dst_fmt +200: + tst WIDTH, #1 + beq 300f + bilinear_interpolate_last_pixel \src_fmt, \dst_fmt +300: + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], #32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], #32 + ldp x8, x9, [x29, -80] + ldp x10, x11, [x29, -96] + ldp x12, x13, [x29, -104] + mov sp, x29 + ldp x29, x30, [sp], 16 + ret + + .unreq OUT + .unreq TOP + .unreq WT + .unreq WB + .unreq X + .unreq UX + .unreq WIDTH + .unreq TMP1 + .unreq TMP2 + .unreq PF_OFFS + .unreq TMP3 + .unreq TMP4 + .unreq STRIDE +pixman_end_asm_function + +.endm + +/*****************************************************************************/ + +.set have_bilinear_interpolate_four_pixels_8888_8888, 1 + +.macro bilinear_interpolate_four_pixels_8888_8888_head + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #2 + asr TMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #2 + + ld1 {v22.2s}, [TMP1], STRIDE + ld1 {v23.2s}, [TMP1] + asr TMP3, X, #16 + add X, X, UX + add TMP3, TOP, TMP3, lsl #2 + umull v8.8h, v22.8b, v28.8b + umlal v8.8h, v23.8b, v29.8b + + ld1 {v22.2s}, [TMP2], STRIDE + ld1 {v23.2s}, [TMP2] + asr TMP4, X, #16 + add X, X, UX + add TMP4, TOP, TMP4, lsl #2 + umull v9.8h, v22.8b, v28.8b + umlal v9.8h, v23.8b, v29.8b + + ld1 {v22.2s}, [TMP3], STRIDE + ld1 {v23.2s}, [TMP3] + umull v10.8h, v22.8b, v28.8b + umlal v10.8h, v23.8b, v29.8b + + ushll v0.4s, v8.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v8.4h, v15.h[0] + umlal2 v0.4s, v8.8h, v15.h[0] + + prfm PREFETCH_MODE, [TMP4, PF_OFFS] + ld1 {v16.2s}, [TMP4], STRIDE + ld1 {v17.2s}, [TMP4] + prfm PREFETCH_MODE, [TMP4, PF_OFFS] + umull v11.8h, v16.8b, v28.8b + umlal v11.8h, v17.8b, v29.8b + + ushll v1.4s, v9.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v1.4s, v9.4h, v15.h[4] +.endm + +.macro bilinear_interpolate_four_pixels_8888_8888_tail + umlal2 v1.4s, v9.8h, v15.h[4] + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + ushll v2.4s, v10.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v2.4s, v10.4h, v15.h[0] + umlal2 v2.4s, v10.8h, v15.h[0] + ushll v3.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v3.4s, v11.4h, v15.h[4] + umlal2 v3.4s, v11.8h, v15.h[4] + add v12.8h, v12.8h, v13.8h + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v1.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn v2.4h, v2.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + shrn2 v2.8h, v3.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + xtn v6.8b, v0.8h + xtn v7.8b, v2.8h + add v12.8h, v12.8h, v13.8h + st1 {v6.2s, v7.2s}, [OUT], #16 +.endm + +.macro bilinear_interpolate_four_pixels_8888_8888_tail_head + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #2 + asr TMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #2 + umlal2 v1.4s, v9.8h, v15.h[4] + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + ushll v2.4s, v10.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v2.4s, v10.4h, v15.h[0] + umlal2 v2.4s, v10.8h, v15.h[0] + ushll v3.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + ld1 {v20.2s}, [TMP1], STRIDE + umlsl v3.4s, v11.4h, v15.h[4] + umlal2 v3.4s, v11.8h, v15.h[4] + ld1 {v21.2s}, [TMP1] + umull v8.8h, v20.8b, v28.8b + umlal v8.8h, v21.8b, v29.8b + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v1.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn v4.4h, v2.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + ld1 {v22.2s}, [TMP2], STRIDE + shrn2 v4.8h, v3.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + ld1 {v23.2s}, [TMP2] + umull v9.8h, v22.8b, v28.8b + asr TMP3, X, #16 + add X, X, UX + add TMP3, TOP, TMP3, lsl #2 + asr TMP4, X, #16 + add X, X, UX + add TMP4, TOP, TMP4, lsl #2 + umlal v9.8h, v23.8b, v29.8b + ld1 {v22.2s}, [TMP3], STRIDE + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + ld1 {v23.2s}, [TMP3] + umull v10.8h, v22.8b, v28.8b + umlal v10.8h, v23.8b, v29.8b + xtn v6.8b, v0.8h + ushll v0.4s, v8.4h, #BILINEAR_INTERPOLATION_BITS + xtn v7.8b, v4.8h + umlsl v0.4s, v8.4h, v15.h[0] + umlal2 v0.4s, v8.8h, v15.h[0] + prfm PREFETCH_MODE, [TMP4, PF_OFFS] + ld1 {v16.2s}, [TMP4], STRIDE + add v12.8h, v12.8h, v13.8h + ld1 {v17.2s}, [TMP4] + prfm PREFETCH_MODE, [TMP4, PF_OFFS] + umull v11.8h, v16.8b, v28.8b + umlal v11.8h, v17.8b, v29.8b + st1 {v6.2s, v7.2s}, [OUT], #16 + ushll v1.4s, v9.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v1.4s, v9.4h, v15.h[4] +.endm + +/*****************************************************************************/ + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon, 8888, 8888, \ + 2, 2, 28, BILINEAR_FLAG_UNROLL_4 + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_8888_0565_SRC_asm_neon, 8888, 0565, \ + 2, 1, 28, BILINEAR_FLAG_UNROLL_8 | BILINEAR_FLAG_USE_ALL_NEON_REGS + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_0565_x888_SRC_asm_neon, 0565, 8888, \ + 1, 2, 28, BILINEAR_FLAG_UNROLL_4 + +generate_bilinear_scanline_func \ + pixman_scaled_bilinear_scanline_0565_0565_SRC_asm_neon, 0565, 0565, \ + 1, 1, 28, BILINEAR_FLAG_UNROLL_4 diff --git a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h new file mode 100644 index 0000000000..6aa6838e76 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h @@ -0,0 +1,1310 @@ +/* + * Copyright © 2009 Nokia 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. + * + * Author: Siarhei Siamashka (siarhei.siamashka@nokia.com) + */ + +/* + * This file contains a macro ('generate_composite_function') which can + * construct 2D image processing functions, based on a common template. + * Any combinations of source, destination and mask images with 8bpp, + * 16bpp, 24bpp, 32bpp color formats are supported. + * + * This macro takes care of: + * - handling of leading and trailing unaligned pixels + * - doing most of the work related to L2 cache preload + * - encourages the use of software pipelining for better instructions + * scheduling + * + * The user of this macro has to provide some configuration parameters + * (bit depths for the images, prefetch distance, etc.) and a set of + * macros, which should implement basic code chunks responsible for + * pixels processing. See 'pixman-armv8-neon-asm.S' file for the usage + * examples. + * + * TODO: + * - try overlapped pixel method (from Ian Rickards) when processing + * exactly two blocks of pixels + * - maybe add an option to do reverse scanline processing + */ + +/* + * Bit flags for 'generate_composite_function' macro which are used + * to tune generated functions behavior. + */ +.set FLAG_DST_WRITEONLY, 0 +.set FLAG_DST_READWRITE, 1 +.set FLAG_DEINTERLEAVE_32BPP, 2 + +/* + * Constants for selecting preferable prefetch type. + */ +.set PREFETCH_TYPE_NONE, 0 /* No prefetch at all */ +.set PREFETCH_TYPE_SIMPLE, 1 /* A simple, fixed-distance-ahead prefetch */ +.set PREFETCH_TYPE_ADVANCED, 2 /* Advanced fine-grained prefetch */ + +/* + * prefetch mode + * available modes are: + * pldl1keep + * pldl1strm + * pldl2keep + * pldl2strm + * pldl3keep + * pldl3strm + */ +#define PREFETCH_MODE pldl1keep + +/* + * Definitions of supplementary pixld/pixst macros (for partial load/store of + * pixel data). + */ + +.macro pixldst1 op, elem_size, reg1, mem_operand, abits + \op {v\()\reg1\().\()\elem_size}, [\()\mem_operand\()], #8 +.endm + +.macro pixldst2 op, elem_size, reg1, reg2, mem_operand, abits + \op {v\()\reg1\().\()\elem_size, v\()\reg2\().\()\elem_size}, [\()\mem_operand\()], #16 +.endm + +.macro pixldst4 op, elem_size, reg1, reg2, reg3, reg4, mem_operand, abits + \op {v\()\reg1\().\()\elem_size, v\()\reg2\().\()\elem_size, v\()\reg3\().\()\elem_size, v\()\reg4\().\()\elem_size}, [\()\mem_operand\()], #32 +.endm + +.macro pixldst0 op, elem_size, reg1, idx, mem_operand, abits, bytes + \op {v\()\reg1\().\()\elem_size}[\idx], [\()\mem_operand\()], #\()\bytes\() +.endm + +.macro pixldst3 op, elem_size, reg1, reg2, reg3, mem_operand + \op {v\()\reg1\().\()\elem_size, v\()\reg2\().\()\elem_size, v\()\reg3\().\()\elem_size}, [\()\mem_operand\()], #24 +.endm + +.macro pixldst30 op, elem_size, reg1, reg2, reg3, idx, mem_operand + \op {v\()\reg1\().\()\elem_size, v\()\reg2\().\()\elem_size, v\()\reg3\().\()\elem_size}[\idx], [\()\mem_operand\()], #3 +.endm + +.macro pixldst numbytes, op, elem_size, basereg, mem_operand, abits +.if \numbytes == 32 + .if \elem_size==32 + pixldst4 \op, 2s, %(\basereg+4), %(\basereg+5), \ + %(\basereg+6), %(\basereg+7), \mem_operand, \abits + .elseif \elem_size==16 + pixldst4 \op, 4h, %(\basereg+4), %(\basereg+5), \ + %(\basereg+6), %(\basereg+7), \mem_operand, \abits + .else + pixldst4 \op, 8b, %(\basereg+4), %(\basereg+5), \ + %(\basereg+6), %(\basereg+7), \mem_operand, \abits + .endif +.elseif \numbytes == 16 + .if \elem_size==32 + pixldst2 \op, 2s, %(\basereg+2), %(\basereg+3), \mem_operand, \abits + .elseif \elem_size==16 + pixldst2 \op, 4h, %(\basereg+2), %(\basereg+3), \mem_operand, \abits + .else + pixldst2 \op, 8b, %(\basereg+2), %(\basereg+3), \mem_operand, \abits + .endif +.elseif \numbytes == 8 + .if \elem_size==32 + pixldst1 \op, 2s, %(\basereg+1), \mem_operand, \abits + .elseif \elem_size==16 + pixldst1 \op, 4h, %(\basereg+1), \mem_operand, \abits + .else + pixldst1 \op, 8b, %(\basereg+1), \mem_operand, \abits + .endif +.elseif \numbytes == 4 + .if !RESPECT_STRICT_ALIGNMENT || (\elem_size == 32) + pixldst0 \op, s, %(\basereg+0), 1, \mem_operand, \abits, 4 + .elseif \elem_size == 16 + pixldst0 \op, h, %(\basereg+0), 2, \mem_operand, \abits, 2 + pixldst0 \op, h, %(\basereg+0), 3, \mem_operand, \abits, 2 + .else + pixldst0 \op, b, %(\basereg+0), 4, \mem_operand, \abits, 1 + pixldst0 \op, b, %(\basereg+0), 5, \mem_operand, \abits, 1 + pixldst0 \op, b, %(\basereg+0), 6, \mem_operand, \abits, 1 + pixldst0 \op, b, %(\basereg+0), 7, \mem_operand, \abits, 1 + .endif +.elseif \numbytes == 2 + .if !RESPECT_STRICT_ALIGNMENT || (\elem_size == 16) + pixldst0 \op, h, %(\basereg+0), 1, \mem_operand, \abits, 2 + .else + pixldst0 \op, b, %(\basereg+0), 2, \mem_operand, \abits, 1 + pixldst0 \op, b, %(\basereg+0), 3, \mem_operand, \abits, 1 + .endif +.elseif \numbytes == 1 + pixldst0 \op, b, %(\basereg+0), 1, \mem_operand, \abits, 1 +.else + .error "unsupported size: \numbytes" +.endif +.endm + +.macro pixld numpix, bpp, basereg, mem_operand, abits=0 +.if \bpp > 0 +.if (\bpp == 32) && (\numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) + pixldst4 ld4, 8b, %(\basereg+4), %(\basereg+5), \ + %(\basereg+6), %(\basereg+7), \mem_operand, \abits +.elseif (\bpp == 24) && (\numpix == 8) + pixldst3 ld3, 8b, %(\basereg+3), %(\basereg+4), %(\basereg+5), \mem_operand +.elseif (\bpp == 24) && (\numpix == 4) + pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 4, \mem_operand + pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 5, \mem_operand + pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 6, \mem_operand + pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 7, \mem_operand +.elseif (\bpp == 24) && (\numpix == 2) + pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 2, \mem_operand + pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 3, \mem_operand +.elseif (\bpp == 24) && (\numpix == 1) + pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 1, \mem_operand +.else + pixldst %(\numpix * \bpp / 8), ld1, %(\bpp), \basereg, \mem_operand, \abits +.endif +.endif +.endm + +.macro pixst numpix, bpp, basereg, mem_operand, abits=0 +.if \bpp > 0 +.if (\bpp == 32) && (\numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) + pixldst4 st4, 8b, %(\basereg+4), %(\basereg+5), \ + %(\basereg+6), %(\basereg+7), \mem_operand, \abits +.elseif (\bpp == 24) && (\numpix == 8) + pixldst3 st3, 8b, %(\basereg+3), %(\basereg+4), %(\basereg+5), \mem_operand +.elseif (\bpp == 24) && (\numpix == 4) + pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 4, \mem_operand + pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 5, \mem_operand + pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 6, \mem_operand + pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 7, \mem_operand +.elseif (\bpp == 24) && (\numpix == 2) + pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 2, \mem_operand + pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 3, \mem_operand +.elseif (\bpp == 24) && (\numpix == 1) + pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 1, \mem_operand +.elseif \numpix * \bpp == 32 && \abits == 32 + pixldst 4, st1, 32, \basereg, \mem_operand, \abits +.elseif \numpix * \bpp == 16 && \abits == 16 + pixldst 2, st1, 16, \basereg, \mem_operand, \abits +.else + pixldst %(\numpix * \bpp / 8), st1, %(\bpp), \basereg, \mem_operand, \abits +.endif +.endif +.endm + +.macro pixld_a numpix, bpp, basereg, mem_operand +.if (\bpp * \numpix) <= 128 + pixld \numpix, \bpp, \basereg, \mem_operand, %(\bpp * \numpix) +.else + pixld \numpix, \bpp, \basereg, \mem_operand, 128 +.endif +.endm + +.macro pixst_a numpix, bpp, basereg, mem_operand +.if (\bpp * \numpix) <= 128 + pixst \numpix, \bpp, \basereg, \mem_operand, %(\bpp * \numpix) +.else + pixst \numpix, \bpp, \basereg, \mem_operand, 128 +.endif +.endm + +/* + * Pixel fetcher for nearest scaling (needs TMP1, TMP2, VX, UNIT_X register + * aliases to be defined) + */ +.macro pixld1_s elem_size, reg1, mem_operand +.if \elem_size == 16 + asr TMP1, VX, #16 + adds VX, VX, UNIT_X + bmi 55f +5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b +55: + add TMP1, \mem_operand, TMP1, lsl #1 + asr TMP2, VX, #16 + adds VX, VX, UNIT_X + bmi 55f +5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b +55: + add TMP2, \mem_operand, TMP2, lsl #1 + ld1 {v\()\reg1\().h}[0], [TMP1] + asr TMP1, VX, #16 + adds VX, VX, UNIT_X + bmi 55f +5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b +55: + add TMP1, \mem_operand, TMP1, lsl #1 + ld1 {v\()\reg1\().h}[1], [TMP2] + asr TMP2, VX, #16 + adds VX, VX, UNIT_X + bmi 55f +5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b +55: + add TMP2, \mem_operand, TMP2, lsl #1 + ld1 {v\()\reg1\().h}[2], [TMP1] + ld1 {v\()\reg1\().h}[3], [TMP2] +.elseif \elem_size == 32 + asr TMP1, VX, #16 + adds VX, VX, UNIT_X + bmi 55f +5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b +55: + add TMP1, \mem_operand, TMP1, lsl #2 + asr TMP2, VX, #16 + adds VX, VX, UNIT_X + bmi 55f +5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b +55: + add TMP2, \mem_operand, TMP2, lsl #2 + ld1 {v\()\reg1\().s}[0], [TMP1] + ld1 {v\()\reg1\().s}[1], [TMP2] +.else + .error "unsupported" +.endif +.endm + +.macro pixld2_s elem_size, reg1, reg2, mem_operand +.if 0 /* \elem_size == 32 */ + mov TMP1, VX, asr #16 + add VX, VX, UNIT_X, asl #1 + add TMP1, \mem_operand, TMP1, asl #2 + mov TMP2, VX, asr #16 + sub VX, VX, UNIT_X + add TMP2, \mem_operand, TMP2, asl #2 + ld1 {v\()\reg1\().s}[0], [TMP1] + mov TMP1, VX, asr #16 + add VX, VX, UNIT_X, asl #1 + add TMP1, \mem_operand, TMP1, asl #2 + ld1 {v\()\reg2\().s}[0], [TMP2, :32] + mov TMP2, VX, asr #16 + add VX, VX, UNIT_X + add TMP2, \mem_operand, TMP2, asl #2 + ld1 {v\()\reg1\().s}[1], [TMP1] + ld1 {v\()\reg2\().s}[1], [TMP2] +.else + pixld1_s \elem_size, \reg1, \mem_operand + pixld1_s \elem_size, \reg2, \mem_operand +.endif +.endm + +.macro pixld0_s elem_size, reg1, idx, mem_operand +.if \elem_size == 16 + asr TMP1, VX, #16 + adds VX, VX, UNIT_X + bmi 55f +5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b +55: + add TMP1, \mem_operand, TMP1, lsl #1 + ld1 {v\()\reg1\().h}[\idx], [TMP1] +.elseif \elem_size == 32 + asr DUMMY, VX, #16 + mov TMP1, DUMMY + adds VX, VX, UNIT_X + bmi 55f +5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b +55: + add TMP1, \mem_operand, TMP1, lsl #2 + ld1 {v\()\reg1\().s}[\idx], [TMP1] +.endif +.endm + +.macro pixld_s_internal numbytes, elem_size, basereg, mem_operand +.if \numbytes == 32 + pixld2_s \elem_size, %(\basereg+4), %(\basereg+5), \mem_operand + pixld2_s \elem_size, %(\basereg+6), %(\basereg+7), \mem_operand + pixdeinterleave \elem_size, %(\basereg+4) +.elseif \numbytes == 16 + pixld2_s \elem_size, %(\basereg+2), %(\basereg+3), \mem_operand +.elseif \numbytes == 8 + pixld1_s \elem_size, %(\basereg+1), \mem_operand +.elseif \numbytes == 4 + .if \elem_size == 32 + pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand + .elseif \elem_size == 16 + pixld0_s \elem_size, %(\basereg+0), 2, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 3, \mem_operand + .else + pixld0_s \elem_size, %(\basereg+0), 4, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 5, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 6, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 7, \mem_operand + .endif +.elseif \numbytes == 2 + .if \elem_size == 16 + pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand + .else + pixld0_s \elem_size, %(\basereg+0), 2, \mem_operand + pixld0_s \elem_size, %(\basereg+0), 3, \mem_operand + .endif +.elseif \numbytes == 1 + pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand +.else + .error "unsupported size: \numbytes" +.endif +.endm + +.macro pixld_s numpix, bpp, basereg, mem_operand +.if \bpp > 0 + pixld_s_internal %(\numpix * \bpp / 8), %(\bpp), \basereg, \mem_operand +.endif +.endm + +.macro vuzp8 reg1, reg2 + umov DUMMY, v16.d[0] + uzp1 v16.8b, v\()\reg1\().8b, v\()\reg2\().8b + uzp2 v\()\reg2\().8b, v\()\reg1\().8b, v\()\reg2\().8b + mov v\()\reg1\().8b, v16.8b + mov v16.d[0], DUMMY +.endm + +.macro vzip8 reg1, reg2 + umov DUMMY, v16.d[0] + zip1 v16.8b, v\()\reg1\().8b, v\()\reg2\().8b + zip2 v\()\reg2\().8b, v\()\reg1\().8b, v\()\reg2\().8b + mov v\()\reg1\().8b, v16.8b + mov v16.d[0], DUMMY +.endm + +/* deinterleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ +.macro pixdeinterleave bpp, basereg +.if (\bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) + vuzp8 %(\basereg+0), %(\basereg+1) + vuzp8 %(\basereg+2), %(\basereg+3) + vuzp8 %(\basereg+1), %(\basereg+3) + vuzp8 %(\basereg+0), %(\basereg+2) +.endif +.endm + +/* interleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ +.macro pixinterleave bpp, basereg +.if (\bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) + vzip8 %(\basereg+0), %(\basereg+2) + vzip8 %(\basereg+1), %(\basereg+3) + vzip8 %(\basereg+2), %(\basereg+3) + vzip8 %(\basereg+0), %(\basereg+1) +.endif +.endm + +/* + * This is a macro for implementing cache preload. The main idea is that + * cache preload logic is mostly independent from the rest of pixels + * processing code. It starts at the top left pixel and moves forward + * across pixels and can jump across scanlines. Prefetch distance is + * handled in an 'incremental' way: it starts from 0 and advances to the + * optimal distance over time. After reaching optimal prefetch distance, + * it is kept constant. There are some checks which prevent prefetching + * unneeded pixel lines below the image (but it still can prefetch a bit + * more data on the right side of the image - not a big issue and may + * be actually helpful when rendering text glyphs). Additional trick is + * the use of LDR instruction for prefetch instead of PLD when moving to + * the next line, the point is that we have a high chance of getting TLB + * miss in this case, and PLD would be useless. + * + * This sounds like it may introduce a noticeable overhead (when working with + * fully cached data). But in reality, due to having a separate pipeline and + * instruction queue for NEON unit in ARM Cortex-A8, normal ARM code can + * execute simultaneously with NEON and be completely shadowed by it. Thus + * we get no performance overhead at all (*). This looks like a very nice + * feature of Cortex-A8, if used wisely. We don't have a hardware prefetcher, + * but still can implement some rather advanced prefetch logic in software + * for almost zero cost! + * + * (*) The overhead of the prefetcher is visible when running some trivial + * pixels processing like simple copy. Anyway, having prefetch is a must + * when working with the graphics data. + */ +.macro PF a, x:vararg +.if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_ADVANCED) + \a \x +.endif +.endm + +.macro cache_preload std_increment, boost_increment +.if (src_bpp_shift >= 0) || (dst_r_bpp != 0) || (mask_bpp_shift >= 0) +.if \std_increment != 0 + PF add, PF_X, PF_X, #\std_increment +.endif + PF tst, PF_CTL, #0xF + PF beq, 71f + PF add, PF_X, PF_X, #\boost_increment + PF sub, PF_CTL, PF_CTL, #1 +71: + PF cmp, PF_X, ORIG_W +.if src_bpp_shift >= 0 + PF lsl, DUMMY, PF_X, #src_bpp_shift + PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] +.endif +.if dst_r_bpp != 0 + PF lsl, DUMMY, PF_X, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] +.endif +.if mask_bpp_shift >= 0 + PF lsl, DUMMY, PF_X, #mask_bpp_shift + PF prfm, PREFETCH_MODE, [PF_MASK, DUMMY] +.endif + PF ble, 71f + PF sub, PF_X, PF_X, ORIG_W + PF subs, PF_CTL, PF_CTL, #0x10 +71: + PF ble, 72f +.if src_bpp_shift >= 0 + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF ldrsb, DUMMY, [PF_SRC, DUMMY] + PF add, PF_SRC, PF_SRC, #1 +.endif +.if dst_r_bpp != 0 + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF ldrsb, DUMMY, [PF_DST, DUMMY] + PF add, PF_DST, PF_DST, #1 +.endif +.if mask_bpp_shift >= 0 + PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift + PF ldrsb, DUMMY, [PF_MASK, DUMMY] + PF add, PF_MASK, PF_MASK, #1 +.endif +72: +.endif +.endm + +.macro cache_preload_simple +.if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_SIMPLE) +.if src_bpp > 0 + prfm PREFETCH_MODE, [SRC, #(PREFETCH_DISTANCE_SIMPLE * src_bpp / 8)] +.endif +.if dst_r_bpp > 0 + prfm PREFETCH_MODE, [DST_R, #(PREFETCH_DISTANCE_SIMPLE * dst_r_bpp / 8)] +.endif +.if mask_bpp > 0 + prfm PREFETCH_MODE, [MASK, #(PREFETCH_DISTANCE_SIMPLE * mask_bpp / 8)] +.endif +.endif +.endm + +.macro fetch_mask_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK +.endm + +/* + * Macro which is used to process leading pixels until destination + * pointer is properly aligned (at 16 bytes boundary). When destination + * buffer uses 16bpp format, this is unnecessary, or even pointless. + */ +.macro ensure_destination_ptr_alignment process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head +.if dst_w_bpp != 24 + tst DST_R, #0xF + beq 52f + +.if src_bpp > 0 || mask_bpp > 0 || dst_r_bpp > 0 +.irp lowbit, 1, 2, 4, 8, 16 + +.if (dst_w_bpp <= (\lowbit * 8)) && ((\lowbit * 8) < (pixblock_size * dst_w_bpp)) +.if \lowbit < 16 /* we don't need more than 16-byte alignment */ + tst DST_R, #\lowbit + beq 51f +.endif + pixld_src (\lowbit * 8 / dst_w_bpp), src_bpp, src_basereg, SRC + pixld (\lowbit * 8 / dst_w_bpp), mask_bpp, mask_basereg, MASK +.if dst_r_bpp > 0 + pixld_a (\lowbit * 8 / dst_r_bpp), dst_r_bpp, dst_r_basereg, DST_R +.else + add DST_R, DST_R, #\lowbit +.endif + PF add, PF_X, PF_X, #(\lowbit * 8 / dst_w_bpp) + sub W, W, #(\lowbit * 8 / dst_w_bpp) +51: +.endif +.endr +.endif + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + + \process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple + \process_pixblock_tail + + pixinterleave dst_w_bpp, dst_w_basereg + +.irp lowbit, 1, 2, 4, 8, 16 +.if (dst_w_bpp <= (\lowbit * 8)) && ((\lowbit * 8) < (pixblock_size * dst_w_bpp)) +.if \lowbit < 16 /* we don't need more than 16-byte alignment */ + tst DST_W, #\lowbit + beq 51f +.endif +.if src_bpp == 0 && mask_bpp == 0 && dst_r_bpp == 0 + sub W, W, #(\lowbit * 8 / dst_w_bpp) +.endif + pixst_a (\lowbit * 8 / dst_w_bpp), dst_w_bpp, dst_w_basereg, DST_W +51: +.endif +.endr +.endif +52: +.endm + +/* + * Special code for processing up to (pixblock_size - 1) remaining + * trailing pixels. As SIMD processing performs operation on + * pixblock_size pixels, anything smaller than this has to be loaded + * and stored in a special way. Loading and storing of pixel data is + * performed in such a way that we fill some 'slots' in the NEON + * registers (some slots naturally are unused), then perform compositing + * operation as usual. In the end, the data is taken from these 'slots' + * and saved to memory. + * + * cache_preload_flag - allows to suppress prefetch if + * set to 0 + * dst_aligned_flag - selects whether destination buffer + * is aligned + */ +.macro process_trailing_pixels cache_preload_flag, \ + dst_aligned_flag, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + tst W, #(pixblock_size - 1) + beq 52f +.if src_bpp > 0 || mask_bpp > 0 || dst_r_bpp > 0 +.irp chunk_size, 16, 8, 4, 2, 1 +.if pixblock_size > \chunk_size + tst W, #\chunk_size + beq 51f + pixld_src \chunk_size, src_bpp, src_basereg, SRC + pixld \chunk_size, mask_bpp, mask_basereg, MASK +.if \dst_aligned_flag != 0 + pixld_a \chunk_size, dst_r_bpp, dst_r_basereg, DST_R +.else + pixld \chunk_size, dst_r_bpp, dst_r_basereg, DST_R +.endif +.if \cache_preload_flag != 0 + PF add, PF_X, PF_X, #\chunk_size +.endif +51: +.endif +.endr +.endif + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + + \process_pixblock_head +.if \cache_preload_flag != 0 + cache_preload 0, pixblock_size + cache_preload_simple +.endif + \process_pixblock_tail + pixinterleave dst_w_bpp, dst_w_basereg +.irp chunk_size, 16, 8, 4, 2, 1 +.if pixblock_size > \chunk_size + tst W, #\chunk_size + beq 51f +.if \dst_aligned_flag != 0 + pixst_a \chunk_size, dst_w_bpp, dst_w_basereg, DST_W +.else + pixst \chunk_size, dst_w_bpp, dst_w_basereg, DST_W +.endif +51: +.endif +.endr +52: +.endm + +/* + * Macro, which performs all the needed operations to switch to the next + * scanline and start the next loop iteration unless all the scanlines + * are already processed. + */ +.macro advance_to_next_scanline start_of_loop_label + mov W, ORIG_W + add DST_W, DST_W, DST_STRIDE, lsl #dst_bpp_shift +.if src_bpp != 0 + add SRC, SRC, SRC_STRIDE, lsl #src_bpp_shift +.endif +.if mask_bpp != 0 + add MASK, MASK, MASK_STRIDE, lsl #mask_bpp_shift +.endif +.if (dst_w_bpp != 24) + sub DST_W, DST_W, W, lsl #dst_bpp_shift +.endif +.if (src_bpp != 24) && (src_bpp != 0) + sub SRC, SRC, W, lsl #src_bpp_shift +.endif +.if (mask_bpp != 24) && (mask_bpp != 0) + sub MASK, MASK, W, lsl #mask_bpp_shift +.endif + subs H, H, #1 + mov DST_R, DST_W + bge \start_of_loop_label +.endm + +/* + * Registers are allocated in the following way by default: + * v0, v1, v2, v3 - reserved for loading source pixel data + * v4, v5, v6, v7 - reserved for loading destination pixel data + * v24, v25, v26, v27 - reserved for loading mask pixel data + * v28, v29, v30, v31 - final destination pixel data for writeback to memory + */ +.macro generate_composite_function fname, \ + src_bpp_, \ + mask_bpp_, \ + dst_w_bpp_, \ + flags, \ + pixblock_size_, \ + prefetch_distance, \ + init, \ + cleanup, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + + pixman_asm_function \fname + stp x29, x30, [sp, -16]! + mov x29, sp + sub sp, sp, 232 /* push all registers */ + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], #32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], #32 + stp x8, x9, [x29, -80] + stp x10, x11, [x29, -96] + stp x12, x13, [x29, -112] + stp x14, x15, [x29, -128] + stp x16, x17, [x29, -144] + stp x18, x19, [x29, -160] + stp x20, x21, [x29, -176] + stp x22, x23, [x29, -192] + stp x24, x25, [x29, -208] + stp x26, x27, [x29, -224] + str x28, [x29, -232] + +/* + * Select prefetch type for this function. If prefetch distance is + * set to 0 or one of the color formats is 24bpp, SIMPLE prefetch + * has to be used instead of ADVANCED. + */ + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_DEFAULT +.if \prefetch_distance == 0 + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE +.elseif (PREFETCH_TYPE_CURRENT > PREFETCH_TYPE_SIMPLE) && \ + ((\src_bpp_ == 24) || (\mask_bpp_ == 24) || (\dst_w_bpp_ == 24)) + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_SIMPLE +.endif + +/* + * Make some macro arguments globally visible and accessible + * from other macros + */ + .set src_bpp, \src_bpp_ + .set mask_bpp, \mask_bpp_ + .set dst_w_bpp, \dst_w_bpp_ + .set pixblock_size, \pixblock_size_ + .set dst_w_basereg, \dst_w_basereg_ + .set dst_r_basereg, \dst_r_basereg_ + .set src_basereg, \src_basereg_ + .set mask_basereg, \mask_basereg_ + + .macro pixld_src x:vararg + pixld \x + .endm + .macro fetch_src_pixblock + pixld_src pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + .endm +/* + * Assign symbolic names to registers + */ + W .req x0 /* width (is updated during processing) */ + H .req x1 /* height (is updated during processing) */ + DST_W .req x2 /* destination buffer pointer for writes */ + DST_STRIDE .req x3 /* destination image stride */ + SRC .req x4 /* source buffer pointer */ + SRC_STRIDE .req x5 /* source image stride */ + MASK .req x6 /* mask pointer */ + MASK_STRIDE .req x7 /* mask stride */ + + DST_R .req x8 /* destination buffer pointer for reads */ + + PF_CTL .req x9 /* combined lines counter and prefetch */ + /* distance increment counter */ + PF_X .req x10 /* pixel index in a scanline for current */ + /* pretetch position */ + PF_SRC .req x11 /* pointer to source scanline start */ + /* for prefetch purposes */ + PF_DST .req x12 /* pointer to destination scanline start */ + /* for prefetch purposes */ + PF_MASK .req x13 /* pointer to mask scanline start */ + /* for prefetch purposes */ + + ORIG_W .req x14 /* saved original width */ + DUMMY .req x15 /* temporary register */ + + sxtw x0, w0 + sxtw x1, w1 + sxtw x3, w3 + sxtw x5, w5 + sxtw x7, w7 + + .set mask_bpp_shift, -1 +.if src_bpp == 32 + .set src_bpp_shift, 2 +.elseif src_bpp == 24 + .set src_bpp_shift, 0 +.elseif src_bpp == 16 + .set src_bpp_shift, 1 +.elseif src_bpp == 8 + .set src_bpp_shift, 0 +.elseif src_bpp == 0 + .set src_bpp_shift, -1 +.else + .error "requested src bpp (src_bpp) is not supported" +.endif +.if mask_bpp == 32 + .set mask_bpp_shift, 2 +.elseif mask_bpp == 24 + .set mask_bpp_shift, 0 +.elseif mask_bpp == 8 + .set mask_bpp_shift, 0 +.elseif mask_bpp == 0 + .set mask_bpp_shift, -1 +.else + .error "requested mask bpp (mask_bpp) is not supported" +.endif +.if dst_w_bpp == 32 + .set dst_bpp_shift, 2 +.elseif dst_w_bpp == 24 + .set dst_bpp_shift, 0 +.elseif dst_w_bpp == 16 + .set dst_bpp_shift, 1 +.elseif dst_w_bpp == 8 + .set dst_bpp_shift, 0 +.else + .error "requested dst bpp (dst_w_bpp) is not supported" +.endif + +.if (((\flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp +.else + .set dst_r_bpp, 0 +.endif +.if (((\flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 +.else + .set DEINTERLEAVE_32BPP_ENABLED, 0 +.endif + +.if \prefetch_distance < 0 || \prefetch_distance > 15 + .error "invalid prefetch distance (\prefetch_distance)" +.endif + + PF mov, PF_X, #0 + mov DST_R, DST_W + +.if src_bpp == 24 + sub SRC_STRIDE, SRC_STRIDE, W + sub SRC_STRIDE, SRC_STRIDE, W, lsl #1 +.endif +.if mask_bpp == 24 + sub MASK_STRIDE, MASK_STRIDE, W + sub MASK_STRIDE, MASK_STRIDE, W, lsl #1 +.endif +.if dst_w_bpp == 24 + sub DST_STRIDE, DST_STRIDE, W + sub DST_STRIDE, DST_STRIDE, W, lsl #1 +.endif + +/* + * Setup advanced prefetcher initial state + */ + PF mov, PF_SRC, SRC + PF mov, PF_DST, DST_R + PF mov, PF_MASK, MASK + /* PF_CTL = \prefetch_distance | ((h - 1) << 4) */ + PF lsl, DUMMY, H, #4 + PF mov, PF_CTL, DUMMY + PF add, PF_CTL, PF_CTL, #(\prefetch_distance - 0x10) + + \init + subs H, H, #1 + mov ORIG_W, W + blt 9f + cmp W, #(pixblock_size * 2) + blt 800f +/* + * This is the start of the pipelined loop, which if optimized for + * long scanlines + */ +0: + ensure_destination_ptr_alignment \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + PF add, PF_X, PF_X, #pixblock_size + \process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple + subs W, W, #(pixblock_size * 2) + blt 200f + +100: + \process_pixblock_tail_head + cache_preload_simple + subs W, W, #pixblock_size + bge 100b + +200: + \process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 1, 1, \ + \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + advance_to_next_scanline 0b + + \cleanup +1000: + /* pop all registers */ + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x8, x9, [x29, -80] + ldp x10, x11, [x29, -96] + ldp x12, x13, [x29, -112] + ldp x14, x15, [x29, -128] + ldp x16, x17, [x29, -144] + ldp x18, x19, [x29, -160] + ldp x20, x21, [x29, -176] + ldp x22, x23, [x29, -192] + ldp x24, x25, [x29, -208] + ldp x26, x27, [x29, -224] + ldr x28, [x29, -232] + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ +/* + * This is the start of the loop, designed to process images with small width + * (less than pixblock_size * 2 pixels). In this case neither pipelining + * nor prefetch are used. + */ +800: +.if src_bpp_shift >= 0 + PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift + PF prfm, PREFETCH_MODE, [SRC, DUMMY] +.endif +.if dst_r_bpp != 0 + PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift + PF prfm, PREFETCH_MODE, [DST_R, DUMMY] +.endif +.if mask_bpp_shift >= 0 + PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift + PF prfm, PREFETCH_MODE, [MASK, DUMMY] +.endif + /* Process exactly pixblock_size pixels if needed */ + tst W, #pixblock_size + beq 100f + pixld pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + \process_pixblock_head + \process_pixblock_tail + pixst pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W +100: + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 0, 0, \ + \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + advance_to_next_scanline 800b +9: + \cleanup + /* pop all registers */ + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x8, x9, [x29, -80] + ldp x10, x11, [x29, -96] + ldp x12, x13, [x29, -112] + ldp x14, x15, [x29, -128] + ldp x16, x17, [x29, -144] + ldp x18, x19, [x29, -160] + ldp x20, x21, [x29, -176] + ldp x22, x23, [x29, -192] + ldp x24, x25, [x29, -208] + ldp x26, x27, [x29, -224] + ldr x28, [x29, -232] + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ + + .purgem fetch_src_pixblock + .purgem pixld_src + + .unreq SRC + .unreq MASK + .unreq DST_R + .unreq DST_W + .unreq ORIG_W + .unreq W + .unreq H + .unreq SRC_STRIDE + .unreq DST_STRIDE + .unreq MASK_STRIDE + .unreq PF_CTL + .unreq PF_X + .unreq PF_SRC + .unreq PF_DST + .unreq PF_MASK + .unreq DUMMY + pixman_end_asm_function +.endm + +/* + * A simplified variant of function generation template for a single + * scanline processing (for implementing pixman combine functions) + */ +.macro generate_composite_function_scanline use_nearest_scaling, \ + fname, \ + src_bpp_, \ + mask_bpp_, \ + dst_w_bpp_, \ + flags, \ + pixblock_size_, \ + init, \ + cleanup, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + + pixman_asm_function \fname + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE + +/* + * Make some macro arguments globally visible and accessible + * from other macros + */ + .set src_bpp, \src_bpp_ + .set mask_bpp, \mask_bpp_ + .set dst_w_bpp, \dst_w_bpp_ + .set pixblock_size, \pixblock_size_ + .set dst_w_basereg, \dst_w_basereg_ + .set dst_r_basereg, \dst_r_basereg_ + .set src_basereg, \src_basereg_ + .set mask_basereg, \mask_basereg_ + +.if \use_nearest_scaling != 0 + /* + * Assign symbolic names to registers for nearest scaling + */ + W .req x0 + DST_W .req x1 + SRC .req x2 + VX .req x3 + UNIT_X .req x4 + SRC_WIDTH_FIXED .req x5 + MASK .req x6 + TMP1 .req x8 + TMP2 .req x9 + DST_R .req x10 + DUMMY .req x30 + + .macro pixld_src x:vararg + pixld_s \x + .endm + + sxtw x0, w0 + sxtw x3, w3 + sxtw x4, w4 + sxtw x5, w5 + + stp x29, x30, [sp, -16]! + mov x29, sp + sub sp, sp, 88 + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + stp x8, x9, [x29, -80] + str x10, [x29, -88] +.else + /* + * Assign symbolic names to registers + */ + W .req x0 /* width (is updated during processing) */ + DST_W .req x1 /* destination buffer pointer for writes */ + SRC .req x2 /* source buffer pointer */ + MASK .req x3 /* mask pointer */ + DST_R .req x4 /* destination buffer pointer for reads */ + DUMMY .req x30 + + .macro pixld_src x:vararg + pixld \x + .endm + + sxtw x0, w0 + + stp x29, x30, [sp, -16]! + mov x29, sp + sub sp, sp, 64 + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 +.endif + +.if (((\flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp +.else + .set dst_r_bpp, 0 +.endif +.if (((\flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 +.else + .set DEINTERLEAVE_32BPP_ENABLED, 0 +.endif + + .macro fetch_src_pixblock + pixld_src pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + .endm + + \init + mov DST_R, DST_W + + cmp W, #pixblock_size + blt 800f + + ensure_destination_ptr_alignment \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + + subs W, W, #pixblock_size + blt 700f + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK + \process_pixblock_head + subs W, W, #pixblock_size + blt 200f +100: + \process_pixblock_tail_head + subs W, W, #pixblock_size + bge 100b +200: + \process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W +700: + /* Process the remaining trailing pixels in the scanline (dst aligned) */ + process_trailing_pixels 0, 1, \ + \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + + \cleanup +.if \use_nearest_scaling != 0 + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x8, x9, [x29, -80] + ldr x10, [x29, -96] + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ +.else + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ +.endif +800: + /* Process the remaining trailing pixels in the scanline (dst unaligned) */ + process_trailing_pixels 0, 0, \ + \process_pixblock_head, \ + \process_pixblock_tail, \ + \process_pixblock_tail_head + + \cleanup +.if \use_nearest_scaling != 0 + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x8, x9, [x29, -80] + ldr x10, [x29, -88] + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ + + .unreq DUMMY + .unreq DST_R + .unreq SRC + .unreq W + .unreq VX + .unreq UNIT_X + .unreq TMP1 + .unreq TMP2 + .unreq DST_W + .unreq MASK + .unreq SRC_WIDTH_FIXED + +.else + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ + + .unreq DUMMY + .unreq SRC + .unreq MASK + .unreq DST_R + .unreq DST_W + .unreq W +.endif + + .purgem fetch_src_pixblock + .purgem pixld_src + + pixman_end_asm_function +.endm + +.macro generate_composite_function_single_scanline x:vararg + generate_composite_function_scanline 0, \x +.endm + +.macro generate_composite_function_nearest_scanline x:vararg + generate_composite_function_scanline 1, \x +.endm + +/* Default prologue/epilogue, nothing special needs to be done */ + +.macro default_init +.endm + +.macro default_cleanup +.endm + +/* + * Prologue/epilogue variant which additionally saves/restores v8-v15 + * registers (they need to be saved/restored by callee according to ABI). + * This is required if the code needs to use all the NEON registers. + */ + +.macro default_init_need_all_regs +.endm + +.macro default_cleanup_need_all_regs +.endm + +/******************************************************************************/ + +/* + * Conversion of 8 r5g6b6 pixels packed in 128-bit register (in) + * into a planar a8r8g8b8 format (with a, r, g, b color components + * stored into 64-bit registers out_a, out_r, out_g, out_b respectively). + * + * Warning: the conversion is destructive and the original + * value (in) is lost. + */ +.macro convert_0565_to_8888 in, out_a, out_r, out_g, out_b + shrn \()\out_r\().8b, \()\in\().8h, #8 + shrn \()\out_g\().8b, \()\in\().8h, #3 + sli \()\in\().8h, \()\in\().8h, #5 + movi \()\out_a\().8b, #255 + sri \()\out_r\().8b, \()\out_r\().8b, #5 + sri \()\out_g\().8b, \()\out_g\().8b, #6 + shrn \()\out_b\().8b, \()\in\().8h, #2 +.endm + +.macro convert_0565_to_x888 in, out_r, out_g, out_b + shrn \()\out_r\().8b, \()\in\().8h, #8 + shrn \()\out_g\().8b, \()\in\().8h, #3 + sli \()\in\().8h, \()\in\().8h, #5 + sri \()\out_r\().8b, \()\out_r\().8b, #5 + sri \()\out_g\().8b, \()\out_g\().8b, #6 + shrn \()\out_b\().8b, \()\in\().8h, #2 +.endm + +/* + * Conversion from planar a8r8g8b8 format (with a, r, g, b color components + * in 64-bit registers in_a, in_r, in_g, in_b respectively) into 8 r5g6b6 + * pixels packed in 128-bit register (out). Requires two temporary 128-bit + * registers (tmp1, tmp2) + */ +.macro convert_8888_to_0565 in_r, in_g, in_b, out, tmp1, tmp2 + ushll \()\tmp1\().8h, \()\in_g\().8b, #7 + shl \()\tmp1\().8h, \()\tmp1\().8h, #1 + ushll \()\out\().8h, \()\in_r\().8b, #7 + shl \()\out\().8h, \()\out\().8h, #1 + ushll \()\tmp2\().8h, \()\in_b\().8b, #7 + shl \()\tmp2\().8h, \()\tmp2\().8h, #1 + sri \()\out\().8h, \()\tmp1\().8h, #5 + sri \()\out\().8h, \()\tmp2\().8h, #11 +.endm + +/* + * Conversion of four r5g6b5 pixels (in) to four x8r8g8b8 pixels + * returned in (out0, out1) registers pair. Requires one temporary + * 64-bit register (tmp). 'out1' and 'in' may overlap, the original + * value from 'in' is lost + */ +.macro convert_four_0565_to_x888_packed in, out0, out1, tmp + shl \()\out0\().4h, \()\in\().4h, #5 /* G top 6 bits */ + shl \()\tmp\().4h, \()\in\().4h, #11 /* B top 5 bits */ + sri \()\in\().4h, \()\in\().4h, #5 /* R is ready \in top bits */ + sri \()\out0\().4h, \()\out0\().4h, #6 /* G is ready \in top bits */ + sri \()\tmp\().4h, \()\tmp\().4h, #5 /* B is ready \in top bits */ + ushr \()\out1\().4h, \()\in\().4h, #8 /* R is \in place */ + sri \()\out0\().4h, \()\tmp\().4h, #8 /* G \() B is \in place */ + zip1 \()\tmp\().4h, \()\out0\().4h, \()\out1\().4h /* everything is \in place */ + zip2 \()\out1\().4h, \()\out0\().4h, \()\out1\().4h + mov \()\out0\().d[0], \()\tmp\().d[0] +.endm diff --git a/gfx/cairo/libpixman/src/pixman-bits-image.c b/gfx/cairo/libpixman/src/pixman-bits-image.c new file mode 100644 index 0000000000..f050f35316 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-bits-image.c @@ -0,0 +1,1383 @@ +/* + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. + * 2005 Lars Knoll & Zack Rusin, Trolltech + * 2008 Aaron Plattner, NVIDIA Corporation + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007, 2009 Red Hat, Inc. + * Copyright © 2008 André Tupinambá + * + * 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. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include "pixman-private.h" +#include "pixman-combine32.h" +#include "pixman-inlines.h" +#include "dither/blue-noise-64x64.h" + +/* Fetch functions */ + +static force_inline void +fetch_pixel_no_alpha_32 (bits_image_t *image, + int x, int y, pixman_bool_t check_bounds, + void *out) +{ + uint32_t *ret = out; + + if (check_bounds && + (x < 0 || x >= image->width || y < 0 || y >= image->height)) + *ret = 0; + else + *ret = image->fetch_pixel_32 (image, x, y); +} + +static force_inline void +fetch_pixel_no_alpha_float (bits_image_t *image, + int x, int y, pixman_bool_t check_bounds, + void *out) +{ + argb_t *ret = out; + + if (check_bounds && + (x < 0 || x >= image->width || y < 0 || y >= image->height)) + ret->a = ret->r = ret->g = ret->b = 0.f; + else + *ret = image->fetch_pixel_float (image, x, y); +} + +typedef void (* get_pixel_t) (bits_image_t *image, + int x, int y, pixman_bool_t check_bounds, void *out); + +static force_inline void +bits_image_fetch_pixel_nearest (bits_image_t *image, + pixman_fixed_t x, + pixman_fixed_t y, + get_pixel_t get_pixel, + void *out) +{ + int x0 = pixman_fixed_to_int (x - pixman_fixed_e); + int y0 = pixman_fixed_to_int (y - pixman_fixed_e); + + if (image->common.repeat != PIXMAN_REPEAT_NONE) + { + repeat (image->common.repeat, &x0, image->width); + repeat (image->common.repeat, &y0, image->height); + + get_pixel (image, x0, y0, FALSE, out); + } + else + { + get_pixel (image, x0, y0, TRUE, out); + } +} + +static force_inline void +bits_image_fetch_pixel_bilinear_32 (bits_image_t *image, + pixman_fixed_t x, + pixman_fixed_t y, + get_pixel_t get_pixel, + void *out) +{ + pixman_repeat_t repeat_mode = image->common.repeat; + int width = image->width; + int height = image->height; + int x1, y1, x2, y2; + uint32_t tl, tr, bl, br; + int32_t distx, disty; + uint32_t *ret = out; + + x1 = x - pixman_fixed_1 / 2; + y1 = y - pixman_fixed_1 / 2; + + distx = pixman_fixed_to_bilinear_weight (x1); + disty = pixman_fixed_to_bilinear_weight (y1); + + x1 = pixman_fixed_to_int (x1); + y1 = pixman_fixed_to_int (y1); + x2 = x1 + 1; + y2 = y1 + 1; + + if (repeat_mode != PIXMAN_REPEAT_NONE) + { + repeat (repeat_mode, &x1, width); + repeat (repeat_mode, &y1, height); + repeat (repeat_mode, &x2, width); + repeat (repeat_mode, &y2, height); + + get_pixel (image, x1, y1, FALSE, &tl); + get_pixel (image, x2, y1, FALSE, &tr); + get_pixel (image, x1, y2, FALSE, &bl); + get_pixel (image, x2, y2, FALSE, &br); + } + else + { + get_pixel (image, x1, y1, TRUE, &tl); + get_pixel (image, x2, y1, TRUE, &tr); + get_pixel (image, x1, y2, TRUE, &bl); + get_pixel (image, x2, y2, TRUE, &br); + } + + *ret = bilinear_interpolation (tl, tr, bl, br, distx, disty); +} + +static force_inline void +bits_image_fetch_pixel_bilinear_float (bits_image_t *image, + pixman_fixed_t x, + pixman_fixed_t y, + get_pixel_t get_pixel, + void *out) +{ + pixman_repeat_t repeat_mode = image->common.repeat; + int width = image->width; + int height = image->height; + int x1, y1, x2, y2; + argb_t tl, tr, bl, br; + float distx, disty; + argb_t *ret = out; + + x1 = x - pixman_fixed_1 / 2; + y1 = y - pixman_fixed_1 / 2; + + distx = ((float)pixman_fixed_fraction(x1)) / 65536.f; + disty = ((float)pixman_fixed_fraction(y1)) / 65536.f; + + x1 = pixman_fixed_to_int (x1); + y1 = pixman_fixed_to_int (y1); + x2 = x1 + 1; + y2 = y1 + 1; + + if (repeat_mode != PIXMAN_REPEAT_NONE) + { + repeat (repeat_mode, &x1, width); + repeat (repeat_mode, &y1, height); + repeat (repeat_mode, &x2, width); + repeat (repeat_mode, &y2, height); + + get_pixel (image, x1, y1, FALSE, &tl); + get_pixel (image, x2, y1, FALSE, &tr); + get_pixel (image, x1, y2, FALSE, &bl); + get_pixel (image, x2, y2, FALSE, &br); + } + else + { + get_pixel (image, x1, y1, TRUE, &tl); + get_pixel (image, x2, y1, TRUE, &tr); + get_pixel (image, x1, y2, TRUE, &bl); + get_pixel (image, x2, y2, TRUE, &br); + } + + *ret = bilinear_interpolation_float (tl, tr, bl, br, distx, disty); +} + +static force_inline void accum_32(unsigned int *satot, unsigned int *srtot, + unsigned int *sgtot, unsigned int *sbtot, + const void *p, pixman_fixed_t f) +{ + uint32_t pixel = *(uint32_t *)p; + + *srtot += (int)RED_8 (pixel) * f; + *sgtot += (int)GREEN_8 (pixel) * f; + *sbtot += (int)BLUE_8 (pixel) * f; + *satot += (int)ALPHA_8 (pixel) * f; +} + +static force_inline void reduce_32(unsigned int satot, unsigned int srtot, + unsigned int sgtot, unsigned int sbtot, + void *p) +{ + uint32_t *ret = p; + + satot = (satot + 0x8000) >> 16; + srtot = (srtot + 0x8000) >> 16; + sgtot = (sgtot + 0x8000) >> 16; + sbtot = (sbtot + 0x8000) >> 16; + + satot = CLIP (satot, 0, 0xff); + srtot = CLIP (srtot, 0, 0xff); + sgtot = CLIP (sgtot, 0, 0xff); + sbtot = CLIP (sbtot, 0, 0xff); + + *ret = ((satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot)); +} + +static force_inline void accum_float(unsigned int *satot, unsigned int *srtot, + unsigned int *sgtot, unsigned int *sbtot, + const void *p, pixman_fixed_t f) +{ + const argb_t *pixel = p; + + *satot += pixel->a * f; + *srtot += pixel->r * f; + *sgtot += pixel->g * f; + *sbtot += pixel->b * f; +} + +static force_inline void reduce_float(unsigned int satot, unsigned int srtot, + unsigned int sgtot, unsigned int sbtot, + void *p) +{ + argb_t *ret = p; + + ret->a = CLIP (satot / 65536.f, 0.f, 1.f); + ret->r = CLIP (srtot / 65536.f, 0.f, 1.f); + ret->g = CLIP (sgtot / 65536.f, 0.f, 1.f); + ret->b = CLIP (sbtot / 65536.f, 0.f, 1.f); +} + +typedef void (* accumulate_pixel_t) (unsigned int *satot, unsigned int *srtot, + unsigned int *sgtot, unsigned int *sbtot, + const void *pixel, pixman_fixed_t f); + +typedef void (* reduce_pixel_t) (unsigned int satot, unsigned int srtot, + unsigned int sgtot, unsigned int sbtot, + void *out); + +static force_inline void +bits_image_fetch_pixel_convolution (bits_image_t *image, + pixman_fixed_t x, + pixman_fixed_t y, + get_pixel_t get_pixel, + void *out, + accumulate_pixel_t accum, + reduce_pixel_t reduce) +{ + pixman_fixed_t *params = image->common.filter_params; + int x_off = (params[0] - pixman_fixed_1) >> 1; + int y_off = (params[1] - pixman_fixed_1) >> 1; + int32_t cwidth = pixman_fixed_to_int (params[0]); + int32_t cheight = pixman_fixed_to_int (params[1]); + int32_t i, j, x1, x2, y1, y2; + pixman_repeat_t repeat_mode = image->common.repeat; + int width = image->width; + int height = image->height; + unsigned int srtot, sgtot, sbtot, satot; + + params += 2; + + x1 = pixman_fixed_to_int (x - pixman_fixed_e - x_off); + y1 = pixman_fixed_to_int (y - pixman_fixed_e - y_off); + x2 = x1 + cwidth; + y2 = y1 + cheight; + + srtot = sgtot = sbtot = satot = 0; + + for (i = y1; i < y2; ++i) + { + for (j = x1; j < x2; ++j) + { + int rx = j; + int ry = i; + + pixman_fixed_t f = *params; + + if (f) + { + /* Must be big enough to hold a argb_t */ + argb_t pixel; + + if (repeat_mode != PIXMAN_REPEAT_NONE) + { + repeat (repeat_mode, &rx, width); + repeat (repeat_mode, &ry, height); + + get_pixel (image, rx, ry, FALSE, &pixel); + } + else + { + get_pixel (image, rx, ry, TRUE, &pixel); + } + + accum (&satot, &srtot, &sgtot, &sbtot, &pixel, f); + } + + params++; + } + } + + reduce (satot, srtot, sgtot, sbtot, out); +} + +static void +bits_image_fetch_pixel_separable_convolution (bits_image_t *image, + pixman_fixed_t x, + pixman_fixed_t y, + get_pixel_t get_pixel, + void *out, + accumulate_pixel_t accum, + reduce_pixel_t reduce) +{ + pixman_fixed_t *params = image->common.filter_params; + pixman_repeat_t repeat_mode = image->common.repeat; + int width = image->width; + int height = image->height; + int cwidth = pixman_fixed_to_int (params[0]); + int cheight = pixman_fixed_to_int (params[1]); + int x_phase_bits = pixman_fixed_to_int (params[2]); + int y_phase_bits = pixman_fixed_to_int (params[3]); + int x_phase_shift = 16 - x_phase_bits; + int y_phase_shift = 16 - y_phase_bits; + int x_off = ((cwidth << 16) - pixman_fixed_1) >> 1; + int y_off = ((cheight << 16) - pixman_fixed_1) >> 1; + pixman_fixed_t *y_params; + unsigned int srtot, sgtot, sbtot, satot; + int32_t x1, x2, y1, y2; + int32_t px, py; + int i, j; + + /* Round x and y to the middle of the closest phase before continuing. This + * ensures that the convolution matrix is aligned right, since it was + * positioned relative to a particular phase (and not relative to whatever + * exact fraction we happen to get here). + */ + x = ((x >> x_phase_shift) << x_phase_shift) + ((1 << x_phase_shift) >> 1); + y = ((y >> y_phase_shift) << y_phase_shift) + ((1 << y_phase_shift) >> 1); + + px = (x & 0xffff) >> x_phase_shift; + py = (y & 0xffff) >> y_phase_shift; + + y_params = params + 4 + (1 << x_phase_bits) * cwidth + py * cheight; + + x1 = pixman_fixed_to_int (x - pixman_fixed_e - x_off); + y1 = pixman_fixed_to_int (y - pixman_fixed_e - y_off); + x2 = x1 + cwidth; + y2 = y1 + cheight; + + srtot = sgtot = sbtot = satot = 0; + + for (i = y1; i < y2; ++i) + { + pixman_fixed_48_16_t fy = *y_params++; + pixman_fixed_t *x_params = params + 4 + px * cwidth; + + if (fy) + { + for (j = x1; j < x2; ++j) + { + pixman_fixed_t fx = *x_params++; + int rx = j; + int ry = i; + + if (fx) + { + /* Must be big enough to hold a argb_t */ + argb_t pixel; + pixman_fixed_t f; + + if (repeat_mode != PIXMAN_REPEAT_NONE) + { + repeat (repeat_mode, &rx, width); + repeat (repeat_mode, &ry, height); + + get_pixel (image, rx, ry, FALSE, &pixel); + } + else + { + get_pixel (image, rx, ry, TRUE, &pixel); + } + + f = (fy * fx + 0x8000) >> 16; + + accum(&satot, &srtot, &sgtot, &sbtot, &pixel, f); + } + } + } + } + + + reduce(satot, srtot, sgtot, sbtot, out); +} + +static force_inline void +bits_image_fetch_pixel_filtered (bits_image_t *image, + pixman_bool_t wide, + pixman_fixed_t x, + pixman_fixed_t y, + get_pixel_t get_pixel, + void *out) +{ + switch (image->common.filter) + { + case PIXMAN_FILTER_NEAREST: + case PIXMAN_FILTER_FAST: + bits_image_fetch_pixel_nearest (image, x, y, get_pixel, out); + break; + + case PIXMAN_FILTER_BILINEAR: + case PIXMAN_FILTER_GOOD: + case PIXMAN_FILTER_BEST: + if (wide) + bits_image_fetch_pixel_bilinear_float (image, x, y, get_pixel, out); + else + bits_image_fetch_pixel_bilinear_32 (image, x, y, get_pixel, out); + break; + + case PIXMAN_FILTER_CONVOLUTION: + if (wide) + { + bits_image_fetch_pixel_convolution (image, x, y, + get_pixel, out, + accum_float, + reduce_float); + } + else + { + bits_image_fetch_pixel_convolution (image, x, y, + get_pixel, out, + accum_32, reduce_32); + } + break; + + case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: + if (wide) + { + bits_image_fetch_pixel_separable_convolution (image, x, y, + get_pixel, out, + accum_float, + reduce_float); + } + else + { + bits_image_fetch_pixel_separable_convolution (image, x, y, + get_pixel, out, + accum_32, reduce_32); + } + break; + + default: + assert (0); + break; + } +} + +static uint32_t * +__bits_image_fetch_affine_no_alpha (pixman_iter_t * iter, + pixman_bool_t wide, + const uint32_t * mask) +{ + pixman_image_t *image = iter->image; + int offset = iter->x; + int line = iter->y++; + int width = iter->width; + uint32_t * buffer = iter->buffer; + + const uint32_t wide_zero[4] = {0}; + pixman_fixed_t x, y; + pixman_fixed_t ux, uy; + pixman_vector_t v; + int i; + get_pixel_t get_pixel = + wide ? fetch_pixel_no_alpha_float : fetch_pixel_no_alpha_32; + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (image->common.transform) + { + if (!pixman_transform_point_3d (image->common.transform, &v)) + return iter->buffer; + + ux = image->common.transform->matrix[0][0]; + uy = image->common.transform->matrix[1][0]; + } + else + { + ux = pixman_fixed_1; + uy = 0; + } + + x = v.vector[0]; + y = v.vector[1]; + + for (i = 0; i < width; ++i) + { + if (!mask || (!wide && mask[i]) || + (wide && memcmp(&mask[4 * i], wide_zero, 16) != 0)) + { + bits_image_fetch_pixel_filtered ( + &image->bits, wide, x, y, get_pixel, buffer); + } + + x += ux; + y += uy; + buffer += wide ? 4 : 1; + } + + return iter->buffer; +} + +static uint32_t * +bits_image_fetch_affine_no_alpha_32 (pixman_iter_t *iter, + const uint32_t *mask) +{ + return __bits_image_fetch_affine_no_alpha(iter, FALSE, mask); +} + +static uint32_t * +bits_image_fetch_affine_no_alpha_float (pixman_iter_t *iter, + const uint32_t *mask) +{ + return __bits_image_fetch_affine_no_alpha(iter, TRUE, mask); +} + +/* General fetcher */ +static force_inline void +fetch_pixel_general_32 (bits_image_t *image, + int x, int y, pixman_bool_t check_bounds, + void *out) +{ + uint32_t pixel, *ret = out; + + if (check_bounds && + (x < 0 || x >= image->width || y < 0 || y >= image->height)) + { + *ret = 0; + return; + } + + pixel = image->fetch_pixel_32 (image, x, y); + + if (image->common.alpha_map) + { + uint32_t pixel_a; + + x -= image->common.alpha_origin_x; + y -= image->common.alpha_origin_y; + + if (x < 0 || x >= image->common.alpha_map->width || + y < 0 || y >= image->common.alpha_map->height) + { + pixel_a = 0; + } + else + { + pixel_a = image->common.alpha_map->fetch_pixel_32 ( + image->common.alpha_map, x, y); + + pixel_a = ALPHA_8 (pixel_a); + } + + pixel &= 0x00ffffff; + pixel |= (pixel_a << 24); + } + + *ret = pixel; +} + +static force_inline void +fetch_pixel_general_float (bits_image_t *image, + int x, int y, pixman_bool_t check_bounds, + void *out) +{ + argb_t *ret = out; + + if (check_bounds && + (x < 0 || x >= image->width || y < 0 || y >= image->height)) + { + ret->a = ret->r = ret->g = ret->b = 0; + return; + } + + *ret = image->fetch_pixel_float (image, x, y); + + if (image->common.alpha_map) + { + x -= image->common.alpha_origin_x; + y -= image->common.alpha_origin_y; + + if (x < 0 || x >= image->common.alpha_map->width || + y < 0 || y >= image->common.alpha_map->height) + { + ret->a = 0.f; + } + else + { + argb_t alpha; + + alpha = image->common.alpha_map->fetch_pixel_float ( + image->common.alpha_map, x, y); + + ret->a = alpha.a; + } + } +} + +static uint32_t * +__bits_image_fetch_general (pixman_iter_t *iter, + pixman_bool_t wide, + const uint32_t *mask) +{ + pixman_image_t *image = iter->image; + int offset = iter->x; + int line = iter->y++; + int width = iter->width; + uint32_t * buffer = iter->buffer; + get_pixel_t get_pixel = + wide ? fetch_pixel_general_float : fetch_pixel_general_32; + + const uint32_t wide_zero[4] = {0}; + pixman_fixed_t x, y, w; + pixman_fixed_t ux, uy, uw; + pixman_vector_t v; + int i; + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (image->common.transform) + { + if (!pixman_transform_point_3d (image->common.transform, &v)) + return buffer; + + ux = image->common.transform->matrix[0][0]; + uy = image->common.transform->matrix[1][0]; + uw = image->common.transform->matrix[2][0]; + } + else + { + ux = pixman_fixed_1; + uy = 0; + uw = 0; + } + + x = v.vector[0]; + y = v.vector[1]; + w = v.vector[2]; + + for (i = 0; i < width; ++i) + { + pixman_fixed_t x0, y0; + + if (!mask || (!wide && mask[i]) || + (wide && memcmp(&mask[4 * i], wide_zero, 16) != 0)) + { + if (w != 0) + { + x0 = ((uint64_t)x << 16) / w; + y0 = ((uint64_t)y << 16) / w; + } + else + { + x0 = 0; + y0 = 0; + } + + bits_image_fetch_pixel_filtered ( + &image->bits, wide, x0, y0, get_pixel, buffer); + } + + x += ux; + y += uy; + w += uw; + buffer += wide ? 4 : 1; + } + + return iter->buffer; +} + +static uint32_t * +bits_image_fetch_general_32 (pixman_iter_t *iter, + const uint32_t *mask) +{ + return __bits_image_fetch_general(iter, FALSE, mask); +} + +static uint32_t * +bits_image_fetch_general_float (pixman_iter_t *iter, + const uint32_t *mask) +{ + return __bits_image_fetch_general(iter, TRUE, mask); +} + +static void +replicate_pixel_32 (bits_image_t * bits, + int x, + int y, + int width, + uint32_t * buffer) +{ + uint32_t color; + uint32_t *end; + + color = bits->fetch_pixel_32 (bits, x, y); + + end = buffer + width; + while (buffer < end) + *(buffer++) = color; +} + +static void +replicate_pixel_float (bits_image_t * bits, + int x, + int y, + int width, + uint32_t * b) +{ + argb_t color; + argb_t *buffer = (argb_t *)b; + argb_t *end; + + color = bits->fetch_pixel_float (bits, x, y); + + end = buffer + width; + while (buffer < end) + *(buffer++) = color; +} + +static void +bits_image_fetch_untransformed_repeat_none (bits_image_t *image, + pixman_bool_t wide, + int x, + int y, + int width, + uint32_t * buffer) +{ + uint32_t w; + + if (y < 0 || y >= image->height) + { + memset (buffer, 0, width * (wide? sizeof (argb_t) : 4)); + return; + } + + if (x < 0) + { + w = MIN (width, -x); + + memset (buffer, 0, w * (wide ? sizeof (argb_t) : 4)); + + width -= w; + buffer += w * (wide? 4 : 1); + x += w; + } + + if (x < image->width) + { + w = MIN (width, image->width - x); + + if (wide) + image->fetch_scanline_float (image, x, y, w, buffer, NULL); + else + image->fetch_scanline_32 (image, x, y, w, buffer, NULL); + + width -= w; + buffer += w * (wide? 4 : 1); + x += w; + } + + memset (buffer, 0, width * (wide ? sizeof (argb_t) : 4)); +} + +static void +bits_image_fetch_untransformed_repeat_normal (bits_image_t *image, + pixman_bool_t wide, + int x, + int y, + int width, + uint32_t * buffer) +{ + uint32_t w; + + while (y < 0) + y += image->height; + + while (y >= image->height) + y -= image->height; + + if (image->width == 1) + { + if (wide) + replicate_pixel_float (image, 0, y, width, buffer); + else + replicate_pixel_32 (image, 0, y, width, buffer); + + return; + } + + while (width) + { + while (x < 0) + x += image->width; + while (x >= image->width) + x -= image->width; + + w = MIN (width, image->width - x); + + if (wide) + image->fetch_scanline_float (image, x, y, w, buffer, NULL); + else + image->fetch_scanline_32 (image, x, y, w, buffer, NULL); + + buffer += w * (wide? 4 : 1); + x += w; + width -= w; + } +} + +static uint32_t * +bits_image_fetch_untransformed_32 (pixman_iter_t * iter, + const uint32_t *mask) +{ + pixman_image_t *image = iter->image; + int x = iter->x; + int y = iter->y; + int width = iter->width; + uint32_t * buffer = iter->buffer; + + if (image->common.repeat == PIXMAN_REPEAT_NONE) + { + bits_image_fetch_untransformed_repeat_none ( + &image->bits, FALSE, x, y, width, buffer); + } + else + { + bits_image_fetch_untransformed_repeat_normal ( + &image->bits, FALSE, x, y, width, buffer); + } + + iter->y++; + return buffer; +} + +static uint32_t * +bits_image_fetch_untransformed_float (pixman_iter_t * iter, + const uint32_t *mask) +{ + pixman_image_t *image = iter->image; + int x = iter->x; + int y = iter->y; + int width = iter->width; + uint32_t * buffer = iter->buffer; + + if (image->common.repeat == PIXMAN_REPEAT_NONE) + { + bits_image_fetch_untransformed_repeat_none ( + &image->bits, TRUE, x, y, width, buffer); + } + else + { + bits_image_fetch_untransformed_repeat_normal ( + &image->bits, TRUE, x, y, width, buffer); + } + + iter->y++; + return buffer; +} + +typedef struct +{ + pixman_format_code_t format; + uint32_t flags; + pixman_iter_get_scanline_t get_scanline_32; + pixman_iter_get_scanline_t get_scanline_float; +} fetcher_info_t; + +static const fetcher_info_t fetcher_info[] = +{ + { PIXMAN_any, + (FAST_PATH_NO_ALPHA_MAP | + FAST_PATH_ID_TRANSFORM | + FAST_PATH_NO_CONVOLUTION_FILTER | + FAST_PATH_NO_PAD_REPEAT | + FAST_PATH_NO_REFLECT_REPEAT), + bits_image_fetch_untransformed_32, + bits_image_fetch_untransformed_float + }, + + /* Affine, no alpha */ + { PIXMAN_any, + (FAST_PATH_NO_ALPHA_MAP | FAST_PATH_HAS_TRANSFORM | FAST_PATH_AFFINE_TRANSFORM), + bits_image_fetch_affine_no_alpha_32, + bits_image_fetch_affine_no_alpha_float, + }, + + /* General */ + { PIXMAN_any, + 0, + bits_image_fetch_general_32, + bits_image_fetch_general_float, + }, + + { PIXMAN_null }, +}; + +static void +bits_image_property_changed (pixman_image_t *image) +{ + _pixman_bits_image_setup_accessors (&image->bits); +} + +void +_pixman_bits_image_src_iter_init (pixman_image_t *image, pixman_iter_t *iter) +{ + pixman_format_code_t format = image->common.extended_format_code; + uint32_t flags = image->common.flags; + const fetcher_info_t *info; + + for (info = fetcher_info; info->format != PIXMAN_null; ++info) + { + if ((info->format == format || info->format == PIXMAN_any) && + (info->flags & flags) == info->flags) + { + if (iter->iter_flags & ITER_NARROW) + { + iter->get_scanline = info->get_scanline_32; + } + else + { + iter->get_scanline = info->get_scanline_float; + } + return; + } + } + + /* Just in case we somehow didn't find a scanline function */ + iter->get_scanline = _pixman_iter_get_scanline_noop; +} + +static uint32_t * +dest_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) +{ + pixman_image_t *image = iter->image; + int x = iter->x; + int y = iter->y; + int width = iter->width; + uint32_t * buffer = iter->buffer; + + image->bits.fetch_scanline_32 (&image->bits, x, y, width, buffer, mask); + if (image->common.alpha_map) + { + uint32_t *alpha; + + if ((alpha = malloc (width * sizeof (uint32_t)))) + { + int i; + + x -= image->common.alpha_origin_x; + y -= image->common.alpha_origin_y; + + image->common.alpha_map->fetch_scanline_32 ( + image->common.alpha_map, x, y, width, alpha, mask); + + for (i = 0; i < width; ++i) + { + buffer[i] &= ~0xff000000; + buffer[i] |= (alpha[i] & 0xff000000); + } + + free (alpha); + } + } + + return iter->buffer; +} + +static uint32_t * +dest_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) +{ + bits_image_t * image = &iter->image->bits; + int x = iter->x; + int y = iter->y; + int width = iter->width; + argb_t * buffer = (argb_t *)iter->buffer; + + image->fetch_scanline_float ( + image, x, y, width, (uint32_t *)buffer, mask); + if (image->common.alpha_map) + { + argb_t *alpha; + + if ((alpha = malloc (width * sizeof (argb_t)))) + { + int i; + + x -= image->common.alpha_origin_x; + y -= image->common.alpha_origin_y; + + image->common.alpha_map->fetch_scanline_float ( + image->common.alpha_map, x, y, width, (uint32_t *)alpha, mask); + + for (i = 0; i < width; ++i) + buffer[i].a = alpha[i].a; + + free (alpha); + } + } + + return iter->buffer; +} + +static void +dest_write_back_narrow (pixman_iter_t *iter) +{ + bits_image_t * image = &iter->image->bits; + int x = iter->x; + int y = iter->y; + int width = iter->width; + const uint32_t *buffer = iter->buffer; + + image->store_scanline_32 (image, x, y, width, buffer); + + if (image->common.alpha_map) + { + x -= image->common.alpha_origin_x; + y -= image->common.alpha_origin_y; + + image->common.alpha_map->store_scanline_32 ( + image->common.alpha_map, x, y, width, buffer); + } + + iter->y++; +} + +static float +dither_factor_blue_noise_64 (int x, int y) +{ + float m = dither_blue_noise_64x64[((y & 0x3f) << 6) | (x & 0x3f)]; + return m * (1. / 4096.f) + (1. / 8192.f); +} + +static float +dither_factor_bayer_8 (int x, int y) +{ + uint32_t m; + + y ^= x; + + /* Compute reverse(interleave(xor(x mod n, y mod n), x mod n)) + * Here n = 8 and `mod n` is the bottom 3 bits. + */ + m = ((y & 0x1) << 5) | ((x & 0x1) << 4) | + ((y & 0x2) << 2) | ((x & 0x2) << 1) | + ((y & 0x4) >> 1) | ((x & 0x4) >> 2); + + /* m is in range [0, 63]. We scale it to [0, 63.0f/64.0f], then + * shift it to to [1.0f/128.0f, 127.0f/128.0f] so that 0 < d < 1. + * This ensures exact values are not changed by dithering. + */ + return (float)(m) * (1 / 64.0f) + (1.0f / 128.0f); +} + +typedef float (* dither_factor_t)(int x, int y); + +static force_inline float +dither_apply_channel (float f, float d, float s) +{ + /* float_to_unorm splits the [0, 1] segment in (1 << n_bits) + * subsections of equal length; however unorm_to_float does not + * map to the center of those sections. In fact, pixel value u is + * mapped to: + * + * u u u 1 + * -------------- = ---------- + -------------- * ---------- + * 2^n_bits - 1 2^n_bits 2^n_bits - 1 2^n_bits + * + * Hence if f = u / (2^n_bits - 1) is exactly representable on a + * n_bits palette, all the numbers between + * + * u + * ---------- = f - f * 2^n_bits = f + (0 - f) * 2^n_bits + * 2^n_bits + * + * and + * + * u + 1 + * ---------- = f - (f - 1) * 2^n_bits = f + (1 - f) * 2^n_bits + * 2^n_bits + * + * are also mapped back to u. + * + * Hence the following calculation ensures that we add as much + * noise as possible without perturbing values which are exactly + * representable in the target colorspace. Note that this corresponds to + * mixing the original color with noise with a ratio of `1 / 2^n_bits`. + */ + return f + (d - f) * s; +} + +static force_inline float +dither_compute_scale (int n_bits) +{ + // No dithering for wide formats + if (n_bits == 0 || n_bits >= 32) + return 0.f; + + return 1.f / (float)(1 << n_bits); +} + +static const uint32_t * +dither_apply_ordered (pixman_iter_t *iter, dither_factor_t factor) +{ + bits_image_t *image = &iter->image->bits; + int x = iter->x + image->dither_offset_x; + int y = iter->y + image->dither_offset_y; + int width = iter->width; + argb_t *buffer = (argb_t *)iter->buffer; + + pixman_format_code_t format = image->format; + int a_size = PIXMAN_FORMAT_A (format); + int r_size = PIXMAN_FORMAT_R (format); + int g_size = PIXMAN_FORMAT_G (format); + int b_size = PIXMAN_FORMAT_B (format); + + float a_scale = dither_compute_scale (a_size); + float r_scale = dither_compute_scale (r_size); + float g_scale = dither_compute_scale (g_size); + float b_scale = dither_compute_scale (b_size); + + int i; + float d; + + for (i = 0; i < width; ++i) + { + d = factor (x + i, y); + + buffer->a = dither_apply_channel (buffer->a, d, a_scale); + buffer->r = dither_apply_channel (buffer->r, d, r_scale); + buffer->g = dither_apply_channel (buffer->g, d, g_scale); + buffer->b = dither_apply_channel (buffer->b, d, b_scale); + + buffer++; + } + + return iter->buffer; +} + +static void +dest_write_back_wide (pixman_iter_t *iter) +{ + bits_image_t * image = &iter->image->bits; + int x = iter->x; + int y = iter->y; + int width = iter->width; + const uint32_t *buffer = iter->buffer; + + switch (image->dither) + { + case PIXMAN_DITHER_NONE: + break; + + case PIXMAN_DITHER_GOOD: + case PIXMAN_DITHER_BEST: + case PIXMAN_DITHER_ORDERED_BLUE_NOISE_64: + buffer = dither_apply_ordered (iter, dither_factor_blue_noise_64); + break; + + case PIXMAN_DITHER_FAST: + case PIXMAN_DITHER_ORDERED_BAYER_8: + buffer = dither_apply_ordered (iter, dither_factor_bayer_8); + break; + } + + image->store_scanline_float (image, x, y, width, buffer); + + if (image->common.alpha_map) + { + x -= image->common.alpha_origin_x; + y -= image->common.alpha_origin_y; + + image->common.alpha_map->store_scanline_float ( + image->common.alpha_map, x, y, width, buffer); + } + + iter->y++; +} + +void +_pixman_bits_image_dest_iter_init (pixman_image_t *image, pixman_iter_t *iter) +{ + if (iter->iter_flags & ITER_NARROW) + { + if ((iter->iter_flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) == + (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) + { + iter->get_scanline = _pixman_iter_get_scanline_noop; + } + else + { + iter->get_scanline = dest_get_scanline_narrow; + } + + iter->write_back = dest_write_back_narrow; + } + else + { + iter->get_scanline = dest_get_scanline_wide; + iter->write_back = dest_write_back_wide; + } +} + +static uint32_t * +create_bits (pixman_format_code_t format, + int width, + int height, + int * rowstride_bytes, + pixman_bool_t clear) +{ + int stride; + size_t buf_size; + int bpp; + + /* what follows is a long-winded way, avoiding any possibility of integer + * overflows, of saying: + * stride = ((width * bpp + 0x1f) >> 5) * sizeof (uint32_t); + */ + + bpp = PIXMAN_FORMAT_BPP (format); + if (_pixman_multiply_overflows_int (width, bpp)) + return NULL; + + stride = width * bpp; + if (_pixman_addition_overflows_int (stride, 0x1f)) + return NULL; + + stride += 0x1f; + stride >>= 5; + + stride *= sizeof (uint32_t); + + if (_pixman_multiply_overflows_size (height, stride)) + return NULL; + + buf_size = (size_t)height * stride; + + if (rowstride_bytes) + *rowstride_bytes = stride; + + if (clear) + return calloc (buf_size, 1); + else + return malloc (buf_size); +} + +pixman_bool_t +_pixman_bits_image_init (pixman_image_t * image, + pixman_format_code_t format, + int width, + int height, + uint32_t * bits, + int rowstride, + pixman_bool_t clear) +{ + uint32_t *free_me = NULL; + + if (PIXMAN_FORMAT_BPP (format) == 128) + return_val_if_fail(!(rowstride % 4), FALSE); + + if (!bits && width && height) + { + int rowstride_bytes; + + free_me = bits = create_bits (format, width, height, &rowstride_bytes, clear); + + if (!bits) + return FALSE; + + rowstride = rowstride_bytes / (int) sizeof (uint32_t); + } + + _pixman_image_init (image); + + image->type = BITS; + image->bits.format = format; + image->bits.width = width; + image->bits.height = height; + image->bits.bits = bits; + image->bits.free_me = free_me; + image->bits.dither = PIXMAN_DITHER_NONE; + image->bits.dither_offset_x = 0; + image->bits.dither_offset_y = 0; + image->bits.read_func = NULL; + image->bits.write_func = NULL; + image->bits.rowstride = rowstride; + image->bits.indexed = NULL; + + image->common.property_changed = bits_image_property_changed; + + _pixman_image_reset_clip_region (image); + + return TRUE; +} + +static pixman_image_t * +create_bits_image_internal (pixman_format_code_t format, + int width, + int height, + uint32_t * bits, + int rowstride_bytes, + pixman_bool_t clear) +{ + pixman_image_t *image; + + /* must be a whole number of uint32_t's + */ + return_val_if_fail ( + bits == NULL || (rowstride_bytes % sizeof (uint32_t)) == 0, NULL); + + return_val_if_fail (PIXMAN_FORMAT_BPP (format) >= PIXMAN_FORMAT_DEPTH (format), NULL); + + image = _pixman_image_allocate (); + + if (!image) + return NULL; + + if (!_pixman_bits_image_init (image, format, width, height, bits, + rowstride_bytes / (int) sizeof (uint32_t), + clear)) + { + free (image); + return NULL; + } + + return image; +} + +/* If bits is NULL, a buffer will be allocated and initialized to 0 */ +PIXMAN_EXPORT pixman_image_t * +pixman_image_create_bits (pixman_format_code_t format, + int width, + int height, + uint32_t * bits, + int rowstride_bytes) +{ + return create_bits_image_internal ( + format, width, height, bits, rowstride_bytes, TRUE); +} + + +/* If bits is NULL, a buffer will be allocated and _not_ initialized */ +PIXMAN_EXPORT pixman_image_t * +pixman_image_create_bits_no_clear (pixman_format_code_t format, + int width, + int height, + uint32_t * bits, + int rowstride_bytes) +{ + return create_bits_image_internal ( + format, width, height, bits, rowstride_bytes, FALSE); +} diff --git a/gfx/cairo/libpixman/src/pixman-combine-float.c b/gfx/cairo/libpixman/src/pixman-combine-float.c new file mode 100644 index 0000000000..f5145bc9d7 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-combine-float.c @@ -0,0 +1,1158 @@ +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ +/* + * Copyright © 2010, 2012 Soren Sandmann Pedersen + * Copyright © 2010, 2012 Red Hat, Inc. + * + * 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. + * + * Author: Soren Sandmann Pedersen (sandmann@cs.au.dk) + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "pixman-private.h" + +/* Workaround for http://gcc.gnu.org/PR54965 */ +/* GCC 4.6 has problems with force_inline, so just use normal inline instead */ +#if defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 6) +#undef force_inline +#define force_inline __inline__ +#endif + +typedef float (* combine_channel_t) (float sa, float s, float da, float d); + +static force_inline void +combine_inner (pixman_bool_t component, + float *dest, const float *src, const float *mask, int n_pixels, + combine_channel_t combine_a, combine_channel_t combine_c) +{ + int i; + + if (!mask) + { + for (i = 0; i < 4 * n_pixels; i += 4) + { + float sa = src[i + 0]; + float sr = src[i + 1]; + float sg = src[i + 2]; + float sb = src[i + 3]; + + float da = dest[i + 0]; + float dr = dest[i + 1]; + float dg = dest[i + 2]; + float db = dest[i + 3]; + + dest[i + 0] = combine_a (sa, sa, da, da); + dest[i + 1] = combine_c (sa, sr, da, dr); + dest[i + 2] = combine_c (sa, sg, da, dg); + dest[i + 3] = combine_c (sa, sb, da, db); + } + } + else + { + for (i = 0; i < 4 * n_pixels; i += 4) + { + float sa, sr, sg, sb; + float ma, mr, mg, mb; + float da, dr, dg, db; + + sa = src[i + 0]; + sr = src[i + 1]; + sg = src[i + 2]; + sb = src[i + 3]; + + if (component) + { + ma = mask[i + 0]; + mr = mask[i + 1]; + mg = mask[i + 2]; + mb = mask[i + 3]; + + sr *= mr; + sg *= mg; + sb *= mb; + + ma *= sa; + mr *= sa; + mg *= sa; + mb *= sa; + + sa = ma; + } + else + { + ma = mask[i + 0]; + + sa *= ma; + sr *= ma; + sg *= ma; + sb *= ma; + + ma = mr = mg = mb = sa; + } + + da = dest[i + 0]; + dr = dest[i + 1]; + dg = dest[i + 2]; + db = dest[i + 3]; + + dest[i + 0] = combine_a (ma, sa, da, da); + dest[i + 1] = combine_c (mr, sr, da, dr); + dest[i + 2] = combine_c (mg, sg, da, dg); + dest[i + 3] = combine_c (mb, sb, da, db); + } + } +} + +#define MAKE_COMBINER(name, component, combine_a, combine_c) \ + static void \ + combine_ ## name ## _float (pixman_implementation_t *imp, \ + pixman_op_t op, \ + float *dest, \ + const float *src, \ + const float *mask, \ + int n_pixels) \ + { \ + combine_inner (component, dest, src, mask, n_pixels, \ + combine_a, combine_c); \ + } + +#define MAKE_COMBINERS(name, combine_a, combine_c) \ + MAKE_COMBINER(name ## _ca, TRUE, combine_a, combine_c) \ + MAKE_COMBINER(name ## _u, FALSE, combine_a, combine_c) + + +/* + * Porter/Duff operators + */ +typedef enum +{ + ZERO, + ONE, + SRC_ALPHA, + DEST_ALPHA, + INV_SA, + INV_DA, + SA_OVER_DA, + DA_OVER_SA, + INV_SA_OVER_DA, + INV_DA_OVER_SA, + ONE_MINUS_SA_OVER_DA, + ONE_MINUS_DA_OVER_SA, + ONE_MINUS_INV_DA_OVER_SA, + ONE_MINUS_INV_SA_OVER_DA +} combine_factor_t; + +#define CLAMP(f) \ + (((f) < 0)? 0 : (((f) > 1.0) ? 1.0 : (f))) + +static force_inline float +get_factor (combine_factor_t factor, float sa, float da) +{ + float f = -1; + + switch (factor) + { + case ZERO: + f = 0.0f; + break; + + case ONE: + f = 1.0f; + break; + + case SRC_ALPHA: + f = sa; + break; + + case DEST_ALPHA: + f = da; + break; + + case INV_SA: + f = 1 - sa; + break; + + case INV_DA: + f = 1 - da; + break; + + case SA_OVER_DA: + if (FLOAT_IS_ZERO (da)) + f = 1.0f; + else + f = CLAMP (sa / da); + break; + + case DA_OVER_SA: + if (FLOAT_IS_ZERO (sa)) + f = 1.0f; + else + f = CLAMP (da / sa); + break; + + case INV_SA_OVER_DA: + if (FLOAT_IS_ZERO (da)) + f = 1.0f; + else + f = CLAMP ((1.0f - sa) / da); + break; + + case INV_DA_OVER_SA: + if (FLOAT_IS_ZERO (sa)) + f = 1.0f; + else + f = CLAMP ((1.0f - da) / sa); + break; + + case ONE_MINUS_SA_OVER_DA: + if (FLOAT_IS_ZERO (da)) + f = 0.0f; + else + f = CLAMP (1.0f - sa / da); + break; + + case ONE_MINUS_DA_OVER_SA: + if (FLOAT_IS_ZERO (sa)) + f = 0.0f; + else + f = CLAMP (1.0f - da / sa); + break; + + case ONE_MINUS_INV_DA_OVER_SA: + if (FLOAT_IS_ZERO (sa)) + f = 0.0f; + else + f = CLAMP (1.0f - (1.0f - da) / sa); + break; + + case ONE_MINUS_INV_SA_OVER_DA: + if (FLOAT_IS_ZERO (da)) + f = 0.0f; + else + f = CLAMP (1.0f - (1.0f - sa) / da); + break; + } + + return f; +} + +#define MAKE_PD_COMBINERS(name, a, b) \ + static float force_inline \ + pd_combine_ ## name (float sa, float s, float da, float d) \ + { \ + const float fa = get_factor (a, sa, da); \ + const float fb = get_factor (b, sa, da); \ + \ + return MIN (1.0f, s * fa + d * fb); \ + } \ + \ + MAKE_COMBINERS(name, pd_combine_ ## name, pd_combine_ ## name) + +MAKE_PD_COMBINERS (clear, ZERO, ZERO) +MAKE_PD_COMBINERS (src, ONE, ZERO) +MAKE_PD_COMBINERS (dst, ZERO, ONE) +MAKE_PD_COMBINERS (over, ONE, INV_SA) +MAKE_PD_COMBINERS (over_reverse, INV_DA, ONE) +MAKE_PD_COMBINERS (in, DEST_ALPHA, ZERO) +MAKE_PD_COMBINERS (in_reverse, ZERO, SRC_ALPHA) +MAKE_PD_COMBINERS (out, INV_DA, ZERO) +MAKE_PD_COMBINERS (out_reverse, ZERO, INV_SA) +MAKE_PD_COMBINERS (atop, DEST_ALPHA, INV_SA) +MAKE_PD_COMBINERS (atop_reverse, INV_DA, SRC_ALPHA) +MAKE_PD_COMBINERS (xor, INV_DA, INV_SA) +MAKE_PD_COMBINERS (add, ONE, ONE) + +MAKE_PD_COMBINERS (saturate, INV_DA_OVER_SA, ONE) + +MAKE_PD_COMBINERS (disjoint_clear, ZERO, ZERO) +MAKE_PD_COMBINERS (disjoint_src, ONE, ZERO) +MAKE_PD_COMBINERS (disjoint_dst, ZERO, ONE) +MAKE_PD_COMBINERS (disjoint_over, ONE, INV_SA_OVER_DA) +MAKE_PD_COMBINERS (disjoint_over_reverse, INV_DA_OVER_SA, ONE) +MAKE_PD_COMBINERS (disjoint_in, ONE_MINUS_INV_DA_OVER_SA, ZERO) +MAKE_PD_COMBINERS (disjoint_in_reverse, ZERO, ONE_MINUS_INV_SA_OVER_DA) +MAKE_PD_COMBINERS (disjoint_out, INV_DA_OVER_SA, ZERO) +MAKE_PD_COMBINERS (disjoint_out_reverse, ZERO, INV_SA_OVER_DA) +MAKE_PD_COMBINERS (disjoint_atop, ONE_MINUS_INV_DA_OVER_SA, INV_SA_OVER_DA) +MAKE_PD_COMBINERS (disjoint_atop_reverse, INV_DA_OVER_SA, ONE_MINUS_INV_SA_OVER_DA) +MAKE_PD_COMBINERS (disjoint_xor, INV_DA_OVER_SA, INV_SA_OVER_DA) + +MAKE_PD_COMBINERS (conjoint_clear, ZERO, ZERO) +MAKE_PD_COMBINERS (conjoint_src, ONE, ZERO) +MAKE_PD_COMBINERS (conjoint_dst, ZERO, ONE) +MAKE_PD_COMBINERS (conjoint_over, ONE, ONE_MINUS_SA_OVER_DA) +MAKE_PD_COMBINERS (conjoint_over_reverse, ONE_MINUS_DA_OVER_SA, ONE) +MAKE_PD_COMBINERS (conjoint_in, DA_OVER_SA, ZERO) +MAKE_PD_COMBINERS (conjoint_in_reverse, ZERO, SA_OVER_DA) +MAKE_PD_COMBINERS (conjoint_out, ONE_MINUS_DA_OVER_SA, ZERO) +MAKE_PD_COMBINERS (conjoint_out_reverse, ZERO, ONE_MINUS_SA_OVER_DA) +MAKE_PD_COMBINERS (conjoint_atop, DA_OVER_SA, ONE_MINUS_SA_OVER_DA) +MAKE_PD_COMBINERS (conjoint_atop_reverse, ONE_MINUS_DA_OVER_SA, SA_OVER_DA) +MAKE_PD_COMBINERS (conjoint_xor, ONE_MINUS_DA_OVER_SA, ONE_MINUS_SA_OVER_DA) + +/* + * PDF blend modes: + * + * The following blend modes have been taken from the PDF ISO 32000 + * specification, which at this point in time is available from + * + * http://www.adobe.com/devnet/pdf/pdf_reference.html + * + * The specific documents of interest are the PDF spec itself: + * + * http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf + * + * chapters 11.3.5 and 11.3.6 and a later supplement for Adobe Acrobat + * 9.1 and Reader 9.1: + * + * http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/adobe_supplement_iso32000_1.pdf + * + * that clarifies the specifications for blend modes ColorDodge and + * ColorBurn. + * + * The formula for computing the final pixel color given in 11.3.6 is: + * + * αr × Cr = (1 – αs) × αb × Cb + (1 – αb) × αs × Cs + αb × αs × B(Cb, Cs) + * + * with B() is the blend function. When B(Cb, Cs) = Cs, this formula + * reduces to the regular OVER operator. + * + * Cs and Cb are not premultiplied, so in our implementation we instead + * use: + * + * cr = (1 – αs) × cb + (1 – αb) × cs + αb × αs × B (cb/αb, cs/αs) + * + * where cr, cs, and cb are premultiplied colors, and where the + * + * αb × αs × B(cb/αb, cs/αs) + * + * part is first arithmetically simplified under the assumption that αb + * and αs are not 0, and then updated to produce a meaningful result when + * they are. + * + * For all the blend mode operators, the alpha channel is given by + * + * αr = αs + αb + αb × αs + */ + +#define MAKE_SEPARABLE_PDF_COMBINERS(name) \ + static force_inline float \ + combine_ ## name ## _a (float sa, float s, float da, float d) \ + { \ + return da + sa - da * sa; \ + } \ + \ + static force_inline float \ + combine_ ## name ## _c (float sa, float s, float da, float d) \ + { \ + float f = (1 - sa) * d + (1 - da) * s; \ + \ + return f + blend_ ## name (sa, s, da, d); \ + } \ + \ + MAKE_COMBINERS (name, combine_ ## name ## _a, combine_ ## name ## _c) + +/* + * Multiply + * + * ad * as * B(d / ad, s / as) + * = ad * as * d/ad * s/as + * = d * s + * + */ +static force_inline float +blend_multiply (float sa, float s, float da, float d) +{ + return d * s; +} + +/* + * Screen + * + * ad * as * B(d/ad, s/as) + * = ad * as * (d/ad + s/as - s/as * d/ad) + * = ad * s + as * d - s * d + */ +static force_inline float +blend_screen (float sa, float s, float da, float d) +{ + return d * sa + s * da - s * d; +} + +/* + * Overlay + * + * ad * as * B(d/ad, s/as) + * = ad * as * Hardlight (s, d) + * = if (d / ad < 0.5) + * as * ad * Multiply (s/as, 2 * d/ad) + * else + * as * ad * Screen (s/as, 2 * d / ad - 1) + * = if (d < 0.5 * ad) + * as * ad * s/as * 2 * d /ad + * else + * as * ad * (s/as + 2 * d / ad - 1 - s / as * (2 * d / ad - 1)) + * = if (2 * d < ad) + * 2 * s * d + * else + * ad * s + 2 * as * d - as * ad - ad * s * (2 * d / ad - 1) + * = if (2 * d < ad) + * 2 * s * d + * else + * as * ad - 2 * (ad - d) * (as - s) + */ +static force_inline float +blend_overlay (float sa, float s, float da, float d) +{ + if (2 * d < da) + return 2 * s * d; + else + return sa * da - 2 * (da - d) * (sa - s); +} + +/* + * Darken + * + * ad * as * B(d/ad, s/as) + * = ad * as * MIN(d/ad, s/as) + * = MIN (as * d, ad * s) + */ +static force_inline float +blend_darken (float sa, float s, float da, float d) +{ + s = s * da; + d = d * sa; + + if (s > d) + return d; + else + return s; +} + +/* + * Lighten + * + * ad * as * B(d/ad, s/as) + * = ad * as * MAX(d/ad, s/as) + * = MAX (as * d, ad * s) + */ +static force_inline float +blend_lighten (float sa, float s, float da, float d) +{ + s = s * da; + d = d * sa; + + if (s > d) + return s; + else + return d; +} + +/* + * Color dodge + * + * ad * as * B(d/ad, s/as) + * = if d/ad = 0 + * ad * as * 0 + * else if (d/ad >= (1 - s/as) + * ad * as * 1 + * else + * ad * as * ((d/ad) / (1 - s/as)) + * = if d = 0 + * 0 + * elif as * d >= ad * (as - s) + * ad * as + * else + * as * (as * d / (as - s)) + * + */ +static force_inline float +blend_color_dodge (float sa, float s, float da, float d) +{ + if (FLOAT_IS_ZERO (d)) + return 0.0f; + else if (d * sa >= sa * da - s * da) + return sa * da; + else if (FLOAT_IS_ZERO (sa - s)) + return sa * da; + else + return sa * sa * d / (sa - s); +} + +/* + * Color burn + * + * We modify the first clause "if d = 1" to "if d >= 1" since with + * premultiplied colors d > 1 can actually happen. + * + * ad * as * B(d/ad, s/as) + * = if d/ad >= 1 + * ad * as * 1 + * elif (1 - d/ad) >= s/as + * ad * as * 0 + * else + * ad * as * (1 - ((1 - d/ad) / (s/as))) + * = if d >= ad + * ad * as + * elif as * ad - as * d >= ad * s + * 0 + * else + * ad * as - as * as * (ad - d) / s + */ +static force_inline float +blend_color_burn (float sa, float s, float da, float d) +{ + if (d >= da) + return sa * da; + else if (sa * (da - d) >= s * da) + return 0.0f; + else if (FLOAT_IS_ZERO (s)) + return 0.0f; + else + return sa * (da - sa * (da - d) / s); +} + +/* + * Hard light + * + * ad * as * B(d/ad, s/as) + * = if (s/as <= 0.5) + * ad * as * Multiply (d/ad, 2 * s/as) + * else + * ad * as * Screen (d/ad, 2 * s/as - 1) + * = if 2 * s <= as + * ad * as * d/ad * 2 * s / as + * else + * ad * as * (d/ad + (2 * s/as - 1) + d/ad * (2 * s/as - 1)) + * = if 2 * s <= as + * 2 * s * d + * else + * as * ad - 2 * (ad - d) * (as - s) + */ +static force_inline float +blend_hard_light (float sa, float s, float da, float d) +{ + if (2 * s < sa) + return 2 * s * d; + else + return sa * da - 2 * (da - d) * (sa - s); +} + +/* + * Soft light + * + * ad * as * B(d/ad, s/as) + * = if (s/as <= 0.5) + * ad * as * (d/ad - (1 - 2 * s/as) * d/ad * (1 - d/ad)) + * else if (d/ad <= 0.25) + * ad * as * (d/ad + (2 * s/as - 1) * ((((16 * d/ad - 12) * d/ad + 4) * d/ad) - d/ad)) + * else + * ad * as * (d/ad + (2 * s/as - 1) * sqrt (d/ad)) + * = if (2 * s <= as) + * d * as - d * (ad - d) * (as - 2 * s) / ad; + * else if (4 * d <= ad) + * (2 * s - as) * d * ((16 * d / ad - 12) * d / ad + 3); + * else + * d * as + (sqrt (d * ad) - d) * (2 * s - as); + */ +static force_inline float +blend_soft_light (float sa, float s, float da, float d) +{ + if (2 * s <= sa) + { + if (FLOAT_IS_ZERO (da)) + return d * sa; + else + return d * sa - d * (da - d) * (sa - 2 * s) / da; + } + else + { + if (FLOAT_IS_ZERO (da)) + { + return d * sa; + } + else + { + if (4 * d <= da) + return d * sa + (2 * s - sa) * d * ((16 * d / da - 12) * d / da + 3); + else + return d * sa + (sqrtf (d * da) - d) * (2 * s - sa); + } + } +} + +/* + * Difference + * + * ad * as * B(s/as, d/ad) + * = ad * as * abs (s/as - d/ad) + * = if (s/as <= d/ad) + * ad * as * (d/ad - s/as) + * else + * ad * as * (s/as - d/ad) + * = if (ad * s <= as * d) + * as * d - ad * s + * else + * ad * s - as * d + */ +static force_inline float +blend_difference (float sa, float s, float da, float d) +{ + float dsa = d * sa; + float sda = s * da; + + if (sda < dsa) + return dsa - sda; + else + return sda - dsa; +} + +/* + * Exclusion + * + * ad * as * B(s/as, d/ad) + * = ad * as * (d/ad + s/as - 2 * d/ad * s/as) + * = as * d + ad * s - 2 * s * d + */ +static force_inline float +blend_exclusion (float sa, float s, float da, float d) +{ + return s * da + d * sa - 2 * d * s; +} + +MAKE_SEPARABLE_PDF_COMBINERS (multiply) +MAKE_SEPARABLE_PDF_COMBINERS (screen) +MAKE_SEPARABLE_PDF_COMBINERS (overlay) +MAKE_SEPARABLE_PDF_COMBINERS (darken) +MAKE_SEPARABLE_PDF_COMBINERS (lighten) +MAKE_SEPARABLE_PDF_COMBINERS (color_dodge) +MAKE_SEPARABLE_PDF_COMBINERS (color_burn) +MAKE_SEPARABLE_PDF_COMBINERS (hard_light) +MAKE_SEPARABLE_PDF_COMBINERS (soft_light) +MAKE_SEPARABLE_PDF_COMBINERS (difference) +MAKE_SEPARABLE_PDF_COMBINERS (exclusion) + +/* + * PDF nonseperable blend modes are implemented using the following functions + * to operate in Hsl space, with Cmax, Cmid, Cmin referring to the max, mid + * and min value of the red, green and blue components. + * + * LUM (C) = 0.3 × Cred + 0.59 × Cgreen + 0.11 × Cblue + * + * clip_color (C): + * l = LUM (C) + * min = Cmin + * max = Cmax + * if n < 0.0 + * C = l + (((C – l) × l) ⁄ (l – min)) + * if x > 1.0 + * C = l + (((C – l) × (1 – l) ) ⁄ (max – l)) + * return C + * + * set_lum (C, l): + * d = l – LUM (C) + * C += d + * return clip_color (C) + * + * SAT (C) = CH_MAX (C) - CH_MIN (C) + * + * set_sat (C, s): + * if Cmax > Cmin + * Cmid = ( ( ( Cmid – Cmin ) × s ) ⁄ ( Cmax – Cmin ) ) + * Cmax = s + * else + * Cmid = Cmax = 0.0 + * Cmin = 0.0 + * return C + */ + +/* For premultiplied colors, we need to know what happens when C is + * multiplied by a real number. LUM and SAT are linear: + * + * LUM (r × C) = r × LUM (C) SAT (r * C) = r * SAT (C) + * + * If we extend clip_color with an extra argument a and change + * + * if x >= 1.0 + * + * into + * + * if x >= a + * + * then clip_color is also linear: + * + * r * clip_color (C, a) = clip_color (r * C, r * a); + * + * for positive r. + * + * Similarly, we can extend set_lum with an extra argument that is just passed + * on to clip_color: + * + * r * set_lum (C, l, a) + * + * = r × clip_color (C + l - LUM (C), a) + * + * = clip_color (r * C + r × l - r * LUM (C), r * a) + * + * = set_lum (r * C, r * l, r * a) + * + * Finally, set_sat: + * + * r * set_sat (C, s) = set_sat (x * C, r * s) + * + * The above holds for all non-zero x, because the x'es in the fraction for + * C_mid cancel out. Specifically, it holds for x = r: + * + * r * set_sat (C, s) = set_sat (r * C, r * s) + * + */ +typedef struct +{ + float r; + float g; + float b; +} rgb_t; + +static force_inline float +minf (float a, float b) +{ + return a < b? a : b; +} + +static force_inline float +maxf (float a, float b) +{ + return a > b? a : b; +} + +static force_inline float +channel_min (const rgb_t *c) +{ + return minf (minf (c->r, c->g), c->b); +} + +static force_inline float +channel_max (const rgb_t *c) +{ + return maxf (maxf (c->r, c->g), c->b); +} + +static force_inline float +get_lum (const rgb_t *c) +{ + return c->r * 0.3f + c->g * 0.59f + c->b * 0.11f; +} + +static force_inline float +get_sat (const rgb_t *c) +{ + return channel_max (c) - channel_min (c); +} + +static void +clip_color (rgb_t *color, float a) +{ + float l = get_lum (color); + float n = channel_min (color); + float x = channel_max (color); + float t; + + if (n < 0.0f) + { + t = l - n; + if (FLOAT_IS_ZERO (t)) + { + color->r = 0.0f; + color->g = 0.0f; + color->b = 0.0f; + } + else + { + color->r = l + (((color->r - l) * l) / t); + color->g = l + (((color->g - l) * l) / t); + color->b = l + (((color->b - l) * l) / t); + } + } + if (x > a) + { + t = x - l; + if (FLOAT_IS_ZERO (t)) + { + color->r = a; + color->g = a; + color->b = a; + } + else + { + color->r = l + (((color->r - l) * (a - l) / t)); + color->g = l + (((color->g - l) * (a - l) / t)); + color->b = l + (((color->b - l) * (a - l) / t)); + } + } +} + +static void +set_lum (rgb_t *color, float sa, float l) +{ + float d = l - get_lum (color); + + color->r = color->r + d; + color->g = color->g + d; + color->b = color->b + d; + + clip_color (color, sa); +} + +static void +set_sat (rgb_t *src, float sat) +{ + float *max, *mid, *min; + float t; + + if (src->r > src->g) + { + if (src->r > src->b) + { + max = &(src->r); + + if (src->g > src->b) + { + mid = &(src->g); + min = &(src->b); + } + else + { + mid = &(src->b); + min = &(src->g); + } + } + else + { + max = &(src->b); + mid = &(src->r); + min = &(src->g); + } + } + else + { + if (src->r > src->b) + { + max = &(src->g); + mid = &(src->r); + min = &(src->b); + } + else + { + min = &(src->r); + + if (src->g > src->b) + { + max = &(src->g); + mid = &(src->b); + } + else + { + max = &(src->b); + mid = &(src->g); + } + } + } + + t = *max - *min; + + if (FLOAT_IS_ZERO (t)) + { + *mid = *max = 0.0f; + } + else + { + *mid = ((*mid - *min) * sat) / t; + *max = sat; + } + + *min = 0.0f; +} + +/* Hue: + * + * as * ad * B(s/as, d/as) + * = as * ad * set_lum (set_sat (s/as, SAT (d/ad)), LUM (d/ad), 1) + * = set_lum (set_sat (ad * s, as * SAT (d)), as * LUM (d), as * ad) + * + */ +static force_inline void +blend_hsl_hue (rgb_t *res, + const rgb_t *dest, float da, + const rgb_t *src, float sa) +{ + res->r = src->r * da; + res->g = src->g * da; + res->b = src->b * da; + + set_sat (res, get_sat (dest) * sa); + set_lum (res, sa * da, get_lum (dest) * sa); +} + +/* + * Saturation + * + * as * ad * B(s/as, d/ad) + * = as * ad * set_lum (set_sat (d/ad, SAT (s/as)), LUM (d/ad), 1) + * = set_lum (as * ad * set_sat (d/ad, SAT (s/as)), + * as * LUM (d), as * ad) + * = set_lum (set_sat (as * d, ad * SAT (s), as * LUM (d), as * ad)) + */ +static force_inline void +blend_hsl_saturation (rgb_t *res, + const rgb_t *dest, float da, + const rgb_t *src, float sa) +{ + res->r = dest->r * sa; + res->g = dest->g * sa; + res->b = dest->b * sa; + + set_sat (res, get_sat (src) * da); + set_lum (res, sa * da, get_lum (dest) * sa); +} + +/* + * Color + * + * as * ad * B(s/as, d/as) + * = as * ad * set_lum (s/as, LUM (d/ad), 1) + * = set_lum (s * ad, as * LUM (d), as * ad) + */ +static force_inline void +blend_hsl_color (rgb_t *res, + const rgb_t *dest, float da, + const rgb_t *src, float sa) +{ + res->r = src->r * da; + res->g = src->g * da; + res->b = src->b * da; + + set_lum (res, sa * da, get_lum (dest) * sa); +} + +/* + * Luminosity + * + * as * ad * B(s/as, d/ad) + * = as * ad * set_lum (d/ad, LUM (s/as), 1) + * = set_lum (as * d, ad * LUM (s), as * ad) + */ +static force_inline void +blend_hsl_luminosity (rgb_t *res, + const rgb_t *dest, float da, + const rgb_t *src, float sa) +{ + res->r = dest->r * sa; + res->g = dest->g * sa; + res->b = dest->b * sa; + + set_lum (res, sa * da, get_lum (src) * da); +} + +#define MAKE_NON_SEPARABLE_PDF_COMBINERS(name) \ + static void \ + combine_ ## name ## _u_float (pixman_implementation_t *imp, \ + pixman_op_t op, \ + float *dest, \ + const float *src, \ + const float *mask, \ + int n_pixels) \ + { \ + int i; \ + \ + for (i = 0; i < 4 * n_pixels; i += 4) \ + { \ + float sa, da; \ + rgb_t sc, dc, rc; \ + \ + sa = src[i + 0]; \ + sc.r = src[i + 1]; \ + sc.g = src[i + 2]; \ + sc.b = src[i + 3]; \ + \ + da = dest[i + 0]; \ + dc.r = dest[i + 1]; \ + dc.g = dest[i + 2]; \ + dc.b = dest[i + 3]; \ + \ + if (mask) \ + { \ + float ma = mask[i + 0]; \ + \ + /* Component alpha is not supported for HSL modes */ \ + sa *= ma; \ + sc.r *= ma; \ + sc.g *= ma; \ + sc.g *= ma; \ + } \ + \ + blend_ ## name (&rc, &dc, da, &sc, sa); \ + \ + dest[i + 0] = sa + da - sa * da; \ + dest[i + 1] = (1 - sa) * dc.r + (1 - da) * sc.r + rc.r; \ + dest[i + 2] = (1 - sa) * dc.g + (1 - da) * sc.g + rc.g; \ + dest[i + 3] = (1 - sa) * dc.b + (1 - da) * sc.b + rc.b; \ + } \ + } + +MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_hue) +MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_saturation) +MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_color) +MAKE_NON_SEPARABLE_PDF_COMBINERS(hsl_luminosity) + +void +_pixman_setup_combiner_functions_float (pixman_implementation_t *imp) +{ + /* Unified alpha */ + imp->combine_float[PIXMAN_OP_CLEAR] = combine_clear_u_float; + imp->combine_float[PIXMAN_OP_SRC] = combine_src_u_float; + imp->combine_float[PIXMAN_OP_DST] = combine_dst_u_float; + imp->combine_float[PIXMAN_OP_OVER] = combine_over_u_float; + imp->combine_float[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_u_float; + imp->combine_float[PIXMAN_OP_IN] = combine_in_u_float; + imp->combine_float[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_u_float; + imp->combine_float[PIXMAN_OP_OUT] = combine_out_u_float; + imp->combine_float[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_u_float; + imp->combine_float[PIXMAN_OP_ATOP] = combine_atop_u_float; + imp->combine_float[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_u_float; + imp->combine_float[PIXMAN_OP_XOR] = combine_xor_u_float; + imp->combine_float[PIXMAN_OP_ADD] = combine_add_u_float; + imp->combine_float[PIXMAN_OP_SATURATE] = combine_saturate_u_float; + + /* Disjoint, unified */ + imp->combine_float[PIXMAN_OP_DISJOINT_CLEAR] = combine_disjoint_clear_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_SRC] = combine_disjoint_src_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_DST] = combine_disjoint_dst_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_disjoint_over_reverse_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_u_float; + imp->combine_float[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_u_float; + + /* Conjoint, unified */ + imp->combine_float[PIXMAN_OP_CONJOINT_CLEAR] = combine_conjoint_clear_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_SRC] = combine_conjoint_src_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_DST] = combine_conjoint_dst_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_u_float; + imp->combine_float[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_u_float; + + /* PDF operators, unified */ + imp->combine_float[PIXMAN_OP_MULTIPLY] = combine_multiply_u_float; + imp->combine_float[PIXMAN_OP_SCREEN] = combine_screen_u_float; + imp->combine_float[PIXMAN_OP_OVERLAY] = combine_overlay_u_float; + imp->combine_float[PIXMAN_OP_DARKEN] = combine_darken_u_float; + imp->combine_float[PIXMAN_OP_LIGHTEN] = combine_lighten_u_float; + imp->combine_float[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_u_float; + imp->combine_float[PIXMAN_OP_COLOR_BURN] = combine_color_burn_u_float; + imp->combine_float[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_u_float; + imp->combine_float[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_u_float; + imp->combine_float[PIXMAN_OP_DIFFERENCE] = combine_difference_u_float; + imp->combine_float[PIXMAN_OP_EXCLUSION] = combine_exclusion_u_float; + + imp->combine_float[PIXMAN_OP_HSL_HUE] = combine_hsl_hue_u_float; + imp->combine_float[PIXMAN_OP_HSL_SATURATION] = combine_hsl_saturation_u_float; + imp->combine_float[PIXMAN_OP_HSL_COLOR] = combine_hsl_color_u_float; + imp->combine_float[PIXMAN_OP_HSL_LUMINOSITY] = combine_hsl_luminosity_u_float; + + /* Component alpha combiners */ + imp->combine_float_ca[PIXMAN_OP_CLEAR] = combine_clear_ca_float; + imp->combine_float_ca[PIXMAN_OP_SRC] = combine_src_ca_float; + imp->combine_float_ca[PIXMAN_OP_DST] = combine_dst_ca_float; + imp->combine_float_ca[PIXMAN_OP_OVER] = combine_over_ca_float; + imp->combine_float_ca[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_IN] = combine_in_ca_float; + imp->combine_float_ca[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_OUT] = combine_out_ca_float; + imp->combine_float_ca[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_ATOP] = combine_atop_ca_float; + imp->combine_float_ca[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_XOR] = combine_xor_ca_float; + imp->combine_float_ca[PIXMAN_OP_ADD] = combine_add_ca_float; + imp->combine_float_ca[PIXMAN_OP_SATURATE] = combine_saturate_ca_float; + + /* Disjoint CA */ + imp->combine_float_ca[PIXMAN_OP_DISJOINT_CLEAR] = combine_disjoint_clear_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_SRC] = combine_disjoint_src_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_DST] = combine_disjoint_dst_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_disjoint_over_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_ca_float; + + /* Conjoint CA */ + imp->combine_float_ca[PIXMAN_OP_CONJOINT_CLEAR] = combine_conjoint_clear_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_SRC] = combine_conjoint_src_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_DST] = combine_conjoint_dst_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_ca_float; + imp->combine_float_ca[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_ca_float; + + /* PDF operators CA */ + imp->combine_float_ca[PIXMAN_OP_MULTIPLY] = combine_multiply_ca_float; + imp->combine_float_ca[PIXMAN_OP_SCREEN] = combine_screen_ca_float; + imp->combine_float_ca[PIXMAN_OP_OVERLAY] = combine_overlay_ca_float; + imp->combine_float_ca[PIXMAN_OP_DARKEN] = combine_darken_ca_float; + imp->combine_float_ca[PIXMAN_OP_LIGHTEN] = combine_lighten_ca_float; + imp->combine_float_ca[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_ca_float; + imp->combine_float_ca[PIXMAN_OP_COLOR_BURN] = combine_color_burn_ca_float; + imp->combine_float_ca[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_ca_float; + imp->combine_float_ca[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_ca_float; + imp->combine_float_ca[PIXMAN_OP_DIFFERENCE] = combine_difference_ca_float; + imp->combine_float_ca[PIXMAN_OP_EXCLUSION] = combine_exclusion_ca_float; + + /* It is not clear that these make sense, so make them noops for now */ + imp->combine_float_ca[PIXMAN_OP_HSL_HUE] = combine_dst_u_float; + imp->combine_float_ca[PIXMAN_OP_HSL_SATURATION] = combine_dst_u_float; + imp->combine_float_ca[PIXMAN_OP_HSL_COLOR] = combine_dst_u_float; + imp->combine_float_ca[PIXMAN_OP_HSL_LUMINOSITY] = combine_dst_u_float; +} diff --git a/gfx/cairo/libpixman/src/pixman-combine32.c b/gfx/cairo/libpixman/src/pixman-combine32.c new file mode 100644 index 0000000000..4a89384d9c --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-combine32.c @@ -0,0 +1,1189 @@ +/* + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. + * 2005 Lars Knoll & Zack Rusin, Trolltech + * + * 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. + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "pixman-private.h" +#include "pixman-combine32.h" + +/* component alpha helper functions */ + +static void +combine_mask_ca (uint32_t *src, uint32_t *mask) +{ + uint32_t a = *mask; + + uint32_t x; + uint16_t xa; + + if (!a) + { + *(src) = 0; + return; + } + + x = *(src); + if (a == ~0) + { + x = x >> A_SHIFT; + x |= x << G_SHIFT; + x |= x << R_SHIFT; + *(mask) = x; + return; + } + + xa = x >> A_SHIFT; + UN8x4_MUL_UN8x4 (x, a); + *(src) = x; + + UN8x4_MUL_UN8 (a, xa); + *(mask) = a; +} + +static void +combine_mask_value_ca (uint32_t *src, const uint32_t *mask) +{ + uint32_t a = *mask; + uint32_t x; + + if (!a) + { + *(src) = 0; + return; + } + + if (a == ~0) + return; + + x = *(src); + UN8x4_MUL_UN8x4 (x, a); + *(src) = x; +} + +static void +combine_mask_alpha_ca (const uint32_t *src, uint32_t *mask) +{ + uint32_t a = *(mask); + uint32_t x; + + if (!a) + return; + + x = *(src) >> A_SHIFT; + if (x == MASK) + return; + + if (a == ~0) + { + x |= x << G_SHIFT; + x |= x << R_SHIFT; + *(mask) = x; + return; + } + + UN8x4_MUL_UN8 (a, x); + *(mask) = a; +} + +/* + * There are two ways of handling alpha -- either as a single unified value or + * a separate value for each component, hence each macro must have two + * versions. The unified alpha version has a 'u' at the end of the name, + * the component version has a 'ca'. Similarly, functions which deal with + * this difference will have two versions using the same convention. + */ + +static force_inline uint32_t +combine_mask (const uint32_t *src, const uint32_t *mask, int i) +{ + uint32_t s, m; + + if (mask) + { + m = *(mask + i) >> A_SHIFT; + + if (!m) + return 0; + } + + s = *(src + i); + + if (mask) + UN8x4_MUL_UN8 (s, m); + + return s; +} + +static void +combine_clear (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + memset (dest, 0, width * sizeof (uint32_t)); +} + +static void +combine_dst (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + return; +} + +static void +combine_src_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + if (!mask) + { + memcpy (dest, src, width * sizeof (uint32_t)); + } + else + { + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + + *(dest + i) = s; + } + } +} + +static void +combine_over_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + if (!mask) + { + for (i = 0; i < width; ++i) + { + uint32_t s = *(src + i); + uint32_t a = ALPHA_8 (s); + if (a == 0xFF) + { + *(dest + i) = s; + } + else if (s) + { + uint32_t d = *(dest + i); + uint32_t ia = a ^ 0xFF; + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); + *(dest + i) = d; + } + } + } + else + { + for (i = 0; i < width; ++i) + { + uint32_t m = ALPHA_8 (*(mask + i)); + if (m == 0xFF) + { + uint32_t s = *(src + i); + uint32_t a = ALPHA_8 (s); + if (a == 0xFF) + { + *(dest + i) = s; + } + else if (s) + { + uint32_t d = *(dest + i); + uint32_t ia = a ^ 0xFF; + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); + *(dest + i) = d; + } + } + else if (m) + { + uint32_t s = *(src + i); + if (s) + { + uint32_t d = *(dest + i); + UN8x4_MUL_UN8 (s, m); + UN8x4_MUL_UN8_ADD_UN8x4 (d, ALPHA_8 (~s), s); + *(dest + i) = d; + } + } + } + } +} + +static void +combine_over_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t d = *(dest + i); + uint32_t ia = ALPHA_8 (~*(dest + i)); + UN8x4_MUL_UN8_ADD_UN8x4 (s, ia, d); + *(dest + i) = s; + } +} + +static void +combine_in_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t a = ALPHA_8 (*(dest + i)); + UN8x4_MUL_UN8 (s, a); + *(dest + i) = s; + } +} + +static void +combine_in_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t d = *(dest + i); + uint32_t a = ALPHA_8 (s); + UN8x4_MUL_UN8 (d, a); + *(dest + i) = d; + } +} + +static void +combine_out_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t a = ALPHA_8 (~*(dest + i)); + UN8x4_MUL_UN8 (s, a); + *(dest + i) = s; + } +} + +static void +combine_out_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t d = *(dest + i); + uint32_t a = ALPHA_8 (~s); + UN8x4_MUL_UN8 (d, a); + *(dest + i) = d; + } +} + +static void +combine_atop_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t d = *(dest + i); + uint32_t dest_a = ALPHA_8 (d); + uint32_t src_ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_a, d, src_ia); + *(dest + i) = s; + } +} + +static void +combine_atop_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t d = *(dest + i); + uint32_t src_a = ALPHA_8 (s); + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_a); + *(dest + i) = s; + } +} + +static void +combine_xor_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t d = *(dest + i); + uint32_t src_ia = ALPHA_8 (~s); + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_ia); + *(dest + i) = s; + } +} + +static void +combine_add_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t d = *(dest + i); + UN8x4_ADD_UN8x4 (d, s); + *(dest + i) = d; + } +} + +/* + * PDF blend modes: + * + * The following blend modes have been taken from the PDF ISO 32000 + * specification, which at this point in time is available from + * + * http://www.adobe.com/devnet/pdf/pdf_reference.html + * + * The specific documents of interest are the PDF spec itself: + * + * http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf + * + * chapters 11.3.5 and 11.3.6 and a later supplement for Adobe Acrobat + * 9.1 and Reader 9.1: + * + * http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/adobe_supplement_iso32000_1.pdf + * + * that clarifies the specifications for blend modes ColorDodge and + * ColorBurn. + * + * The formula for computing the final pixel color given in 11.3.6 is: + * + * αr × Cr = (1 – αs) × αb × Cb + (1 – αb) × αs × Cs + αb × αs × B(Cb, Cs) + * + * with B() is the blend function. When B(Cb, Cs) = Cs, this formula + * reduces to the regular OVER operator. + * + * Cs and Cb are not premultiplied, so in our implementation we instead + * use: + * + * cr = (1 – αs) × cb + (1 – αb) × cs + αb × αs × B (cb/αb, cs/αs) + * + * where cr, cs, and cb are premultiplied colors, and where the + * + * αb × αs × B(cb/αb, cs/αs) + * + * part is first arithmetically simplified under the assumption that αb + * and αs are not 0, and then updated to produce a meaningful result when + * they are. + * + * For all the blend mode operators, the alpha channel is given by + * + * αr = αs + αb + αb × αs + */ + +/* + * Multiply + * + * ad * as * B(d / ad, s / as) + * = ad * as * d/ad * s/as + * = d * s + * + */ +static void +combine_multiply_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = combine_mask (src, mask, i); + uint32_t d = *(dest + i); + uint32_t ss = s; + uint32_t src_ia = ALPHA_8 (~s); + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (ss, dest_ia, d, src_ia); + UN8x4_MUL_UN8x4 (d, s); + UN8x4_ADD_UN8x4 (d, ss); + + *(dest + i) = d; + } +} + +static void +combine_multiply_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t m = *(mask + i); + uint32_t s = *(src + i); + uint32_t d = *(dest + i); + uint32_t r = d; + uint32_t dest_ia = ALPHA_8 (~d); + + combine_mask_ca (&s, &m); + + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (r, ~m, s, dest_ia); + UN8x4_MUL_UN8x4 (d, s); + UN8x4_ADD_UN8x4 (r, d); + + *(dest + i) = r; + } +} + +#define CLAMP(v, low, high) \ + do \ + { \ + if (v < (low)) \ + v = (low); \ + if (v > (high)) \ + v = (high); \ + } while (0) + +#define PDF_SEPARABLE_BLEND_MODE(name) \ + static void \ + combine_ ## name ## _u (pixman_implementation_t *imp, \ + pixman_op_t op, \ + uint32_t * dest, \ + const uint32_t * src, \ + const uint32_t * mask, \ + int width) \ + { \ + int i; \ + for (i = 0; i < width; ++i) \ + { \ + uint32_t s = combine_mask (src, mask, i); \ + uint32_t d = *(dest + i); \ + uint8_t sa = ALPHA_8 (s); \ + uint8_t isa = ~sa; \ + uint8_t da = ALPHA_8 (d); \ + uint8_t ida = ~da; \ + uint32_t ra, rr, rg, rb; \ + \ + ra = da * 0xff + sa * 0xff - sa * da; \ + rr = isa * RED_8 (d) + ida * RED_8 (s); \ + rg = isa * GREEN_8 (d) + ida * GREEN_8 (s); \ + rb = isa * BLUE_8 (d) + ida * BLUE_8 (s); \ + \ + rr += blend_ ## name (RED_8 (d), da, RED_8 (s), sa); \ + rg += blend_ ## name (GREEN_8 (d), da, GREEN_8 (s), sa); \ + rb += blend_ ## name (BLUE_8 (d), da, BLUE_8 (s), sa); \ + \ + CLAMP (ra, 0, 255 * 255); \ + CLAMP (rr, 0, 255 * 255); \ + CLAMP (rg, 0, 255 * 255); \ + CLAMP (rb, 0, 255 * 255); \ + \ + ra = DIV_ONE_UN8 (ra); \ + rr = DIV_ONE_UN8 (rr); \ + rg = DIV_ONE_UN8 (rg); \ + rb = DIV_ONE_UN8 (rb); \ + \ + *(dest + i) = ra << 24 | rr << 16 | rg << 8 | rb; \ + } \ + } \ + \ + static void \ + combine_ ## name ## _ca (pixman_implementation_t *imp, \ + pixman_op_t op, \ + uint32_t * dest, \ + const uint32_t * src, \ + const uint32_t * mask, \ + int width) \ + { \ + int i; \ + for (i = 0; i < width; ++i) \ + { \ + uint32_t m = *(mask + i); \ + uint32_t s = *(src + i); \ + uint32_t d = *(dest + i); \ + uint8_t da = ALPHA_8 (d); \ + uint8_t ida = ~da; \ + uint32_t ra, rr, rg, rb; \ + uint8_t ira, iga, iba; \ + \ + combine_mask_ca (&s, &m); \ + \ + ira = ~RED_8 (m); \ + iga = ~GREEN_8 (m); \ + iba = ~BLUE_8 (m); \ + \ + ra = da * 0xff + ALPHA_8 (s) * 0xff - ALPHA_8 (s) * da; \ + rr = ira * RED_8 (d) + ida * RED_8 (s); \ + rg = iga * GREEN_8 (d) + ida * GREEN_8 (s); \ + rb = iba * BLUE_8 (d) + ida * BLUE_8 (s); \ + \ + rr += blend_ ## name (RED_8 (d), da, RED_8 (s), RED_8 (m)); \ + rg += blend_ ## name (GREEN_8 (d), da, GREEN_8 (s), GREEN_8 (m)); \ + rb += blend_ ## name (BLUE_8 (d), da, BLUE_8 (s), BLUE_8 (m)); \ + \ + CLAMP (ra, 0, 255 * 255); \ + CLAMP (rr, 0, 255 * 255); \ + CLAMP (rg, 0, 255 * 255); \ + CLAMP (rb, 0, 255 * 255); \ + \ + ra = DIV_ONE_UN8 (ra); \ + rr = DIV_ONE_UN8 (rr); \ + rg = DIV_ONE_UN8 (rg); \ + rb = DIV_ONE_UN8 (rb); \ + \ + *(dest + i) = ra << 24 | rr << 16 | rg << 8 | rb; \ + } \ + } + +/* + * Screen + * + * ad * as * B(d/ad, s/as) + * = ad * as * (d/ad + s/as - s/as * d/ad) + * = ad * s + as * d - s * d + */ +static inline int32_t +blend_screen (int32_t d, int32_t ad, int32_t s, int32_t as) +{ + return s * ad + d * as - s * d; +} + +PDF_SEPARABLE_BLEND_MODE (screen) + +/* + * Overlay + * + * ad * as * B(d/ad, s/as) + * = ad * as * Hardlight (s, d) + * = if (d / ad < 0.5) + * as * ad * Multiply (s/as, 2 * d/ad) + * else + * as * ad * Screen (s/as, 2 * d / ad - 1) + * = if (d < 0.5 * ad) + * as * ad * s/as * 2 * d /ad + * else + * as * ad * (s/as + 2 * d / ad - 1 - s / as * (2 * d / ad - 1)) + * = if (2 * d < ad) + * 2 * s * d + * else + * ad * s + 2 * as * d - as * ad - ad * s * (2 * d / ad - 1) + * = if (2 * d < ad) + * 2 * s * d + * else + * as * ad - 2 * (ad - d) * (as - s) + */ +static inline int32_t +blend_overlay (int32_t d, int32_t ad, int32_t s, int32_t as) +{ + uint32_t r; + + if (2 * d < ad) + r = 2 * s * d; + else + r = as * ad - 2 * (ad - d) * (as - s); + + return r; +} + +PDF_SEPARABLE_BLEND_MODE (overlay) + +/* + * Darken + * + * ad * as * B(d/ad, s/as) + * = ad * as * MIN(d/ad, s/as) + * = MIN (as * d, ad * s) + */ +static inline int32_t +blend_darken (int32_t d, int32_t ad, int32_t s, int32_t as) +{ + s = ad * s; + d = as * d; + + return s > d ? d : s; +} + +PDF_SEPARABLE_BLEND_MODE (darken) + +/* + * Lighten + * + * ad * as * B(d/ad, s/as) + * = ad * as * MAX(d/ad, s/as) + * = MAX (as * d, ad * s) + */ +static inline int32_t +blend_lighten (int32_t d, int32_t ad, int32_t s, int32_t as) +{ + s = ad * s; + d = as * d; + + return s > d ? s : d; +} + +PDF_SEPARABLE_BLEND_MODE (lighten) + +/* + * Hard light + * + * ad * as * B(d/ad, s/as) + * = if (s/as <= 0.5) + * ad * as * Multiply (d/ad, 2 * s/as) + * else + * ad * as * Screen (d/ad, 2 * s/as - 1) + * = if 2 * s <= as + * ad * as * d/ad * 2 * s / as + * else + * ad * as * (d/ad + (2 * s/as - 1) + d/ad * (2 * s/as - 1)) + * = if 2 * s <= as + * 2 * s * d + * else + * as * ad - 2 * (ad - d) * (as - s) + */ +static inline int32_t +blend_hard_light (int32_t d, int32_t ad, int32_t s, int32_t as) +{ + if (2 * s < as) + return 2 * s * d; + else + return as * ad - 2 * (ad - d) * (as - s); +} + +PDF_SEPARABLE_BLEND_MODE (hard_light) + +/* + * Difference + * + * ad * as * B(s/as, d/ad) + * = ad * as * abs (s/as - d/ad) + * = if (s/as <= d/ad) + * ad * as * (d/ad - s/as) + * else + * ad * as * (s/as - d/ad) + * = if (ad * s <= as * d) + * as * d - ad * s + * else + * ad * s - as * d + */ +static inline int32_t +blend_difference (int32_t d, int32_t ad, int32_t s, int32_t as) +{ + int32_t das = d * as; + int32_t sad = s * ad; + + if (sad < das) + return das - sad; + else + return sad - das; +} + +PDF_SEPARABLE_BLEND_MODE (difference) + +/* + * Exclusion + * + * ad * as * B(s/as, d/ad) + * = ad * as * (d/ad + s/as - 2 * d/ad * s/as) + * = as * d + ad * s - 2 * s * d + */ + +/* This can be made faster by writing it directly and not using + * PDF_SEPARABLE_BLEND_MODE, but that's a performance optimization */ + +static inline int32_t +blend_exclusion (int32_t d, int32_t ad, int32_t s, int32_t as) +{ + return s * ad + d * as - 2 * d * s; +} + +PDF_SEPARABLE_BLEND_MODE (exclusion) + +#undef PDF_SEPARABLE_BLEND_MODE + +/* Component alpha combiners */ + +static void +combine_clear_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + memset (dest, 0, width * sizeof(uint32_t)); +} + +static void +combine_src_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + + combine_mask_value_ca (&s, &m); + + *(dest + i) = s; + } +} + +static void +combine_over_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + uint32_t a; + + combine_mask_ca (&s, &m); + + a = ~m; + if (a) + { + uint32_t d = *(dest + i); + UN8x4_MUL_UN8x4_ADD_UN8x4 (d, a, s); + s = d; + } + + *(dest + i) = s; + } +} + +static void +combine_over_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t d = *(dest + i); + uint32_t a = ~d >> A_SHIFT; + + if (a) + { + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + + UN8x4_MUL_UN8x4 (s, m); + UN8x4_MUL_UN8_ADD_UN8x4 (s, a, d); + + *(dest + i) = s; + } + } +} + +static void +combine_in_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t d = *(dest + i); + uint16_t a = d >> A_SHIFT; + uint32_t s = 0; + + if (a) + { + uint32_t m = *(mask + i); + + s = *(src + i); + combine_mask_value_ca (&s, &m); + + if (a != MASK) + UN8x4_MUL_UN8 (s, a); + } + + *(dest + i) = s; + } +} + +static void +combine_in_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + uint32_t a; + + combine_mask_alpha_ca (&s, &m); + + a = m; + if (a != ~0) + { + uint32_t d = 0; + + if (a) + { + d = *(dest + i); + UN8x4_MUL_UN8x4 (d, a); + } + + *(dest + i) = d; + } + } +} + +static void +combine_out_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t d = *(dest + i); + uint16_t a = ~d >> A_SHIFT; + uint32_t s = 0; + + if (a) + { + uint32_t m = *(mask + i); + + s = *(src + i); + combine_mask_value_ca (&s, &m); + + if (a != MASK) + UN8x4_MUL_UN8 (s, a); + } + + *(dest + i) = s; + } +} + +static void +combine_out_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + uint32_t a; + + combine_mask_alpha_ca (&s, &m); + + a = ~m; + if (a != ~0) + { + uint32_t d = 0; + + if (a) + { + d = *(dest + i); + UN8x4_MUL_UN8x4 (d, a); + } + + *(dest + i) = d; + } + } +} + +static void +combine_atop_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t d = *(dest + i); + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + uint32_t ad; + uint16_t as = d >> A_SHIFT; + + combine_mask_ca (&s, &m); + + ad = ~m; + + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ad, s, as); + + *(dest + i) = d; + } +} + +static void +combine_atop_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t d = *(dest + i); + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + uint32_t ad; + uint16_t as = ~d >> A_SHIFT; + + combine_mask_ca (&s, &m); + + ad = m; + + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ad, s, as); + + *(dest + i) = d; + } +} + +static void +combine_xor_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t d = *(dest + i); + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + uint32_t ad; + uint16_t as = ~d >> A_SHIFT; + + combine_mask_ca (&s, &m); + + ad = ~m; + + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ad, s, as); + + *(dest + i) = d; + } +} + +static void +combine_add_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t s = *(src + i); + uint32_t m = *(mask + i); + uint32_t d = *(dest + i); + + combine_mask_value_ca (&s, &m); + + UN8x4_ADD_UN8x4 (d, s); + + *(dest + i) = d; + } +} + +void +_pixman_setup_combiner_functions_32 (pixman_implementation_t *imp) +{ + /* Unified alpha */ + imp->combine_32[PIXMAN_OP_CLEAR] = combine_clear; + imp->combine_32[PIXMAN_OP_SRC] = combine_src_u; + imp->combine_32[PIXMAN_OP_DST] = combine_dst; + imp->combine_32[PIXMAN_OP_OVER] = combine_over_u; + imp->combine_32[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_u; + imp->combine_32[PIXMAN_OP_IN] = combine_in_u; + imp->combine_32[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_u; + imp->combine_32[PIXMAN_OP_OUT] = combine_out_u; + imp->combine_32[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_u; + imp->combine_32[PIXMAN_OP_ATOP] = combine_atop_u; + imp->combine_32[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_u; + imp->combine_32[PIXMAN_OP_XOR] = combine_xor_u; + imp->combine_32[PIXMAN_OP_ADD] = combine_add_u; + + imp->combine_32[PIXMAN_OP_MULTIPLY] = combine_multiply_u; + imp->combine_32[PIXMAN_OP_SCREEN] = combine_screen_u; + imp->combine_32[PIXMAN_OP_OVERLAY] = combine_overlay_u; + imp->combine_32[PIXMAN_OP_DARKEN] = combine_darken_u; + imp->combine_32[PIXMAN_OP_LIGHTEN] = combine_lighten_u; + imp->combine_32[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_u; + imp->combine_32[PIXMAN_OP_DIFFERENCE] = combine_difference_u; + imp->combine_32[PIXMAN_OP_EXCLUSION] = combine_exclusion_u; + + /* Component alpha combiners */ + imp->combine_32_ca[PIXMAN_OP_CLEAR] = combine_clear_ca; + imp->combine_32_ca[PIXMAN_OP_SRC] = combine_src_ca; + /* dest */ + imp->combine_32_ca[PIXMAN_OP_OVER] = combine_over_ca; + imp->combine_32_ca[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_IN] = combine_in_ca; + imp->combine_32_ca[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_OUT] = combine_out_ca; + imp->combine_32_ca[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_ATOP] = combine_atop_ca; + imp->combine_32_ca[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_XOR] = combine_xor_ca; + imp->combine_32_ca[PIXMAN_OP_ADD] = combine_add_ca; + + imp->combine_32_ca[PIXMAN_OP_MULTIPLY] = combine_multiply_ca; + imp->combine_32_ca[PIXMAN_OP_SCREEN] = combine_screen_ca; + imp->combine_32_ca[PIXMAN_OP_OVERLAY] = combine_overlay_ca; + imp->combine_32_ca[PIXMAN_OP_DARKEN] = combine_darken_ca; + imp->combine_32_ca[PIXMAN_OP_LIGHTEN] = combine_lighten_ca; + imp->combine_32_ca[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_ca; + imp->combine_32_ca[PIXMAN_OP_DIFFERENCE] = combine_difference_ca; + imp->combine_32_ca[PIXMAN_OP_EXCLUSION] = combine_exclusion_ca; +} diff --git a/gfx/cairo/libpixman/src/pixman-combine32.h b/gfx/cairo/libpixman/src/pixman-combine32.h new file mode 100644 index 0000000000..59bb2477aa --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-combine32.h @@ -0,0 +1,272 @@ +#define COMPONENT_SIZE 8 +#define MASK 0xff +#define ONE_HALF 0x80 + +#define A_SHIFT 8 * 3 +#define R_SHIFT 8 * 2 +#define G_SHIFT 8 +#define A_MASK 0xff000000 +#define R_MASK 0xff0000 +#define G_MASK 0xff00 + +#define RB_MASK 0xff00ff +#define AG_MASK 0xff00ff00 +#define RB_ONE_HALF 0x800080 +#define RB_MASK_PLUS_ONE 0x1000100 + +#define ALPHA_8(x) ((x) >> A_SHIFT) +#define RED_8(x) (((x) >> R_SHIFT) & MASK) +#define GREEN_8(x) (((x) >> G_SHIFT) & MASK) +#define BLUE_8(x) ((x) & MASK) + +/* + * ARMv6 has UQADD8 instruction, which implements unsigned saturated + * addition for 8-bit values packed in 32-bit registers. It is very useful + * for UN8x4_ADD_UN8x4, UN8_rb_ADD_UN8_rb and ADD_UN8 macros (which would + * otherwise need a lot of arithmetic operations to simulate this operation). + * Since most of the major ARM linux distros are built for ARMv7, we are + * much less dependent on runtime CPU detection and can get practical + * benefits from conditional compilation here for a lot of users. + */ + +#if defined(USE_GCC_INLINE_ASM) && defined(__arm__) && \ + !defined(__aarch64__) && (!defined(__thumb__) || defined(__thumb2__)) +#if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \ + defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_7__) || \ + defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \ + defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) + +static force_inline uint32_t +un8x4_add_un8x4 (uint32_t x, uint32_t y) +{ + uint32_t t; + asm ("uqadd8 %0, %1, %2" : "=r" (t) : "%r" (x), "r" (y)); + return t; +} + +#define UN8x4_ADD_UN8x4(x, y) \ + ((x) = un8x4_add_un8x4 ((x), (y))) + +#define UN8_rb_ADD_UN8_rb(x, y, t) \ + ((t) = un8x4_add_un8x4 ((x), (y)), (x) = (t)) + +#define ADD_UN8(x, y, t) \ + ((t) = (x), un8x4_add_un8x4 ((t), (y))) + +#endif +#endif + +/*****************************************************************************/ + +/* + * Helper macros. + */ + +#define MUL_UN8(a, b, t) \ + ((t) = (a) * (uint16_t)(b) + ONE_HALF, ((((t) >> G_SHIFT ) + (t) ) >> G_SHIFT )) + +#define DIV_UN8(a, b) \ + (((uint16_t) (a) * MASK + ((b) / 2)) / (b)) + +#ifndef ADD_UN8 +#define ADD_UN8(x, y, t) \ + ((t) = (x) + (y), \ + (uint32_t) (uint8_t) ((t) | (0 - ((t) >> G_SHIFT)))) +#endif + +#define DIV_ONE_UN8(x) \ + (((x) + ONE_HALF + (((x) + ONE_HALF) >> G_SHIFT)) >> G_SHIFT) + +/* + * The methods below use some tricks to be able to do two color + * components at the same time. + */ + +/* + * x_rb = (x_rb * a) / 255 + */ +#define UN8_rb_MUL_UN8(x, a, t) \ + do \ + { \ + t = ((x) & RB_MASK) * (a); \ + t += RB_ONE_HALF; \ + x = (t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT; \ + x &= RB_MASK; \ + } while (0) + +/* + * x_rb = min (x_rb + y_rb, 255) + */ +#ifndef UN8_rb_ADD_UN8_rb +#define UN8_rb_ADD_UN8_rb(x, y, t) \ + do \ + { \ + t = ((x) + (y)); \ + t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); \ + x = (t & RB_MASK); \ + } while (0) +#endif + +/* + * x_rb = (x_rb * a_rb) / 255 + */ +#define UN8_rb_MUL_UN8_rb(x, a, t) \ + do \ + { \ + t = (x & MASK) * (a & MASK); \ + t |= (x & R_MASK) * ((a >> R_SHIFT) & MASK); \ + t += RB_ONE_HALF; \ + t = (t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT; \ + x = t & RB_MASK; \ + } while (0) + +/* + * x_c = (x_c * a) / 255 + */ +#define UN8x4_MUL_UN8(x, a) \ + do \ + { \ + uint32_t r1__, r2__, t__; \ + \ + r1__ = (x); \ + UN8_rb_MUL_UN8 (r1__, (a), t__); \ + \ + r2__ = (x) >> G_SHIFT; \ + UN8_rb_MUL_UN8 (r2__, (a), t__); \ + \ + (x) = r1__ | (r2__ << G_SHIFT); \ + } while (0) + +/* + * x_c = (x_c * a) / 255 + y_c + */ +#define UN8x4_MUL_UN8_ADD_UN8x4(x, a, y) \ + do \ + { \ + uint32_t r1__, r2__, r3__, t__; \ + \ + r1__ = (x); \ + r2__ = (y) & RB_MASK; \ + UN8_rb_MUL_UN8 (r1__, (a), t__); \ + UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ + \ + r2__ = (x) >> G_SHIFT; \ + r3__ = ((y) >> G_SHIFT) & RB_MASK; \ + UN8_rb_MUL_UN8 (r2__, (a), t__); \ + UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ + \ + (x) = r1__ | (r2__ << G_SHIFT); \ + } while (0) + +/* + * x_c = (x_c * a + y_c * b) / 255 + */ +#define UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8(x, a, y, b) \ + do \ + { \ + uint32_t r1__, r2__, r3__, t__; \ + \ + r1__ = (x); \ + r2__ = (y); \ + UN8_rb_MUL_UN8 (r1__, (a), t__); \ + UN8_rb_MUL_UN8 (r2__, (b), t__); \ + UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ + \ + r2__ = ((x) >> G_SHIFT); \ + r3__ = ((y) >> G_SHIFT); \ + UN8_rb_MUL_UN8 (r2__, (a), t__); \ + UN8_rb_MUL_UN8 (r3__, (b), t__); \ + UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ + \ + (x) = r1__ | (r2__ << G_SHIFT); \ + } while (0) + +/* + * x_c = (x_c * a_c) / 255 + */ +#define UN8x4_MUL_UN8x4(x, a) \ + do \ + { \ + uint32_t r1__, r2__, r3__, t__; \ + \ + r1__ = (x); \ + r2__ = (a); \ + UN8_rb_MUL_UN8_rb (r1__, r2__, t__); \ + \ + r2__ = (x) >> G_SHIFT; \ + r3__ = (a) >> G_SHIFT; \ + UN8_rb_MUL_UN8_rb (r2__, r3__, t__); \ + \ + (x) = r1__ | (r2__ << G_SHIFT); \ + } while (0) + +/* + * x_c = (x_c * a_c) / 255 + y_c + */ +#define UN8x4_MUL_UN8x4_ADD_UN8x4(x, a, y) \ + do \ + { \ + uint32_t r1__, r2__, r3__, t__; \ + \ + r1__ = (x); \ + r2__ = (a); \ + UN8_rb_MUL_UN8_rb (r1__, r2__, t__); \ + r2__ = (y) & RB_MASK; \ + UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ + \ + r2__ = ((x) >> G_SHIFT); \ + r3__ = ((a) >> G_SHIFT); \ + UN8_rb_MUL_UN8_rb (r2__, r3__, t__); \ + r3__ = ((y) >> G_SHIFT) & RB_MASK; \ + UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ + \ + (x) = r1__ | (r2__ << G_SHIFT); \ + } while (0) + +/* + * x_c = (x_c * a_c + y_c * b) / 255 + */ +#define UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8(x, a, y, b) \ + do \ + { \ + uint32_t r1__, r2__, r3__, t__; \ + \ + r1__ = (x); \ + r2__ = (a); \ + UN8_rb_MUL_UN8_rb (r1__, r2__, t__); \ + r2__ = (y); \ + UN8_rb_MUL_UN8 (r2__, (b), t__); \ + UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ + \ + r2__ = (x) >> G_SHIFT; \ + r3__ = (a) >> G_SHIFT; \ + UN8_rb_MUL_UN8_rb (r2__, r3__, t__); \ + r3__ = (y) >> G_SHIFT; \ + UN8_rb_MUL_UN8 (r3__, (b), t__); \ + UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ + \ + x = r1__ | (r2__ << G_SHIFT); \ + } while (0) + +/* + x_c = min(x_c + y_c, 255) +*/ +#ifndef UN8x4_ADD_UN8x4 +#define UN8x4_ADD_UN8x4(x, y) \ + do \ + { \ + uint32_t r1__, r2__, r3__, t__; \ + \ + r1__ = (x) & RB_MASK; \ + r2__ = (y) & RB_MASK; \ + UN8_rb_ADD_UN8_rb (r1__, r2__, t__); \ + \ + r2__ = ((x) >> G_SHIFT) & RB_MASK; \ + r3__ = ((y) >> G_SHIFT) & RB_MASK; \ + UN8_rb_ADD_UN8_rb (r2__, r3__, t__); \ + \ + x = r1__ | (r2__ << G_SHIFT); \ + } while (0) +#endif diff --git a/gfx/cairo/libpixman/src/pixman-compiler.h b/gfx/cairo/libpixman/src/pixman-compiler.h new file mode 100644 index 0000000000..dd62dc7ef3 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-compiler.h @@ -0,0 +1,253 @@ +/* Pixman uses some non-standard compiler features. This file ensures + * they exist + * + * The features are: + * + * FUNC must be defined to expand to the current function + * PIXMAN_EXPORT should be defined to whatever is required to + * export functions from a shared library + * limits limits for various types must be defined + * inline must be defined + * force_inline must be defined + */ +#if defined (__GNUC__) +# define FUNC ((const char*) (__PRETTY_FUNCTION__)) +#elif defined (__sun) || (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) +# define FUNC ((const char*) (__func__)) +#else +# define FUNC ((const char*) ("???")) +#endif + +#if defined (__GNUC__) +# define unlikely(expr) __builtin_expect ((expr), 0) +#else +# define unlikely(expr) (expr) +#endif + +#if defined (__GNUC__) +# define MAYBE_UNUSED __attribute__((unused)) +#else +# define MAYBE_UNUSED +#endif + +#ifndef INT16_MIN +# define INT16_MIN (-32767-1) +#endif + +#ifndef INT16_MAX +# define INT16_MAX (32767) +#endif + +#ifndef INT32_MIN +# define INT32_MIN (-2147483647-1) +#endif + +#ifndef INT32_MAX +# define INT32_MAX (2147483647) +#endif + +#ifndef UINT32_MIN +# define UINT32_MIN (0) +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (4294967295U) +#endif + +#ifndef INT64_MIN +# define INT64_MIN (-9223372036854775807-1) +#endif + +#ifndef INT64_MAX +# define INT64_MAX (9223372036854775807) +#endif + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t)-1) +#endif + + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +#ifdef _MSC_VER +/* 'inline' is available only in C++ in MSVC */ +# define inline __inline +# define force_inline __forceinline +# define noinline __declspec(noinline) +#elif defined __GNUC__ || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) +# define inline __inline__ +# define force_inline __inline__ __attribute__ ((__always_inline__)) +# define noinline __attribute__((noinline)) +#else +# ifndef force_inline +# define force_inline inline +# endif +# ifndef noinline +# define noinline +# endif +#endif + +/* In libxul builds we don't ever want to export pixman symbols */ +#if 1 +#include "prcpucfg.h" + +#ifdef HAVE_VISIBILITY_HIDDEN_ATTRIBUTE +#define CVISIBILITY_HIDDEN __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define CVISIBILITY_HIDDEN __hidden +#else +#define CVISIBILITY_HIDDEN +#endif + +/* In libxul builds we don't ever want to export cairo symbols */ +#define PIXMAN_EXPORT extern CVISIBILITY_HIDDEN + +#else + +/* GCC visibility */ +#if defined(__GNUC__) && __GNUC__ >= 4 && !defined(_WIN32) +# define PIXMAN_EXPORT __attribute__ ((visibility("default"))) +/* Sun Studio 8 visibility */ +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +# define PIXMAN_EXPORT __global +#elif defined (_MSC_VER) || defined(__MINGW32__) +# define PIXMAN_EXPORT PIXMAN_API +#else +# define PIXMAN_EXPORT +#endif + +#endif + +/* member offsets */ +#define CONTAINER_OF(type, member, data) \ + ((type *)(((uint8_t *)data) - offsetof (type, member))) + +/* TLS */ +#if defined(PIXMAN_NO_TLS) + +# define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ + static type name; +# define PIXMAN_GET_THREAD_LOCAL(name) \ + (&name) + +#elif defined(TLS) + +# define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ + static TLS type name; +# define PIXMAN_GET_THREAD_LOCAL(name) \ + (&name) + +#elif defined(__MINGW32__) + +# define _NO_W32_PSEUDO_MODIFIERS +# include + +# define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ + static volatile int tls_ ## name ## _initialized = 0; \ + static void *tls_ ## name ## _mutex = NULL; \ + static unsigned tls_ ## name ## _index; \ + \ + static type * \ + tls_ ## name ## _alloc (void) \ + { \ + type *value = calloc (1, sizeof (type)); \ + if (value) \ + TlsSetValue (tls_ ## name ## _index, value); \ + return value; \ + } \ + \ + static force_inline type * \ + tls_ ## name ## _get (void) \ + { \ + type *value; \ + if (!tls_ ## name ## _initialized) \ + { \ + if (!tls_ ## name ## _mutex) \ + { \ + void *mutex = CreateMutexA (NULL, 0, NULL); \ + if (InterlockedCompareExchangePointer ( \ + &tls_ ## name ## _mutex, mutex, NULL) != NULL) \ + { \ + CloseHandle (mutex); \ + } \ + } \ + WaitForSingleObject (tls_ ## name ## _mutex, 0xFFFFFFFF); \ + if (!tls_ ## name ## _initialized) \ + { \ + tls_ ## name ## _index = TlsAlloc (); \ + tls_ ## name ## _initialized = 1; \ + } \ + ReleaseMutex (tls_ ## name ## _mutex); \ + } \ + if (tls_ ## name ## _index == 0xFFFFFFFF) \ + return NULL; \ + value = TlsGetValue (tls_ ## name ## _index); \ + if (!value) \ + value = tls_ ## name ## _alloc (); \ + return value; \ + } + +# define PIXMAN_GET_THREAD_LOCAL(name) \ + tls_ ## name ## _get () + +#elif defined(_MSC_VER) + +# define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ + static __declspec(thread) type name; +# define PIXMAN_GET_THREAD_LOCAL(name) \ + (&name) + +#elif defined(HAVE_PTHREADS) + +#include + +# define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ + static pthread_once_t tls_ ## name ## _once_control = PTHREAD_ONCE_INIT; \ + static pthread_key_t tls_ ## name ## _key; \ + \ + static void \ + tls_ ## name ## _destroy_value (void *value) \ + { \ + free (value); \ + } \ + \ + static void \ + tls_ ## name ## _make_key (void) \ + { \ + pthread_key_create (&tls_ ## name ## _key, \ + tls_ ## name ## _destroy_value); \ + } \ + \ + static type * \ + tls_ ## name ## _alloc (void) \ + { \ + type *value = calloc (1, sizeof (type)); \ + if (value) \ + pthread_setspecific (tls_ ## name ## _key, value); \ + return value; \ + } \ + \ + static force_inline type * \ + tls_ ## name ## _get (void) \ + { \ + type *value = NULL; \ + if (pthread_once (&tls_ ## name ## _once_control, \ + tls_ ## name ## _make_key) == 0) \ + { \ + value = pthread_getspecific (tls_ ## name ## _key); \ + if (!value) \ + value = tls_ ## name ## _alloc (); \ + } \ + return value; \ + } + +# define PIXMAN_GET_THREAD_LOCAL(name) \ + tls_ ## name ## _get () + +#else + +# error "Unknown thread local support for this system. Pixman will not work with multiple threads. Define PIXMAN_NO_TLS to acknowledge and accept this limitation and compile pixman without thread-safety support." + +#endif diff --git a/gfx/cairo/libpixman/src/pixman-conical-gradient.c b/gfx/cairo/libpixman/src/pixman-conical-gradient.c new file mode 100644 index 0000000000..a39e20c4eb --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-conical-gradient.c @@ -0,0 +1,220 @@ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. + * 2005 Lars Knoll & Zack Rusin, Trolltech + * + * 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. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "pixman-private.h" + +static force_inline double +coordinates_to_parameter (double x, double y, double angle) +{ + double t; + + t = atan2 (y, x) + angle; + + while (t < 0) + t += 2 * M_PI; + + while (t >= 2 * M_PI) + t -= 2 * M_PI; + + return 1 - t * (1 / (2 * M_PI)); /* Scale t to [0, 1] and + * make rotation CCW + */ +} + +static uint32_t * +conical_get_scanline (pixman_iter_t *iter, + const uint32_t *mask, + int Bpp, + pixman_gradient_walker_write_t write_pixel) +{ + pixman_image_t *image = iter->image; + int x = iter->x; + int y = iter->y; + int width = iter->width; + uint32_t *buffer = iter->buffer; + + gradient_t *gradient = (gradient_t *)image; + conical_gradient_t *conical = (conical_gradient_t *)image; + uint32_t *end = buffer + width * (Bpp / 4); + pixman_gradient_walker_t walker; + pixman_bool_t affine = TRUE; + double cx = 1.; + double cy = 0.; + double cz = 0.; + double rx = x + 0.5; + double ry = y + 0.5; + double rz = 1.; + + _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); + + if (image->common.transform) + { + pixman_vector_t v; + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point_3d (image->common.transform, &v)) + return iter->buffer; + + cx = image->common.transform->matrix[0][0] / 65536.; + cy = image->common.transform->matrix[1][0] / 65536.; + cz = image->common.transform->matrix[2][0] / 65536.; + + rx = v.vector[0] / 65536.; + ry = v.vector[1] / 65536.; + rz = v.vector[2] / 65536.; + + affine = + image->common.transform->matrix[2][0] == 0 && + v.vector[2] == pixman_fixed_1; + } + + if (affine) + { + rx -= conical->center.x / 65536.; + ry -= conical->center.y / 65536.; + + while (buffer < end) + { + if (!mask || *mask++) + { + double t = coordinates_to_parameter (rx, ry, conical->angle); + + write_pixel (&walker, + (pixman_fixed_48_16_t)pixman_double_to_fixed (t), + buffer); + } + + buffer += (Bpp / 4); + + rx += cx; + ry += cy; + } + } + else + { + while (buffer < end) + { + double x, y; + + if (!mask || *mask++) + { + double t; + + if (rz != 0) + { + x = rx / rz; + y = ry / rz; + } + else + { + x = y = 0.; + } + + x -= conical->center.x / 65536.; + y -= conical->center.y / 65536.; + + t = coordinates_to_parameter (x, y, conical->angle); + + write_pixel (&walker, + (pixman_fixed_48_16_t)pixman_double_to_fixed (t), + buffer); + } + + buffer += (Bpp / 4); + + rx += cx; + ry += cy; + rz += cz; + } + } + + iter->y++; + return iter->buffer; +} + +static uint32_t * +conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) +{ + return conical_get_scanline (iter, mask, 4, + _pixman_gradient_walker_write_narrow); +} + +static uint32_t * +conical_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) +{ + return conical_get_scanline (iter, NULL, 16, + _pixman_gradient_walker_write_wide); +} + +void +_pixman_conical_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) +{ + if (iter->iter_flags & ITER_NARROW) + iter->get_scanline = conical_get_scanline_narrow; + else + iter->get_scanline = conical_get_scanline_wide; +} + +PIXMAN_EXPORT pixman_image_t * +pixman_image_create_conical_gradient (const pixman_point_fixed_t * center, + pixman_fixed_t angle, + const pixman_gradient_stop_t *stops, + int n_stops) +{ + pixman_image_t *image = _pixman_image_allocate (); + conical_gradient_t *conical; + + if (!image) + return NULL; + + conical = &image->conical; + + if (!_pixman_init_gradient (&conical->common, stops, n_stops)) + { + free (image); + return NULL; + } + + angle = MOD (angle, pixman_int_to_fixed (360)); + + image->type = CONICAL; + + conical->center = *center; + conical->angle = (pixman_fixed_to_double (angle) / 180.0) * M_PI; + + return image; +} + diff --git a/gfx/cairo/libpixman/src/pixman-edge-accessors.c b/gfx/cairo/libpixman/src/pixman-edge-accessors.c new file mode 100644 index 0000000000..ea3a31e2f5 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-edge-accessors.c @@ -0,0 +1,4 @@ + +#define PIXMAN_FB_ACCESSORS + +#include "pixman-edge.c" diff --git a/gfx/cairo/libpixman/src/pixman-edge-imp.h b/gfx/cairo/libpixman/src/pixman-edge-imp.h new file mode 100644 index 0000000000..a4698eddb2 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-edge-imp.h @@ -0,0 +1,182 @@ +/* + * Copyright © 2004 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. + */ + +#ifndef rasterize_span +#endif + +static void +RASTERIZE_EDGES (pixman_image_t *image, + pixman_edge_t *l, + pixman_edge_t *r, + pixman_fixed_t t, + pixman_fixed_t b) +{ + pixman_fixed_t y = t; + uint32_t *line; + uint32_t *buf = (image)->bits.bits; + int stride = (image)->bits.rowstride; + int width = (image)->bits.width; + + line = buf + pixman_fixed_to_int (y) * stride; + + for (;;) + { + pixman_fixed_t lx; + pixman_fixed_t rx; + int lxi; + int rxi; + + lx = l->x; + rx = r->x; +#if N_BITS == 1 + /* For the non-antialiased case, round the coordinates up, in effect + * sampling just slightly to the left of the pixel. This is so that + * when the sample point lies exactly on the line, we round towards + * north-west. + * + * (The AA case does a similar adjustment in RENDER_SAMPLES_X) + */ + lx += X_FRAC_FIRST(1) - pixman_fixed_e; + rx += X_FRAC_FIRST(1) - pixman_fixed_e; +#endif + /* clip X */ + if (lx < 0) + lx = 0; + if (pixman_fixed_to_int (rx) >= width) +#if N_BITS == 1 + rx = pixman_int_to_fixed (width); +#else + /* Use the last pixel of the scanline, covered 100%. + * We can't use the first pixel following the scanline, + * because accessing it could result in a buffer overrun. + */ + rx = pixman_int_to_fixed (width) - 1; +#endif + + /* Skip empty (or backwards) sections */ + if (rx > lx) + { + + /* Find pixel bounds for span */ + lxi = pixman_fixed_to_int (lx); + rxi = pixman_fixed_to_int (rx); + +#if N_BITS == 1 + { + +#define LEFT_MASK(x) \ + (((x) & 0x1f) ? \ + SCREEN_SHIFT_RIGHT (0xffffffff, (x) & 0x1f) : 0) +#define RIGHT_MASK(x) \ + (((32 - (x)) & 0x1f) ? \ + SCREEN_SHIFT_LEFT (0xffffffff, (32 - (x)) & 0x1f) : 0) + +#define MASK_BITS(x,w,l,n,r) { \ + n = (w); \ + r = RIGHT_MASK ((x) + n); \ + l = LEFT_MASK (x); \ + if (l) { \ + n -= 32 - ((x) & 0x1f); \ + if (n < 0) { \ + n = 0; \ + l &= r; \ + r = 0; \ + } \ + } \ + n >>= 5; \ + } + + uint32_t *a = line; + uint32_t startmask; + uint32_t endmask; + int nmiddle; + int width = rxi - lxi; + int x = lxi; + + a += x >> 5; + x &= 0x1f; + + MASK_BITS (x, width, startmask, nmiddle, endmask); + + if (startmask) { + WRITE(image, a, READ(image, a) | startmask); + a++; + } + while (nmiddle--) + WRITE(image, a++, 0xffffffff); + if (endmask) + WRITE(image, a, READ(image, a) | endmask); + } +#else + { + DEFINE_ALPHA(line,lxi); + int lxs; + int rxs; + + /* Sample coverage for edge pixels */ + lxs = RENDER_SAMPLES_X (lx, N_BITS); + rxs = RENDER_SAMPLES_X (rx, N_BITS); + + /* Add coverage across row */ + if (lxi == rxi) + { + ADD_ALPHA (rxs - lxs); + } + else + { + int xi; + + ADD_ALPHA (N_X_FRAC(N_BITS) - lxs); + STEP_ALPHA; + for (xi = lxi + 1; xi < rxi; xi++) + { + ADD_ALPHA (N_X_FRAC(N_BITS)); + STEP_ALPHA; + } + ADD_ALPHA (rxs); + } + } +#endif + } + + if (y == b) + break; + +#if N_BITS > 1 + if (pixman_fixed_frac (y) != Y_FRAC_LAST(N_BITS)) + { + RENDER_EDGE_STEP_SMALL (l); + RENDER_EDGE_STEP_SMALL (r); + y += STEP_Y_SMALL(N_BITS); + } + else +#endif + { + RENDER_EDGE_STEP_BIG (l); + RENDER_EDGE_STEP_BIG (r); + y += STEP_Y_BIG(N_BITS); + line += stride; + } + } +} + +#undef rasterize_span diff --git a/gfx/cairo/libpixman/src/pixman-edge.c b/gfx/cairo/libpixman/src/pixman-edge.c new file mode 100644 index 0000000000..ad6dfc4cfa --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-edge.c @@ -0,0 +1,385 @@ +/* + * Copyright © 2004 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "pixman-private.h" +#include "pixman-accessor.h" + +/* + * Step across a small sample grid gap + */ +#define RENDER_EDGE_STEP_SMALL(edge) \ + { \ + edge->x += edge->stepx_small; \ + edge->e += edge->dx_small; \ + if (edge->e > 0) \ + { \ + edge->e -= edge->dy; \ + edge->x += edge->signdx; \ + } \ + } + +/* + * Step across a large sample grid gap + */ +#define RENDER_EDGE_STEP_BIG(edge) \ + { \ + edge->x += edge->stepx_big; \ + edge->e += edge->dx_big; \ + if (edge->e > 0) \ + { \ + edge->e -= edge->dy; \ + edge->x += edge->signdx; \ + } \ + } + +#ifdef PIXMAN_FB_ACCESSORS +#define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_accessors +#else +#define PIXMAN_RASTERIZE_EDGES pixman_rasterize_edges_no_accessors +#endif + +/* + * 4 bit alpha + */ + +#define N_BITS 4 +#define RASTERIZE_EDGES rasterize_edges_4 + +#ifndef WORDS_BIGENDIAN +#define SHIFT_4(o) ((o) << 2) +#else +#define SHIFT_4(o) ((1 - (o)) << 2) +#endif + +#define GET_4(x, o) (((x) >> SHIFT_4 (o)) & 0xf) +#define PUT_4(x, o, v) \ + (((x) & ~(0xf << SHIFT_4 (o))) | (((v) & 0xf) << SHIFT_4 (o))) + +#define DEFINE_ALPHA(line, x) \ + uint8_t *__ap = (uint8_t *) line + ((x) >> 1); \ + int __ao = (x) & 1 + +#define STEP_ALPHA ((__ap += __ao), (__ao ^= 1)) + +#define ADD_ALPHA(a) \ + { \ + uint8_t __o = READ (image, __ap); \ + uint8_t __a = (a) + GET_4 (__o, __ao); \ + WRITE (image, __ap, PUT_4 (__o, __ao, __a | (0 - ((__a) >> 4)))); \ + } + +#include "pixman-edge-imp.h" + +#undef ADD_ALPHA +#undef STEP_ALPHA +#undef DEFINE_ALPHA +#undef RASTERIZE_EDGES +#undef N_BITS + + +/* + * 1 bit alpha + */ + +#define N_BITS 1 +#define RASTERIZE_EDGES rasterize_edges_1 + +#include "pixman-edge-imp.h" + +#undef RASTERIZE_EDGES +#undef N_BITS + +/* + * 8 bit alpha + */ + +static force_inline uint8_t +clip255 (int x) +{ + if (x > 255) + return 255; + + return x; +} + +#define ADD_SATURATE_8(buf, val, length) \ + do \ + { \ + int i__ = (length); \ + uint8_t *buf__ = (buf); \ + int val__ = (val); \ + \ + while (i__--) \ + { \ + WRITE (image, (buf__), clip255 (READ (image, (buf__)) + (val__))); \ + (buf__)++; \ + } \ + } while (0) + +/* + * We want to detect the case where we add the same value to a long + * span of pixels. The triangles on the end are filled in while we + * count how many sub-pixel scanlines contribute to the middle section. + * + * +--------------------------+ + * fill_height =| \ / + * +------------------+ + * |================| + * fill_start fill_end + */ +static void +rasterize_edges_8 (pixman_image_t *image, + pixman_edge_t * l, + pixman_edge_t * r, + pixman_fixed_t t, + pixman_fixed_t b) +{ + pixman_fixed_t y = t; + uint32_t *line; + int fill_start = -1, fill_end = -1; + int fill_size = 0; + uint32_t *buf = (image)->bits.bits; + int stride = (image)->bits.rowstride; + int width = (image)->bits.width; + + line = buf + pixman_fixed_to_int (y) * stride; + + for (;;) + { + uint8_t *ap = (uint8_t *) line; + pixman_fixed_t lx, rx; + int lxi, rxi; + + /* clip X */ + lx = l->x; + if (lx < 0) + lx = 0; + + rx = r->x; + + if (pixman_fixed_to_int (rx) >= width) + { + /* Use the last pixel of the scanline, covered 100%. + * We can't use the first pixel following the scanline, + * because accessing it could result in a buffer overrun. + */ + rx = pixman_int_to_fixed (width) - 1; + } + + /* Skip empty (or backwards) sections */ + if (rx > lx) + { + int lxs, rxs; + + /* Find pixel bounds for span. */ + lxi = pixman_fixed_to_int (lx); + rxi = pixman_fixed_to_int (rx); + + /* Sample coverage for edge pixels */ + lxs = RENDER_SAMPLES_X (lx, 8); + rxs = RENDER_SAMPLES_X (rx, 8); + + /* Add coverage across row */ + if (lxi == rxi) + { + WRITE (image, ap + lxi, + clip255 (READ (image, ap + lxi) + rxs - lxs)); + } + else + { + WRITE (image, ap + lxi, + clip255 (READ (image, ap + lxi) + N_X_FRAC (8) - lxs)); + + /* Move forward so that lxi/rxi is the pixel span */ + lxi++; + + /* Don't bother trying to optimize the fill unless + * the span is longer than 4 pixels. */ + if (rxi - lxi > 4) + { + if (fill_start < 0) + { + fill_start = lxi; + fill_end = rxi; + fill_size++; + } + else + { + if (lxi >= fill_end || rxi < fill_start) + { + /* We're beyond what we saved, just fill it */ + ADD_SATURATE_8 (ap + fill_start, + fill_size * N_X_FRAC (8), + fill_end - fill_start); + fill_start = lxi; + fill_end = rxi; + fill_size = 1; + } + else + { + /* Update fill_start */ + if (lxi > fill_start) + { + ADD_SATURATE_8 (ap + fill_start, + fill_size * N_X_FRAC (8), + lxi - fill_start); + fill_start = lxi; + } + else if (lxi < fill_start) + { + ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), + fill_start - lxi); + } + + /* Update fill_end */ + if (rxi < fill_end) + { + ADD_SATURATE_8 (ap + rxi, + fill_size * N_X_FRAC (8), + fill_end - rxi); + fill_end = rxi; + } + else if (fill_end < rxi) + { + ADD_SATURATE_8 (ap + fill_end, + N_X_FRAC (8), + rxi - fill_end); + } + fill_size++; + } + } + } + else + { + ADD_SATURATE_8 (ap + lxi, N_X_FRAC (8), rxi - lxi); + } + + WRITE (image, ap + rxi, clip255 (READ (image, ap + rxi) + rxs)); + } + } + + if (y == b) + { + /* We're done, make sure we clean up any remaining fill. */ + if (fill_start != fill_end) + { + if (fill_size == N_Y_FRAC (8)) + { + MEMSET_WRAPPED (image, ap + fill_start, + 0xff, fill_end - fill_start); + } + else + { + ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), + fill_end - fill_start); + } + } + break; + } + + if (pixman_fixed_frac (y) != Y_FRAC_LAST (8)) + { + RENDER_EDGE_STEP_SMALL (l); + RENDER_EDGE_STEP_SMALL (r); + y += STEP_Y_SMALL (8); + } + else + { + RENDER_EDGE_STEP_BIG (l); + RENDER_EDGE_STEP_BIG (r); + y += STEP_Y_BIG (8); + if (fill_start != fill_end) + { + if (fill_size == N_Y_FRAC (8)) + { + MEMSET_WRAPPED (image, ap + fill_start, + 0xff, fill_end - fill_start); + } + else + { + ADD_SATURATE_8 (ap + fill_start, fill_size * N_X_FRAC (8), + fill_end - fill_start); + } + + fill_start = fill_end = -1; + fill_size = 0; + } + + line += stride; + } + } +} + +#ifndef PIXMAN_FB_ACCESSORS +static +#endif +void +PIXMAN_RASTERIZE_EDGES (pixman_image_t *image, + pixman_edge_t * l, + pixman_edge_t * r, + pixman_fixed_t t, + pixman_fixed_t b) +{ + switch (PIXMAN_FORMAT_BPP (image->bits.format)) + { + case 1: + rasterize_edges_1 (image, l, r, t, b); + break; + + case 4: + rasterize_edges_4 (image, l, r, t, b); + break; + + case 8: + rasterize_edges_8 (image, l, r, t, b); + break; + + default: + break; + } +} + +#ifndef PIXMAN_FB_ACCESSORS + +PIXMAN_EXPORT void +pixman_rasterize_edges (pixman_image_t *image, + pixman_edge_t * l, + pixman_edge_t * r, + pixman_fixed_t t, + pixman_fixed_t b) +{ + return_if_fail (image->type == BITS); + return_if_fail (PIXMAN_FORMAT_TYPE (image->bits.format) == PIXMAN_TYPE_A); + + if (image->bits.read_func || image->bits.write_func) + pixman_rasterize_edges_accessors (image, l, r, t, b); + else + pixman_rasterize_edges_no_accessors (image, l, r, t, b); +} + +#endif diff --git a/gfx/cairo/libpixman/src/pixman-fast-path.c b/gfx/cairo/libpixman/src/pixman-fast-path.c new file mode 100644 index 0000000000..fa5ea03e59 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-fast-path.c @@ -0,0 +1,3298 @@ +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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: Keith Packard, SuSE, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include "pixman-private.h" +#include "pixman-combine32.h" +#include "pixman-inlines.h" + +static force_inline uint32_t +fetch_24 (uint8_t *a) +{ + if (((uintptr_t)a) & 1) + { +#ifdef WORDS_BIGENDIAN + return (*a << 16) | (*(uint16_t *)(a + 1)); +#else + return *a | (*(uint16_t *)(a + 1) << 8); +#endif + } + else + { +#ifdef WORDS_BIGENDIAN + return (*(uint16_t *)a << 8) | *(a + 2); +#else + return *(uint16_t *)a | (*(a + 2) << 16); +#endif + } +} + +static force_inline void +store_24 (uint8_t *a, + uint32_t v) +{ + if (((uintptr_t)a) & 1) + { +#ifdef WORDS_BIGENDIAN + *a = (uint8_t) (v >> 16); + *(uint16_t *)(a + 1) = (uint16_t) (v); +#else + *a = (uint8_t) (v); + *(uint16_t *)(a + 1) = (uint16_t) (v >> 8); +#endif + } + else + { +#ifdef WORDS_BIGENDIAN + *(uint16_t *)a = (uint16_t)(v >> 8); + *(a + 2) = (uint8_t)v; +#else + *(uint16_t *)a = (uint16_t)v; + *(a + 2) = (uint8_t)(v >> 16); +#endif + } +} + +static force_inline uint32_t +over (uint32_t src, + uint32_t dest) +{ + uint32_t a = ~src >> 24; + + UN8x4_MUL_UN8_ADD_UN8x4 (dest, a, src); + + return dest; +} + +static force_inline uint32_t +in (uint32_t x, + uint8_t y) +{ + uint16_t a = y; + + UN8x4_MUL_UN8 (x, a); + + return x; +} + +/* + * Naming convention: + * + * op_src_mask_dest + */ +static void +fast_composite_over_x888_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *src, *src_line; + uint32_t *dst, *dst_line; + uint8_t *mask, *mask_line; + int src_stride, mask_stride, dst_stride; + uint8_t m; + uint32_t s, d; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + src = src_line; + src_line += src_stride; + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + + w = width; + while (w--) + { + m = *mask++; + if (m) + { + s = *src | 0xff000000; + + if (m == 0xff) + { + *dst = s; + } + else + { + d = in (s, m); + *dst = over (d, *dst); + } + } + src++; + dst++; + } + } +} + +static void +fast_composite_in_n_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint8_t *dst_line, *dst; + uint8_t *mask_line, *mask, m; + int dst_stride, mask_stride; + int32_t w; + uint16_t t; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + if (srca == 0xff) + { + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + m = *mask++; + + if (m == 0) + *dst = 0; + else if (m != 0xff) + *dst = MUL_UN8 (m, *dst, t); + + dst++; + } + } + } + else + { + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + m = *mask++; + m = MUL_UN8 (m, srca, t); + + if (m == 0) + *dst = 0; + else if (m != 0xff) + *dst = MUL_UN8 (m, *dst, t); + + dst++; + } + } + } +} + +static void +fast_composite_in_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + uint8_t s; + uint16_t t; + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + s = *src++; + + if (s == 0) + *dst = 0; + else if (s != 0xff) + *dst = MUL_UN8 (s, *dst, t); + + dst++; + } + } +} + +static void +fast_composite_over_n_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint32_t *dst_line, *dst, d; + uint8_t *mask_line, *mask, m; + int dst_stride, mask_stride; + int32_t w; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + m = *mask++; + if (m == 0xff) + { + if (srca == 0xff) + *dst = src; + else + *dst = over (src, *dst); + } + else if (m) + { + d = in (src, m); + *dst = over (d, *dst); + } + dst++; + } + } +} + +static void +fast_composite_add_n_8888_8888_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, s; + uint32_t *dst_line, *dst, d; + uint32_t *mask_line, *mask, ma; + int dst_stride, mask_stride; + int32_t w; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + ma = *mask++; + + if (ma) + { + d = *dst; + s = src; + + UN8x4_MUL_UN8x4_ADD_UN8x4 (s, ma, d); + + *dst = s; + } + + dst++; + } + } +} + +static void +fast_composite_over_n_8888_8888_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca, s; + uint32_t *dst_line, *dst, d; + uint32_t *mask_line, *mask, ma; + int dst_stride, mask_stride; + int32_t w; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + ma = *mask++; + if (ma == 0xffffffff) + { + if (srca == 0xff) + *dst = src; + else + *dst = over (src, *dst); + } + else if (ma) + { + d = *dst; + s = src; + + UN8x4_MUL_UN8x4 (s, ma); + UN8x4_MUL_UN8 (ma, srca); + ma = ~ma; + UN8x4_MUL_UN8x4_ADD_UN8x4 (d, ma, s); + + *dst = d; + } + + dst++; + } + } +} + +static void +fast_composite_over_n_8_0888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint8_t *dst_line, *dst; + uint32_t d; + uint8_t *mask_line, *mask, m; + int dst_stride, mask_stride; + int32_t w; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 3); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + m = *mask++; + if (m == 0xff) + { + if (srca == 0xff) + { + d = src; + } + else + { + d = fetch_24 (dst); + d = over (src, d); + } + store_24 (dst, d); + } + else if (m) + { + d = over (in (src, m), fetch_24 (dst)); + store_24 (dst, d); + } + dst += 3; + } + } +} + +static void +fast_composite_over_n_8_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint16_t *dst_line, *dst; + uint32_t d; + uint8_t *mask_line, *mask, m; + int dst_stride, mask_stride; + int32_t w; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + m = *mask++; + if (m == 0xff) + { + if (srca == 0xff) + { + d = src; + } + else + { + d = *dst; + d = over (src, convert_0565_to_0888 (d)); + } + *dst = convert_8888_to_0565 (d); + } + else if (m) + { + d = *dst; + d = over (in (src, m), convert_0565_to_0888 (d)); + *dst = convert_8888_to_0565 (d); + } + dst++; + } + } +} + +static void +fast_composite_over_n_8888_0565_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca, s; + uint16_t src16; + uint16_t *dst_line, *dst; + uint32_t d; + uint32_t *mask_line, *mask, ma; + int dst_stride, mask_stride; + int32_t w; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + return; + + src16 = convert_8888_to_0565 (src); + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + ma = *mask++; + if (ma == 0xffffffff) + { + if (srca == 0xff) + { + *dst = src16; + } + else + { + d = *dst; + d = over (src, convert_0565_to_0888 (d)); + *dst = convert_8888_to_0565 (d); + } + } + else if (ma) + { + d = *dst; + d = convert_0565_to_0888 (d); + + s = src; + + UN8x4_MUL_UN8x4 (s, ma); + UN8x4_MUL_UN8 (ma, srca); + ma = ~ma; + UN8x4_MUL_UN8x4_ADD_UN8x4 (d, ma, s); + + *dst = convert_8888_to_0565 (d); + } + dst++; + } + } +} + +static void +fast_composite_over_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src, s; + int dst_stride, src_stride; + uint8_t a; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + s = *src++; + a = s >> 24; + if (a == 0xff) + *dst = s; + else if (s) + *dst = over (s, *dst); + dst++; + } + } +} + +static void +fast_composite_src_x888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + *dst++ = (*src++) | 0xff000000; + } +} + +#if 0 +static void +fast_composite_over_8888_0888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint32_t d; + uint32_t *src_line, *src, s; + uint8_t a; + int dst_stride, src_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 3); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + s = *src++; + a = s >> 24; + if (a) + { + if (a == 0xff) + d = s; + else + d = over (s, fetch_24 (dst)); + + store_24 (dst, d); + } + dst += 3; + } + } +} +#endif + +static void +fast_composite_over_8888_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst; + uint32_t d; + uint32_t *src_line, *src, s; + uint8_t a; + int dst_stride, src_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + s = *src++; + a = s >> 24; + if (s) + { + if (a == 0xff) + { + d = s; + } + else + { + d = *dst; + d = over (s, convert_0565_to_0888 (d)); + } + *dst = convert_8888_to_0565 (d); + } + dst++; + } + } +} + +static void +fast_composite_add_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + uint8_t s, d; + uint16_t t; + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + s = *src++; + if (s) + { + if (s != 0xff) + { + d = *dst; + t = d + s; + s = t | (0 - (t >> 8)); + } + *dst = s; + } + dst++; + } + } +} + +static void +fast_composite_add_0565_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst; + uint32_t d; + uint16_t *src_line, *src; + uint32_t s; + int dst_stride, src_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint16_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + s = *src++; + if (s) + { + d = *dst; + s = convert_0565_to_8888 (s); + if (d) + { + d = convert_0565_to_8888 (d); + UN8x4_ADD_UN8x4 (s, d); + } + *dst = convert_8888_to_0565 (s); + } + dst++; + } + } +} + +static void +fast_composite_add_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + uint32_t s, d; + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + s = *src++; + if (s) + { + if (s != 0xffffffff) + { + d = *dst; + if (d) + UN8x4_ADD_UN8x4 (s, d); + } + *dst = s; + } + dst++; + } + } +} + +static void +fast_composite_add_n_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + uint32_t src; + uint8_t sa; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + sa = (src >> 24); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w--) + { + uint16_t tmp; + uint16_t a; + uint32_t m, d; + uint32_t r; + + a = *mask++; + d = *dst; + + m = MUL_UN8 (sa, a, tmp); + r = ADD_UN8 (m, d, tmp); + + *dst++ = r; + } + } +} + +#ifdef WORDS_BIGENDIAN +#define CREATE_BITMASK(n) (0x80000000 >> (n)) +#define UPDATE_BITMASK(n) ((n) >> 1) +#else +#define CREATE_BITMASK(n) (1U << (n)) +#define UPDATE_BITMASK(n) ((n) << 1) +#endif + +#define TEST_BIT(p, n) \ + (*((p) + ((n) >> 5)) & CREATE_BITMASK ((n) & 31)) +#define SET_BIT(p, n) \ + do { *((p) + ((n) >> 5)) |= CREATE_BITMASK ((n) & 31); } while (0); + +static void +fast_composite_add_1_1 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (src_image, 0, src_y, uint32_t, + src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, 0, dest_y, uint32_t, + dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + /* + * TODO: improve performance by processing uint32_t data instead + * of individual bits + */ + if (TEST_BIT (src, src_x + w)) + SET_BIT (dst, dest_x + w); + } + } +} + +static void +fast_composite_over_n_1_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint32_t *dst, *dst_line; + uint32_t *mask, *mask_line; + int mask_stride, dst_stride; + uint32_t bitcache, bitmask; + int32_t w; + + if (width <= 0) + return; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + srca = src >> 24; + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, + dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, 0, mask_y, uint32_t, + mask_stride, mask_line, 1); + mask_line += mask_x >> 5; + + if (srca == 0xff) + { + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + bitcache = *mask++; + bitmask = CREATE_BITMASK (mask_x & 31); + + while (w--) + { + if (bitmask == 0) + { + bitcache = *mask++; + bitmask = CREATE_BITMASK (0); + } + if (bitcache & bitmask) + *dst = src; + bitmask = UPDATE_BITMASK (bitmask); + dst++; + } + } + } + else + { + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + bitcache = *mask++; + bitmask = CREATE_BITMASK (mask_x & 31); + + while (w--) + { + if (bitmask == 0) + { + bitcache = *mask++; + bitmask = CREATE_BITMASK (0); + } + if (bitcache & bitmask) + *dst = over (src, *dst); + bitmask = UPDATE_BITMASK (bitmask); + dst++; + } + } + } +} + +static void +fast_composite_over_n_1_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint16_t *dst, *dst_line; + uint32_t *mask, *mask_line; + int mask_stride, dst_stride; + uint32_t bitcache, bitmask; + int32_t w; + uint32_t d; + uint16_t src565; + + if (width <= 0) + return; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + srca = src >> 24; + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, + dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, 0, mask_y, uint32_t, + mask_stride, mask_line, 1); + mask_line += mask_x >> 5; + + if (srca == 0xff) + { + src565 = convert_8888_to_0565 (src); + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + bitcache = *mask++; + bitmask = CREATE_BITMASK (mask_x & 31); + + while (w--) + { + if (bitmask == 0) + { + bitcache = *mask++; + bitmask = CREATE_BITMASK (0); + } + if (bitcache & bitmask) + *dst = src565; + bitmask = UPDATE_BITMASK (bitmask); + dst++; + } + } + } + else + { + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + bitcache = *mask++; + bitmask = CREATE_BITMASK (mask_x & 31); + + while (w--) + { + if (bitmask == 0) + { + bitcache = *mask++; + bitmask = CREATE_BITMASK (0); + } + if (bitcache & bitmask) + { + d = over (src, convert_0565_to_0888 (*dst)); + *dst = convert_8888_to_0565 (d); + } + bitmask = UPDATE_BITMASK (bitmask); + dst++; + } + } + } +} + +/* + * Simple bitblt + */ + +static void +fast_composite_solid_fill (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (dest_image->bits.format == PIXMAN_a1) + { + src = src >> 31; + } + else if (dest_image->bits.format == PIXMAN_a8) + { + src = src >> 24; + } + else if (dest_image->bits.format == PIXMAN_r5g6b5 || + dest_image->bits.format == PIXMAN_b5g6r5) + { + src = convert_8888_to_0565 (src); + } + + pixman_fill (dest_image->bits.bits, dest_image->bits.rowstride, + PIXMAN_FORMAT_BPP (dest_image->bits.format), + dest_x, dest_y, + width, height, + src); +} + +static void +fast_composite_src_memcpy (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + int bpp = PIXMAN_FORMAT_BPP (dest_image->bits.format) / 8; + uint32_t n_bytes = width * bpp; + int dst_stride, src_stride; + uint8_t *dst; + uint8_t *src; + + src_stride = src_image->bits.rowstride * 4; + dst_stride = dest_image->bits.rowstride * 4; + + src = (uint8_t *)src_image->bits.bits + src_y * src_stride + src_x * bpp; + dst = (uint8_t *)dest_image->bits.bits + dest_y * dst_stride + dest_x * bpp; + + while (height--) + { + memcpy (dst, src, n_bytes); + + dst += dst_stride; + src += src_stride; + } +} + +FAST_NEAREST (8888_8888_cover, 8888, 8888, uint32_t, uint32_t, SRC, COVER) +FAST_NEAREST (8888_8888_none, 8888, 8888, uint32_t, uint32_t, SRC, NONE) +FAST_NEAREST (8888_8888_pad, 8888, 8888, uint32_t, uint32_t, SRC, PAD) +FAST_NEAREST (8888_8888_normal, 8888, 8888, uint32_t, uint32_t, SRC, NORMAL) +FAST_NEAREST (x888_8888_cover, x888, 8888, uint32_t, uint32_t, SRC, COVER) +FAST_NEAREST (x888_8888_pad, x888, 8888, uint32_t, uint32_t, SRC, PAD) +FAST_NEAREST (x888_8888_normal, x888, 8888, uint32_t, uint32_t, SRC, NORMAL) +FAST_NEAREST (8888_8888_cover, 8888, 8888, uint32_t, uint32_t, OVER, COVER) +FAST_NEAREST (8888_8888_none, 8888, 8888, uint32_t, uint32_t, OVER, NONE) +FAST_NEAREST (8888_8888_pad, 8888, 8888, uint32_t, uint32_t, OVER, PAD) +FAST_NEAREST (8888_8888_normal, 8888, 8888, uint32_t, uint32_t, OVER, NORMAL) +FAST_NEAREST (8888_565_cover, 8888, 0565, uint32_t, uint16_t, SRC, COVER) +FAST_NEAREST (8888_565_none, 8888, 0565, uint32_t, uint16_t, SRC, NONE) +FAST_NEAREST (8888_565_pad, 8888, 0565, uint32_t, uint16_t, SRC, PAD) +FAST_NEAREST (8888_565_normal, 8888, 0565, uint32_t, uint16_t, SRC, NORMAL) +FAST_NEAREST (565_565_normal, 0565, 0565, uint16_t, uint16_t, SRC, NORMAL) +FAST_NEAREST (8888_565_cover, 8888, 0565, uint32_t, uint16_t, OVER, COVER) +FAST_NEAREST (8888_565_none, 8888, 0565, uint32_t, uint16_t, OVER, NONE) +FAST_NEAREST (8888_565_pad, 8888, 0565, uint32_t, uint16_t, OVER, PAD) +FAST_NEAREST (8888_565_normal, 8888, 0565, uint32_t, uint16_t, OVER, NORMAL) + +#define REPEAT_MIN_WIDTH 32 + +static void +fast_composite_tiled_repeat (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + pixman_composite_func_t func; + pixman_format_code_t mask_format; + uint32_t src_flags, mask_flags; + int32_t sx, sy; + int32_t width_remain; + int32_t num_pixels; + int32_t src_width; + int32_t i, j; + pixman_image_t extended_src_image; + uint32_t extended_src[REPEAT_MIN_WIDTH * 2]; + pixman_bool_t need_src_extension; + uint32_t *src_line; + int32_t src_stride; + int32_t src_bpp; + pixman_composite_info_t info2 = *info; + + src_flags = (info->src_flags & ~FAST_PATH_NORMAL_REPEAT) | + FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; + + if (mask_image) + { + mask_format = mask_image->common.extended_format_code; + mask_flags = info->mask_flags; + } + else + { + mask_format = PIXMAN_null; + mask_flags = FAST_PATH_IS_OPAQUE; + } + + _pixman_implementation_lookup_composite ( + imp->toplevel, info->op, + src_image->common.extended_format_code, src_flags, + mask_format, mask_flags, + dest_image->common.extended_format_code, info->dest_flags, + &imp, &func); + + src_bpp = PIXMAN_FORMAT_BPP (src_image->bits.format); + + if (src_image->bits.width < REPEAT_MIN_WIDTH && + (src_bpp == 32 || src_bpp == 16 || src_bpp == 8) && + !src_image->bits.indexed) + { + sx = src_x; + sx = MOD (sx, src_image->bits.width); + sx += width; + src_width = 0; + + while (src_width < REPEAT_MIN_WIDTH && src_width <= sx) + src_width += src_image->bits.width; + + src_stride = (src_width * (src_bpp >> 3) + 3) / (int) sizeof (uint32_t); + + /* Initialize/validate stack-allocated temporary image */ + _pixman_bits_image_init (&extended_src_image, src_image->bits.format, + src_width, 1, &extended_src[0], src_stride, + FALSE); + _pixman_image_validate (&extended_src_image); + + info2.src_image = &extended_src_image; + need_src_extension = TRUE; + } + else + { + src_width = src_image->bits.width; + need_src_extension = FALSE; + } + + sx = src_x; + sy = src_y; + + while (--height >= 0) + { + sx = MOD (sx, src_width); + sy = MOD (sy, src_image->bits.height); + + if (need_src_extension) + { + if (src_bpp == 32) + { + PIXMAN_IMAGE_GET_LINE (src_image, 0, sy, uint32_t, src_stride, src_line, 1); + + for (i = 0; i < src_width; ) + { + for (j = 0; j < src_image->bits.width; j++, i++) + extended_src[i] = src_line[j]; + } + } + else if (src_bpp == 16) + { + uint16_t *src_line_16; + + PIXMAN_IMAGE_GET_LINE (src_image, 0, sy, uint16_t, src_stride, + src_line_16, 1); + src_line = (uint32_t*)src_line_16; + + for (i = 0; i < src_width; ) + { + for (j = 0; j < src_image->bits.width; j++, i++) + ((uint16_t*)extended_src)[i] = ((uint16_t*)src_line)[j]; + } + } + else if (src_bpp == 8) + { + uint8_t *src_line_8; + + PIXMAN_IMAGE_GET_LINE (src_image, 0, sy, uint8_t, src_stride, + src_line_8, 1); + src_line = (uint32_t*)src_line_8; + + for (i = 0; i < src_width; ) + { + for (j = 0; j < src_image->bits.width; j++, i++) + ((uint8_t*)extended_src)[i] = ((uint8_t*)src_line)[j]; + } + } + + info2.src_y = 0; + } + else + { + info2.src_y = sy; + } + + width_remain = width; + + while (width_remain > 0) + { + num_pixels = src_width - sx; + + if (num_pixels > width_remain) + num_pixels = width_remain; + + info2.src_x = sx; + info2.width = num_pixels; + info2.height = 1; + + func (imp, &info2); + + width_remain -= num_pixels; + info2.mask_x += num_pixels; + info2.dest_x += num_pixels; + sx = 0; + } + + sx = src_x; + sy++; + info2.mask_x = info->mask_x; + info2.mask_y++; + info2.dest_x = info->dest_x; + info2.dest_y++; + } + + if (need_src_extension) + _pixman_image_fini (&extended_src_image); +} + +/* Use more unrolling for src_0565_0565 because it is typically CPU bound */ +static force_inline void +scaled_nearest_scanline_565_565_SRC (uint16_t * dst, + const uint16_t * src, + int32_t w, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t max_vx, + pixman_bool_t fully_transparent_src) +{ + uint16_t tmp1, tmp2, tmp3, tmp4; + while ((w -= 4) >= 0) + { + tmp1 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + tmp2 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + tmp3 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + tmp4 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + *dst++ = tmp1; + *dst++ = tmp2; + *dst++ = tmp3; + *dst++ = tmp4; + } + if (w & 2) + { + tmp1 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + tmp2 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + *dst++ = tmp1; + *dst++ = tmp2; + } + if (w & 1) + *dst = *(src + pixman_fixed_to_int (vx)); +} + +FAST_NEAREST_MAINLOOP (565_565_cover_SRC, + scaled_nearest_scanline_565_565_SRC, + uint16_t, uint16_t, COVER) +FAST_NEAREST_MAINLOOP (565_565_none_SRC, + scaled_nearest_scanline_565_565_SRC, + uint16_t, uint16_t, NONE) +FAST_NEAREST_MAINLOOP (565_565_pad_SRC, + scaled_nearest_scanline_565_565_SRC, + uint16_t, uint16_t, PAD) + +static force_inline uint32_t +fetch_nearest (pixman_repeat_t src_repeat, + pixman_format_code_t format, + uint32_t *src, int x, int src_width) +{ + if (repeat (src_repeat, &x, src_width)) + { + if (format == PIXMAN_x8r8g8b8 || format == PIXMAN_x8b8g8r8) + return *(src + x) | 0xff000000; + else + return *(src + x); + } + else + { + return 0; + } +} + +static force_inline void +combine_over (uint32_t s, uint32_t *dst) +{ + if (s) + { + uint8_t ia = 0xff - (s >> 24); + + if (ia) + UN8x4_MUL_UN8_ADD_UN8x4 (*dst, ia, s); + else + *dst = s; + } +} + +static force_inline void +combine_src (uint32_t s, uint32_t *dst) +{ + *dst = s; +} + +static void +fast_composite_scaled_nearest (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line; + uint32_t *src_line; + int dst_stride, src_stride; + int src_width, src_height; + pixman_repeat_t src_repeat; + pixman_fixed_t unit_x, unit_y; + pixman_format_code_t src_format; + pixman_vector_t v; + pixman_fixed_t vy; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + /* pass in 0 instead of src_x and src_y because src_x and src_y need to be + * transformed from destination space to source space + */ + PIXMAN_IMAGE_GET_LINE (src_image, 0, 0, uint32_t, src_stride, src_line, 1); + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (src_x) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (src_y) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point_3d (src_image->common.transform, &v)) + return; + + unit_x = src_image->common.transform->matrix[0][0]; + unit_y = src_image->common.transform->matrix[1][1]; + + /* Round down to closest integer, ensuring that 0.5 rounds to 0, not 1 */ + v.vector[0] -= pixman_fixed_e; + v.vector[1] -= pixman_fixed_e; + + src_height = src_image->bits.height; + src_width = src_image->bits.width; + src_repeat = src_image->common.repeat; + src_format = src_image->bits.format; + + vy = v.vector[1]; + while (height--) + { + pixman_fixed_t vx = v.vector[0]; + int y = pixman_fixed_to_int (vy); + uint32_t *dst = dst_line; + + dst_line += dst_stride; + + /* adjust the y location by a unit vector in the y direction + * this is equivalent to transforming y+1 of the destination point to source space */ + vy += unit_y; + + if (!repeat (src_repeat, &y, src_height)) + { + if (op == PIXMAN_OP_SRC) + memset (dst, 0, sizeof (*dst) * width); + } + else + { + int w = width; + + uint32_t *src = src_line + y * src_stride; + + while (w >= 2) + { + uint32_t s1, s2; + int x1, x2; + + x1 = pixman_fixed_to_int (vx); + vx += unit_x; + + x2 = pixman_fixed_to_int (vx); + vx += unit_x; + + w -= 2; + + s1 = fetch_nearest (src_repeat, src_format, src, x1, src_width); + s2 = fetch_nearest (src_repeat, src_format, src, x2, src_width); + + if (op == PIXMAN_OP_OVER) + { + combine_over (s1, dst++); + combine_over (s2, dst++); + } + else + { + combine_src (s1, dst++); + combine_src (s2, dst++); + } + } + + while (w--) + { + uint32_t s; + int x; + + x = pixman_fixed_to_int (vx); + vx += unit_x; + + s = fetch_nearest (src_repeat, src_format, src, x, src_width); + + if (op == PIXMAN_OP_OVER) + combine_over (s, dst++); + else + combine_src (s, dst++); + } + } + } +} + +#define CACHE_LINE_SIZE 64 + +#define FAST_SIMPLE_ROTATE(suffix, pix_type) \ + \ +static void \ +blt_rotated_90_trivial_##suffix (pix_type *dst, \ + int dst_stride, \ + const pix_type *src, \ + int src_stride, \ + int w, \ + int h) \ +{ \ + int x, y; \ + for (y = 0; y < h; y++) \ + { \ + const pix_type *s = src + (h - y - 1); \ + pix_type *d = dst + dst_stride * y; \ + for (x = 0; x < w; x++) \ + { \ + *d++ = *s; \ + s += src_stride; \ + } \ + } \ +} \ + \ +static void \ +blt_rotated_270_trivial_##suffix (pix_type *dst, \ + int dst_stride, \ + const pix_type *src, \ + int src_stride, \ + int w, \ + int h) \ +{ \ + int x, y; \ + for (y = 0; y < h; y++) \ + { \ + const pix_type *s = src + src_stride * (w - 1) + y; \ + pix_type *d = dst + dst_stride * y; \ + for (x = 0; x < w; x++) \ + { \ + *d++ = *s; \ + s -= src_stride; \ + } \ + } \ +} \ + \ +static void \ +blt_rotated_90_##suffix (pix_type *dst, \ + int dst_stride, \ + const pix_type *src, \ + int src_stride, \ + int W, \ + int H) \ +{ \ + int x; \ + int leading_pixels = 0, trailing_pixels = 0; \ + const int TILE_SIZE = CACHE_LINE_SIZE / sizeof(pix_type); \ + \ + /* \ + * split processing into handling destination as TILE_SIZExH cache line \ + * aligned vertical stripes (optimistically assuming that destination \ + * stride is a multiple of cache line, if not - it will be just a bit \ + * slower) \ + */ \ + \ + if ((uintptr_t)dst & (CACHE_LINE_SIZE - 1)) \ + { \ + leading_pixels = TILE_SIZE - (((uintptr_t)dst & \ + (CACHE_LINE_SIZE - 1)) / sizeof(pix_type)); \ + if (leading_pixels > W) \ + leading_pixels = W; \ + \ + /* unaligned leading part NxH (where N < TILE_SIZE) */ \ + blt_rotated_90_trivial_##suffix ( \ + dst, \ + dst_stride, \ + src, \ + src_stride, \ + leading_pixels, \ + H); \ + \ + dst += leading_pixels; \ + src += leading_pixels * src_stride; \ + W -= leading_pixels; \ + } \ + \ + if ((uintptr_t)(dst + W) & (CACHE_LINE_SIZE - 1)) \ + { \ + trailing_pixels = (((uintptr_t)(dst + W) & \ + (CACHE_LINE_SIZE - 1)) / sizeof(pix_type)); \ + if (trailing_pixels > W) \ + trailing_pixels = W; \ + W -= trailing_pixels; \ + } \ + \ + for (x = 0; x < W; x += TILE_SIZE) \ + { \ + /* aligned middle part TILE_SIZExH */ \ + blt_rotated_90_trivial_##suffix ( \ + dst + x, \ + dst_stride, \ + src + src_stride * x, \ + src_stride, \ + TILE_SIZE, \ + H); \ + } \ + \ + if (trailing_pixels) \ + { \ + /* unaligned trailing part NxH (where N < TILE_SIZE) */ \ + blt_rotated_90_trivial_##suffix ( \ + dst + W, \ + dst_stride, \ + src + W * src_stride, \ + src_stride, \ + trailing_pixels, \ + H); \ + } \ +} \ + \ +static void \ +blt_rotated_270_##suffix (pix_type *dst, \ + int dst_stride, \ + const pix_type *src, \ + int src_stride, \ + int W, \ + int H) \ +{ \ + int x; \ + int leading_pixels = 0, trailing_pixels = 0; \ + const int TILE_SIZE = CACHE_LINE_SIZE / sizeof(pix_type); \ + \ + /* \ + * split processing into handling destination as TILE_SIZExH cache line \ + * aligned vertical stripes (optimistically assuming that destination \ + * stride is a multiple of cache line, if not - it will be just a bit \ + * slower) \ + */ \ + \ + if ((uintptr_t)dst & (CACHE_LINE_SIZE - 1)) \ + { \ + leading_pixels = TILE_SIZE - (((uintptr_t)dst & \ + (CACHE_LINE_SIZE - 1)) / sizeof(pix_type)); \ + if (leading_pixels > W) \ + leading_pixels = W; \ + \ + /* unaligned leading part NxH (where N < TILE_SIZE) */ \ + blt_rotated_270_trivial_##suffix ( \ + dst, \ + dst_stride, \ + src + src_stride * (W - leading_pixels), \ + src_stride, \ + leading_pixels, \ + H); \ + \ + dst += leading_pixels; \ + W -= leading_pixels; \ + } \ + \ + if ((uintptr_t)(dst + W) & (CACHE_LINE_SIZE - 1)) \ + { \ + trailing_pixels = (((uintptr_t)(dst + W) & \ + (CACHE_LINE_SIZE - 1)) / sizeof(pix_type)); \ + if (trailing_pixels > W) \ + trailing_pixels = W; \ + W -= trailing_pixels; \ + src += trailing_pixels * src_stride; \ + } \ + \ + for (x = 0; x < W; x += TILE_SIZE) \ + { \ + /* aligned middle part TILE_SIZExH */ \ + blt_rotated_270_trivial_##suffix ( \ + dst + x, \ + dst_stride, \ + src + src_stride * (W - x - TILE_SIZE), \ + src_stride, \ + TILE_SIZE, \ + H); \ + } \ + \ + if (trailing_pixels) \ + { \ + /* unaligned trailing part NxH (where N < TILE_SIZE) */ \ + blt_rotated_270_trivial_##suffix ( \ + dst + W, \ + dst_stride, \ + src - trailing_pixels * src_stride, \ + src_stride, \ + trailing_pixels, \ + H); \ + } \ +} \ + \ +static void \ +fast_composite_rotate_90_##suffix (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + pix_type *dst_line; \ + pix_type *src_line; \ + int dst_stride, src_stride; \ + int src_x_t, src_y_t; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, pix_type, \ + dst_stride, dst_line, 1); \ + src_x_t = -src_y + pixman_fixed_to_int ( \ + src_image->common.transform->matrix[0][2] + \ + pixman_fixed_1 / 2 - pixman_fixed_e) - height;\ + src_y_t = src_x + pixman_fixed_to_int ( \ + src_image->common.transform->matrix[1][2] + \ + pixman_fixed_1 / 2 - pixman_fixed_e); \ + PIXMAN_IMAGE_GET_LINE (src_image, src_x_t, src_y_t, pix_type, \ + src_stride, src_line, 1); \ + blt_rotated_90_##suffix (dst_line, dst_stride, src_line, src_stride, \ + width, height); \ +} \ + \ +static void \ +fast_composite_rotate_270_##suffix (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + pix_type *dst_line; \ + pix_type *src_line; \ + int dst_stride, src_stride; \ + int src_x_t, src_y_t; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, pix_type, \ + dst_stride, dst_line, 1); \ + src_x_t = src_y + pixman_fixed_to_int ( \ + src_image->common.transform->matrix[0][2] + \ + pixman_fixed_1 / 2 - pixman_fixed_e); \ + src_y_t = -src_x + pixman_fixed_to_int ( \ + src_image->common.transform->matrix[1][2] + \ + pixman_fixed_1 / 2 - pixman_fixed_e) - width; \ + PIXMAN_IMAGE_GET_LINE (src_image, src_x_t, src_y_t, pix_type, \ + src_stride, src_line, 1); \ + blt_rotated_270_##suffix (dst_line, dst_stride, src_line, src_stride, \ + width, height); \ +} + +FAST_SIMPLE_ROTATE (8, uint8_t) +FAST_SIMPLE_ROTATE (565, uint16_t) +FAST_SIMPLE_ROTATE (8888, uint32_t) + +static const pixman_fast_path_t c_fast_paths[] = +{ + PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, fast_composite_over_n_8_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, fast_composite_over_n_8_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, r8g8b8, fast_composite_over_n_8_0888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, b8g8r8, fast_composite_over_n_8_0888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, fast_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, fast_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, fast_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, fast_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a1, a8r8g8b8, fast_composite_over_n_1_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a1, x8r8g8b8, fast_composite_over_n_1_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a1, a8b8g8r8, fast_composite_over_n_1_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a1, x8b8g8r8, fast_composite_over_n_1_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a1, r5g6b5, fast_composite_over_n_1_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, a1, b5g6r5, fast_composite_over_n_1_0565), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, fast_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, fast_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, fast_composite_over_n_8888_0565_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, fast_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, fast_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, fast_composite_over_n_8888_0565_ca), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, x8r8g8b8, fast_composite_over_x888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, a8r8g8b8, fast_composite_over_x888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, x8b8g8r8, fast_composite_over_x888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, a8b8g8r8, fast_composite_over_x888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, fast_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, fast_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, fast_composite_over_8888_0565), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, fast_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, fast_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, fast_composite_over_8888_0565), + PIXMAN_STD_FAST_PATH (ADD, r5g6b5, null, r5g6b5, fast_composite_add_0565_0565), + PIXMAN_STD_FAST_PATH (ADD, b5g6r5, null, b5g6r5, fast_composite_add_0565_0565), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, fast_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, fast_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, fast_composite_add_8_8), + PIXMAN_STD_FAST_PATH (ADD, a1, null, a1, fast_composite_add_1_1), + PIXMAN_STD_FAST_PATH_CA (ADD, solid, a8r8g8b8, a8r8g8b8, fast_composite_add_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, fast_composite_add_n_8_8), + PIXMAN_STD_FAST_PATH (SRC, solid, null, a8r8g8b8, fast_composite_solid_fill), + PIXMAN_STD_FAST_PATH (SRC, solid, null, x8r8g8b8, fast_composite_solid_fill), + PIXMAN_STD_FAST_PATH (SRC, solid, null, a8b8g8r8, fast_composite_solid_fill), + PIXMAN_STD_FAST_PATH (SRC, solid, null, x8b8g8r8, fast_composite_solid_fill), + PIXMAN_STD_FAST_PATH (SRC, solid, null, a1, fast_composite_solid_fill), + PIXMAN_STD_FAST_PATH (SRC, solid, null, a8, fast_composite_solid_fill), + PIXMAN_STD_FAST_PATH (SRC, solid, null, r5g6b5, fast_composite_solid_fill), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, fast_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, fast_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, b8g8r8a8, null, b8g8r8x8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, b8g8r8a8, null, b8g8r8a8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, b8g8r8x8, null, b8g8r8x8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, r8g8b8, null, r8g8b8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, b8g8r8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, x1r5g5b5, null, x1r5g5b5, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, a1r5g5b5, null, x1r5g5b5, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (SRC, a8, null, a8, fast_composite_src_memcpy), + PIXMAN_STD_FAST_PATH (IN, a8, null, a8, fast_composite_in_8_8), + PIXMAN_STD_FAST_PATH (IN, solid, a8, a8, fast_composite_in_n_8_8), + + SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, 8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, 8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8, 8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, 8888_8888), + + SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, 8888_8888), + SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, 8888_8888), + + SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, r5g6b5, 8888_565), + SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, r5g6b5, 8888_565), + + SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, r5g6b5, 565_565), + + SIMPLE_NEAREST_FAST_PATH_COVER (SRC, x8r8g8b8, a8r8g8b8, x888_8888), + SIMPLE_NEAREST_FAST_PATH_COVER (SRC, x8b8g8r8, a8b8g8r8, x888_8888), + SIMPLE_NEAREST_FAST_PATH_PAD (SRC, x8r8g8b8, a8r8g8b8, x888_8888), + SIMPLE_NEAREST_FAST_PATH_PAD (SRC, x8b8g8r8, a8b8g8r8, x888_8888), + SIMPLE_NEAREST_FAST_PATH_NORMAL (SRC, x8r8g8b8, a8r8g8b8, x888_8888), + SIMPLE_NEAREST_FAST_PATH_NORMAL (SRC, x8b8g8r8, a8b8g8r8, x888_8888), + + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, 8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, 8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, 8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, 8888_8888), + + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, r5g6b5, 8888_565), + +#define NEAREST_FAST_PATH(op,s,d) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, SCALED_NEAREST_FLAGS, \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest, \ + } + + NEAREST_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8), + NEAREST_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8), + NEAREST_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8), + NEAREST_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8), + + NEAREST_FAST_PATH (SRC, x8r8g8b8, a8r8g8b8), + NEAREST_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8), + NEAREST_FAST_PATH (SRC, x8b8g8r8, a8b8g8r8), + NEAREST_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8), + + NEAREST_FAST_PATH (OVER, x8r8g8b8, x8r8g8b8), + NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8), + NEAREST_FAST_PATH (OVER, x8b8g8r8, x8b8g8r8), + NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8), + + NEAREST_FAST_PATH (OVER, x8r8g8b8, a8r8g8b8), + NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8), + NEAREST_FAST_PATH (OVER, x8b8g8r8, a8b8g8r8), + NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8), + +#define SIMPLE_ROTATE_FLAGS(angle) \ + (FAST_PATH_ROTATE_ ## angle ## _TRANSFORM | \ + FAST_PATH_NEAREST_FILTER | \ + FAST_PATH_SAMPLES_COVER_CLIP_NEAREST | \ + FAST_PATH_STANDARD_FLAGS) + +#define SIMPLE_ROTATE_FAST_PATH(op,s,d,suffix) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, SIMPLE_ROTATE_FLAGS (90), \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_rotate_90_##suffix, \ + }, \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, SIMPLE_ROTATE_FLAGS (270), \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_rotate_270_##suffix, \ + } + + SIMPLE_ROTATE_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, 8888), + SIMPLE_ROTATE_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, 8888), + SIMPLE_ROTATE_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, 8888), + SIMPLE_ROTATE_FAST_PATH (SRC, r5g6b5, r5g6b5, 565), + SIMPLE_ROTATE_FAST_PATH (SRC, a8, a8, 8), + + /* Simple repeat fast path entry. */ + { PIXMAN_OP_any, + PIXMAN_any, + (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | FAST_PATH_BITS_IMAGE | + FAST_PATH_NORMAL_REPEAT), + PIXMAN_any, 0, + PIXMAN_any, FAST_PATH_STD_DEST_FLAGS, + fast_composite_tiled_repeat + }, + + { PIXMAN_OP_NONE }, +}; + +#ifdef WORDS_BIGENDIAN +#define A1_FILL_MASK(n, offs) (((1U << (n)) - 1) << (32 - (offs) - (n))) +#else +#define A1_FILL_MASK(n, offs) (((1U << (n)) - 1) << (offs)) +#endif + +static force_inline void +pixman_fill1_line (uint32_t *dst, int offs, int width, int v) +{ + if (offs) + { + int leading_pixels = 32 - offs; + if (leading_pixels >= width) + { + if (v) + *dst |= A1_FILL_MASK (width, offs); + else + *dst &= ~A1_FILL_MASK (width, offs); + return; + } + else + { + if (v) + *dst++ |= A1_FILL_MASK (leading_pixels, offs); + else + *dst++ &= ~A1_FILL_MASK (leading_pixels, offs); + width -= leading_pixels; + } + } + while (width >= 32) + { + if (v) + *dst++ = 0xFFFFFFFF; + else + *dst++ = 0; + width -= 32; + } + if (width > 0) + { + if (v) + *dst |= A1_FILL_MASK (width, 0); + else + *dst &= ~A1_FILL_MASK (width, 0); + } +} + +static void +pixman_fill1 (uint32_t *bits, + int stride, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + uint32_t *dst = bits + y * stride + (x >> 5); + int offs = x & 31; + + if (filler & 1) + { + while (height--) + { + pixman_fill1_line (dst, offs, width, 1); + dst += stride; + } + } + else + { + while (height--) + { + pixman_fill1_line (dst, offs, width, 0); + dst += stride; + } + } +} + +static void +pixman_fill8 (uint32_t *bits, + int stride, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + int byte_stride = stride * (int) sizeof (uint32_t); + uint8_t *dst = (uint8_t *) bits; + uint8_t v = filler & 0xff; + int i; + + dst = dst + y * byte_stride + x; + + while (height--) + { + for (i = 0; i < width; ++i) + dst[i] = v; + + dst += byte_stride; + } +} + +static void +pixman_fill16 (uint32_t *bits, + int stride, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + int short_stride = + (stride * (int)sizeof (uint32_t)) / (int)sizeof (uint16_t); + uint16_t *dst = (uint16_t *)bits; + uint16_t v = filler & 0xffff; + int i; + + dst = dst + y * short_stride + x; + + while (height--) + { + for (i = 0; i < width; ++i) + dst[i] = v; + + dst += short_stride; + } +} + +static void +pixman_fill32 (uint32_t *bits, + int stride, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + int i; + + bits = bits + y * stride + x; + + while (height--) + { + for (i = 0; i < width; ++i) + bits[i] = filler; + + bits += stride; + } +} + +static pixman_bool_t +fast_path_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + switch (bpp) + { + case 1: + pixman_fill1 (bits, stride, x, y, width, height, filler); + break; + + case 8: + pixman_fill8 (bits, stride, x, y, width, height, filler); + break; + + case 16: + pixman_fill16 (bits, stride, x, y, width, height, filler); + break; + + case 32: + pixman_fill32 (bits, stride, x, y, width, height, filler); + break; + + default: + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static uint32_t * +fast_fetch_r5g6b5 (pixman_iter_t *iter, const uint32_t *mask) +{ + int32_t w = iter->width; + uint32_t *dst = iter->buffer; + const uint16_t *src = (const uint16_t *)iter->bits; + + iter->bits += iter->stride; + + /* Align the source buffer at 4 bytes boundary */ + if (w > 0 && ((uintptr_t)src & 3)) + { + *dst++ = convert_0565_to_8888 (*src++); + w--; + } + /* Process two pixels per iteration */ + while ((w -= 2) >= 0) + { + uint32_t sr, sb, sg, t0, t1; + uint32_t s = *(const uint32_t *)src; + src += 2; + sr = (s >> 8) & 0x00F800F8; + sb = (s << 3) & 0x00F800F8; + sg = (s >> 3) & 0x00FC00FC; + sr |= sr >> 5; + sb |= sb >> 5; + sg |= sg >> 6; + t0 = ((sr << 16) & 0x00FF0000) | ((sg << 8) & 0x0000FF00) | + (sb & 0xFF) | 0xFF000000; + t1 = (sr & 0x00FF0000) | ((sg >> 8) & 0x0000FF00) | + (sb >> 16) | 0xFF000000; +#ifdef WORDS_BIGENDIAN + *dst++ = t1; + *dst++ = t0; +#else + *dst++ = t0; + *dst++ = t1; +#endif + } + if (w & 1) + { + *dst = convert_0565_to_8888 (*src); + } + + return iter->buffer; +} + +static uint32_t * +fast_dest_fetch_noop (pixman_iter_t *iter, const uint32_t *mask) +{ + iter->bits += iter->stride; + return iter->buffer; +} + +/* Helper function for a workaround, which tries to ensure that 0x1F001F + * constant is always allocated in a register on RISC architectures. + */ +static force_inline uint32_t +convert_8888_to_0565_workaround (uint32_t s, uint32_t x1F001F) +{ + uint32_t a, b; + a = (s >> 3) & x1F001F; + b = s & 0xFC00; + a |= a >> 5; + a |= b >> 5; + return a; +} + +static void +fast_write_back_r5g6b5 (pixman_iter_t *iter) +{ + int32_t w = iter->width; + uint16_t *dst = (uint16_t *)(iter->bits - iter->stride); + const uint32_t *src = iter->buffer; + /* Workaround to ensure that x1F001F variable is allocated in a register */ + static volatile uint32_t volatile_x1F001F = 0x1F001F; + uint32_t x1F001F = volatile_x1F001F; + + while ((w -= 4) >= 0) + { + uint32_t s1 = *src++; + uint32_t s2 = *src++; + uint32_t s3 = *src++; + uint32_t s4 = *src++; + *dst++ = convert_8888_to_0565_workaround (s1, x1F001F); + *dst++ = convert_8888_to_0565_workaround (s2, x1F001F); + *dst++ = convert_8888_to_0565_workaround (s3, x1F001F); + *dst++ = convert_8888_to_0565_workaround (s4, x1F001F); + } + if (w & 2) + { + *dst++ = convert_8888_to_0565_workaround (*src++, x1F001F); + *dst++ = convert_8888_to_0565_workaround (*src++, x1F001F); + } + if (w & 1) + { + *dst = convert_8888_to_0565_workaround (*src, x1F001F); + } +} + +typedef struct +{ + int y; + uint64_t * buffer; +} line_t; + +typedef struct +{ + line_t lines[2]; + pixman_fixed_t y; + pixman_fixed_t x; + uint64_t data[1]; +} bilinear_info_t; + +static void +fetch_horizontal (bits_image_t *image, line_t *line, + int y, pixman_fixed_t x, pixman_fixed_t ux, int n) +{ + uint32_t *bits = image->bits + y * image->rowstride; + int i; + + for (i = 0; i < n; ++i) + { + int x0 = pixman_fixed_to_int (x); + int x1 = x0 + 1; + int32_t dist_x; + + uint32_t left = *(bits + x0); + uint32_t right = *(bits + x1); + + dist_x = pixman_fixed_to_bilinear_weight (x); + dist_x <<= (8 - BILINEAR_INTERPOLATION_BITS); + +#if SIZEOF_LONG <= 4 + { + uint32_t lag, rag, ag; + uint32_t lrb, rrb, rb; + + lag = (left & 0xff00ff00) >> 8; + rag = (right & 0xff00ff00) >> 8; + ag = (lag << 8) + dist_x * (rag - lag); + + lrb = (left & 0x00ff00ff); + rrb = (right & 0x00ff00ff); + rb = (lrb << 8) + dist_x * (rrb - lrb); + + *((uint32_t *)(line->buffer + i)) = ag; + *((uint32_t *)(line->buffer + i) + 1) = rb; + } +#else + { + uint64_t lagrb, ragrb; + uint32_t lag, rag; + uint32_t lrb, rrb; + + lag = (left & 0xff00ff00); + lrb = (left & 0x00ff00ff); + rag = (right & 0xff00ff00); + rrb = (right & 0x00ff00ff); + lagrb = (((uint64_t)lag) << 24) | lrb; + ragrb = (((uint64_t)rag) << 24) | rrb; + + line->buffer[i] = (lagrb << 8) + dist_x * (ragrb - lagrb); + } +#endif + + x += ux; + } + + line->y = y; +} + +static uint32_t * +fast_fetch_bilinear_cover (pixman_iter_t *iter, const uint32_t *mask) +{ + pixman_fixed_t fx, ux; + bilinear_info_t *info = iter->data; + line_t *line0, *line1; + int y0, y1; + int32_t dist_y; + int i; + + COMPILE_TIME_ASSERT (BILINEAR_INTERPOLATION_BITS < 8); + + fx = info->x; + ux = iter->image->common.transform->matrix[0][0]; + + y0 = pixman_fixed_to_int (info->y); + y1 = y0 + 1; + dist_y = pixman_fixed_to_bilinear_weight (info->y); + dist_y <<= (8 - BILINEAR_INTERPOLATION_BITS); + + line0 = &info->lines[y0 & 0x01]; + line1 = &info->lines[y1 & 0x01]; + + if (line0->y != y0) + { + fetch_horizontal ( + &iter->image->bits, line0, y0, fx, ux, iter->width); + } + + if (line1->y != y1) + { + fetch_horizontal ( + &iter->image->bits, line1, y1, fx, ux, iter->width); + } + + for (i = 0; i < iter->width; ++i) + { +#if SIZEOF_LONG <= 4 + uint32_t ta, tr, tg, tb; + uint32_t ba, br, bg, bb; + uint32_t tag, trb; + uint32_t bag, brb; + uint32_t a, r, g, b; + + tag = *((uint32_t *)(line0->buffer + i)); + trb = *((uint32_t *)(line0->buffer + i) + 1); + bag = *((uint32_t *)(line1->buffer + i)); + brb = *((uint32_t *)(line1->buffer + i) + 1); + + ta = tag >> 16; + ba = bag >> 16; + a = (ta << 8) + dist_y * (ba - ta); + + tr = trb >> 16; + br = brb >> 16; + r = (tr << 8) + dist_y * (br - tr); + + tg = tag & 0xffff; + bg = bag & 0xffff; + g = (tg << 8) + dist_y * (bg - tg); + + tb = trb & 0xffff; + bb = brb & 0xffff; + b = (tb << 8) + dist_y * (bb - tb); + + a = (a << 8) & 0xff000000; + r = (r << 0) & 0x00ff0000; + g = (g >> 8) & 0x0000ff00; + b = (b >> 16) & 0x000000ff; +#else + uint64_t top = line0->buffer[i]; + uint64_t bot = line1->buffer[i]; + uint64_t tar = (top & 0xffff0000ffff0000ULL) >> 16; + uint64_t bar = (bot & 0xffff0000ffff0000ULL) >> 16; + uint64_t tgb = (top & 0x0000ffff0000ffffULL); + uint64_t bgb = (bot & 0x0000ffff0000ffffULL); + uint64_t ar, gb; + uint32_t a, r, g, b; + + ar = (tar << 8) + dist_y * (bar - tar); + gb = (tgb << 8) + dist_y * (bgb - tgb); + + a = ((ar >> 24) & 0xff000000); + r = ((ar >> 0) & 0x00ff0000); + g = ((gb >> 40) & 0x0000ff00); + b = ((gb >> 16) & 0x000000ff); +#endif + + iter->buffer[i] = a | r | g | b; + } + + info->y += iter->image->common.transform->matrix[1][1]; + + return iter->buffer; +} + +static void +bilinear_cover_iter_fini (pixman_iter_t *iter) +{ + free (iter->data); +} + +static void +fast_bilinear_cover_iter_init (pixman_iter_t *iter, const pixman_iter_info_t *iter_info) +{ + int width = iter->width; + bilinear_info_t *info; + pixman_vector_t v; + + /* Reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (iter->x) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (iter->y) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point_3d (iter->image->common.transform, &v)) + goto fail; + + info = malloc (sizeof (*info) + (2 * width - 1) * sizeof (uint64_t)); + if (!info) + goto fail; + + info->x = v.vector[0] - pixman_fixed_1 / 2; + info->y = v.vector[1] - pixman_fixed_1 / 2; + + /* It is safe to set the y coordinates to -1 initially + * because COVER_CLIP_BILINEAR ensures that we will only + * be asked to fetch lines in the [0, height) interval + */ + info->lines[0].y = -1; + info->lines[0].buffer = &(info->data[0]); + info->lines[1].y = -1; + info->lines[1].buffer = &(info->data[width]); + + iter->get_scanline = fast_fetch_bilinear_cover; + iter->fini = bilinear_cover_iter_fini; + + iter->data = info; + return; + +fail: + /* Something went wrong, either a bad matrix or OOM; in such cases, + * we don't guarantee any particular rendering. + */ + _pixman_log_error ( + FUNC, "Allocation failure or bad matrix, skipping rendering\n"); + + iter->get_scanline = _pixman_iter_get_scanline_noop; + iter->fini = NULL; +} + +static uint32_t * +bits_image_fetch_bilinear_no_repeat_8888 (pixman_iter_t *iter, + const uint32_t *mask) +{ + + pixman_image_t * ima = iter->image; + int offset = iter->x; + int line = iter->y++; + int width = iter->width; + uint32_t * buffer = iter->buffer; + + bits_image_t *bits = &ima->bits; + pixman_fixed_t x_top, x_bottom, x; + pixman_fixed_t ux_top, ux_bottom, ux; + pixman_vector_t v; + uint32_t top_mask, bottom_mask; + uint32_t *top_row; + uint32_t *bottom_row; + uint32_t *end; + uint32_t zero[2] = { 0, 0 }; + uint32_t one = 1; + int y, y1, y2; + int disty; + int mask_inc; + int w; + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point_3d (bits->common.transform, &v)) + return iter->buffer; + + ux = ux_top = ux_bottom = bits->common.transform->matrix[0][0]; + x = x_top = x_bottom = v.vector[0] - pixman_fixed_1/2; + + y = v.vector[1] - pixman_fixed_1/2; + disty = pixman_fixed_to_bilinear_weight (y); + + /* Load the pointers to the first and second lines from the source + * image that bilinear code must read. + * + * The main trick in this code is about the check if any line are + * outside of the image; + * + * When I realize that a line (any one) is outside, I change + * the pointer to a dummy area with zeros. Once I change this, I + * must be sure the pointer will not change, so I set the + * variables to each pointer increments inside the loop. + */ + y1 = pixman_fixed_to_int (y); + y2 = y1 + 1; + + if (y1 < 0 || y1 >= bits->height) + { + top_row = zero; + x_top = 0; + ux_top = 0; + } + else + { + top_row = bits->bits + y1 * bits->rowstride; + x_top = x; + ux_top = ux; + } + + if (y2 < 0 || y2 >= bits->height) + { + bottom_row = zero; + x_bottom = 0; + ux_bottom = 0; + } + else + { + bottom_row = bits->bits + y2 * bits->rowstride; + x_bottom = x; + ux_bottom = ux; + } + + /* Instead of checking whether the operation uses the mast in + * each loop iteration, verify this only once and prepare the + * variables to make the code smaller inside the loop. + */ + if (!mask) + { + mask_inc = 0; + mask = &one; + } + else + { + /* If have a mask, prepare the variables to check it */ + mask_inc = 1; + } + + /* If both are zero, then the whole thing is zero */ + if (top_row == zero && bottom_row == zero) + { + memset (buffer, 0, width * sizeof (uint32_t)); + return iter->buffer; + } + else if (bits->format == PIXMAN_x8r8g8b8) + { + if (top_row == zero) + { + top_mask = 0; + bottom_mask = 0xff000000; + } + else if (bottom_row == zero) + { + top_mask = 0xff000000; + bottom_mask = 0; + } + else + { + top_mask = 0xff000000; + bottom_mask = 0xff000000; + } + } + else + { + top_mask = 0; + bottom_mask = 0; + } + + end = buffer + width; + + /* Zero fill to the left of the image */ + while (buffer < end && x < pixman_fixed_minus_1) + { + *buffer++ = 0; + x += ux; + x_top += ux_top; + x_bottom += ux_bottom; + mask += mask_inc; + } + + /* Left edge + */ + while (buffer < end && x < 0) + { + uint32_t tr, br; + int32_t distx; + + tr = top_row[pixman_fixed_to_int (x_top) + 1] | top_mask; + br = bottom_row[pixman_fixed_to_int (x_bottom) + 1] | bottom_mask; + + distx = pixman_fixed_to_bilinear_weight (x); + + *buffer++ = bilinear_interpolation (0, tr, 0, br, distx, disty); + + x += ux; + x_top += ux_top; + x_bottom += ux_bottom; + mask += mask_inc; + } + + /* Main part */ + w = pixman_int_to_fixed (bits->width - 1); + + while (buffer < end && x < w) + { + if (*mask) + { + uint32_t tl, tr, bl, br; + int32_t distx; + + tl = top_row [pixman_fixed_to_int (x_top)] | top_mask; + tr = top_row [pixman_fixed_to_int (x_top) + 1] | top_mask; + bl = bottom_row [pixman_fixed_to_int (x_bottom)] | bottom_mask; + br = bottom_row [pixman_fixed_to_int (x_bottom) + 1] | bottom_mask; + + distx = pixman_fixed_to_bilinear_weight (x); + + *buffer = bilinear_interpolation (tl, tr, bl, br, distx, disty); + } + + buffer++; + x += ux; + x_top += ux_top; + x_bottom += ux_bottom; + mask += mask_inc; + } + + /* Right Edge */ + w = pixman_int_to_fixed (bits->width); + while (buffer < end && x < w) + { + if (*mask) + { + uint32_t tl, bl; + int32_t distx; + + tl = top_row [pixman_fixed_to_int (x_top)] | top_mask; + bl = bottom_row [pixman_fixed_to_int (x_bottom)] | bottom_mask; + + distx = pixman_fixed_to_bilinear_weight (x); + + *buffer = bilinear_interpolation (tl, 0, bl, 0, distx, disty); + } + + buffer++; + x += ux; + x_top += ux_top; + x_bottom += ux_bottom; + mask += mask_inc; + } + + /* Zero fill to the left of the image */ + while (buffer < end) + *buffer++ = 0; + + return iter->buffer; +} + +typedef uint32_t (* convert_pixel_t) (const uint8_t *row, int x); + +static force_inline void +bits_image_fetch_separable_convolution_affine (pixman_image_t * image, + int offset, + int line, + int width, + uint32_t * buffer, + const uint32_t * mask, + + convert_pixel_t convert_pixel, + pixman_format_code_t format, + pixman_repeat_t repeat_mode) +{ + bits_image_t *bits = &image->bits; + pixman_fixed_t *params = image->common.filter_params; + int cwidth = pixman_fixed_to_int (params[0]); + int cheight = pixman_fixed_to_int (params[1]); + int x_off = ((cwidth << 16) - pixman_fixed_1) >> 1; + int y_off = ((cheight << 16) - pixman_fixed_1) >> 1; + int x_phase_bits = pixman_fixed_to_int (params[2]); + int y_phase_bits = pixman_fixed_to_int (params[3]); + int x_phase_shift = 16 - x_phase_bits; + int y_phase_shift = 16 - y_phase_bits; + pixman_fixed_t vx, vy; + pixman_fixed_t ux, uy; + pixman_vector_t v; + int k; + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point_3d (image->common.transform, &v)) + return; + + ux = image->common.transform->matrix[0][0]; + uy = image->common.transform->matrix[1][0]; + + vx = v.vector[0]; + vy = v.vector[1]; + + for (k = 0; k < width; ++k) + { + pixman_fixed_t *y_params; + int satot, srtot, sgtot, sbtot; + pixman_fixed_t x, y; + int32_t x1, x2, y1, y2; + int32_t px, py; + int i, j; + + if (mask && !mask[k]) + goto next; + + /* Round x and y to the middle of the closest phase before continuing. This + * ensures that the convolution matrix is aligned right, since it was + * positioned relative to a particular phase (and not relative to whatever + * exact fraction we happen to get here). + */ + x = ((vx >> x_phase_shift) << x_phase_shift) + ((1 << x_phase_shift) >> 1); + y = ((vy >> y_phase_shift) << y_phase_shift) + ((1 << y_phase_shift) >> 1); + + px = (x & 0xffff) >> x_phase_shift; + py = (y & 0xffff) >> y_phase_shift; + + x1 = pixman_fixed_to_int (x - pixman_fixed_e - x_off); + y1 = pixman_fixed_to_int (y - pixman_fixed_e - y_off); + x2 = x1 + cwidth; + y2 = y1 + cheight; + + satot = srtot = sgtot = sbtot = 0; + + y_params = params + 4 + (1 << x_phase_bits) * cwidth + py * cheight; + + for (i = y1; i < y2; ++i) + { + pixman_fixed_t fy = *y_params++; + + if (fy) + { + pixman_fixed_t *x_params = params + 4 + px * cwidth; + + for (j = x1; j < x2; ++j) + { + pixman_fixed_t fx = *x_params++; + int rx = j; + int ry = i; + + if (fx) + { + pixman_fixed_t f; + uint32_t pixel, mask; + uint8_t *row; + + mask = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; + + if (repeat_mode != PIXMAN_REPEAT_NONE) + { + repeat (repeat_mode, &rx, bits->width); + repeat (repeat_mode, &ry, bits->height); + + row = (uint8_t *)(bits->bits + bits->rowstride * ry); + pixel = convert_pixel (row, rx) | mask; + } + else + { + if (rx < 0 || ry < 0 || rx >= bits->width || ry >= bits->height) + { + pixel = 0; + } + else + { + row = (uint8_t *)(bits->bits + bits->rowstride * ry); + pixel = convert_pixel (row, rx) | mask; + } + } + + f = ((pixman_fixed_32_32_t)fx * fy + 0x8000) >> 16; + srtot += (int)RED_8 (pixel) * f; + sgtot += (int)GREEN_8 (pixel) * f; + sbtot += (int)BLUE_8 (pixel) * f; + satot += (int)ALPHA_8 (pixel) * f; + } + } + } + } + + satot = (satot + 0x8000) >> 16; + srtot = (srtot + 0x8000) >> 16; + sgtot = (sgtot + 0x8000) >> 16; + sbtot = (sbtot + 0x8000) >> 16; + + satot = CLIP (satot, 0, 0xff); + srtot = CLIP (srtot, 0, 0xff); + sgtot = CLIP (sgtot, 0, 0xff); + sbtot = CLIP (sbtot, 0, 0xff); + +#ifdef WORDS_BIGENDIAN + buffer[k] = (satot << 0) | (srtot << 8) | (sgtot << 16) | (sbtot << 24); +#else + buffer[k] = (satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot << 0); +#endif + + next: + vx += ux; + vy += uy; + } +} + +static const uint8_t zero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +static force_inline void +bits_image_fetch_bilinear_affine (pixman_image_t * image, + int offset, + int line, + int width, + uint32_t * buffer, + const uint32_t * mask, + + convert_pixel_t convert_pixel, + pixman_format_code_t format, + pixman_repeat_t repeat_mode) +{ + pixman_fixed_t x, y; + pixman_fixed_t ux, uy; + pixman_vector_t v; + bits_image_t *bits = &image->bits; + int i; + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point_3d (image->common.transform, &v)) + return; + + ux = image->common.transform->matrix[0][0]; + uy = image->common.transform->matrix[1][0]; + + x = v.vector[0]; + y = v.vector[1]; + + for (i = 0; i < width; ++i) + { + int x1, y1, x2, y2; + uint32_t tl, tr, bl, br; + int32_t distx, disty; + int width = image->bits.width; + int height = image->bits.height; + const uint8_t *row1; + const uint8_t *row2; + + if (mask && !mask[i]) + goto next; + + x1 = x - pixman_fixed_1 / 2; + y1 = y - pixman_fixed_1 / 2; + + distx = pixman_fixed_to_bilinear_weight (x1); + disty = pixman_fixed_to_bilinear_weight (y1); + + y1 = pixman_fixed_to_int (y1); + y2 = y1 + 1; + x1 = pixman_fixed_to_int (x1); + x2 = x1 + 1; + + if (repeat_mode != PIXMAN_REPEAT_NONE) + { + uint32_t mask; + + mask = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; + + repeat (repeat_mode, &x1, width); + repeat (repeat_mode, &y1, height); + repeat (repeat_mode, &x2, width); + repeat (repeat_mode, &y2, height); + + row1 = (uint8_t *)(bits->bits + bits->rowstride * y1); + row2 = (uint8_t *)(bits->bits + bits->rowstride * y2); + + tl = convert_pixel (row1, x1) | mask; + tr = convert_pixel (row1, x2) | mask; + bl = convert_pixel (row2, x1) | mask; + br = convert_pixel (row2, x2) | mask; + } + else + { + uint32_t mask1, mask2; + int bpp; + + /* Note: PIXMAN_FORMAT_BPP() returns an unsigned value, + * which means if you use it in expressions, those + * expressions become unsigned themselves. Since + * the variables below can be negative in some cases, + * that will lead to crashes on 64 bit architectures. + * + * So this line makes sure bpp is signed + */ + bpp = PIXMAN_FORMAT_BPP (format); + + if (x1 >= width || x2 < 0 || y1 >= height || y2 < 0) + { + buffer[i] = 0; + goto next; + } + + if (y2 == 0) + { + row1 = zero; + mask1 = 0; + } + else + { + row1 = (uint8_t *)(bits->bits + bits->rowstride * y1); + row1 += bpp / 8 * x1; + + mask1 = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; + } + + if (y1 == height - 1) + { + row2 = zero; + mask2 = 0; + } + else + { + row2 = (uint8_t *)(bits->bits + bits->rowstride * y2); + row2 += bpp / 8 * x1; + + mask2 = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; + } + + if (x2 == 0) + { + tl = 0; + bl = 0; + } + else + { + tl = convert_pixel (row1, 0) | mask1; + bl = convert_pixel (row2, 0) | mask2; + } + + if (x1 == width - 1) + { + tr = 0; + br = 0; + } + else + { + tr = convert_pixel (row1, 1) | mask1; + br = convert_pixel (row2, 1) | mask2; + } + } + + buffer[i] = bilinear_interpolation ( + tl, tr, bl, br, distx, disty); + + next: + x += ux; + y += uy; + } +} + +static force_inline void +bits_image_fetch_nearest_affine (pixman_image_t * image, + int offset, + int line, + int width, + uint32_t * buffer, + const uint32_t * mask, + + convert_pixel_t convert_pixel, + pixman_format_code_t format, + pixman_repeat_t repeat_mode) +{ + pixman_fixed_t x, y; + pixman_fixed_t ux, uy; + pixman_vector_t v; + bits_image_t *bits = &image->bits; + int i; + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point_3d (image->common.transform, &v)) + return; + + ux = image->common.transform->matrix[0][0]; + uy = image->common.transform->matrix[1][0]; + + x = v.vector[0]; + y = v.vector[1]; + + for (i = 0; i < width; ++i) + { + int width, height, x0, y0; + const uint8_t *row; + + if (mask && !mask[i]) + goto next; + + width = image->bits.width; + height = image->bits.height; + x0 = pixman_fixed_to_int (x - pixman_fixed_e); + y0 = pixman_fixed_to_int (y - pixman_fixed_e); + + if (repeat_mode == PIXMAN_REPEAT_NONE && + (y0 < 0 || y0 >= height || x0 < 0 || x0 >= width)) + { + buffer[i] = 0; + } + else + { + uint32_t mask = PIXMAN_FORMAT_A (format)? 0 : 0xff000000; + + if (repeat_mode != PIXMAN_REPEAT_NONE) + { + repeat (repeat_mode, &x0, width); + repeat (repeat_mode, &y0, height); + } + + row = (uint8_t *)(bits->bits + bits->rowstride * y0); + + buffer[i] = convert_pixel (row, x0) | mask; + } + + next: + x += ux; + y += uy; + } +} + +static force_inline uint32_t +convert_a8r8g8b8 (const uint8_t *row, int x) +{ + return *(((uint32_t *)row) + x); +} + +static force_inline uint32_t +convert_x8r8g8b8 (const uint8_t *row, int x) +{ + return *(((uint32_t *)row) + x); +} + +static force_inline uint32_t +convert_a8 (const uint8_t *row, int x) +{ + return (uint32_t) *(row + x) << 24; +} + +static force_inline uint32_t +convert_r5g6b5 (const uint8_t *row, int x) +{ + return convert_0565_to_0888 (*((uint16_t *)row + x)); +} + +#define MAKE_SEPARABLE_CONVOLUTION_FETCHER(name, format, repeat_mode) \ + static uint32_t * \ + bits_image_fetch_separable_convolution_affine_ ## name (pixman_iter_t *iter, \ + const uint32_t * mask) \ + { \ + bits_image_fetch_separable_convolution_affine ( \ + iter->image, \ + iter->x, iter->y++, \ + iter->width, \ + iter->buffer, mask, \ + convert_ ## format, \ + PIXMAN_ ## format, \ + repeat_mode); \ + \ + return iter->buffer; \ + } + +#define MAKE_BILINEAR_FETCHER(name, format, repeat_mode) \ + static uint32_t * \ + bits_image_fetch_bilinear_affine_ ## name (pixman_iter_t *iter, \ + const uint32_t * mask) \ + { \ + bits_image_fetch_bilinear_affine (iter->image, \ + iter->x, iter->y++, \ + iter->width, \ + iter->buffer, mask, \ + convert_ ## format, \ + PIXMAN_ ## format, \ + repeat_mode); \ + return iter->buffer; \ + } + +#define MAKE_NEAREST_FETCHER(name, format, repeat_mode) \ + static uint32_t * \ + bits_image_fetch_nearest_affine_ ## name (pixman_iter_t *iter, \ + const uint32_t * mask) \ + { \ + bits_image_fetch_nearest_affine (iter->image, \ + iter->x, iter->y++, \ + iter->width, \ + iter->buffer, mask, \ + convert_ ## format, \ + PIXMAN_ ## format, \ + repeat_mode); \ + return iter->buffer; \ + } + +#define MAKE_FETCHERS(name, format, repeat_mode) \ + MAKE_NEAREST_FETCHER (name, format, repeat_mode) \ + MAKE_BILINEAR_FETCHER (name, format, repeat_mode) \ + MAKE_SEPARABLE_CONVOLUTION_FETCHER (name, format, repeat_mode) + +MAKE_FETCHERS (pad_a8r8g8b8, a8r8g8b8, PIXMAN_REPEAT_PAD) +MAKE_FETCHERS (none_a8r8g8b8, a8r8g8b8, PIXMAN_REPEAT_NONE) +MAKE_FETCHERS (reflect_a8r8g8b8, a8r8g8b8, PIXMAN_REPEAT_REFLECT) +MAKE_FETCHERS (normal_a8r8g8b8, a8r8g8b8, PIXMAN_REPEAT_NORMAL) +MAKE_FETCHERS (pad_x8r8g8b8, x8r8g8b8, PIXMAN_REPEAT_PAD) +MAKE_FETCHERS (none_x8r8g8b8, x8r8g8b8, PIXMAN_REPEAT_NONE) +MAKE_FETCHERS (reflect_x8r8g8b8, x8r8g8b8, PIXMAN_REPEAT_REFLECT) +MAKE_FETCHERS (normal_x8r8g8b8, x8r8g8b8, PIXMAN_REPEAT_NORMAL) +MAKE_FETCHERS (pad_a8, a8, PIXMAN_REPEAT_PAD) +MAKE_FETCHERS (none_a8, a8, PIXMAN_REPEAT_NONE) +MAKE_FETCHERS (reflect_a8, a8, PIXMAN_REPEAT_REFLECT) +MAKE_FETCHERS (normal_a8, a8, PIXMAN_REPEAT_NORMAL) +MAKE_FETCHERS (pad_r5g6b5, r5g6b5, PIXMAN_REPEAT_PAD) +MAKE_FETCHERS (none_r5g6b5, r5g6b5, PIXMAN_REPEAT_NONE) +MAKE_FETCHERS (reflect_r5g6b5, r5g6b5, PIXMAN_REPEAT_REFLECT) +MAKE_FETCHERS (normal_r5g6b5, r5g6b5, PIXMAN_REPEAT_NORMAL) + +#define IMAGE_FLAGS \ + (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \ + FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) + +static const pixman_iter_info_t fast_iters[] = +{ + { PIXMAN_r5g6b5, IMAGE_FLAGS, ITER_NARROW | ITER_SRC, + _pixman_iter_init_bits_stride, fast_fetch_r5g6b5, NULL }, + + { PIXMAN_r5g6b5, FAST_PATH_STD_DEST_FLAGS, + ITER_NARROW | ITER_DEST, + _pixman_iter_init_bits_stride, + fast_fetch_r5g6b5, fast_write_back_r5g6b5 }, + + { PIXMAN_r5g6b5, FAST_PATH_STD_DEST_FLAGS, + ITER_NARROW | ITER_DEST | ITER_IGNORE_RGB | ITER_IGNORE_ALPHA, + _pixman_iter_init_bits_stride, + fast_dest_fetch_noop, fast_write_back_r5g6b5 }, + + { PIXMAN_a8r8g8b8, + (FAST_PATH_STANDARD_FLAGS | + FAST_PATH_SCALE_TRANSFORM | + FAST_PATH_BILINEAR_FILTER | + FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR), + ITER_NARROW | ITER_SRC, + fast_bilinear_cover_iter_init, + NULL, NULL + }, + +#define FAST_BILINEAR_FLAGS \ + (FAST_PATH_NO_ALPHA_MAP | \ + FAST_PATH_NO_ACCESSORS | \ + FAST_PATH_HAS_TRANSFORM | \ + FAST_PATH_AFFINE_TRANSFORM | \ + FAST_PATH_X_UNIT_POSITIVE | \ + FAST_PATH_Y_UNIT_ZERO | \ + FAST_PATH_NONE_REPEAT | \ + FAST_PATH_BILINEAR_FILTER) + + { PIXMAN_a8r8g8b8, + FAST_BILINEAR_FLAGS, + ITER_NARROW | ITER_SRC, + NULL, bits_image_fetch_bilinear_no_repeat_8888, NULL + }, + + { PIXMAN_x8r8g8b8, + FAST_BILINEAR_FLAGS, + ITER_NARROW | ITER_SRC, + NULL, bits_image_fetch_bilinear_no_repeat_8888, NULL + }, + +#define GENERAL_BILINEAR_FLAGS \ + (FAST_PATH_NO_ALPHA_MAP | \ + FAST_PATH_NO_ACCESSORS | \ + FAST_PATH_HAS_TRANSFORM | \ + FAST_PATH_AFFINE_TRANSFORM | \ + FAST_PATH_BILINEAR_FILTER) + +#define GENERAL_NEAREST_FLAGS \ + (FAST_PATH_NO_ALPHA_MAP | \ + FAST_PATH_NO_ACCESSORS | \ + FAST_PATH_HAS_TRANSFORM | \ + FAST_PATH_AFFINE_TRANSFORM | \ + FAST_PATH_NEAREST_FILTER) + +#define GENERAL_SEPARABLE_CONVOLUTION_FLAGS \ + (FAST_PATH_NO_ALPHA_MAP | \ + FAST_PATH_NO_ACCESSORS | \ + FAST_PATH_HAS_TRANSFORM | \ + FAST_PATH_AFFINE_TRANSFORM | \ + FAST_PATH_SEPARABLE_CONVOLUTION_FILTER) + +#define SEPARABLE_CONVOLUTION_AFFINE_FAST_PATH(name, format, repeat) \ + { PIXMAN_ ## format, \ + GENERAL_SEPARABLE_CONVOLUTION_FLAGS | FAST_PATH_ ## repeat ## _REPEAT, \ + ITER_NARROW | ITER_SRC, \ + NULL, bits_image_fetch_separable_convolution_affine_ ## name, NULL \ + }, + +#define BILINEAR_AFFINE_FAST_PATH(name, format, repeat) \ + { PIXMAN_ ## format, \ + GENERAL_BILINEAR_FLAGS | FAST_PATH_ ## repeat ## _REPEAT, \ + ITER_NARROW | ITER_SRC, \ + NULL, bits_image_fetch_bilinear_affine_ ## name, NULL, \ + }, + +#define NEAREST_AFFINE_FAST_PATH(name, format, repeat) \ + { PIXMAN_ ## format, \ + GENERAL_NEAREST_FLAGS | FAST_PATH_ ## repeat ## _REPEAT, \ + ITER_NARROW | ITER_SRC, \ + NULL, bits_image_fetch_nearest_affine_ ## name, NULL \ + }, + +#define AFFINE_FAST_PATHS(name, format, repeat) \ + NEAREST_AFFINE_FAST_PATH(name, format, repeat) \ + BILINEAR_AFFINE_FAST_PATH(name, format, repeat) \ + SEPARABLE_CONVOLUTION_AFFINE_FAST_PATH(name, format, repeat) + + AFFINE_FAST_PATHS (pad_a8r8g8b8, a8r8g8b8, PAD) + AFFINE_FAST_PATHS (none_a8r8g8b8, a8r8g8b8, NONE) + AFFINE_FAST_PATHS (reflect_a8r8g8b8, a8r8g8b8, REFLECT) + AFFINE_FAST_PATHS (normal_a8r8g8b8, a8r8g8b8, NORMAL) + AFFINE_FAST_PATHS (pad_x8r8g8b8, x8r8g8b8, PAD) + AFFINE_FAST_PATHS (none_x8r8g8b8, x8r8g8b8, NONE) + AFFINE_FAST_PATHS (reflect_x8r8g8b8, x8r8g8b8, REFLECT) + AFFINE_FAST_PATHS (normal_x8r8g8b8, x8r8g8b8, NORMAL) + AFFINE_FAST_PATHS (pad_a8, a8, PAD) + AFFINE_FAST_PATHS (none_a8, a8, NONE) + AFFINE_FAST_PATHS (reflect_a8, a8, REFLECT) + AFFINE_FAST_PATHS (normal_a8, a8, NORMAL) + AFFINE_FAST_PATHS (pad_r5g6b5, r5g6b5, PAD) + AFFINE_FAST_PATHS (none_r5g6b5, r5g6b5, NONE) + AFFINE_FAST_PATHS (reflect_r5g6b5, r5g6b5, REFLECT) + AFFINE_FAST_PATHS (normal_r5g6b5, r5g6b5, NORMAL) + + { PIXMAN_null }, +}; + +pixman_implementation_t * +_pixman_implementation_create_fast_path (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = _pixman_implementation_create (fallback, c_fast_paths); + + imp->fill = fast_path_fill; + imp->iter_info = fast_iters; + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-filter.c b/gfx/cairo/libpixman/src/pixman-filter.c new file mode 100644 index 0000000000..5f3b752f9b --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-filter.c @@ -0,0 +1,478 @@ +/* + * Copyright 2012, Red Hat, Inc. + * Copyright 2012, Soren Sandmann + * + * 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. + * + * Author: Soren Sandmann + */ +#include +#include +#include +#include +#include +#ifdef HAVE_CONFIG_H +#include +#endif +#include "pixman-private.h" + +typedef double (* kernel_func_t) (double x); + +typedef struct +{ + pixman_kernel_t kernel; + kernel_func_t func; + double width; +} filter_info_t; + +static double +impulse_kernel (double x) +{ + return (x == 0.0)? 1.0 : 0.0; +} + +static double +box_kernel (double x) +{ + return 1; +} + +static double +linear_kernel (double x) +{ + return 1 - fabs (x); +} + +static double +gaussian_kernel (double x) +{ +#define SQRT2 (1.4142135623730950488016887242096980785696718753769480) +#define SIGMA (SQRT2 / 2.0) + + return exp (- x * x / (2 * SIGMA * SIGMA)) / (SIGMA * sqrt (2.0 * M_PI)); +} + +static double +sinc (double x) +{ + if (x == 0.0) + return 1.0; + else + return sin (M_PI * x) / (M_PI * x); +} + +static double +lanczos (double x, int n) +{ + return sinc (x) * sinc (x * (1.0 / n)); +} + +static double +lanczos2_kernel (double x) +{ + return lanczos (x, 2); +} + +static double +lanczos3_kernel (double x) +{ + return lanczos (x, 3); +} + +static double +nice_kernel (double x) +{ + return lanczos3_kernel (x * 0.75); +} + +static double +general_cubic (double x, double B, double C) +{ + double ax = fabs(x); + + 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; + } +} + +static double +cubic_kernel (double x) +{ + /* This is the Mitchell-Netravali filter. + * + * (0.0, 0.5) would give us the Catmull-Rom spline, + * but that one seems to be indistinguishable from Lanczos2. + */ + return general_cubic (x, 1/3.0, 1/3.0); +} + +static const filter_info_t filters[] = +{ + { PIXMAN_KERNEL_IMPULSE, impulse_kernel, 0.0 }, + { PIXMAN_KERNEL_BOX, box_kernel, 1.0 }, + { PIXMAN_KERNEL_LINEAR, linear_kernel, 2.0 }, + { PIXMAN_KERNEL_CUBIC, cubic_kernel, 4.0 }, + { PIXMAN_KERNEL_GAUSSIAN, gaussian_kernel, 5.0 }, + { PIXMAN_KERNEL_LANCZOS2, lanczos2_kernel, 4.0 }, + { PIXMAN_KERNEL_LANCZOS3, lanczos3_kernel, 6.0 }, + { PIXMAN_KERNEL_LANCZOS3_STRETCHED, nice_kernel, 8.0 }, +}; + +/* This function scales @kernel2 by @scale, then + * aligns @x1 in @kernel1 with @x2 in @kernel2 and + * and integrates the product of the kernels across @width. + * + * This function assumes that the intervals are within + * the kernels in question. E.g., the caller must not + * try to integrate a linear kernel ouside of [-1:1] + */ +static double +integral (pixman_kernel_t kernel1, double x1, + pixman_kernel_t kernel2, double scale, double x2, + double width) +{ + if (kernel1 == PIXMAN_KERNEL_BOX && kernel2 == PIXMAN_KERNEL_BOX) + { + return width; + } + /* The LINEAR filter is not differentiable at 0, so if the + * integration interval crosses zero, break it into two + * separate integrals. + */ + else if (kernel1 == PIXMAN_KERNEL_LINEAR && x1 < 0 && x1 + width > 0) + { + return + integral (kernel1, x1, kernel2, scale, x2, - x1) + + integral (kernel1, 0, kernel2, scale, x2 - x1, width + x1); + } + else if (kernel2 == PIXMAN_KERNEL_LINEAR && x2 < 0 && x2 + width > 0) + { + return + integral (kernel1, x1, kernel2, scale, x2, - x2) + + integral (kernel1, x1 - x2, kernel2, scale, 0, width + x2); + } + else if (kernel1 == PIXMAN_KERNEL_IMPULSE) + { + assert (width == 0.0); + return filters[kernel2].func (x2 * scale); + } + else if (kernel2 == PIXMAN_KERNEL_IMPULSE) + { + assert (width == 0.0); + return filters[kernel1].func (x1); + } + else + { + /* Integration via Simpson's rule + * See http://www.intmath.com/integration/6-simpsons-rule.php + * 12 segments (6 cubic approximations) seems to produce best + * result for lanczos3.linear, which was the combination that + * showed the most errors. This makes sense as the lanczos3 + * filter is 6 wide. + */ +#define N_SEGMENTS 12 +#define SAMPLE(a1, a2) \ + (filters[kernel1].func ((a1)) * filters[kernel2].func ((a2) * scale)) + + double s = 0.0; + double h = width / N_SEGMENTS; + int i; + + s = SAMPLE (x1, x2); + + for (i = 1; i < N_SEGMENTS; i += 2) + { + double a1 = x1 + h * i; + double a2 = x2 + h * i; + s += 4 * SAMPLE (a1, a2); + } + + for (i = 2; i < N_SEGMENTS; i += 2) + { + double a1 = x1 + h * i; + double a2 = x2 + h * i; + s += 2 * SAMPLE (a1, a2); + } + + s += SAMPLE (x1 + width, x2 + width); + + return h * s * (1.0 / 3.0); + } +} + +static void +create_1d_filter (int width, + pixman_kernel_t reconstruct, + pixman_kernel_t sample, + double scale, + int n_phases, + pixman_fixed_t *p) +{ + double step; + int i; + + step = 1.0 / n_phases; + + for (i = 0; i < n_phases; ++i) + { + double frac = step / 2.0 + i * step; + pixman_fixed_t new_total; + int x, x1, x2; + double total, e; + + /* Sample convolution of reconstruction and sampling + * filter. See rounding.txt regarding the rounding + * and sample positions. + */ + + x1 = ceil (frac - width / 2.0 - 0.5); + x2 = x1 + width; + + total = 0; + for (x = x1; x < x2; ++x) + { + double pos = x + 0.5 - frac; + double rlow = - filters[reconstruct].width / 2.0; + double rhigh = rlow + filters[reconstruct].width; + double slow = pos - scale * filters[sample].width / 2.0; + double shigh = slow + scale * filters[sample].width; + double c = 0.0; + double ilow, ihigh; + + if (rhigh >= slow && rlow <= shigh) + { + ilow = MAX (slow, rlow); + ihigh = MIN (shigh, rhigh); + + c = integral (reconstruct, ilow, + sample, 1.0 / scale, ilow - pos, + ihigh - ilow); + } + + *p = (pixman_fixed_t)floor (c * 65536.0 + 0.5); + total += *p; + p++; + } + + /* Normalize, with error diffusion */ + p -= width; + total = 65536.0 / total; + new_total = 0; + e = 0.0; + for (x = x1; x < x2; ++x) + { + double v = (*p) * total + e; + pixman_fixed_t t = floor (v + 0.5); + + e = v - t; + new_total += t; + *p++ = t; + } + + /* pixman_fixed_e's worth of error may remain; put it + * at the first sample, since that is the only one that + * hasn't had any error diffused into it. + */ + *(p - width) += pixman_fixed_1 - new_total; + } +} + + +static int +filter_width (pixman_kernel_t reconstruct, pixman_kernel_t sample, double size) +{ + return ceil (filters[reconstruct].width + size * filters[sample].width); +} + +#ifdef PIXMAN_GNUPLOT + +/* If enable-gnuplot is configured, then you can pipe the output of a + * pixman-using program to gnuplot and get a continuously-updated plot + * of the horizontal filter. This works well with demos/scale to test + * the filter generation. + * + * The plot is all the different subposition filters shuffled + * together. This is misleading in a few cases: + * + * IMPULSE.BOX - goes up and down as the subfilters have different + * numbers of non-zero samples + * IMPULSE.TRIANGLE - somewhat crooked for the same reason + * 1-wide filters - looks triangular, but a 1-wide box would be more + * accurate + */ +static void +gnuplot_filter (int width, int n_phases, const pixman_fixed_t* p) +{ + double step; + int i, j; + int first; + + step = 1.0 / n_phases; + + printf ("set style line 1 lc rgb '#0060ad' lt 1 lw 0.5 pt 7 pi 1 ps 0.5\n"); + printf ("plot [x=%g:%g] '-' with linespoints ls 1\n", -width*0.5, width*0.5); + /* Print a point at the origin so that y==0 line is included: */ + printf ("0 0\n\n"); + + /* The position of the first sample of the phase corresponding to + * frac is given by: + * + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + * + * We have to find the frac that minimizes this expression. + * + * For odd widths, we have + * + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + * = ceil (frac) + K - frac + * = 1 + K - frac + * + * for some K, so this is minimized when frac is maximized and + * strictly growing with frac. So for odd widths, we can simply + * start at the last phase and go backwards. + * + * For even widths, we have + * + * ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + * = ceil (frac - 0.5) + K - frac + * + * The graph for this function (ignoring K) looks like this: + * + * 0.5 + * | |\ + * | | \ + * | | \ + * 0 | | \ + * |\ | + * | \ | + * | \ | + * -0.5 | \| + * --------------------------------- + * 0 0.5 1 + * + * So in this case we need to start with the phase whose frac is + * less than, but as close as possible to 0.5, then go backwards + * until we hit the first phase, then wrap around to the last + * phase and continue backwards. + * + * Which phase is as close as possible 0.5? The locations of the + * sampling point corresponding to the kth phase is given by + * 1/(2 * n_phases) + k / n_phases: + * + * 1/(2 * n_phases) + k / n_phases = 0.5 + * + * from which it follows that + * + * k = (n_phases - 1) / 2 + * + * rounded down is the phase in question. + */ + if (width & 1) + first = n_phases - 1; + else + first = (n_phases - 1) / 2; + + for (j = 0; j < width; ++j) + { + for (i = 0; i < n_phases; ++i) + { + int phase = first - i; + double frac, pos; + + if (phase < 0) + phase = n_phases + phase; + + frac = step / 2.0 + phase * step; + pos = ceil (frac - width / 2.0 - 0.5) + 0.5 - frac + j; + + printf ("%g %g\n", + pos, + pixman_fixed_to_double (*(p + phase * width + j))); + } + } + + printf ("e\n"); + fflush (stdout); +} + +#endif + +/* Create the parameter list for a SEPARABLE_CONVOLUTION filter + * with the given kernels and scale parameters + */ +PIXMAN_EXPORT pixman_fixed_t * +pixman_filter_create_separable_convolution (int *n_values, + pixman_fixed_t scale_x, + pixman_fixed_t scale_y, + pixman_kernel_t reconstruct_x, + pixman_kernel_t reconstruct_y, + pixman_kernel_t sample_x, + pixman_kernel_t sample_y, + int subsample_bits_x, + int subsample_bits_y) +{ + double sx = fabs (pixman_fixed_to_double (scale_x)); + double sy = fabs (pixman_fixed_to_double (scale_y)); + pixman_fixed_t *params; + int subsample_x, subsample_y; + int width, height; + + width = filter_width (reconstruct_x, sample_x, sx); + subsample_x = (1 << subsample_bits_x); + + height = filter_width (reconstruct_y, sample_y, sy); + subsample_y = (1 << subsample_bits_y); + + *n_values = 4 + width * subsample_x + height * subsample_y; + + params = malloc (*n_values * sizeof (pixman_fixed_t)); + if (!params) + return NULL; + + params[0] = pixman_int_to_fixed (width); + params[1] = pixman_int_to_fixed (height); + params[2] = pixman_int_to_fixed (subsample_bits_x); + params[3] = pixman_int_to_fixed (subsample_bits_y); + + create_1d_filter (width, reconstruct_x, sample_x, sx, subsample_x, + params + 4); + create_1d_filter (height, reconstruct_y, sample_y, sy, subsample_y, + params + 4 + width * subsample_x); + +#ifdef PIXMAN_GNUPLOT + gnuplot_filter(width, subsample_x, params + 4); +#endif + + return params; +} diff --git a/gfx/cairo/libpixman/src/pixman-general.c b/gfx/cairo/libpixman/src/pixman-general.c new file mode 100644 index 0000000000..7e5a0d09cc --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-general.c @@ -0,0 +1,264 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. + * 2005 Lars Knoll & Zack Rusin, Trolltech + * 2008 Aaron Plattner, NVIDIA Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "pixman-private.h" + +static void +general_iter_init (pixman_iter_t *iter, const pixman_iter_info_t *info) +{ + pixman_image_t *image = iter->image; + + switch (image->type) + { + case BITS: + if ((iter->iter_flags & ITER_SRC) == ITER_SRC) + _pixman_bits_image_src_iter_init (image, iter); + else + _pixman_bits_image_dest_iter_init (image, iter); + break; + + case LINEAR: + _pixman_linear_gradient_iter_init (image, iter); + break; + + case RADIAL: + _pixman_radial_gradient_iter_init (image, iter); + break; + + case CONICAL: + _pixman_conical_gradient_iter_init (image, iter); + break; + + case SOLID: + _pixman_log_error (FUNC, "Solid image not handled by noop"); + break; + + default: + _pixman_log_error (FUNC, "Pixman bug: unknown image type\n"); + break; + } +} + +static const pixman_iter_info_t general_iters[] = +{ + { PIXMAN_any, 0, 0, general_iter_init, NULL, NULL }, + { PIXMAN_null }, +}; + +typedef struct op_info_t op_info_t; +struct op_info_t +{ + uint8_t src, dst; +}; + +#define ITER_IGNORE_BOTH \ + (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB | ITER_LOCALIZED_ALPHA) + +static const op_info_t op_flags[PIXMAN_N_OPERATORS] = +{ + /* Src Dst */ + { ITER_IGNORE_BOTH, ITER_IGNORE_BOTH }, /* CLEAR */ + { ITER_LOCALIZED_ALPHA, ITER_IGNORE_BOTH }, /* SRC */ + { ITER_IGNORE_BOTH, ITER_LOCALIZED_ALPHA }, /* DST */ + { 0, ITER_LOCALIZED_ALPHA }, /* OVER */ + { ITER_LOCALIZED_ALPHA, 0 }, /* OVER_REVERSE */ + { ITER_LOCALIZED_ALPHA, ITER_IGNORE_RGB }, /* IN */ + { ITER_IGNORE_RGB, ITER_LOCALIZED_ALPHA }, /* IN_REVERSE */ + { ITER_LOCALIZED_ALPHA, ITER_IGNORE_RGB }, /* OUT */ + { ITER_IGNORE_RGB, ITER_LOCALIZED_ALPHA }, /* OUT_REVERSE */ + { 0, 0 }, /* ATOP */ + { 0, 0 }, /* ATOP_REVERSE */ + { 0, 0 }, /* XOR */ + { ITER_LOCALIZED_ALPHA, ITER_LOCALIZED_ALPHA }, /* ADD */ + { 0, 0 }, /* SATURATE */ +}; + +#define SCANLINE_BUFFER_LENGTH 8192 + +static pixman_bool_t +operator_needs_division (pixman_op_t op) +{ + static const uint8_t needs_division[] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* SATURATE */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, /* DISJOINT */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, /* CONJOINT */ + 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, /* blend ops */ + }; + + return needs_division[op]; +} + +static void +general_composite_rect (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t stack_scanline_buffer[3 * SCANLINE_BUFFER_LENGTH]; + uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer; + uint8_t *src_buffer, *mask_buffer, *dest_buffer; + pixman_iter_t src_iter, mask_iter, dest_iter; + pixman_combine_32_func_t compose; + pixman_bool_t component_alpha; + iter_flags_t width_flag, src_iter_flags; + int Bpp; + int i; + + if ((src_image->common.flags & FAST_PATH_NARROW_FORMAT) && + (!mask_image || mask_image->common.flags & FAST_PATH_NARROW_FORMAT) && + (dest_image->common.flags & FAST_PATH_NARROW_FORMAT) && + !(operator_needs_division (op)) && + (dest_image->bits.dither == PIXMAN_DITHER_NONE)) + { + width_flag = ITER_NARROW; + Bpp = 4; + } + else + { + width_flag = ITER_WIDE; + Bpp = 16; + } + +#define ALIGN(addr) \ + ((uint8_t *)((((uintptr_t)(addr)) + 15) & (~15))) + + if (width <= 0 || _pixman_multiply_overflows_int (width, Bpp * 3)) + return; + + if (width * Bpp * 3 > sizeof (stack_scanline_buffer) - 15 * 3) + { + scanline_buffer = pixman_malloc_ab_plus_c (width, Bpp * 3, 15 * 3); + + if (!scanline_buffer) + return; + + memset (scanline_buffer, 0, width * Bpp * 3 + 15 * 3); + } + else + { + memset (stack_scanline_buffer, 0, sizeof (stack_scanline_buffer)); + } + + src_buffer = ALIGN (scanline_buffer); + mask_buffer = ALIGN (src_buffer + width * Bpp); + dest_buffer = ALIGN (mask_buffer + width * Bpp); + + if (width_flag == ITER_WIDE) + { + /* To make sure there aren't any NANs in the buffers */ + memset (src_buffer, 0, width * Bpp); + memset (mask_buffer, 0, width * Bpp); + memset (dest_buffer, 0, width * Bpp); + } + + /* src iter */ + src_iter_flags = width_flag | op_flags[op].src | ITER_SRC; + + _pixman_implementation_iter_init (imp->toplevel, &src_iter, src_image, + src_x, src_y, width, height, + src_buffer, src_iter_flags, + info->src_flags); + + /* mask iter */ + if ((src_iter_flags & (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) == + (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) + { + /* If it doesn't matter what the source is, then it doesn't matter + * what the mask is + */ + mask_image = NULL; + } + + component_alpha = mask_image && mask_image->common.component_alpha; + + _pixman_implementation_iter_init ( + imp->toplevel, &mask_iter, + mask_image, mask_x, mask_y, width, height, mask_buffer, + ITER_SRC | width_flag | (component_alpha? 0 : ITER_IGNORE_RGB), + info->mask_flags); + + /* dest iter */ + _pixman_implementation_iter_init ( + imp->toplevel, &dest_iter, dest_image, dest_x, dest_y, width, height, + dest_buffer, ITER_DEST | width_flag | op_flags[op].dst, info->dest_flags); + + compose = _pixman_implementation_lookup_combiner ( + imp->toplevel, op, component_alpha, width_flag != ITER_WIDE); + + for (i = 0; i < height; ++i) + { + uint32_t *s, *m, *d; + + m = mask_iter.get_scanline (&mask_iter, NULL); + s = src_iter.get_scanline (&src_iter, m); + d = dest_iter.get_scanline (&dest_iter, NULL); + + compose (imp->toplevel, op, d, s, m, width); + + dest_iter.write_back (&dest_iter); + } + + if (src_iter.fini) + src_iter.fini (&src_iter); + if (mask_iter.fini) + mask_iter.fini (&mask_iter); + if (dest_iter.fini) + dest_iter.fini (&dest_iter); + + if (scanline_buffer != (uint8_t *) stack_scanline_buffer) + free (scanline_buffer); +} + +static const pixman_fast_path_t general_fast_path[] = +{ + { PIXMAN_OP_any, PIXMAN_any, 0, PIXMAN_any, 0, PIXMAN_any, 0, general_composite_rect }, + { PIXMAN_OP_NONE } +}; + +pixman_implementation_t * +_pixman_implementation_create_general (void) +{ + pixman_implementation_t *imp = _pixman_implementation_create (NULL, general_fast_path); + + _pixman_setup_combiner_functions_32 (imp); + _pixman_setup_combiner_functions_float (imp); + + imp->iter_info = general_iters; + + return imp; +} + diff --git a/gfx/cairo/libpixman/src/pixman-glyph.c b/gfx/cairo/libpixman/src/pixman-glyph.c new file mode 100644 index 0000000000..96a349ab47 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-glyph.c @@ -0,0 +1,676 @@ +/* + * Copyright 2010, 2012, Soren Sandmann + * Copyright 2010, 2011, 2012, Red Hat, Inc + * + * 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. + * + * Author: Soren Sandmann + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include "pixman-private.h" + +#include + +typedef struct glyph_metrics_t glyph_metrics_t; +typedef struct glyph_t glyph_t; + +#define TOMBSTONE ((glyph_t *)0x1) + +/* XXX: These numbers are arbitrary---we've never done any measurements. + */ +#define N_GLYPHS_HIGH_WATER (16384) +#define N_GLYPHS_LOW_WATER (8192) +#define HASH_SIZE (2 * N_GLYPHS_HIGH_WATER) +#define HASH_MASK (HASH_SIZE - 1) + +struct glyph_t +{ + void * font_key; + void * glyph_key; + int origin_x; + int origin_y; + pixman_image_t * image; + pixman_link_t mru_link; +}; + +struct pixman_glyph_cache_t +{ + int n_glyphs; + int n_tombstones; + int freeze_count; + pixman_list_t mru; + glyph_t * glyphs[HASH_SIZE]; +}; + +static void +free_glyph (glyph_t *glyph) +{ + pixman_list_unlink (&glyph->mru_link); + pixman_image_unref (glyph->image); + free (glyph); +} + +static unsigned int +hash (const void *font_key, const void *glyph_key) +{ + size_t key = (size_t)font_key + (size_t)glyph_key; + + /* This hash function is based on one found on Thomas Wang's + * web page at + * + * http://www.concentric.net/~Ttwang/tech/inthash.htm + * + */ + key = (key << 15) - key - 1; + key = key ^ (key >> 12); + key = key + (key << 2); + key = key ^ (key >> 4); + key = key + (key << 3) + (key << 11); + key = key ^ (key >> 16); + + return key; +} + +static glyph_t * +lookup_glyph (pixman_glyph_cache_t *cache, + void *font_key, + void *glyph_key) +{ + unsigned idx; + glyph_t *g; + + idx = hash (font_key, glyph_key); + while ((g = cache->glyphs[idx++ & HASH_MASK])) + { + if (g != TOMBSTONE && + g->font_key == font_key && + g->glyph_key == glyph_key) + { + return g; + } + } + + return NULL; +} + +static void +insert_glyph (pixman_glyph_cache_t *cache, + glyph_t *glyph) +{ + unsigned idx; + glyph_t **loc; + + idx = hash (glyph->font_key, glyph->glyph_key); + + /* Note: we assume that there is room in the table. If there isn't, + * this will be an infinite loop. + */ + do + { + loc = &cache->glyphs[idx++ & HASH_MASK]; + } while (*loc && *loc != TOMBSTONE); + + if (*loc == TOMBSTONE) + cache->n_tombstones--; + cache->n_glyphs++; + + *loc = glyph; +} + +static void +remove_glyph (pixman_glyph_cache_t *cache, + glyph_t *glyph) +{ + unsigned idx; + + idx = hash (glyph->font_key, glyph->glyph_key); + while (cache->glyphs[idx & HASH_MASK] != glyph) + idx++; + + cache->glyphs[idx & HASH_MASK] = TOMBSTONE; + cache->n_tombstones++; + cache->n_glyphs--; + + /* Eliminate tombstones if possible */ + if (cache->glyphs[(idx + 1) & HASH_MASK] == NULL) + { + while (cache->glyphs[idx & HASH_MASK] == TOMBSTONE) + { + cache->glyphs[idx & HASH_MASK] = NULL; + cache->n_tombstones--; + idx--; + } + } +} + +static void +clear_table (pixman_glyph_cache_t *cache) +{ + int i; + + for (i = 0; i < HASH_SIZE; ++i) + { + glyph_t *glyph = cache->glyphs[i]; + + if (glyph && glyph != TOMBSTONE) + free_glyph (glyph); + + cache->glyphs[i] = NULL; + } + + cache->n_glyphs = 0; + cache->n_tombstones = 0; +} + +PIXMAN_EXPORT pixman_glyph_cache_t * +pixman_glyph_cache_create (void) +{ + pixman_glyph_cache_t *cache; + + if (!(cache = malloc (sizeof *cache))) + return NULL; + + memset (cache->glyphs, 0, sizeof (cache->glyphs)); + cache->n_glyphs = 0; + cache->n_tombstones = 0; + cache->freeze_count = 0; + + pixman_list_init (&cache->mru); + + return cache; +} + +PIXMAN_EXPORT void +pixman_glyph_cache_destroy (pixman_glyph_cache_t *cache) +{ + return_if_fail (cache->freeze_count == 0); + + clear_table (cache); + + free (cache); +} + +PIXMAN_EXPORT void +pixman_glyph_cache_freeze (pixman_glyph_cache_t *cache) +{ + cache->freeze_count++; +} + +PIXMAN_EXPORT void +pixman_glyph_cache_thaw (pixman_glyph_cache_t *cache) +{ + if (--cache->freeze_count == 0 && + cache->n_glyphs + cache->n_tombstones > N_GLYPHS_HIGH_WATER) + { + if (cache->n_tombstones > N_GLYPHS_HIGH_WATER) + { + /* More than half the entries are + * tombstones. Just dump the whole table. + */ + clear_table (cache); + } + + while (cache->n_glyphs > N_GLYPHS_LOW_WATER) + { + glyph_t *glyph = CONTAINER_OF (glyph_t, mru_link, cache->mru.tail); + + remove_glyph (cache, glyph); + free_glyph (glyph); + } + } +} + +PIXMAN_EXPORT const void * +pixman_glyph_cache_lookup (pixman_glyph_cache_t *cache, + void *font_key, + void *glyph_key) +{ + return lookup_glyph (cache, font_key, glyph_key); +} + +PIXMAN_EXPORT const void * +pixman_glyph_cache_insert (pixman_glyph_cache_t *cache, + void *font_key, + void *glyph_key, + int origin_x, + int origin_y, + pixman_image_t *image) +{ + glyph_t *glyph; + int32_t width, height; + + return_val_if_fail (cache->freeze_count > 0, NULL); + return_val_if_fail (image->type == BITS, NULL); + + width = image->bits.width; + height = image->bits.height; + + if (cache->n_glyphs >= HASH_SIZE) + return NULL; + + if (!(glyph = malloc (sizeof *glyph))) + return NULL; + + glyph->font_key = font_key; + glyph->glyph_key = glyph_key; + glyph->origin_x = origin_x; + glyph->origin_y = origin_y; + + if (!(glyph->image = pixman_image_create_bits ( + image->bits.format, width, height, NULL, -1))) + { + free (glyph); + return NULL; + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + image, NULL, glyph->image, 0, 0, 0, 0, 0, 0, + width, height); + + if (PIXMAN_FORMAT_A (glyph->image->bits.format) != 0 && + PIXMAN_FORMAT_RGB (glyph->image->bits.format) != 0) + { + pixman_image_set_component_alpha (glyph->image, TRUE); + } + + pixman_list_prepend (&cache->mru, &glyph->mru_link); + + _pixman_image_validate (glyph->image); + insert_glyph (cache, glyph); + + return glyph; +} + +PIXMAN_EXPORT void +pixman_glyph_cache_remove (pixman_glyph_cache_t *cache, + void *font_key, + void *glyph_key) +{ + glyph_t *glyph; + + if ((glyph = lookup_glyph (cache, font_key, glyph_key))) + { + remove_glyph (cache, glyph); + + free_glyph (glyph); + } +} + +PIXMAN_EXPORT void +pixman_glyph_get_extents (pixman_glyph_cache_t *cache, + int n_glyphs, + pixman_glyph_t *glyphs, + pixman_box32_t *extents) +{ + int i; + + extents->x1 = extents->y1 = INT32_MAX; + extents->x2 = extents->y2 = INT32_MIN; + + for (i = 0; i < n_glyphs; ++i) + { + glyph_t *glyph = (glyph_t *)glyphs[i].glyph; + int x1, y1, x2, y2; + + x1 = glyphs[i].x - glyph->origin_x; + y1 = glyphs[i].y - glyph->origin_y; + x2 = glyphs[i].x - glyph->origin_x + glyph->image->bits.width; + y2 = glyphs[i].y - glyph->origin_y + glyph->image->bits.height; + + if (x1 < extents->x1) + extents->x1 = x1; + if (y1 < extents->y1) + extents->y1 = y1; + if (x2 > extents->x2) + extents->x2 = x2; + if (y2 > extents->y2) + extents->y2 = y2; + } +} + +/* This function returns a format that is suitable for use as a mask for the + * set of glyphs in question. + */ +PIXMAN_EXPORT pixman_format_code_t +pixman_glyph_get_mask_format (pixman_glyph_cache_t *cache, + int n_glyphs, + const pixman_glyph_t *glyphs) +{ + pixman_format_code_t format = PIXMAN_a1; + int i; + + for (i = 0; i < n_glyphs; ++i) + { + const glyph_t *glyph = glyphs[i].glyph; + pixman_format_code_t glyph_format = glyph->image->bits.format; + + if (PIXMAN_FORMAT_TYPE (glyph_format) == PIXMAN_TYPE_A) + { + if (PIXMAN_FORMAT_A (glyph_format) > PIXMAN_FORMAT_A (format)) + format = glyph_format; + } + else + { + return PIXMAN_a8r8g8b8; + } + } + + return format; +} + +static pixman_bool_t +box32_intersect (pixman_box32_t *dest, + const pixman_box32_t *box1, + const pixman_box32_t *box2) +{ + dest->x1 = MAX (box1->x1, box2->x1); + dest->y1 = MAX (box1->y1, box2->y1); + dest->x2 = MIN (box1->x2, box2->x2); + dest->y2 = MIN (box1->y2, box2->y2); + + return dest->x2 > dest->x1 && dest->y2 > dest->y1; +} + +#if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) +__attribute__((__force_align_arg_pointer__)) +#endif +PIXMAN_EXPORT void +pixman_composite_glyphs_no_mask (pixman_op_t op, + pixman_image_t *src, + pixman_image_t *dest, + int32_t src_x, + int32_t src_y, + int32_t dest_x, + int32_t dest_y, + pixman_glyph_cache_t *cache, + int n_glyphs, + const pixman_glyph_t *glyphs) +{ + pixman_region32_t region; + pixman_format_code_t glyph_format = PIXMAN_null; + uint32_t glyph_flags = 0; + pixman_format_code_t dest_format; + uint32_t dest_flags; + pixman_composite_func_t func = NULL; + pixman_implementation_t *implementation = NULL; + pixman_composite_info_t info; + int i; + + _pixman_image_validate (src); + _pixman_image_validate (dest); + + dest_format = dest->common.extended_format_code; + dest_flags = dest->common.flags; + + pixman_region32_init (®ion); + if (!_pixman_compute_composite_region32 ( + ®ion, + src, NULL, dest, + src_x - dest_x, src_y - dest_y, 0, 0, 0, 0, + dest->bits.width, dest->bits.height)) + { + goto out; + } + + info.op = op; + info.src_image = src; + info.dest_image = dest; + info.src_flags = src->common.flags; + info.dest_flags = dest->common.flags; + + for (i = 0; i < n_glyphs; ++i) + { + glyph_t *glyph = (glyph_t *)glyphs[i].glyph; + pixman_image_t *glyph_img = glyph->image; + pixman_box32_t glyph_box; + pixman_box32_t *pbox; + uint32_t extra = FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; + pixman_box32_t composite_box; + int n; + + glyph_box.x1 = dest_x + glyphs[i].x - glyph->origin_x; + glyph_box.y1 = dest_y + glyphs[i].y - glyph->origin_y; + glyph_box.x2 = glyph_box.x1 + glyph->image->bits.width; + glyph_box.y2 = glyph_box.y1 + glyph->image->bits.height; + + pbox = pixman_region32_rectangles (®ion, &n); + + info.mask_image = glyph_img; + + while (n--) + { + if (box32_intersect (&composite_box, pbox, &glyph_box)) + { + if (glyph_img->common.extended_format_code != glyph_format || + glyph_img->common.flags != glyph_flags) + { + glyph_format = glyph_img->common.extended_format_code; + glyph_flags = glyph_img->common.flags; + + _pixman_implementation_lookup_composite ( + get_implementation(), op, + src->common.extended_format_code, src->common.flags, + glyph_format, glyph_flags | extra, + dest_format, dest_flags, + &implementation, &func); + } + + info.src_x = src_x + composite_box.x1 - dest_x; + info.src_y = src_y + composite_box.y1 - dest_y; + info.mask_x = composite_box.x1 - (dest_x + glyphs[i].x - glyph->origin_x); + info.mask_y = composite_box.y1 - (dest_y + glyphs[i].y - glyph->origin_y); + info.dest_x = composite_box.x1; + info.dest_y = composite_box.y1; + info.width = composite_box.x2 - composite_box.x1; + info.height = composite_box.y2 - composite_box.y1; + + info.mask_flags = glyph_flags; + + func (implementation, &info); + } + + pbox++; + } + pixman_list_move_to_front (&cache->mru, &glyph->mru_link); + } + +out: + pixman_region32_fini (®ion); +} + +static void +add_glyphs (pixman_glyph_cache_t *cache, + pixman_image_t *dest, + int off_x, int off_y, + int n_glyphs, const pixman_glyph_t *glyphs) +{ + pixman_format_code_t glyph_format = PIXMAN_null; + uint32_t glyph_flags = 0; + pixman_composite_func_t func = NULL; + pixman_implementation_t *implementation = NULL; + pixman_format_code_t dest_format; + uint32_t dest_flags; + pixman_box32_t dest_box; + pixman_composite_info_t info; + pixman_image_t *white_img = NULL; + pixman_bool_t white_src = FALSE; + int i; + + _pixman_image_validate (dest); + + dest_format = dest->common.extended_format_code; + dest_flags = dest->common.flags; + + info.op = PIXMAN_OP_ADD; + info.dest_image = dest; + info.src_x = 0; + info.src_y = 0; + info.dest_flags = dest_flags; + + dest_box.x1 = 0; + dest_box.y1 = 0; + dest_box.x2 = dest->bits.width; + dest_box.y2 = dest->bits.height; + + for (i = 0; i < n_glyphs; ++i) + { + glyph_t *glyph = (glyph_t *)glyphs[i].glyph; + pixman_image_t *glyph_img = glyph->image; + pixman_box32_t glyph_box; + pixman_box32_t composite_box; + + if (glyph_img->common.extended_format_code != glyph_format || + glyph_img->common.flags != glyph_flags) + { + pixman_format_code_t src_format, mask_format; + + glyph_format = glyph_img->common.extended_format_code; + glyph_flags = glyph_img->common.flags; + + if (glyph_format == dest->bits.format) + { + src_format = glyph_format; + mask_format = PIXMAN_null; + info.src_flags = glyph_flags | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; + info.mask_flags = FAST_PATH_IS_OPAQUE; + info.mask_image = NULL; + white_src = FALSE; + } + else + { + if (!white_img) + { + static const pixman_color_t white = { 0xffff, 0xffff, 0xffff, 0xffff }; + + if (!(white_img = pixman_image_create_solid_fill (&white))) + goto out; + + _pixman_image_validate (white_img); + } + + src_format = PIXMAN_solid; + mask_format = glyph_format; + info.src_flags = white_img->common.flags; + info.mask_flags = glyph_flags | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; + info.src_image = white_img; + white_src = TRUE; + } + + _pixman_implementation_lookup_composite ( + get_implementation(), PIXMAN_OP_ADD, + src_format, info.src_flags, + mask_format, info.mask_flags, + dest_format, dest_flags, + &implementation, &func); + } + + glyph_box.x1 = glyphs[i].x - glyph->origin_x + off_x; + glyph_box.y1 = glyphs[i].y - glyph->origin_y + off_y; + glyph_box.x2 = glyph_box.x1 + glyph->image->bits.width; + glyph_box.y2 = glyph_box.y1 + glyph->image->bits.height; + + if (box32_intersect (&composite_box, &glyph_box, &dest_box)) + { + int src_x = composite_box.x1 - glyph_box.x1; + int src_y = composite_box.y1 - glyph_box.y1; + + if (white_src) + info.mask_image = glyph_img; + else + info.src_image = glyph_img; + + info.mask_x = info.src_x = src_x; + info.mask_y = info.src_y = src_y; + info.dest_x = composite_box.x1; + info.dest_y = composite_box.y1; + info.width = composite_box.x2 - composite_box.x1; + info.height = composite_box.y2 - composite_box.y1; + + func (implementation, &info); + + pixman_list_move_to_front (&cache->mru, &glyph->mru_link); + } + } + +out: + if (white_img) + pixman_image_unref (white_img); +} + +/* Conceptually, for each glyph, (white IN glyph) is PIXMAN_OP_ADDed to an + * infinitely big mask image at the position such that the glyph origin point + * is positioned at the (glyphs[i].x, glyphs[i].y) point. + * + * Then (mask_x, mask_y) in the infinite mask and (src_x, src_y) in the source + * image are both aligned with (dest_x, dest_y) in the destination image. Then + * these three images are composited within the + * + * (dest_x, dest_y, dst_x + width, dst_y + height) + * + * rectangle. + * + * TODO: + * - Trim the mask to the destination clip/image? + * - Trim composite region based on sources, when the op ignores 0s. + */ +#if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) +__attribute__((__force_align_arg_pointer__)) +#endif +PIXMAN_EXPORT void +pixman_composite_glyphs (pixman_op_t op, + pixman_image_t *src, + pixman_image_t *dest, + pixman_format_code_t mask_format, + int32_t src_x, + int32_t src_y, + int32_t mask_x, + int32_t mask_y, + int32_t dest_x, + int32_t dest_y, + int32_t width, + int32_t height, + pixman_glyph_cache_t *cache, + int n_glyphs, + const pixman_glyph_t *glyphs) +{ + pixman_image_t *mask; + + if (!(mask = pixman_image_create_bits (mask_format, width, height, NULL, -1))) + return; + + if (PIXMAN_FORMAT_A (mask_format) != 0 && + PIXMAN_FORMAT_RGB (mask_format) != 0) + { + pixman_image_set_component_alpha (mask, TRUE); + } + + add_glyphs (cache, mask, - mask_x, - mask_y, n_glyphs, glyphs); + + pixman_image_composite32 (op, src, mask, dest, + src_x, src_y, + 0, 0, + dest_x, dest_y, + width, height); + + pixman_image_unref (mask); +} diff --git a/gfx/cairo/libpixman/src/pixman-gradient-walker.c b/gfx/cairo/libpixman/src/pixman-gradient-walker.c new file mode 100644 index 0000000000..fb7f401dac --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-gradient-walker.c @@ -0,0 +1,264 @@ +/* + * + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. + * 2005 Lars Knoll & Zack Rusin, Trolltech + * + * 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. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include "pixman-private.h" + +void +_pixman_gradient_walker_init (pixman_gradient_walker_t *walker, + gradient_t * gradient, + pixman_repeat_t repeat) +{ + walker->num_stops = gradient->n_stops; + walker->stops = gradient->stops; + walker->left_x = 0; + walker->right_x = 0x10000; + walker->a_s = 0.0f; + walker->a_b = 0.0f; + walker->r_s = 0.0f; + walker->r_b = 0.0f; + walker->g_s = 0.0f; + walker->g_b = 0.0f; + walker->b_s = 0.0f; + walker->b_b = 0.0f; + walker->repeat = repeat; + + walker->need_reset = TRUE; +} + +static void +gradient_walker_reset (pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t pos) +{ + int64_t x, left_x, right_x; + pixman_color_t *left_c, *right_c; + int n, count = walker->num_stops; + pixman_gradient_stop_t *stops = walker->stops; + float la, lr, lg, lb; + float ra, rr, rg, rb; + float lx, rx; + + if (walker->repeat == PIXMAN_REPEAT_NORMAL) + { + x = (int32_t)pos & 0xffff; + } + else if (walker->repeat == PIXMAN_REPEAT_REFLECT) + { + x = (int32_t)pos & 0xffff; + if ((int32_t)pos & 0x10000) + x = 0x10000 - x; + } + else + { + x = pos; + } + + for (n = 0; n < count; n++) + { + if (x < stops[n].x) + break; + } + + left_x = stops[n - 1].x; + left_c = &stops[n - 1].color; + + right_x = stops[n].x; + right_c = &stops[n].color; + + if (walker->repeat == PIXMAN_REPEAT_NORMAL) + { + left_x += (pos - x); + right_x += (pos - x); + } + else if (walker->repeat == PIXMAN_REPEAT_REFLECT) + { + if ((int32_t)pos & 0x10000) + { + pixman_color_t *tmp_c; + int32_t tmp_x; + + tmp_x = 0x10000 - right_x; + right_x = 0x10000 - left_x; + left_x = tmp_x; + + tmp_c = right_c; + right_c = left_c; + left_c = tmp_c; + + x = 0x10000 - x; + } + left_x += (pos - x); + right_x += (pos - x); + } + else if (walker->repeat == PIXMAN_REPEAT_NONE) + { + if (n == 0) + right_c = left_c; + else if (n == count) + left_c = right_c; + } + + /* The alpha/red/green/blue channels are scaled to be in [0, 1]. + * This ensures that after premultiplication all channels will + * be in the [0, 1] interval. + */ + la = (left_c->alpha * (1.0f/257.0f)); + lr = (left_c->red * (1.0f/257.0f)); + lg = (left_c->green * (1.0f/257.0f)); + lb = (left_c->blue * (1.0f/257.0f)); + + ra = (right_c->alpha * (1.0f/257.0f)); + rr = (right_c->red * (1.0f/257.0f)); + rg = (right_c->green * (1.0f/257.0f)); + rb = (right_c->blue * (1.0f/257.0f)); + + lx = left_x * (1.0f/65536.0f); + rx = right_x * (1.0f/65536.0f); + + if (FLOAT_IS_ZERO (rx - lx) || left_x == INT32_MIN || right_x == INT32_MAX) + { + walker->a_s = walker->r_s = walker->g_s = walker->b_s = 0.0f; + walker->a_b = (la + ra) / 510.0f; + walker->r_b = (lr + rr) / 510.0f; + walker->g_b = (lg + rg) / 510.0f; + walker->b_b = (lb + rb) / 510.0f; + } + else + { + float w_rec = 1.0f / (rx - lx); + + walker->a_b = (la * rx - ra * lx) * w_rec * (1.0f/255.0f); + walker->r_b = (lr * rx - rr * lx) * w_rec * (1.0f/255.0f); + walker->g_b = (lg * rx - rg * lx) * w_rec * (1.0f/255.0f); + walker->b_b = (lb * rx - rb * lx) * w_rec * (1.0f/255.0f); + + walker->a_s = (ra - la) * w_rec * (1.0f/255.0f); + walker->r_s = (rr - lr) * w_rec * (1.0f/255.0f); + walker->g_s = (rg - lg) * w_rec * (1.0f/255.0f); + walker->b_s = (rb - lb) * w_rec * (1.0f/255.0f); + } + + walker->left_x = left_x; + walker->right_x = right_x; + + walker->need_reset = FALSE; +} + +static argb_t +pixman_gradient_walker_pixel_float (pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x) +{ + argb_t f; + float y; + + if (walker->need_reset || x < walker->left_x || x >= walker->right_x) + gradient_walker_reset (walker, x); + + y = x * (1.0f / 65536.0f); + + f.a = walker->a_s * y + walker->a_b; + f.r = f.a * (walker->r_s * y + walker->r_b); + f.g = f.a * (walker->g_s * y + walker->g_b); + f.b = f.a * (walker->b_s * y + walker->b_b); + + return f; +} + +static uint32_t +pixman_gradient_walker_pixel_32 (pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x) +{ + argb_t f; + float y; + + if (walker->need_reset || x < walker->left_x || x >= walker->right_x) + gradient_walker_reset (walker, x); + + y = x * (1.0f / 65536.0f); + + /* Instead of [0...1] for ARGB, we want [0...255], + * multiply alpha with 255 and the color channels + * also get multiplied by the alpha multiplier. + * + * We don't use pixman_contract_from_float because it causes a 2x + * slowdown to do so, and the values are already normalized, + * so we don't have to worry about values < 0.f or > 1.f + */ + f.a = 255.f * (walker->a_s * y + walker->a_b); + f.r = f.a * (walker->r_s * y + walker->r_b); + f.g = f.a * (walker->g_s * y + walker->g_b); + f.b = f.a * (walker->b_s * y + walker->b_b); + + return (((uint32_t)(f.a + .5f) << 24) & 0xff000000) | + (((uint32_t)(f.r + .5f) << 16) & 0x00ff0000) | + (((uint32_t)(f.g + .5f) << 8) & 0x0000ff00) | + (((uint32_t)(f.b + .5f) >> 0) & 0x000000ff); +} + +void +_pixman_gradient_walker_write_narrow (pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer) +{ + *buffer = pixman_gradient_walker_pixel_32 (walker, x); +} + +void +_pixman_gradient_walker_write_wide (pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer) +{ + *(argb_t *)buffer = pixman_gradient_walker_pixel_float (walker, x); +} + +void +_pixman_gradient_walker_fill_narrow (pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer, + uint32_t *end) +{ + register uint32_t color; + + color = pixman_gradient_walker_pixel_32 (walker, x); + while (buffer < end) + *buffer++ = color; +} + +void +_pixman_gradient_walker_fill_wide (pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer, + uint32_t *end) +{ + register argb_t color; + argb_t *buffer_wide = (argb_t *)buffer; + argb_t *end_wide = (argb_t *)end; + + color = pixman_gradient_walker_pixel_float (walker, x); + while (buffer_wide < end_wide) + *buffer_wide++ = color; +} diff --git a/gfx/cairo/libpixman/src/pixman-image.c b/gfx/cairo/libpixman/src/pixman-image.c new file mode 100644 index 0000000000..db29ff5b4f --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-image.c @@ -0,0 +1,994 @@ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "pixman-private.h" + +static const pixman_color_t transparent_black = { 0, 0, 0, 0 }; + +static void +gradient_property_changed (pixman_image_t *image) +{ + gradient_t *gradient = &image->gradient; + int n = gradient->n_stops; + pixman_gradient_stop_t *stops = gradient->stops; + pixman_gradient_stop_t *begin = &(gradient->stops[-1]); + pixman_gradient_stop_t *end = &(gradient->stops[n]); + + switch (gradient->common.repeat) + { + default: + case PIXMAN_REPEAT_NONE: + begin->x = INT32_MIN; + begin->color = transparent_black; + end->x = INT32_MAX; + end->color = transparent_black; + break; + + case PIXMAN_REPEAT_NORMAL: + begin->x = stops[n - 1].x - pixman_fixed_1; + begin->color = stops[n - 1].color; + end->x = stops[0].x + pixman_fixed_1; + end->color = stops[0].color; + break; + + case PIXMAN_REPEAT_REFLECT: + begin->x = - stops[0].x; + begin->color = stops[0].color; + end->x = pixman_int_to_fixed (2) - stops[n - 1].x; + end->color = stops[n - 1].color; + break; + + case PIXMAN_REPEAT_PAD: + begin->x = INT32_MIN; + begin->color = stops[0].color; + end->x = INT32_MAX; + end->color = stops[n - 1].color; + break; + } +} + +pixman_bool_t +_pixman_init_gradient (gradient_t * gradient, + const pixman_gradient_stop_t *stops, + int n_stops) +{ + return_val_if_fail (n_stops > 0, FALSE); + + /* We allocate two extra stops, one before the beginning of the stop list, + * and one after the end. These stops are initialized to whatever color + * would be used for positions outside the range of the stop list. + * + * This saves a bit of computation in the gradient walker. + * + * The pointer we store in the gradient_t struct still points to the + * first user-supplied struct, so when freeing, we will have to + * subtract one. + */ + gradient->stops = + pixman_malloc_ab (n_stops + 2, sizeof (pixman_gradient_stop_t)); + if (!gradient->stops) + return FALSE; + + gradient->stops += 1; + memcpy (gradient->stops, stops, n_stops * sizeof (pixman_gradient_stop_t)); + gradient->n_stops = n_stops; + + gradient->common.property_changed = gradient_property_changed; + + return TRUE; +} + +void +_pixman_image_init (pixman_image_t *image) +{ + image_common_t *common = &image->common; + + pixman_region32_init (&common->clip_region); + + common->alpha_count = 0; + common->have_clip_region = FALSE; + common->clip_sources = FALSE; + common->transform = NULL; + common->repeat = PIXMAN_REPEAT_NONE; + common->filter = PIXMAN_FILTER_NEAREST; + common->filter_params = NULL; + common->n_filter_params = 0; + common->alpha_map = NULL; + common->component_alpha = FALSE; + common->ref_count = 1; + common->property_changed = NULL; + common->client_clip = FALSE; + common->destroy_func = NULL; + common->destroy_data = NULL; + common->dirty = TRUE; +} + +pixman_bool_t +_pixman_image_fini (pixman_image_t *image) +{ + image_common_t *common = (image_common_t *)image; + + common->ref_count--; + + if (common->ref_count == 0) + { + if (image->common.destroy_func) + image->common.destroy_func (image, image->common.destroy_data); + + pixman_region32_fini (&common->clip_region); + + free (common->transform); + free (common->filter_params); + + if (common->alpha_map) + pixman_image_unref ((pixman_image_t *)common->alpha_map); + + if (image->type == LINEAR || + image->type == RADIAL || + image->type == CONICAL) + { + if (image->gradient.stops) + { + /* See _pixman_init_gradient() for an explanation of the - 1 */ + free (image->gradient.stops - 1); + } + + /* This will trigger if someone adds a property_changed + * method to the linear/radial/conical gradient overwriting + * the general one. + */ + assert ( + image->common.property_changed == gradient_property_changed); + } + + if (image->type == BITS && image->bits.free_me) + free (image->bits.free_me); + + return TRUE; + } + + return FALSE; +} + +pixman_image_t * +_pixman_image_allocate (void) +{ + pixman_image_t *image = malloc (sizeof (pixman_image_t)); + + if (image) + _pixman_image_init (image); + + return image; +} + +static void +image_property_changed (pixman_image_t *image) +{ + image->common.dirty = TRUE; +} + +/* Ref Counting */ +PIXMAN_EXPORT pixman_image_t * +pixman_image_ref (pixman_image_t *image) +{ + image->common.ref_count++; + + return image; +} + +/* returns TRUE when the image is freed */ +PIXMAN_EXPORT pixman_bool_t +pixman_image_unref (pixman_image_t *image) +{ + if (_pixman_image_fini (image)) + { + free (image); + return TRUE; + } + + return FALSE; +} + +PIXMAN_EXPORT void +pixman_image_set_destroy_function (pixman_image_t * image, + pixman_image_destroy_func_t func, + void * data) +{ + image->common.destroy_func = func; + image->common.destroy_data = data; +} + +PIXMAN_EXPORT void * +pixman_image_get_destroy_data (pixman_image_t *image) +{ + return image->common.destroy_data; +} + +void +_pixman_image_reset_clip_region (pixman_image_t *image) +{ + image->common.have_clip_region = FALSE; +} + +/* Executive Summary: This function is a no-op that only exists + * for historical reasons. + * + * There used to be a bug in the X server where it would rely on + * out-of-bounds accesses when it was asked to composite with a + * window as the source. It would create a pixman image pointing + * to some bogus position in memory, but then set a clip region + * to the position where the actual bits were. + * + * Due to a bug in old versions of pixman, where it would not clip + * against the image bounds when a clip region was set, this would + * actually work. So when the pixman bug was fixed, a workaround was + * added to allow certain out-of-bound accesses. This function disabled + * those workarounds. + * + * Since 0.21.2, pixman doesn't do these workarounds anymore, so now + * this function is a no-op. + */ +PIXMAN_EXPORT void +pixman_disable_out_of_bounds_workaround (void) +{ +} + +static void +compute_image_info (pixman_image_t *image) +{ + pixman_format_code_t code; + uint32_t flags = 0; + + /* Transform */ + if (!image->common.transform) + { + flags |= (FAST_PATH_ID_TRANSFORM | + FAST_PATH_X_UNIT_POSITIVE | + FAST_PATH_Y_UNIT_ZERO | + FAST_PATH_AFFINE_TRANSFORM); + } + else + { + flags |= FAST_PATH_HAS_TRANSFORM; + + if (image->common.transform->matrix[2][0] == 0 && + image->common.transform->matrix[2][1] == 0 && + image->common.transform->matrix[2][2] == pixman_fixed_1) + { + flags |= FAST_PATH_AFFINE_TRANSFORM; + + if (image->common.transform->matrix[0][1] == 0 && + image->common.transform->matrix[1][0] == 0) + { + if (image->common.transform->matrix[0][0] == -pixman_fixed_1 && + image->common.transform->matrix[1][1] == -pixman_fixed_1) + { + flags |= FAST_PATH_ROTATE_180_TRANSFORM; + } + flags |= FAST_PATH_SCALE_TRANSFORM; + } + else if (image->common.transform->matrix[0][0] == 0 && + image->common.transform->matrix[1][1] == 0) + { + pixman_fixed_t m01 = image->common.transform->matrix[0][1]; + pixman_fixed_t m10 = image->common.transform->matrix[1][0]; + + if (m01 == -pixman_fixed_1 && m10 == pixman_fixed_1) + flags |= FAST_PATH_ROTATE_90_TRANSFORM; + else if (m01 == pixman_fixed_1 && m10 == -pixman_fixed_1) + flags |= FAST_PATH_ROTATE_270_TRANSFORM; + } + } + + if (image->common.transform->matrix[0][0] > 0) + flags |= FAST_PATH_X_UNIT_POSITIVE; + + if (image->common.transform->matrix[1][0] == 0) + flags |= FAST_PATH_Y_UNIT_ZERO; + } + + /* Filter */ + switch (image->common.filter) + { + case PIXMAN_FILTER_NEAREST: + case PIXMAN_FILTER_FAST: + flags |= (FAST_PATH_NEAREST_FILTER | FAST_PATH_NO_CONVOLUTION_FILTER); + break; + + case PIXMAN_FILTER_BILINEAR: + case PIXMAN_FILTER_GOOD: + case PIXMAN_FILTER_BEST: + flags |= (FAST_PATH_BILINEAR_FILTER | FAST_PATH_NO_CONVOLUTION_FILTER); + + /* Here we have a chance to optimize BILINEAR filter to NEAREST if + * they are equivalent for the currently used transformation matrix. + */ + if (flags & FAST_PATH_ID_TRANSFORM) + { + flags |= FAST_PATH_NEAREST_FILTER; + } + else if (flags & FAST_PATH_AFFINE_TRANSFORM) + { + /* Suppose the transform is + * + * [ t00, t01, t02 ] + * [ t10, t11, t12 ] + * [ 0, 0, 1 ] + * + * and the destination coordinates are (n + 0.5, m + 0.5). Then + * the transformed x coordinate is: + * + * tx = t00 * (n + 0.5) + t01 * (m + 0.5) + t02 + * = t00 * n + t01 * m + t02 + (t00 + t01) * 0.5 + * + * which implies that if t00, t01 and t02 are all integers + * and (t00 + t01) is odd, then tx will be an integer plus 0.5, + * which means a BILINEAR filter will reduce to NEAREST. The same + * applies in the y direction + */ + pixman_fixed_t (*t)[3] = image->common.transform->matrix; + + if ((pixman_fixed_frac ( + t[0][0] | t[0][1] | t[0][2] | + t[1][0] | t[1][1] | t[1][2]) == 0) && + (pixman_fixed_to_int ( + (t[0][0] + t[0][1]) & (t[1][0] + t[1][1])) % 2) == 1) + { + /* FIXME: there are some affine-test failures, showing that + * handling of BILINEAR and NEAREST filter is not quite + * equivalent when getting close to 32K for the translation + * components of the matrix. That's likely some bug, but for + * now just skip BILINEAR->NEAREST optimization in this case. + */ + pixman_fixed_t magic_limit = pixman_int_to_fixed (30000); + if (image->common.transform->matrix[0][2] <= magic_limit && + image->common.transform->matrix[1][2] <= magic_limit && + image->common.transform->matrix[0][2] >= -magic_limit && + image->common.transform->matrix[1][2] >= -magic_limit) + { + flags |= FAST_PATH_NEAREST_FILTER; + } + } + } + break; + + case PIXMAN_FILTER_CONVOLUTION: + break; + + case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: + flags |= FAST_PATH_SEPARABLE_CONVOLUTION_FILTER; + break; + + default: + flags |= FAST_PATH_NO_CONVOLUTION_FILTER; + break; + } + + /* Repeat mode */ + switch (image->common.repeat) + { + case PIXMAN_REPEAT_NONE: + flags |= + FAST_PATH_NO_REFLECT_REPEAT | + FAST_PATH_NO_PAD_REPEAT | + FAST_PATH_NO_NORMAL_REPEAT; + break; + + case PIXMAN_REPEAT_REFLECT: + flags |= + FAST_PATH_NO_PAD_REPEAT | + FAST_PATH_NO_NONE_REPEAT | + FAST_PATH_NO_NORMAL_REPEAT; + break; + + case PIXMAN_REPEAT_PAD: + flags |= + FAST_PATH_NO_REFLECT_REPEAT | + FAST_PATH_NO_NONE_REPEAT | + FAST_PATH_NO_NORMAL_REPEAT; + break; + + default: + flags |= + FAST_PATH_NO_REFLECT_REPEAT | + FAST_PATH_NO_PAD_REPEAT | + FAST_PATH_NO_NONE_REPEAT; + break; + } + + /* Component alpha */ + if (image->common.component_alpha) + flags |= FAST_PATH_COMPONENT_ALPHA; + else + flags |= FAST_PATH_UNIFIED_ALPHA; + + flags |= (FAST_PATH_NO_ACCESSORS | FAST_PATH_NARROW_FORMAT); + + /* Type specific checks */ + switch (image->type) + { + case SOLID: + code = PIXMAN_solid; + + if (image->solid.color.alpha == 0xffff) + flags |= FAST_PATH_IS_OPAQUE; + break; + + case BITS: + if (image->bits.width == 1 && + image->bits.height == 1 && + image->common.repeat != PIXMAN_REPEAT_NONE) + { + code = PIXMAN_solid; + } + else + { + code = image->bits.format; + flags |= FAST_PATH_BITS_IMAGE; + } + + if (!PIXMAN_FORMAT_A (image->bits.format) && + PIXMAN_FORMAT_TYPE (image->bits.format) != PIXMAN_TYPE_GRAY && + PIXMAN_FORMAT_TYPE (image->bits.format) != PIXMAN_TYPE_COLOR) + { + flags |= FAST_PATH_SAMPLES_OPAQUE; + + if (image->common.repeat != PIXMAN_REPEAT_NONE) + flags |= FAST_PATH_IS_OPAQUE; + } + + if (image->bits.read_func || image->bits.write_func) + flags &= ~FAST_PATH_NO_ACCESSORS; + + if (PIXMAN_FORMAT_IS_WIDE (image->bits.format)) + flags &= ~FAST_PATH_NARROW_FORMAT; + break; + + case RADIAL: + code = PIXMAN_unknown; + + /* + * As explained in pixman-radial-gradient.c, every point of + * the plane has a valid associated radius (and thus will be + * colored) if and only if a is negative (i.e. one of the two + * circles contains the other one). + */ + + if (image->radial.a >= 0) + break; + + /* Fall through */ + + case CONICAL: + case LINEAR: + code = PIXMAN_unknown; + + if (image->common.repeat != PIXMAN_REPEAT_NONE) + { + int i; + + flags |= FAST_PATH_IS_OPAQUE; + for (i = 0; i < image->gradient.n_stops; ++i) + { + if (image->gradient.stops[i].color.alpha != 0xffff) + { + flags &= ~FAST_PATH_IS_OPAQUE; + break; + } + } + } + break; + + default: + code = PIXMAN_unknown; + break; + } + + /* Alpha maps are only supported for BITS images, so it's always + * safe to ignore their presense for non-BITS images + */ + if (!image->common.alpha_map || image->type != BITS) + { + flags |= FAST_PATH_NO_ALPHA_MAP; + } + else + { + if (PIXMAN_FORMAT_IS_WIDE (image->common.alpha_map->format)) + flags &= ~FAST_PATH_NARROW_FORMAT; + } + + /* Both alpha maps and convolution filters can introduce + * non-opaqueness in otherwise opaque images. Also + * an image with component alpha turned on is only opaque + * if all channels are opaque, so we simply turn it off + * unconditionally for those images. + */ + if (image->common.alpha_map || + image->common.filter == PIXMAN_FILTER_CONVOLUTION || + image->common.filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION || + image->common.component_alpha) + { + flags &= ~(FAST_PATH_IS_OPAQUE | FAST_PATH_SAMPLES_OPAQUE); + } + + image->common.flags = flags; + image->common.extended_format_code = code; +} + +void +_pixman_image_validate (pixman_image_t *image) +{ + if (image->common.dirty) + { + compute_image_info (image); + + /* It is important that property_changed is + * called *after* compute_image_info() because + * property_changed() can make use of the flags + * to set up accessors etc. + */ + if (image->common.property_changed) + image->common.property_changed (image); + + image->common.dirty = FALSE; + } + + if (image->common.alpha_map) + _pixman_image_validate ((pixman_image_t *)image->common.alpha_map); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_image_set_clip_region32 (pixman_image_t * image, + pixman_region32_t *region) +{ + image_common_t *common = (image_common_t *)image; + pixman_bool_t result; + + if (region) + { + if ((result = pixman_region32_copy (&common->clip_region, region))) + image->common.have_clip_region = TRUE; + } + else + { + _pixman_image_reset_clip_region (image); + + result = TRUE; + } + + image_property_changed (image); + + return result; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_image_set_clip_region (pixman_image_t * image, + pixman_region16_t *region) +{ + image_common_t *common = (image_common_t *)image; + pixman_bool_t result; + + if (region) + { + if ((result = pixman_region32_copy_from_region16 (&common->clip_region, region))) + image->common.have_clip_region = TRUE; + } + else + { + _pixman_image_reset_clip_region (image); + + result = TRUE; + } + + image_property_changed (image); + + return result; +} + +PIXMAN_EXPORT void +pixman_image_set_has_client_clip (pixman_image_t *image, + pixman_bool_t client_clip) +{ + image->common.client_clip = client_clip; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_image_set_transform (pixman_image_t * image, + const pixman_transform_t *transform) +{ + static const pixman_transform_t id = + { + { { pixman_fixed_1, 0, 0 }, + { 0, pixman_fixed_1, 0 }, + { 0, 0, pixman_fixed_1 } } + }; + + image_common_t *common = (image_common_t *)image; + pixman_bool_t result; + + if (common->transform == transform) + return TRUE; + + if (!transform || memcmp (&id, transform, sizeof (pixman_transform_t)) == 0) + { + free (common->transform); + common->transform = NULL; + result = TRUE; + + goto out; + } + + if (common->transform && + memcmp (common->transform, transform, sizeof (pixman_transform_t)) == 0) + { + return TRUE; + } + + if (common->transform == NULL) + common->transform = malloc (sizeof (pixman_transform_t)); + + if (common->transform == NULL) + { + result = FALSE; + + goto out; + } + + memcpy (common->transform, transform, sizeof(pixman_transform_t)); + + result = TRUE; + +out: + image_property_changed (image); + + return result; +} + +PIXMAN_EXPORT void +pixman_image_set_repeat (pixman_image_t *image, + pixman_repeat_t repeat) +{ + if (image->common.repeat == repeat) + return; + + image->common.repeat = repeat; + + image_property_changed (image); +} + +PIXMAN_EXPORT void +pixman_image_set_dither (pixman_image_t *image, + pixman_dither_t dither) +{ + if (image->type == BITS) + { + if (image->bits.dither == dither) + return; + + image->bits.dither = dither; + + image_property_changed (image); + } +} + +PIXMAN_EXPORT void +pixman_image_set_dither_offset (pixman_image_t *image, + int offset_x, + int offset_y) +{ + if (image->type == BITS) + { + if (image->bits.dither_offset_x == offset_x && + image->bits.dither_offset_y == offset_y) + { + return; + } + + image->bits.dither_offset_x = offset_x; + image->bits.dither_offset_y = offset_y; + + image_property_changed (image); + } +} + +PIXMAN_EXPORT pixman_bool_t +pixman_image_set_filter (pixman_image_t * image, + pixman_filter_t filter, + const pixman_fixed_t *params, + int n_params) +{ + image_common_t *common = (image_common_t *)image; + pixman_fixed_t *new_params; + + if (params == common->filter_params && filter == common->filter) + return TRUE; + + if (filter == PIXMAN_FILTER_SEPARABLE_CONVOLUTION) + { + int width = pixman_fixed_to_int (params[0]); + int height = pixman_fixed_to_int (params[1]); + int x_phase_bits = pixman_fixed_to_int (params[2]); + int y_phase_bits = pixman_fixed_to_int (params[3]); + int n_x_phases = (1 << x_phase_bits); + int n_y_phases = (1 << y_phase_bits); + + return_val_if_fail ( + n_params == 4 + n_x_phases * width + n_y_phases * height, FALSE); + } + + new_params = NULL; + if (params) + { + new_params = pixman_malloc_ab (n_params, sizeof (pixman_fixed_t)); + if (!new_params) + return FALSE; + + memcpy (new_params, + params, n_params * sizeof (pixman_fixed_t)); + } + + common->filter = filter; + + if (common->filter_params) + free (common->filter_params); + + common->filter_params = new_params; + common->n_filter_params = n_params; + + image_property_changed (image); + return TRUE; +} + +PIXMAN_EXPORT void +pixman_image_set_source_clipping (pixman_image_t *image, + pixman_bool_t clip_sources) +{ + if (image->common.clip_sources == clip_sources) + return; + + image->common.clip_sources = clip_sources; + + image_property_changed (image); +} + +/* Unlike all the other property setters, this function does not + * copy the content of indexed. Doing this copying is simply + * way, way too expensive. + */ +PIXMAN_EXPORT void +pixman_image_set_indexed (pixman_image_t * image, + const pixman_indexed_t *indexed) +{ + bits_image_t *bits = (bits_image_t *)image; + + if (bits->indexed == indexed) + return; + + bits->indexed = indexed; + + image_property_changed (image); +} + +PIXMAN_EXPORT void +pixman_image_set_alpha_map (pixman_image_t *image, + pixman_image_t *alpha_map, + int16_t x, + int16_t y) +{ + image_common_t *common = (image_common_t *)image; + + return_if_fail (!alpha_map || alpha_map->type == BITS); + + if (alpha_map && common->alpha_count > 0) + { + /* If this image is being used as an alpha map itself, + * then you can't give it an alpha map of its own. + */ + return; + } + + if (alpha_map && alpha_map->common.alpha_map) + { + /* If the image has an alpha map of its own, + * then it can't be used as an alpha map itself + */ + return; + } + + if (common->alpha_map != (bits_image_t *)alpha_map) + { + if (common->alpha_map) + { + common->alpha_map->common.alpha_count--; + + pixman_image_unref ((pixman_image_t *)common->alpha_map); + } + + if (alpha_map) + { + common->alpha_map = (bits_image_t *)pixman_image_ref (alpha_map); + + common->alpha_map->common.alpha_count++; + } + else + { + common->alpha_map = NULL; + } + } + + common->alpha_origin_x = x; + common->alpha_origin_y = y; + + image_property_changed (image); +} + +PIXMAN_EXPORT void +pixman_image_set_component_alpha (pixman_image_t *image, + pixman_bool_t component_alpha) +{ + if (image->common.component_alpha == component_alpha) + return; + + image->common.component_alpha = component_alpha; + + image_property_changed (image); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_image_get_component_alpha (pixman_image_t *image) +{ + return image->common.component_alpha; +} + +PIXMAN_EXPORT void +pixman_image_set_accessors (pixman_image_t * image, + pixman_read_memory_func_t read_func, + pixman_write_memory_func_t write_func) +{ + return_if_fail (image != NULL); + + if (image->type == BITS) + { + /* Accessors only work for <= 32 bpp. */ + if (PIXMAN_FORMAT_BPP(image->bits.format) > 32) + return_if_fail (!read_func && !write_func); + + image->bits.read_func = read_func; + image->bits.write_func = write_func; + + image_property_changed (image); + } +} + +PIXMAN_EXPORT uint32_t * +pixman_image_get_data (pixman_image_t *image) +{ + if (image->type == BITS) + return image->bits.bits; + + return NULL; +} + +PIXMAN_EXPORT int +pixman_image_get_width (pixman_image_t *image) +{ + if (image->type == BITS) + return image->bits.width; + + return 0; +} + +PIXMAN_EXPORT int +pixman_image_get_height (pixman_image_t *image) +{ + if (image->type == BITS) + return image->bits.height; + + return 0; +} + +PIXMAN_EXPORT int +pixman_image_get_stride (pixman_image_t *image) +{ + if (image->type == BITS) + return image->bits.rowstride * (int) sizeof (uint32_t); + + return 0; +} + +PIXMAN_EXPORT int +pixman_image_get_depth (pixman_image_t *image) +{ + if (image->type == BITS) + return PIXMAN_FORMAT_DEPTH (image->bits.format); + + return 0; +} + +PIXMAN_EXPORT pixman_format_code_t +pixman_image_get_format (pixman_image_t *image) +{ + if (image->type == BITS) + return image->bits.format; + + return PIXMAN_null; +} + +uint32_t +_pixman_image_get_solid (pixman_implementation_t *imp, + pixman_image_t * image, + pixman_format_code_t format) +{ + uint32_t result; + + if (image->type == SOLID) + { + result = image->solid.color_32; + } + else if (image->type == BITS) + { + if (image->bits.format == PIXMAN_a8r8g8b8) + result = image->bits.bits[0]; + else if (image->bits.format == PIXMAN_x8r8g8b8) + result = image->bits.bits[0] | 0xff000000; + else if (image->bits.format == PIXMAN_a8) + result = (uint32_t)(*(uint8_t *)image->bits.bits) << 24; + else + goto otherwise; + } + else + { + pixman_iter_t iter; + + otherwise: + _pixman_implementation_iter_init ( + imp, &iter, image, 0, 0, 1, 1, + (uint8_t *)&result, + ITER_NARROW | ITER_SRC, image->common.flags); + + result = *iter.get_scanline (&iter, NULL); + + if (iter.fini) + iter.fini (&iter); + } + + /* If necessary, convert RGB <--> BGR. */ + if (PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_ARGB + && PIXMAN_FORMAT_TYPE (format) != PIXMAN_TYPE_ARGB_SRGB) + { + result = (((result & 0xff000000) >> 0) | + ((result & 0x00ff0000) >> 16) | + ((result & 0x0000ff00) >> 0) | + ((result & 0x000000ff) << 16)); + } + + return result; +} diff --git a/gfx/cairo/libpixman/src/pixman-implementation.c b/gfx/cairo/libpixman/src/pixman-implementation.c new file mode 100644 index 0000000000..5a2cbfead5 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-implementation.c @@ -0,0 +1,417 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include "pixman-private.h" + +pixman_implementation_t * +_pixman_implementation_create (pixman_implementation_t *fallback, + const pixman_fast_path_t *fast_paths) +{ + pixman_implementation_t *imp; + + assert (fast_paths); + + if ((imp = malloc (sizeof (pixman_implementation_t)))) + { + pixman_implementation_t *d; + + memset (imp, 0, sizeof *imp); + + imp->fallback = fallback; + imp->fast_paths = fast_paths; + + /* Make sure the whole fallback chain has the right toplevel */ + for (d = imp; d != NULL; d = d->fallback) + d->toplevel = imp; + } + + return imp; +} + +#define N_CACHED_FAST_PATHS 8 + +typedef struct +{ + struct + { + pixman_implementation_t * imp; + pixman_fast_path_t fast_path; + } cache [N_CACHED_FAST_PATHS]; +} cache_t; + +PIXMAN_DEFINE_THREAD_LOCAL (cache_t, fast_path_cache) + +static void +dummy_composite_rect (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ +} + +void +_pixman_implementation_lookup_composite (pixman_implementation_t *toplevel, + pixman_op_t op, + pixman_format_code_t src_format, + uint32_t src_flags, + pixman_format_code_t mask_format, + uint32_t mask_flags, + pixman_format_code_t dest_format, + uint32_t dest_flags, + pixman_implementation_t **out_imp, + pixman_composite_func_t *out_func) +{ + pixman_implementation_t *imp; + cache_t *cache; + int i; + + /* Check cache for fast paths */ + cache = PIXMAN_GET_THREAD_LOCAL (fast_path_cache); + + for (i = 0; i < N_CACHED_FAST_PATHS; ++i) + { + const pixman_fast_path_t *info = &(cache->cache[i].fast_path); + + /* Note that we check for equality here, not whether + * the cached fast path matches. This is to prevent + * us from selecting an overly general fast path + * when a more specific one would work. + */ + if (info->op == op && + info->src_format == src_format && + info->mask_format == mask_format && + info->dest_format == dest_format && + info->src_flags == src_flags && + info->mask_flags == mask_flags && + info->dest_flags == dest_flags && + info->func) + { + *out_imp = cache->cache[i].imp; + *out_func = cache->cache[i].fast_path.func; + + goto update_cache; + } + } + + for (imp = toplevel; imp != NULL; imp = imp->fallback) + { + const pixman_fast_path_t *info = imp->fast_paths; + + while (info->op != PIXMAN_OP_NONE) + { + if ((info->op == op || info->op == PIXMAN_OP_any) && + /* Formats */ + ((info->src_format == src_format) || + (info->src_format == PIXMAN_any)) && + ((info->mask_format == mask_format) || + (info->mask_format == PIXMAN_any)) && + ((info->dest_format == dest_format) || + (info->dest_format == PIXMAN_any)) && + /* Flags */ + (info->src_flags & src_flags) == info->src_flags && + (info->mask_flags & mask_flags) == info->mask_flags && + (info->dest_flags & dest_flags) == info->dest_flags) + { + *out_imp = imp; + *out_func = info->func; + + /* Set i to the last spot in the cache so that the + * move-to-front code below will work + */ + i = N_CACHED_FAST_PATHS - 1; + + goto update_cache; + } + + ++info; + } + } + + /* We should never reach this point */ + _pixman_log_error ( + FUNC, + "No composite function found\n" + "\n" + "The most likely cause of this is that this system has issues with\n" + "thread local storage\n"); + + *out_imp = NULL; + *out_func = dummy_composite_rect; + return; + +update_cache: + if (i) + { + while (i--) + cache->cache[i + 1] = cache->cache[i]; + + cache->cache[0].imp = *out_imp; + cache->cache[0].fast_path.op = op; + cache->cache[0].fast_path.src_format = src_format; + cache->cache[0].fast_path.src_flags = src_flags; + cache->cache[0].fast_path.mask_format = mask_format; + cache->cache[0].fast_path.mask_flags = mask_flags; + cache->cache[0].fast_path.dest_format = dest_format; + cache->cache[0].fast_path.dest_flags = dest_flags; + cache->cache[0].fast_path.func = *out_func; + } +} + +static void +dummy_combine (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ +} + +pixman_combine_32_func_t +_pixman_implementation_lookup_combiner (pixman_implementation_t *imp, + pixman_op_t op, + pixman_bool_t component_alpha, + pixman_bool_t narrow) +{ + while (imp) + { + pixman_combine_32_func_t f = NULL; + + switch ((narrow << 1) | component_alpha) + { + case 0: /* not narrow, not component alpha */ + f = (pixman_combine_32_func_t)imp->combine_float[op]; + break; + + case 1: /* not narrow, component_alpha */ + f = (pixman_combine_32_func_t)imp->combine_float_ca[op]; + break; + + case 2: /* narrow, not component alpha */ + f = imp->combine_32[op]; + break; + + case 3: /* narrow, component_alpha */ + f = imp->combine_32_ca[op]; + break; + } + + if (f) + return f; + + imp = imp->fallback; + } + + /* We should never reach this point */ + _pixman_log_error (FUNC, "No known combine function\n"); + return dummy_combine; +} + +pixman_bool_t +_pixman_implementation_blt (pixman_implementation_t * imp, + uint32_t * src_bits, + uint32_t * dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height) +{ + while (imp) + { + if (imp->blt && + (*imp->blt) (imp, src_bits, dst_bits, src_stride, dst_stride, + src_bpp, dst_bpp, src_x, src_y, dest_x, dest_y, + width, height)) + { + return TRUE; + } + + imp = imp->fallback; + } + + return FALSE; +} + +pixman_bool_t +_pixman_implementation_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + while (imp) + { + if (imp->fill && + ((*imp->fill) (imp, bits, stride, bpp, x, y, width, height, filler))) + { + return TRUE; + } + + imp = imp->fallback; + } + + return FALSE; +} + +static uint32_t * +get_scanline_null (pixman_iter_t *iter, const uint32_t *mask) +{ + return NULL; +} + +void +_pixman_implementation_iter_init (pixman_implementation_t *imp, + pixman_iter_t *iter, + pixman_image_t *image, + int x, + int y, + int width, + int height, + uint8_t *buffer, + iter_flags_t iter_flags, + uint32_t image_flags) +{ + pixman_format_code_t format; + + iter->image = image; + iter->buffer = (uint32_t *)buffer; + iter->x = x; + iter->y = y; + iter->width = width; + iter->height = height; + iter->iter_flags = iter_flags; + iter->image_flags = image_flags; + iter->fini = NULL; + + if (!iter->image) + { + iter->get_scanline = get_scanline_null; + return; + } + + format = iter->image->common.extended_format_code; + + while (imp) + { + if (imp->iter_info) + { + const pixman_iter_info_t *info; + + for (info = imp->iter_info; info->format != PIXMAN_null; ++info) + { + if ((info->format == PIXMAN_any || info->format == format) && + (info->image_flags & image_flags) == info->image_flags && + (info->iter_flags & iter_flags) == info->iter_flags) + { + iter->get_scanline = info->get_scanline; + iter->write_back = info->write_back; + + if (info->initializer) + info->initializer (iter, info); + return; + } + } + } + + imp = imp->fallback; + } +} + +pixman_bool_t +_pixman_disabled (const char *name) +{ + const char *env; + + if ((env = getenv ("PIXMAN_DISABLE"))) + { + do + { + const char *end; + int len; + + if ((end = strchr (env, ' '))) + len = end - env; + else + len = strlen (env); + + if (strlen (name) == len && strncmp (name, env, len) == 0) + { + printf ("pixman: Disabled %s implementation\n", name); + return TRUE; + } + + env += len; + } + while (*env++); + } + + return FALSE; +} + +static const pixman_fast_path_t empty_fast_path[] = +{ + { PIXMAN_OP_NONE } +}; + +pixman_implementation_t * +_pixman_choose_implementation (void) +{ + pixman_implementation_t *imp; + + imp = _pixman_implementation_create_general(); + + if (!_pixman_disabled ("fast")) + imp = _pixman_implementation_create_fast_path (imp); + + imp = _pixman_x86_get_implementations (imp); + imp = _pixman_arm_get_implementations (imp); + imp = _pixman_ppc_get_implementations (imp); + imp = _pixman_mips_get_implementations (imp); + + imp = _pixman_implementation_create_noop (imp); + + if (_pixman_disabled ("wholeops")) + { + pixman_implementation_t *cur; + + /* Disable all whole-operation paths except the general one, + * so that optimized iterators are used as much as possible. + */ + for (cur = imp; cur->fallback; cur = cur->fallback) + cur->fast_paths = empty_fast_path; + } + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-inlines.h b/gfx/cairo/libpixman/src/pixman-inlines.h new file mode 100644 index 0000000000..f785910f80 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-inlines.h @@ -0,0 +1,1365 @@ +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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: Keith Packard, SuSE, Inc. + */ + +#ifndef PIXMAN_FAST_PATH_H__ +#define PIXMAN_FAST_PATH_H__ + +#include "pixman-private.h" + +#define PIXMAN_REPEAT_COVER -1 + +/* Flags describing input parameters to fast path macro template. + * Turning on some flag values may indicate that + * "some property X is available so template can use this" or + * "some property X should be handled by template". + * + * FLAG_HAVE_SOLID_MASK + * Input mask is solid so template should handle this. + * + * FLAG_HAVE_NON_SOLID_MASK + * Input mask is bits mask so template should handle this. + * + * FLAG_HAVE_SOLID_MASK and FLAG_HAVE_NON_SOLID_MASK are mutually + * exclusive. (It's not allowed to turn both flags on) + */ +#define FLAG_NONE (0) +#define FLAG_HAVE_SOLID_MASK (1 << 1) +#define FLAG_HAVE_NON_SOLID_MASK (1 << 2) + +/* To avoid too short repeated scanline function calls, extend source + * scanlines having width less than below constant value. + */ +#define REPEAT_NORMAL_MIN_WIDTH 64 + +static force_inline pixman_bool_t +repeat (pixman_repeat_t repeat, int *c, int size) +{ + if (repeat == PIXMAN_REPEAT_NONE) + { + if (*c < 0 || *c >= size) + return FALSE; + } + else if (repeat == PIXMAN_REPEAT_NORMAL) + { + while (*c >= size) + *c -= size; + while (*c < 0) + *c += size; + } + else if (repeat == PIXMAN_REPEAT_PAD) + { + *c = CLIP (*c, 0, size - 1); + } + else /* REFLECT */ + { + *c = MOD (*c, size * 2); + if (*c >= size) + *c = size * 2 - *c - 1; + } + return TRUE; +} + +static force_inline int +pixman_fixed_to_bilinear_weight (pixman_fixed_t x) +{ + return (x >> (16 - BILINEAR_INTERPOLATION_BITS)) & + ((1 << BILINEAR_INTERPOLATION_BITS) - 1); +} + +#if BILINEAR_INTERPOLATION_BITS <= 4 +/* Inspired by Filter_32_opaque from Skia */ +static force_inline uint32_t +bilinear_interpolation (uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + int distxy, distxiy, distixy, distixiy; + uint32_t lo, hi; + + distx <<= (4 - BILINEAR_INTERPOLATION_BITS); + disty <<= (4 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = (distx << 4) - distxy; /* distx * (16 - disty) */ + distixy = (disty << 4) - distxy; /* disty * (16 - distx) */ + distixiy = + 16 * 16 - (disty << 4) - + (distx << 4) + distxy; /* (16 - distx) * (16 - disty) */ + + lo = (tl & 0xff00ff) * distixiy; + hi = ((tl >> 8) & 0xff00ff) * distixiy; + + lo += (tr & 0xff00ff) * distxiy; + hi += ((tr >> 8) & 0xff00ff) * distxiy; + + lo += (bl & 0xff00ff) * distixy; + hi += ((bl >> 8) & 0xff00ff) * distixy; + + lo += (br & 0xff00ff) * distxy; + hi += ((br >> 8) & 0xff00ff) * distxy; + + return ((lo >> 8) & 0xff00ff) | (hi & ~0xff00ff); +} + +#else +#if SIZEOF_LONG > 4 + +static force_inline uint32_t +bilinear_interpolation (uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + uint64_t distxy, distxiy, distixy, distixiy; + uint64_t tl64, tr64, bl64, br64; + uint64_t f, r; + + distx <<= (8 - BILINEAR_INTERPOLATION_BITS); + disty <<= (8 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = distx * (256 - disty); + distixy = (256 - distx) * disty; + distixiy = (256 - distx) * (256 - disty); + + /* Alpha and Blue */ + tl64 = tl & 0xff0000ff; + tr64 = tr & 0xff0000ff; + bl64 = bl & 0xff0000ff; + br64 = br & 0xff0000ff; + + f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; + r = f & 0x0000ff0000ff0000ull; + + /* Red and Green */ + tl64 = tl; + tl64 = ((tl64 << 16) & 0x000000ff00000000ull) | (tl64 & 0x0000ff00ull); + + tr64 = tr; + tr64 = ((tr64 << 16) & 0x000000ff00000000ull) | (tr64 & 0x0000ff00ull); + + bl64 = bl; + bl64 = ((bl64 << 16) & 0x000000ff00000000ull) | (bl64 & 0x0000ff00ull); + + br64 = br; + br64 = ((br64 << 16) & 0x000000ff00000000ull) | (br64 & 0x0000ff00ull); + + f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy; + r |= ((f >> 16) & 0x000000ff00000000ull) | (f & 0xff000000ull); + + return (uint32_t)(r >> 16); +} + +#else + +static force_inline uint32_t +bilinear_interpolation (uint32_t tl, uint32_t tr, + uint32_t bl, uint32_t br, + int distx, int disty) +{ + int distxy, distxiy, distixy, distixiy; + uint32_t f, r; + + distx <<= (8 - BILINEAR_INTERPOLATION_BITS); + disty <<= (8 - BILINEAR_INTERPOLATION_BITS); + + distxy = distx * disty; + distxiy = (distx << 8) - distxy; /* distx * (256 - disty) */ + distixy = (disty << 8) - distxy; /* disty * (256 - distx) */ + distixiy = + 256 * 256 - (disty << 8) - + (distx << 8) + distxy; /* (256 - distx) * (256 - disty) */ + + /* Blue */ + r = (tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy; + + /* Green */ + f = (tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy; + r |= f & 0xff000000; + + tl >>= 16; + tr >>= 16; + bl >>= 16; + br >>= 16; + r >>= 16; + + /* Red */ + f = (tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy + + (bl & 0x000000ff) * distixy + (br & 0x000000ff) * distxy; + r |= f & 0x00ff0000; + + /* Alpha */ + f = (tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy + + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy; + r |= f & 0xff000000; + + return r; +} + +#endif +#endif // BILINEAR_INTERPOLATION_BITS <= 4 + +static force_inline argb_t +bilinear_interpolation_float (argb_t tl, argb_t tr, + argb_t bl, argb_t br, + float distx, float disty) +{ + float distxy, distxiy, distixy, distixiy; + argb_t r; + + distxy = distx * disty; + distxiy = distx * (1.f - disty); + distixy = (1.f - distx) * disty; + distixiy = (1.f - distx) * (1.f - disty); + + r.a = tl.a * distixiy + tr.a * distxiy + + bl.a * distixy + br.a * distxy; + r.r = tl.r * distixiy + tr.r * distxiy + + bl.r * distixy + br.r * distxy; + r.g = tl.g * distixiy + tr.g * distxiy + + bl.g * distixy + br.g * distxy; + r.b = tl.b * distixiy + tr.b * distxiy + + bl.b * distixy + br.b * distxy; + + return r; +} + +/* + * For each scanline fetched from source image with PAD repeat: + * - calculate how many pixels need to be padded on the left side + * - calculate how many pixels need to be padded on the right side + * - update width to only count pixels which are fetched from the image + * All this information is returned via 'width', 'left_pad', 'right_pad' + * arguments. The code is assuming that 'unit_x' is positive. + * + * Note: 64-bit math is used in order to avoid potential overflows, which + * is probably excessive in many cases. This particular function + * may need its own correctness test and performance tuning. + */ +static force_inline void +pad_repeat_get_scanline_bounds (int32_t source_image_width, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + int32_t * width, + int32_t * left_pad, + int32_t * right_pad) +{ + int64_t max_vx = (int64_t) source_image_width << 16; + int64_t tmp; + if (vx < 0) + { + tmp = ((int64_t) unit_x - 1 - vx) / unit_x; + if (tmp > *width) + { + *left_pad = *width; + *width = 0; + } + else + { + *left_pad = (int32_t) tmp; + *width -= (int32_t) tmp; + } + } + else + { + *left_pad = 0; + } + tmp = ((int64_t) unit_x - 1 - vx + max_vx) / unit_x - *left_pad; + if (tmp < 0) + { + *right_pad = *width; + *width = 0; + } + else if (tmp >= *width) + { + *right_pad = 0; + } + else + { + *right_pad = *width - (int32_t) tmp; + *width = (int32_t) tmp; + } +} + +/* A macroified version of specialized nearest scalers for some + * common 8888 and 565 formats. It supports SRC and OVER ops. + * + * There are two repeat versions, one that handles repeat normal, + * and one without repeat handling that only works if the src region + * used is completely covered by the pre-repeated source samples. + * + * The loops are unrolled to process two pixels per iteration for better + * performance on most CPU architectures (superscalar processors + * can issue several operations simultaneously, other processors can hide + * instructions latencies by pipelining operations). Unrolling more + * does not make much sense because the compiler will start running out + * of spare registers soon. + */ + +#define GET_8888_ALPHA(s) ((s) >> 24) + /* This is not actually used since we don't have an OVER with + 565 source, but it is needed to build. */ +#define GET_0565_ALPHA(s) 0xff +#define GET_x888_ALPHA(s) 0xff + +#define FAST_NEAREST_SCANLINE(scanline_func_name, SRC_FORMAT, DST_FORMAT, \ + src_type_t, dst_type_t, OP, repeat_mode) \ +static force_inline void \ +scanline_func_name (dst_type_t *dst, \ + const src_type_t *src, \ + int32_t w, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t src_width_fixed, \ + pixman_bool_t fully_transparent_src) \ +{ \ + uint32_t d; \ + src_type_t s1, s2; \ + uint8_t a1, a2; \ + int x1, x2; \ + \ + if (PIXMAN_OP_ ## OP == PIXMAN_OP_OVER && fully_transparent_src) \ + return; \ + \ + if (PIXMAN_OP_ ## OP != PIXMAN_OP_SRC && PIXMAN_OP_ ## OP != PIXMAN_OP_OVER) \ + abort(); \ + \ + while ((w -= 2) >= 0) \ + { \ + x1 = pixman_fixed_to_int (vx); \ + vx += unit_x; \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ + { \ + /* This works because we know that unit_x is positive */ \ + while (vx >= 0) \ + vx -= src_width_fixed; \ + } \ + s1 = *(src + x1); \ + \ + x2 = pixman_fixed_to_int (vx); \ + vx += unit_x; \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ + { \ + /* This works because we know that unit_x is positive */ \ + while (vx >= 0) \ + vx -= src_width_fixed; \ + } \ + s2 = *(src + x2); \ + \ + if (PIXMAN_OP_ ## OP == PIXMAN_OP_OVER) \ + { \ + a1 = GET_ ## SRC_FORMAT ## _ALPHA(s1); \ + a2 = GET_ ## SRC_FORMAT ## _ALPHA(s2); \ + \ + if (a1 == 0xff) \ + { \ + *dst = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s1); \ + } \ + else if (s1) \ + { \ + d = convert_ ## DST_FORMAT ## _to_8888 (*dst); \ + s1 = convert_ ## SRC_FORMAT ## _to_8888 (s1); \ + a1 ^= 0xff; \ + UN8x4_MUL_UN8_ADD_UN8x4 (d, a1, s1); \ + *dst = convert_8888_to_ ## DST_FORMAT (d); \ + } \ + dst++; \ + \ + if (a2 == 0xff) \ + { \ + *dst = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s2); \ + } \ + else if (s2) \ + { \ + d = convert_## DST_FORMAT ## _to_8888 (*dst); \ + s2 = convert_## SRC_FORMAT ## _to_8888 (s2); \ + a2 ^= 0xff; \ + UN8x4_MUL_UN8_ADD_UN8x4 (d, a2, s2); \ + *dst = convert_8888_to_ ## DST_FORMAT (d); \ + } \ + dst++; \ + } \ + else /* PIXMAN_OP_SRC */ \ + { \ + *dst++ = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s1); \ + *dst++ = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s2); \ + } \ + } \ + \ + if (w & 1) \ + { \ + x1 = pixman_fixed_to_int (vx); \ + s1 = *(src + x1); \ + \ + if (PIXMAN_OP_ ## OP == PIXMAN_OP_OVER) \ + { \ + a1 = GET_ ## SRC_FORMAT ## _ALPHA(s1); \ + \ + if (a1 == 0xff) \ + { \ + *dst = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s1); \ + } \ + else if (s1) \ + { \ + d = convert_## DST_FORMAT ## _to_8888 (*dst); \ + s1 = convert_ ## SRC_FORMAT ## _to_8888 (s1); \ + a1 ^= 0xff; \ + UN8x4_MUL_UN8_ADD_UN8x4 (d, a1, s1); \ + *dst = convert_8888_to_ ## DST_FORMAT (d); \ + } \ + dst++; \ + } \ + else /* PIXMAN_OP_SRC */ \ + { \ + *dst++ = convert_ ## SRC_FORMAT ## _to_ ## DST_FORMAT (s1); \ + } \ + } \ +} + +#define FAST_NEAREST_MAINLOOP_INT(scale_func_name, scanline_func, src_type_t, mask_type_t, \ + dst_type_t, repeat_mode, have_mask, mask_is_solid) \ +static void \ +fast_composite_scaled_nearest ## scale_func_name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type_t *dst_line; \ + mask_type_t *mask_line; \ + src_type_t *src_first_line; \ + int y; \ + pixman_fixed_t src_width_fixed = pixman_int_to_fixed (src_image->bits.width); \ + pixman_fixed_t max_vy; \ + pixman_vector_t v; \ + pixman_fixed_t vx, vy; \ + pixman_fixed_t unit_x, unit_y; \ + int32_t left_pad, right_pad; \ + \ + src_type_t *src; \ + dst_type_t *dst; \ + mask_type_t solid_mask; \ + const mask_type_t *mask = &solid_mask; \ + int src_stride, mask_stride, dst_stride; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type_t, dst_stride, dst_line, 1); \ + if (have_mask) \ + { \ + if (mask_is_solid) \ + solid_mask = _pixman_image_get_solid (imp, mask_image, dest_image->bits.format); \ + else \ + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type_t, \ + mask_stride, mask_line, 1); \ + } \ + /* pass in 0 instead of src_x and src_y because src_x and src_y need to be \ + * transformed from destination space to source space */ \ + PIXMAN_IMAGE_GET_LINE (src_image, 0, 0, src_type_t, src_stride, src_first_line, 1); \ + \ + /* reference point is the center of the pixel */ \ + v.vector[0] = pixman_int_to_fixed (src_x) + pixman_fixed_1 / 2; \ + v.vector[1] = pixman_int_to_fixed (src_y) + pixman_fixed_1 / 2; \ + v.vector[2] = pixman_fixed_1; \ + \ + if (!pixman_transform_point_3d (src_image->common.transform, &v)) \ + return; \ + \ + unit_x = src_image->common.transform->matrix[0][0]; \ + unit_y = src_image->common.transform->matrix[1][1]; \ + \ + /* Round down to closest integer, ensuring that 0.5 rounds to 0, not 1 */ \ + v.vector[0] -= pixman_fixed_e; \ + v.vector[1] -= pixman_fixed_e; \ + \ + vx = v.vector[0]; \ + vy = v.vector[1]; \ + \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ + { \ + max_vy = pixman_int_to_fixed (src_image->bits.height); \ + \ + /* Clamp repeating positions inside the actual samples */ \ + repeat (PIXMAN_REPEAT_NORMAL, &vx, src_width_fixed); \ + repeat (PIXMAN_REPEAT_NORMAL, &vy, max_vy); \ + } \ + \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD || \ + PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NONE) \ + { \ + pad_repeat_get_scanline_bounds (src_image->bits.width, vx, unit_x, \ + &width, &left_pad, &right_pad); \ + vx += left_pad * unit_x; \ + } \ + \ + while (--height >= 0) \ + { \ + dst = dst_line; \ + dst_line += dst_stride; \ + if (have_mask && !mask_is_solid) \ + { \ + mask = mask_line; \ + mask_line += mask_stride; \ + } \ + \ + y = pixman_fixed_to_int (vy); \ + vy += unit_y; \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ + repeat (PIXMAN_REPEAT_NORMAL, &vy, max_vy); \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD) \ + { \ + repeat (PIXMAN_REPEAT_PAD, &y, src_image->bits.height); \ + src = src_first_line + src_stride * y; \ + if (left_pad > 0) \ + { \ + scanline_func (mask, dst, \ + src + src_image->bits.width - src_image->bits.width + 1, \ + left_pad, -pixman_fixed_e, 0, src_width_fixed, FALSE); \ + } \ + if (width > 0) \ + { \ + scanline_func (mask + (mask_is_solid ? 0 : left_pad), \ + dst + left_pad, src + src_image->bits.width, width, \ + vx - src_width_fixed, unit_x, src_width_fixed, FALSE); \ + } \ + if (right_pad > 0) \ + { \ + scanline_func (mask + (mask_is_solid ? 0 : left_pad + width), \ + dst + left_pad + width, src + src_image->bits.width, \ + right_pad, -pixman_fixed_e, 0, src_width_fixed, FALSE); \ + } \ + } \ + else if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NONE) \ + { \ + static const src_type_t zero[1] = { 0 }; \ + if (y < 0 || y >= src_image->bits.height) \ + { \ + scanline_func (mask, dst, zero + 1, left_pad + width + right_pad, \ + -pixman_fixed_e, 0, src_width_fixed, TRUE); \ + continue; \ + } \ + src = src_first_line + src_stride * y; \ + if (left_pad > 0) \ + { \ + scanline_func (mask, dst, zero + 1, left_pad, \ + -pixman_fixed_e, 0, src_width_fixed, TRUE); \ + } \ + if (width > 0) \ + { \ + scanline_func (mask + (mask_is_solid ? 0 : left_pad), \ + dst + left_pad, src + src_image->bits.width, width, \ + vx - src_width_fixed, unit_x, src_width_fixed, FALSE); \ + } \ + if (right_pad > 0) \ + { \ + scanline_func (mask + (mask_is_solid ? 0 : left_pad + width), \ + dst + left_pad + width, zero + 1, right_pad, \ + -pixman_fixed_e, 0, src_width_fixed, TRUE); \ + } \ + } \ + else \ + { \ + src = src_first_line + src_stride * y; \ + scanline_func (mask, dst, src + src_image->bits.width, width, vx - src_width_fixed, \ + unit_x, src_width_fixed, FALSE); \ + } \ + } \ +} + +/* A workaround for old sun studio, see: https://bugs.freedesktop.org/show_bug.cgi?id=32764 */ +#define FAST_NEAREST_MAINLOOP_COMMON(scale_func_name, scanline_func, src_type_t, mask_type_t, \ + dst_type_t, repeat_mode, have_mask, mask_is_solid) \ + FAST_NEAREST_MAINLOOP_INT(_ ## scale_func_name, scanline_func, src_type_t, mask_type_t, \ + dst_type_t, repeat_mode, have_mask, mask_is_solid) + +#define FAST_NEAREST_MAINLOOP_NOMASK(scale_func_name, scanline_func, src_type_t, dst_type_t, \ + repeat_mode) \ + static force_inline void \ + scanline_func##scale_func_name##_wrapper ( \ + const uint8_t *mask, \ + dst_type_t *dst, \ + const src_type_t *src, \ + int32_t w, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t fully_transparent_src) \ + { \ + scanline_func (dst, src, w, vx, unit_x, max_vx, fully_transparent_src); \ + } \ + FAST_NEAREST_MAINLOOP_INT (scale_func_name, scanline_func##scale_func_name##_wrapper, \ + src_type_t, uint8_t, dst_type_t, repeat_mode, FALSE, FALSE) + +#define FAST_NEAREST_MAINLOOP(scale_func_name, scanline_func, src_type_t, dst_type_t, \ + repeat_mode) \ + FAST_NEAREST_MAINLOOP_NOMASK(_ ## scale_func_name, scanline_func, src_type_t, \ + dst_type_t, repeat_mode) + +#define FAST_NEAREST(scale_func_name, SRC_FORMAT, DST_FORMAT, \ + src_type_t, dst_type_t, OP, repeat_mode) \ + FAST_NEAREST_SCANLINE(scaled_nearest_scanline_ ## scale_func_name ## _ ## OP, \ + SRC_FORMAT, DST_FORMAT, src_type_t, dst_type_t, \ + OP, repeat_mode) \ + FAST_NEAREST_MAINLOOP_NOMASK(_ ## scale_func_name ## _ ## OP, \ + scaled_nearest_scanline_ ## scale_func_name ## _ ## OP, \ + src_type_t, dst_type_t, repeat_mode) + + +#define SCALED_NEAREST_FLAGS \ + (FAST_PATH_SCALE_TRANSFORM | \ + FAST_PATH_NO_ALPHA_MAP | \ + FAST_PATH_NEAREST_FILTER | \ + FAST_PATH_NO_ACCESSORS | \ + FAST_PATH_NARROW_FORMAT) + +#define SIMPLE_NEAREST_FAST_PATH_NORMAL(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_NORMAL_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _normal ## _ ## op, \ + } + +#define SIMPLE_NEAREST_FAST_PATH_PAD(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_PAD_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _pad ## _ ## op, \ + } + +#define SIMPLE_NEAREST_FAST_PATH_NONE(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_NONE_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _none ## _ ## op, \ + } + +#define SIMPLE_NEAREST_FAST_PATH_COVER(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST, \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _cover ## _ ## op, \ + } + +#define SIMPLE_NEAREST_A8_MASK_FAST_PATH_NORMAL(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_NORMAL_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _normal ## _ ## op, \ + } + +#define SIMPLE_NEAREST_A8_MASK_FAST_PATH_PAD(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_PAD_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _pad ## _ ## op, \ + } + +#define SIMPLE_NEAREST_A8_MASK_FAST_PATH_NONE(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_NONE_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _none ## _ ## op, \ + } + +#define SIMPLE_NEAREST_A8_MASK_FAST_PATH_COVER(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST, \ + PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _cover ## _ ## op, \ + } + +#define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_NORMAL_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _normal ## _ ## op, \ + } + +#define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_PAD(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_PAD_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _pad ## _ ## op, \ + } + +#define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NONE(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_NEAREST_FLAGS | \ + FAST_PATH_NONE_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _none ## _ ## op, \ + } + +#define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_COVER(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + SCALED_NEAREST_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST, \ + PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_nearest_ ## func ## _cover ## _ ## op, \ + } + +/* Prefer the use of 'cover' variant, because it is faster */ +#define SIMPLE_NEAREST_FAST_PATH(op,s,d,func) \ + SIMPLE_NEAREST_FAST_PATH_COVER (op,s,d,func), \ + SIMPLE_NEAREST_FAST_PATH_NONE (op,s,d,func), \ + SIMPLE_NEAREST_FAST_PATH_PAD (op,s,d,func), \ + SIMPLE_NEAREST_FAST_PATH_NORMAL (op,s,d,func) + +#define SIMPLE_NEAREST_A8_MASK_FAST_PATH(op,s,d,func) \ + SIMPLE_NEAREST_A8_MASK_FAST_PATH_COVER (op,s,d,func), \ + SIMPLE_NEAREST_A8_MASK_FAST_PATH_NONE (op,s,d,func), \ + SIMPLE_NEAREST_A8_MASK_FAST_PATH_PAD (op,s,d,func) + +#define SIMPLE_NEAREST_SOLID_MASK_FAST_PATH(op,s,d,func) \ + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_COVER (op,s,d,func), \ + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NONE (op,s,d,func), \ + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_PAD (op,s,d,func), \ + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (op,s,d,func) + +/*****************************************************************************/ + +/* + * Identify 5 zones in each scanline for bilinear scaling. Depending on + * whether 2 pixels to be interpolated are fetched from the image itself, + * from the padding area around it or from both image and padding area. + */ +static force_inline void +bilinear_pad_repeat_get_scanline_bounds (int32_t source_image_width, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + int32_t * left_pad, + int32_t * left_tz, + int32_t * width, + int32_t * right_tz, + int32_t * right_pad) +{ + int width1 = *width, left_pad1, right_pad1; + int width2 = *width, left_pad2, right_pad2; + + pad_repeat_get_scanline_bounds (source_image_width, vx, unit_x, + &width1, &left_pad1, &right_pad1); + pad_repeat_get_scanline_bounds (source_image_width, vx + pixman_fixed_1, + unit_x, &width2, &left_pad2, &right_pad2); + + *left_pad = left_pad2; + *left_tz = left_pad1 - left_pad2; + *right_tz = right_pad2 - right_pad1; + *right_pad = right_pad1; + *width -= *left_pad + *left_tz + *right_tz + *right_pad; +} + +/* + * Main loop template for single pass bilinear scaling. It needs to be + * provided with 'scanline_func' which should do the compositing operation. + * The needed function has the following prototype: + * + * scanline_func (dst_type_t * dst, + * const mask_type_ * mask, + * const src_type_t * src_top, + * const src_type_t * src_bottom, + * int32_t width, + * int weight_top, + * int weight_bottom, + * pixman_fixed_t vx, + * pixman_fixed_t unit_x, + * pixman_fixed_t max_vx, + * pixman_bool_t zero_src) + * + * Where: + * dst - destination scanline buffer for storing results + * mask - mask buffer (or single value for solid mask) + * src_top, src_bottom - two source scanlines + * width - number of pixels to process + * weight_top - weight of the top row for interpolation + * weight_bottom - weight of the bottom row for interpolation + * vx - initial position for fetching the first pair of + * pixels from the source buffer + * unit_x - position increment needed to move to the next pair + * of pixels + * max_vx - image size as a fixed point value, can be used for + * implementing NORMAL repeat (when it is supported) + * zero_src - boolean hint variable, which is set to TRUE when + * all source pixels are fetched from zero padding + * zone for NONE repeat + * + * Note: normally the sum of 'weight_top' and 'weight_bottom' is equal to + * BILINEAR_INTERPOLATION_RANGE, but sometimes it may be less than that + * for NONE repeat when handling fuzzy antialiased top or bottom image + * edges. Also both top and bottom weight variables are guaranteed to + * have value, which is less than BILINEAR_INTERPOLATION_RANGE. + * For example, the weights can fit into unsigned byte or be used + * with 8-bit SIMD multiplication instructions for 8-bit interpolation + * precision. + */ +#define FAST_BILINEAR_MAINLOOP_INT(scale_func_name, scanline_func, src_type_t, mask_type_t, \ + dst_type_t, repeat_mode, flags) \ +static void \ +fast_composite_scaled_bilinear ## scale_func_name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type_t *dst_line; \ + mask_type_t *mask_line; \ + src_type_t *src_first_line; \ + int y1, y2; \ + pixman_fixed_t max_vx = INT32_MAX; /* suppress uninitialized variable warning */ \ + pixman_vector_t v; \ + pixman_fixed_t vx, vy; \ + pixman_fixed_t unit_x, unit_y; \ + int32_t left_pad, left_tz, right_tz, right_pad; \ + \ + dst_type_t *dst; \ + mask_type_t solid_mask; \ + const mask_type_t *mask = &solid_mask; \ + int src_stride, mask_stride, dst_stride; \ + \ + int src_width; \ + pixman_fixed_t src_width_fixed; \ + int max_x; \ + pixman_bool_t need_src_extension; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type_t, dst_stride, dst_line, 1); \ + if (flags & FLAG_HAVE_SOLID_MASK) \ + { \ + solid_mask = _pixman_image_get_solid (imp, mask_image, dest_image->bits.format); \ + mask_stride = 0; \ + } \ + else if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + { \ + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type_t, \ + mask_stride, mask_line, 1); \ + } \ + \ + /* pass in 0 instead of src_x and src_y because src_x and src_y need to be \ + * transformed from destination space to source space */ \ + PIXMAN_IMAGE_GET_LINE (src_image, 0, 0, src_type_t, src_stride, src_first_line, 1); \ + \ + /* reference point is the center of the pixel */ \ + v.vector[0] = pixman_int_to_fixed (src_x) + pixman_fixed_1 / 2; \ + v.vector[1] = pixman_int_to_fixed (src_y) + pixman_fixed_1 / 2; \ + v.vector[2] = pixman_fixed_1; \ + \ + if (!pixman_transform_point_3d (src_image->common.transform, &v)) \ + return; \ + \ + unit_x = src_image->common.transform->matrix[0][0]; \ + unit_y = src_image->common.transform->matrix[1][1]; \ + \ + v.vector[0] -= pixman_fixed_1 / 2; \ + v.vector[1] -= pixman_fixed_1 / 2; \ + \ + vy = v.vector[1]; \ + \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD || \ + PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NONE) \ + { \ + bilinear_pad_repeat_get_scanline_bounds (src_image->bits.width, v.vector[0], unit_x, \ + &left_pad, &left_tz, &width, &right_tz, &right_pad); \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD) \ + { \ + /* PAD repeat does not need special handling for 'transition zones' and */ \ + /* they can be combined with 'padding zones' safely */ \ + left_pad += left_tz; \ + right_pad += right_tz; \ + left_tz = right_tz = 0; \ + } \ + v.vector[0] += left_pad * unit_x; \ + } \ + \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ + { \ + vx = v.vector[0]; \ + repeat (PIXMAN_REPEAT_NORMAL, &vx, pixman_int_to_fixed(src_image->bits.width)); \ + max_x = pixman_fixed_to_int (vx + (width - 1) * (int64_t)unit_x) + 1; \ + \ + if (src_image->bits.width < REPEAT_NORMAL_MIN_WIDTH) \ + { \ + src_width = 0; \ + \ + while (src_width < REPEAT_NORMAL_MIN_WIDTH && src_width <= max_x) \ + src_width += src_image->bits.width; \ + \ + need_src_extension = TRUE; \ + } \ + else \ + { \ + src_width = src_image->bits.width; \ + need_src_extension = FALSE; \ + } \ + \ + src_width_fixed = pixman_int_to_fixed (src_width); \ + } \ + \ + while (--height >= 0) \ + { \ + int weight1, weight2; \ + dst = dst_line; \ + dst_line += dst_stride; \ + vx = v.vector[0]; \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + { \ + mask = mask_line; \ + mask_line += mask_stride; \ + } \ + \ + y1 = pixman_fixed_to_int (vy); \ + weight2 = pixman_fixed_to_bilinear_weight (vy); \ + if (weight2) \ + { \ + /* both weight1 and weight2 are smaller than BILINEAR_INTERPOLATION_RANGE */ \ + y2 = y1 + 1; \ + weight1 = BILINEAR_INTERPOLATION_RANGE - weight2; \ + } \ + else \ + { \ + /* set both top and bottom row to the same scanline and tweak weights */ \ + y2 = y1; \ + weight1 = weight2 = BILINEAR_INTERPOLATION_RANGE / 2; \ + } \ + vy += unit_y; \ + if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_PAD) \ + { \ + src_type_t *src1, *src2; \ + src_type_t buf1[2]; \ + src_type_t buf2[2]; \ + repeat (PIXMAN_REPEAT_PAD, &y1, src_image->bits.height); \ + repeat (PIXMAN_REPEAT_PAD, &y2, src_image->bits.height); \ + src1 = src_first_line + src_stride * y1; \ + src2 = src_first_line + src_stride * y2; \ + \ + if (left_pad > 0) \ + { \ + buf1[0] = buf1[1] = src1[0]; \ + buf2[0] = buf2[1] = src2[0]; \ + scanline_func (dst, mask, \ + buf1, buf2, left_pad, weight1, weight2, 0, 0, 0, FALSE); \ + dst += left_pad; \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + mask += left_pad; \ + } \ + if (width > 0) \ + { \ + scanline_func (dst, mask, \ + src1, src2, width, weight1, weight2, vx, unit_x, 0, FALSE); \ + dst += width; \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + mask += width; \ + } \ + if (right_pad > 0) \ + { \ + buf1[0] = buf1[1] = src1[src_image->bits.width - 1]; \ + buf2[0] = buf2[1] = src2[src_image->bits.width - 1]; \ + scanline_func (dst, mask, \ + buf1, buf2, right_pad, weight1, weight2, 0, 0, 0, FALSE); \ + } \ + } \ + else if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NONE) \ + { \ + src_type_t *src1, *src2; \ + src_type_t buf1[2]; \ + src_type_t buf2[2]; \ + /* handle top/bottom zero padding by just setting weights to 0 if needed */ \ + if (y1 < 0) \ + { \ + weight1 = 0; \ + y1 = 0; \ + } \ + if (y1 >= src_image->bits.height) \ + { \ + weight1 = 0; \ + y1 = src_image->bits.height - 1; \ + } \ + if (y2 < 0) \ + { \ + weight2 = 0; \ + y2 = 0; \ + } \ + if (y2 >= src_image->bits.height) \ + { \ + weight2 = 0; \ + y2 = src_image->bits.height - 1; \ + } \ + src1 = src_first_line + src_stride * y1; \ + src2 = src_first_line + src_stride * y2; \ + \ + if (left_pad > 0) \ + { \ + buf1[0] = buf1[1] = 0; \ + buf2[0] = buf2[1] = 0; \ + scanline_func (dst, mask, \ + buf1, buf2, left_pad, weight1, weight2, 0, 0, 0, TRUE); \ + dst += left_pad; \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + mask += left_pad; \ + } \ + if (left_tz > 0) \ + { \ + buf1[0] = 0; \ + buf1[1] = src1[0]; \ + buf2[0] = 0; \ + buf2[1] = src2[0]; \ + scanline_func (dst, mask, \ + buf1, buf2, left_tz, weight1, weight2, \ + pixman_fixed_frac (vx), unit_x, 0, FALSE); \ + dst += left_tz; \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + mask += left_tz; \ + vx += left_tz * unit_x; \ + } \ + if (width > 0) \ + { \ + scanline_func (dst, mask, \ + src1, src2, width, weight1, weight2, vx, unit_x, 0, FALSE); \ + dst += width; \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + mask += width; \ + vx += width * unit_x; \ + } \ + if (right_tz > 0) \ + { \ + buf1[0] = src1[src_image->bits.width - 1]; \ + buf1[1] = 0; \ + buf2[0] = src2[src_image->bits.width - 1]; \ + buf2[1] = 0; \ + scanline_func (dst, mask, \ + buf1, buf2, right_tz, weight1, weight2, \ + pixman_fixed_frac (vx), unit_x, 0, FALSE); \ + dst += right_tz; \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + mask += right_tz; \ + } \ + if (right_pad > 0) \ + { \ + buf1[0] = buf1[1] = 0; \ + buf2[0] = buf2[1] = 0; \ + scanline_func (dst, mask, \ + buf1, buf2, right_pad, weight1, weight2, 0, 0, 0, TRUE); \ + } \ + } \ + else if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \ + { \ + int32_t num_pixels; \ + int32_t width_remain; \ + src_type_t * src_line_top; \ + src_type_t * src_line_bottom; \ + src_type_t buf1[2]; \ + src_type_t buf2[2]; \ + src_type_t extended_src_line0[REPEAT_NORMAL_MIN_WIDTH*2]; \ + src_type_t extended_src_line1[REPEAT_NORMAL_MIN_WIDTH*2]; \ + int i, j; \ + \ + repeat (PIXMAN_REPEAT_NORMAL, &y1, src_image->bits.height); \ + repeat (PIXMAN_REPEAT_NORMAL, &y2, src_image->bits.height); \ + src_line_top = src_first_line + src_stride * y1; \ + src_line_bottom = src_first_line + src_stride * y2; \ + \ + if (need_src_extension) \ + { \ + for (i=0; ibits.width; j++, i++) \ + { \ + extended_src_line0[i] = src_line_top[j]; \ + extended_src_line1[i] = src_line_bottom[j]; \ + } \ + } \ + \ + src_line_top = &extended_src_line0[0]; \ + src_line_bottom = &extended_src_line1[0]; \ + } \ + \ + /* Top & Bottom wrap around buffer */ \ + buf1[0] = src_line_top[src_width - 1]; \ + buf1[1] = src_line_top[0]; \ + buf2[0] = src_line_bottom[src_width - 1]; \ + buf2[1] = src_line_bottom[0]; \ + \ + width_remain = width; \ + \ + while (width_remain > 0) \ + { \ + /* We use src_width_fixed because it can make vx in original source range */ \ + repeat (PIXMAN_REPEAT_NORMAL, &vx, src_width_fixed); \ + \ + /* Wrap around part */ \ + if (pixman_fixed_to_int (vx) == src_width - 1) \ + { \ + /* for positive unit_x \ + * num_pixels = max(n) + 1, where vx + n*unit_x < src_width_fixed \ + * \ + * vx is in range [0, src_width_fixed - pixman_fixed_e] \ + * So we are safe from overflow. \ + */ \ + num_pixels = ((src_width_fixed - vx - pixman_fixed_e) / unit_x) + 1; \ + \ + if (num_pixels > width_remain) \ + num_pixels = width_remain; \ + \ + scanline_func (dst, mask, buf1, buf2, num_pixels, \ + weight1, weight2, pixman_fixed_frac(vx), \ + unit_x, src_width_fixed, FALSE); \ + \ + width_remain -= num_pixels; \ + vx += num_pixels * unit_x; \ + dst += num_pixels; \ + \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + mask += num_pixels; \ + \ + repeat (PIXMAN_REPEAT_NORMAL, &vx, src_width_fixed); \ + } \ + \ + /* Normal scanline composite */ \ + if (pixman_fixed_to_int (vx) != src_width - 1 && width_remain > 0) \ + { \ + /* for positive unit_x \ + * num_pixels = max(n) + 1, where vx + n*unit_x < (src_width_fixed - 1) \ + * \ + * vx is in range [0, src_width_fixed - pixman_fixed_e] \ + * So we are safe from overflow here. \ + */ \ + num_pixels = ((src_width_fixed - pixman_fixed_1 - vx - pixman_fixed_e) \ + / unit_x) + 1; \ + \ + if (num_pixels > width_remain) \ + num_pixels = width_remain; \ + \ + scanline_func (dst, mask, src_line_top, src_line_bottom, num_pixels, \ + weight1, weight2, vx, unit_x, src_width_fixed, FALSE); \ + \ + width_remain -= num_pixels; \ + vx += num_pixels * unit_x; \ + dst += num_pixels; \ + \ + if (flags & FLAG_HAVE_NON_SOLID_MASK) \ + mask += num_pixels; \ + } \ + } \ + } \ + else \ + { \ + scanline_func (dst, mask, src_first_line + src_stride * y1, \ + src_first_line + src_stride * y2, width, \ + weight1, weight2, vx, unit_x, max_vx, FALSE); \ + } \ + } \ +} + +/* A workaround for old sun studio, see: https://bugs.freedesktop.org/show_bug.cgi?id=32764 */ +#define FAST_BILINEAR_MAINLOOP_COMMON(scale_func_name, scanline_func, src_type_t, mask_type_t, \ + dst_type_t, repeat_mode, flags) \ + FAST_BILINEAR_MAINLOOP_INT(_ ## scale_func_name, scanline_func, src_type_t, mask_type_t,\ + dst_type_t, repeat_mode, flags) + +#define SCALED_BILINEAR_FLAGS \ + (FAST_PATH_SCALE_TRANSFORM | \ + FAST_PATH_NO_ALPHA_MAP | \ + FAST_PATH_BILINEAR_FILTER | \ + FAST_PATH_NO_ACCESSORS | \ + FAST_PATH_NARROW_FORMAT) + +#define SIMPLE_BILINEAR_FAST_PATH_PAD(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_PAD_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _pad ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_FAST_PATH_NONE(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_NONE_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _none ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_FAST_PATH_COVER(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR, \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _cover ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_FAST_PATH_NORMAL(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_NORMAL_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_null, 0, \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _normal ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_PAD(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_PAD_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _pad ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NONE(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_NONE_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _none ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_COVER(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR, \ + PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _cover ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NORMAL(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_NORMAL_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_a8, MASK_FLAGS (a8, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _normal ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_PAD(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_PAD_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _pad ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NONE(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_NONE_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _none ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_COVER(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + SCALED_BILINEAR_FLAGS | FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR, \ + PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _cover ## _ ## op, \ + } + +#define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NORMAL(op,s,d,func) \ + { PIXMAN_OP_ ## op, \ + PIXMAN_ ## s, \ + (SCALED_BILINEAR_FLAGS | \ + FAST_PATH_NORMAL_REPEAT | \ + FAST_PATH_X_UNIT_POSITIVE), \ + PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \ + PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \ + fast_composite_scaled_bilinear_ ## func ## _normal ## _ ## op, \ + } + +/* Prefer the use of 'cover' variant, because it is faster */ +#define SIMPLE_BILINEAR_FAST_PATH(op,s,d,func) \ + SIMPLE_BILINEAR_FAST_PATH_COVER (op,s,d,func), \ + SIMPLE_BILINEAR_FAST_PATH_NONE (op,s,d,func), \ + SIMPLE_BILINEAR_FAST_PATH_PAD (op,s,d,func), \ + SIMPLE_BILINEAR_FAST_PATH_NORMAL (op,s,d,func) + +#define SIMPLE_BILINEAR_A8_MASK_FAST_PATH(op,s,d,func) \ + SIMPLE_BILINEAR_A8_MASK_FAST_PATH_COVER (op,s,d,func), \ + SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NONE (op,s,d,func), \ + SIMPLE_BILINEAR_A8_MASK_FAST_PATH_PAD (op,s,d,func), \ + SIMPLE_BILINEAR_A8_MASK_FAST_PATH_NORMAL (op,s,d,func) + +#define SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH(op,s,d,func) \ + SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_COVER (op,s,d,func), \ + SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NONE (op,s,d,func), \ + SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_PAD (op,s,d,func), \ + SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH_NORMAL (op,s,d,func) + +#endif diff --git a/gfx/cairo/libpixman/src/pixman-linear-gradient.c b/gfx/cairo/libpixman/src/pixman-linear-gradient.c new file mode 100644 index 0000000000..3f528508a1 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c @@ -0,0 +1,292 @@ +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. + * 2005 Lars Knoll & Zack Rusin, Trolltech + * + * 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. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include "pixman-private.h" + +static pixman_bool_t +linear_gradient_is_horizontal (pixman_image_t *image, + int x, + int y, + int width, + int height) +{ + linear_gradient_t *linear = (linear_gradient_t *)image; + pixman_vector_t v; + pixman_fixed_32_32_t l; + pixman_fixed_48_16_t dx, dy; + double inc; + + if (image->common.transform) + { + /* projective transformation */ + if (image->common.transform->matrix[2][0] != 0 || + image->common.transform->matrix[2][1] != 0 || + image->common.transform->matrix[2][2] == 0) + { + return FALSE; + } + + v.vector[0] = image->common.transform->matrix[0][1]; + v.vector[1] = image->common.transform->matrix[1][1]; + v.vector[2] = image->common.transform->matrix[2][2]; + } + else + { + v.vector[0] = 0; + v.vector[1] = pixman_fixed_1; + v.vector[2] = pixman_fixed_1; + } + + dx = linear->p2.x - linear->p1.x; + dy = linear->p2.y - linear->p1.y; + + l = dx * dx + dy * dy; + + if (l == 0) + return FALSE; + + /* + * compute how much the input of the gradient walked changes + * when moving vertically through the whole image + */ + inc = height * (double) pixman_fixed_1 * pixman_fixed_1 * + (dx * v.vector[0] + dy * v.vector[1]) / + (v.vector[2] * (double) l); + + /* check that casting to integer would result in 0 */ + if (-1 < inc && inc < 1) + return TRUE; + + return FALSE; +} + +static uint32_t * +linear_get_scanline (pixman_iter_t *iter, + const uint32_t *mask, + int Bpp, + pixman_gradient_walker_write_t write_pixel, + pixman_gradient_walker_fill_t fill_pixel) +{ + pixman_image_t *image = iter->image; + int x = iter->x; + int y = iter->y; + int width = iter->width; + uint32_t * buffer = iter->buffer; + + pixman_vector_t v, unit; + pixman_fixed_32_32_t l; + pixman_fixed_48_16_t dx, dy; + gradient_t *gradient = (gradient_t *)image; + linear_gradient_t *linear = (linear_gradient_t *)image; + uint32_t *end = buffer + width * (Bpp / 4); + pixman_gradient_walker_t walker; + + _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (image->common.transform) + { + if (!pixman_transform_point_3d (image->common.transform, &v)) + return iter->buffer; + + unit.vector[0] = image->common.transform->matrix[0][0]; + unit.vector[1] = image->common.transform->matrix[1][0]; + unit.vector[2] = image->common.transform->matrix[2][0]; + } + else + { + unit.vector[0] = pixman_fixed_1; + unit.vector[1] = 0; + unit.vector[2] = 0; + } + + dx = linear->p2.x - linear->p1.x; + dy = linear->p2.y - linear->p1.y; + + l = dx * dx + dy * dy; + + if (l == 0 || unit.vector[2] == 0) + { + /* affine transformation only */ + pixman_fixed_32_32_t t, next_inc; + double inc; + + if (l == 0 || v.vector[2] == 0) + { + t = 0; + inc = 0; + } + else + { + double invden, v2; + + invden = pixman_fixed_1 * (double) pixman_fixed_1 / + (l * (double) v.vector[2]); + v2 = v.vector[2] * (1. / pixman_fixed_1); + t = ((dx * v.vector[0] + dy * v.vector[1]) - + (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; + inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden; + } + next_inc = 0; + + if (((pixman_fixed_32_32_t )(inc * width)) == 0) + { + fill_pixel (&walker, t, buffer, end); + } + else + { + int i; + + i = 0; + while (buffer < end) + { + if (!mask || *mask++) + { + write_pixel (&walker, t + next_inc, buffer); + } + i++; + next_inc = inc * i; + buffer += (Bpp / 4); + } + } + } + else + { + /* projective transformation */ + double t; + + t = 0; + + while (buffer < end) + { + if (!mask || *mask++) + { + if (v.vector[2] != 0) + { + double invden, v2; + + invden = pixman_fixed_1 * (double) pixman_fixed_1 / + (l * (double) v.vector[2]); + v2 = v.vector[2] * (1. / pixman_fixed_1); + t = ((dx * v.vector[0] + dy * v.vector[1]) - + (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; + } + + write_pixel (&walker, t, buffer); + } + + buffer += (Bpp / 4); + + v.vector[0] += unit.vector[0]; + v.vector[1] += unit.vector[1]; + v.vector[2] += unit.vector[2]; + } + } + + iter->y++; + + return iter->buffer; +} + +static uint32_t * +linear_get_scanline_narrow (pixman_iter_t *iter, + const uint32_t *mask) +{ + return linear_get_scanline (iter, mask, 4, + _pixman_gradient_walker_write_narrow, + _pixman_gradient_walker_fill_narrow); +} + + +static uint32_t * +linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) +{ + return linear_get_scanline (iter, NULL, 16, + _pixman_gradient_walker_write_wide, + _pixman_gradient_walker_fill_wide); +} + +void +_pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) +{ + if (linear_gradient_is_horizontal ( + iter->image, iter->x, iter->y, iter->width, iter->height)) + { + if (iter->iter_flags & ITER_NARROW) + linear_get_scanline_narrow (iter, NULL); + else + linear_get_scanline_wide (iter, NULL); + + iter->get_scanline = _pixman_iter_get_scanline_noop; + } + else + { + if (iter->iter_flags & ITER_NARROW) + iter->get_scanline = linear_get_scanline_narrow; + else + iter->get_scanline = linear_get_scanline_wide; + } +} + +PIXMAN_EXPORT pixman_image_t * +pixman_image_create_linear_gradient (const pixman_point_fixed_t * p1, + const pixman_point_fixed_t * p2, + const pixman_gradient_stop_t *stops, + int n_stops) +{ + pixman_image_t *image; + linear_gradient_t *linear; + + image = _pixman_image_allocate (); + + if (!image) + return NULL; + + linear = &image->linear; + + if (!_pixman_init_gradient (&linear->common, stops, n_stops)) + { + free (image); + return NULL; + } + + linear->p1 = *p1; + linear->p2 = *p2; + + image->type = LINEAR; + + return image; +} + diff --git a/gfx/cairo/libpixman/src/pixman-matrix.c b/gfx/cairo/libpixman/src/pixman-matrix.c new file mode 100644 index 0000000000..81b6e613ed --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-matrix.c @@ -0,0 +1,1073 @@ +/* + * Copyright © 2008 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 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. + */ + +/* + * Matrix interfaces + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "pixman-private.h" + +#define F(x) pixman_int_to_fixed (x) + +static force_inline int +count_leading_zeros (uint32_t x) +{ +#ifdef HAVE_BUILTIN_CLZ + return __builtin_clz (x); +#else + int n = 0; + while (x) + { + n++; + x >>= 1; + } + return 32 - n; +#endif +} + +/* + * Large signed/unsigned integer division with rounding for the platforms with + * only 64-bit integer data type supported (no 128-bit data type). + * + * Arguments: + * hi, lo - high and low 64-bit parts of the dividend + * div - 48-bit divisor + * + * Returns: lowest 64 bits of the result as a return value and highest 64 + * bits of the result to "result_hi" pointer + */ + +/* grade-school unsigned division (128-bit by 48-bit) with rounding to nearest */ +static force_inline uint64_t +rounded_udiv_128_by_48 (uint64_t hi, + uint64_t lo, + uint64_t div, + uint64_t *result_hi) +{ + uint64_t tmp, remainder, result_lo; + assert(div < ((uint64_t)1 << 48)); + + remainder = hi % div; + *result_hi = hi / div; + + tmp = (remainder << 16) + (lo >> 48); + result_lo = tmp / div; + remainder = tmp % div; + + tmp = (remainder << 16) + ((lo >> 32) & 0xFFFF); + result_lo = (result_lo << 16) + (tmp / div); + remainder = tmp % div; + + tmp = (remainder << 16) + ((lo >> 16) & 0xFFFF); + result_lo = (result_lo << 16) + (tmp / div); + remainder = tmp % div; + + tmp = (remainder << 16) + (lo & 0xFFFF); + result_lo = (result_lo << 16) + (tmp / div); + remainder = tmp % div; + + /* round to nearest */ + if (remainder * 2 >= div && ++result_lo == 0) + *result_hi += 1; + + return result_lo; +} + +/* signed division (128-bit by 49-bit) with rounding to nearest */ +static inline int64_t +rounded_sdiv_128_by_49 (int64_t hi, + uint64_t lo, + int64_t div, + int64_t *signed_result_hi) +{ + uint64_t result_lo, result_hi; + int sign = 0; + if (div < 0) + { + div = -div; + sign ^= 1; + } + if (hi < 0) + { + if (lo != 0) + hi++; + hi = -hi; + lo = -lo; + sign ^= 1; + } + result_lo = rounded_udiv_128_by_48 (hi, lo, div, &result_hi); + if (sign) + { + if (result_lo != 0) + result_hi++; + result_hi = -result_hi; + result_lo = -result_lo; + } + if (signed_result_hi) + { + *signed_result_hi = result_hi; + } + return result_lo; +} + +/* + * Multiply 64.16 fixed point value by (2^scalebits) and convert + * to 128-bit integer. + */ +static force_inline void +fixed_64_16_to_int128 (int64_t hi, + int64_t lo, + int64_t *rhi, + int64_t *rlo, + int scalebits) +{ + /* separate integer and fractional parts */ + hi += lo >> 16; + lo &= 0xFFFF; + + if (scalebits <= 0) + { + *rlo = hi >> (-scalebits); + *rhi = *rlo >> 63; + } + else + { + *rhi = hi >> (64 - scalebits); + *rlo = (uint64_t)hi << scalebits; + if (scalebits < 16) + *rlo += lo >> (16 - scalebits); + else + *rlo += lo << (scalebits - 16); + } +} + +/* + * Convert 112.16 fixed point value to 48.16 with clamping for the out + * of range values. + */ +static force_inline pixman_fixed_48_16_t +fixed_112_16_to_fixed_48_16 (int64_t hi, int64_t lo, pixman_bool_t *clampflag) +{ + if ((lo >> 63) != hi) + { + *clampflag = TRUE; + return hi >= 0 ? INT64_MAX : INT64_MIN; + } + else + { + return lo; + } +} + +/* + * Transform a point with 31.16 fixed point coordinates from the destination + * space to a point with 48.16 fixed point coordinates in the source space. + * No overflows are possible for affine transformations and the results are + * accurate including the least significant bit. Projective transformations + * may overflow, in this case the results are just clamped to return maximum + * or minimum 48.16 values (so that the caller can at least handle the NONE + * and PAD repeats correctly) and the return value is FALSE to indicate that + * such clamping has happened. + */ +PIXMAN_EXPORT pixman_bool_t +pixman_transform_point_31_16 (const pixman_transform_t *t, + const pixman_vector_48_16_t *v, + pixman_vector_48_16_t *result) +{ + pixman_bool_t clampflag = FALSE; + int i; + int64_t tmp[3][2], divint; + uint16_t divfrac; + + /* input vector values must have no more than 31 bits (including sign) + * in the integer part */ + assert (v->v[0] < ((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[0] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[1] < ((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[1] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[2] < ((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[2] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); + + for (i = 0; i < 3; i++) + { + tmp[i][0] = (int64_t)t->matrix[i][0] * (v->v[0] >> 16); + tmp[i][1] = (int64_t)t->matrix[i][0] * (v->v[0] & 0xFFFF); + tmp[i][0] += (int64_t)t->matrix[i][1] * (v->v[1] >> 16); + tmp[i][1] += (int64_t)t->matrix[i][1] * (v->v[1] & 0xFFFF); + tmp[i][0] += (int64_t)t->matrix[i][2] * (v->v[2] >> 16); + tmp[i][1] += (int64_t)t->matrix[i][2] * (v->v[2] & 0xFFFF); + } + + /* + * separate 64-bit integer and 16-bit fractional parts for the divisor, + * which is also scaled by 65536 after fixed point multiplication. + */ + divint = tmp[2][0] + (tmp[2][1] >> 16); + divfrac = tmp[2][1] & 0xFFFF; + + if (divint == pixman_fixed_1 && divfrac == 0) + { + /* + * this is a simple affine transformation + */ + result->v[0] = tmp[0][0] + ((tmp[0][1] + 0x8000) >> 16); + result->v[1] = tmp[1][0] + ((tmp[1][1] + 0x8000) >> 16); + result->v[2] = pixman_fixed_1; + } + else if (divint == 0 && divfrac == 0) + { + /* + * handle zero divisor (if the values are non-zero, set the + * results to maximum positive or minimum negative) + */ + clampflag = TRUE; + + result->v[0] = tmp[0][0] + ((tmp[0][1] + 0x8000) >> 16); + result->v[1] = tmp[1][0] + ((tmp[1][1] + 0x8000) >> 16); + + if (result->v[0] > 0) + result->v[0] = INT64_MAX; + else if (result->v[0] < 0) + result->v[0] = INT64_MIN; + + if (result->v[1] > 0) + result->v[1] = INT64_MAX; + else if (result->v[1] < 0) + result->v[1] = INT64_MIN; + } + else + { + /* + * projective transformation, analyze the top 32 bits of the divisor + */ + int32_t hi32divbits = divint >> 32; + if (hi32divbits < 0) + hi32divbits = ~hi32divbits; + + if (hi32divbits == 0) + { + /* the divisor is small, we can actually keep all the bits */ + int64_t hi, rhi, lo, rlo; + int64_t div = ((uint64_t)divint << 16) + divfrac; + + fixed_64_16_to_int128 (tmp[0][0], tmp[0][1], &hi, &lo, 32); + rlo = rounded_sdiv_128_by_49 (hi, lo, div, &rhi); + result->v[0] = fixed_112_16_to_fixed_48_16 (rhi, rlo, &clampflag); + + fixed_64_16_to_int128 (tmp[1][0], tmp[1][1], &hi, &lo, 32); + rlo = rounded_sdiv_128_by_49 (hi, lo, div, &rhi); + result->v[1] = fixed_112_16_to_fixed_48_16 (rhi, rlo, &clampflag); + } + else + { + /* the divisor needs to be reduced to 48 bits */ + int64_t hi, rhi, lo, rlo, div; + int shift = 32 - count_leading_zeros (hi32divbits); + fixed_64_16_to_int128 (divint, divfrac, &hi, &div, 16 - shift); + + fixed_64_16_to_int128 (tmp[0][0], tmp[0][1], &hi, &lo, 32 - shift); + rlo = rounded_sdiv_128_by_49 (hi, lo, div, &rhi); + result->v[0] = fixed_112_16_to_fixed_48_16 (rhi, rlo, &clampflag); + + fixed_64_16_to_int128 (tmp[1][0], tmp[1][1], &hi, &lo, 32 - shift); + rlo = rounded_sdiv_128_by_49 (hi, lo, div, &rhi); + result->v[1] = fixed_112_16_to_fixed_48_16 (rhi, rlo, &clampflag); + } + } + result->v[2] = pixman_fixed_1; + return !clampflag; +} + +PIXMAN_EXPORT void +pixman_transform_point_31_16_affine (const pixman_transform_t *t, + const pixman_vector_48_16_t *v, + pixman_vector_48_16_t *result) +{ + int64_t hi0, lo0, hi1, lo1; + + /* input vector values must have no more than 31 bits (including sign) + * in the integer part */ + assert (v->v[0] < ((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[0] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[1] < ((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[1] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); + + hi0 = (int64_t)t->matrix[0][0] * (v->v[0] >> 16); + lo0 = (int64_t)t->matrix[0][0] * (v->v[0] & 0xFFFF); + hi0 += (int64_t)t->matrix[0][1] * (v->v[1] >> 16); + lo0 += (int64_t)t->matrix[0][1] * (v->v[1] & 0xFFFF); + hi0 += (int64_t)t->matrix[0][2]; + + hi1 = (int64_t)t->matrix[1][0] * (v->v[0] >> 16); + lo1 = (int64_t)t->matrix[1][0] * (v->v[0] & 0xFFFF); + hi1 += (int64_t)t->matrix[1][1] * (v->v[1] >> 16); + lo1 += (int64_t)t->matrix[1][1] * (v->v[1] & 0xFFFF); + hi1 += (int64_t)t->matrix[1][2]; + + result->v[0] = hi0 + ((lo0 + 0x8000) >> 16); + result->v[1] = hi1 + ((lo1 + 0x8000) >> 16); + result->v[2] = pixman_fixed_1; +} + +PIXMAN_EXPORT void +pixman_transform_point_31_16_3d (const pixman_transform_t *t, + const pixman_vector_48_16_t *v, + pixman_vector_48_16_t *result) +{ + int i; + int64_t tmp[3][2]; + + /* input vector values must have no more than 31 bits (including sign) + * in the integer part */ + assert (v->v[0] < ((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[0] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[1] < ((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[1] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[2] < ((pixman_fixed_48_16_t)1 << (30 + 16))); + assert (v->v[2] >= -((pixman_fixed_48_16_t)1 << (30 + 16))); + + for (i = 0; i < 3; i++) + { + tmp[i][0] = (int64_t)t->matrix[i][0] * (v->v[0] >> 16); + tmp[i][1] = (int64_t)t->matrix[i][0] * (v->v[0] & 0xFFFF); + tmp[i][0] += (int64_t)t->matrix[i][1] * (v->v[1] >> 16); + tmp[i][1] += (int64_t)t->matrix[i][1] * (v->v[1] & 0xFFFF); + tmp[i][0] += (int64_t)t->matrix[i][2] * (v->v[2] >> 16); + tmp[i][1] += (int64_t)t->matrix[i][2] * (v->v[2] & 0xFFFF); + } + + result->v[0] = tmp[0][0] + ((tmp[0][1] + 0x8000) >> 16); + result->v[1] = tmp[1][0] + ((tmp[1][1] + 0x8000) >> 16); + result->v[2] = tmp[2][0] + ((tmp[2][1] + 0x8000) >> 16); +} + +PIXMAN_EXPORT void +pixman_transform_init_identity (struct pixman_transform *matrix) +{ + int i; + + memset (matrix, '\0', sizeof (struct pixman_transform)); + for (i = 0; i < 3; i++) + matrix->matrix[i][i] = F (1); +} + +typedef pixman_fixed_32_32_t pixman_fixed_34_30_t; + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_point_3d (const struct pixman_transform *transform, + struct pixman_vector * vector) +{ + pixman_vector_48_16_t tmp; + tmp.v[0] = vector->vector[0]; + tmp.v[1] = vector->vector[1]; + tmp.v[2] = vector->vector[2]; + + pixman_transform_point_31_16_3d (transform, &tmp, &tmp); + + vector->vector[0] = tmp.v[0]; + vector->vector[1] = tmp.v[1]; + vector->vector[2] = tmp.v[2]; + + return vector->vector[0] == tmp.v[0] && + vector->vector[1] == tmp.v[1] && + vector->vector[2] == tmp.v[2]; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_point (const struct pixman_transform *transform, + struct pixman_vector * vector) +{ + pixman_vector_48_16_t tmp; + tmp.v[0] = vector->vector[0]; + tmp.v[1] = vector->vector[1]; + tmp.v[2] = vector->vector[2]; + + if (!pixman_transform_point_31_16 (transform, &tmp, &tmp)) + return FALSE; + + vector->vector[0] = tmp.v[0]; + vector->vector[1] = tmp.v[1]; + vector->vector[2] = tmp.v[2]; + + return vector->vector[0] == tmp.v[0] && + vector->vector[1] == tmp.v[1] && + vector->vector[2] == tmp.v[2]; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_multiply (struct pixman_transform * dst, + const struct pixman_transform *l, + const struct pixman_transform *r) +{ + struct pixman_transform d; + int dx, dy; + int o; + + for (dy = 0; dy < 3; dy++) + { + for (dx = 0; dx < 3; dx++) + { + pixman_fixed_48_16_t v; + pixman_fixed_32_32_t partial; + + v = 0; + for (o = 0; o < 3; o++) + { + partial = + (pixman_fixed_32_32_t) l->matrix[dy][o] * + (pixman_fixed_32_32_t) r->matrix[o][dx]; + + v += (partial + 0x8000) >> 16; + } + + if (v > pixman_max_fixed_48_16 || v < pixman_min_fixed_48_16) + return FALSE; + + d.matrix[dy][dx] = (pixman_fixed_t) v; + } + } + + *dst = d; + return TRUE; +} + +PIXMAN_EXPORT void +pixman_transform_init_scale (struct pixman_transform *t, + pixman_fixed_t sx, + pixman_fixed_t sy) +{ + memset (t, '\0', sizeof (struct pixman_transform)); + + t->matrix[0][0] = sx; + t->matrix[1][1] = sy; + t->matrix[2][2] = F (1); +} + +static pixman_fixed_t +fixed_inverse (pixman_fixed_t x) +{ + return (pixman_fixed_t) ((((pixman_fixed_48_16_t) F (1)) * F (1)) / x); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_scale (struct pixman_transform *forward, + struct pixman_transform *reverse, + pixman_fixed_t sx, + pixman_fixed_t sy) +{ + struct pixman_transform t; + + if (sx == 0 || sy == 0) + return FALSE; + + if (forward) + { + pixman_transform_init_scale (&t, sx, sy); + if (!pixman_transform_multiply (forward, &t, forward)) + return FALSE; + } + + if (reverse) + { + pixman_transform_init_scale (&t, fixed_inverse (sx), + fixed_inverse (sy)); + if (!pixman_transform_multiply (reverse, reverse, &t)) + return FALSE; + } + + return TRUE; +} + +PIXMAN_EXPORT void +pixman_transform_init_rotate (struct pixman_transform *t, + pixman_fixed_t c, + pixman_fixed_t s) +{ + memset (t, '\0', sizeof (struct pixman_transform)); + + t->matrix[0][0] = c; + t->matrix[0][1] = -s; + t->matrix[1][0] = s; + t->matrix[1][1] = c; + t->matrix[2][2] = F (1); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_rotate (struct pixman_transform *forward, + struct pixman_transform *reverse, + pixman_fixed_t c, + pixman_fixed_t s) +{ + struct pixman_transform t; + + if (forward) + { + pixman_transform_init_rotate (&t, c, s); + if (!pixman_transform_multiply (forward, &t, forward)) + return FALSE; + } + + if (reverse) + { + pixman_transform_init_rotate (&t, c, -s); + if (!pixman_transform_multiply (reverse, reverse, &t)) + return FALSE; + } + + return TRUE; +} + +PIXMAN_EXPORT void +pixman_transform_init_translate (struct pixman_transform *t, + pixman_fixed_t tx, + pixman_fixed_t ty) +{ + memset (t, '\0', sizeof (struct pixman_transform)); + + t->matrix[0][0] = F (1); + t->matrix[0][2] = tx; + t->matrix[1][1] = F (1); + t->matrix[1][2] = ty; + t->matrix[2][2] = F (1); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_translate (struct pixman_transform *forward, + struct pixman_transform *reverse, + pixman_fixed_t tx, + pixman_fixed_t ty) +{ + struct pixman_transform t; + + if (forward) + { + pixman_transform_init_translate (&t, tx, ty); + + if (!pixman_transform_multiply (forward, &t, forward)) + return FALSE; + } + + if (reverse) + { + pixman_transform_init_translate (&t, -tx, -ty); + + if (!pixman_transform_multiply (reverse, reverse, &t)) + return FALSE; + } + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_bounds (const struct pixman_transform *matrix, + struct pixman_box16 * b) + +{ + struct pixman_vector v[4]; + int i; + int x1, y1, x2, y2; + + v[0].vector[0] = F (b->x1); + v[0].vector[1] = F (b->y1); + v[0].vector[2] = F (1); + + v[1].vector[0] = F (b->x2); + v[1].vector[1] = F (b->y1); + v[1].vector[2] = F (1); + + v[2].vector[0] = F (b->x2); + v[2].vector[1] = F (b->y2); + v[2].vector[2] = F (1); + + v[3].vector[0] = F (b->x1); + v[3].vector[1] = F (b->y2); + v[3].vector[2] = F (1); + + for (i = 0; i < 4; i++) + { + if (!pixman_transform_point (matrix, &v[i])) + return FALSE; + + x1 = pixman_fixed_to_int (v[i].vector[0]); + y1 = pixman_fixed_to_int (v[i].vector[1]); + x2 = pixman_fixed_to_int (pixman_fixed_ceil (v[i].vector[0])); + y2 = pixman_fixed_to_int (pixman_fixed_ceil (v[i].vector[1])); + + if (i == 0) + { + b->x1 = x1; + b->y1 = y1; + b->x2 = x2; + b->y2 = y2; + } + else + { + if (x1 < b->x1) b->x1 = x1; + if (y1 < b->y1) b->y1 = y1; + if (x2 > b->x2) b->x2 = x2; + if (y2 > b->y2) b->y2 = y2; + } + } + + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_invert (struct pixman_transform * dst, + const struct pixman_transform *src) +{ + struct pixman_f_transform m; + + pixman_f_transform_from_pixman_transform (&m, src); + + if (!pixman_f_transform_invert (&m, &m)) + return FALSE; + + if (!pixman_transform_from_pixman_f_transform (dst, &m)) + return FALSE; + + return TRUE; +} + +static pixman_bool_t +within_epsilon (pixman_fixed_t a, + pixman_fixed_t b, + pixman_fixed_t epsilon) +{ + pixman_fixed_t t = a - b; + + if (t < 0) + t = -t; + + return t <= epsilon; +} + +#define EPSILON (pixman_fixed_t) (2) + +#define IS_SAME(a, b) (within_epsilon (a, b, EPSILON)) +#define IS_ZERO(a) (within_epsilon (a, 0, EPSILON)) +#define IS_ONE(a) (within_epsilon (a, F (1), EPSILON)) +#define IS_UNIT(a) \ + (within_epsilon (a, F (1), EPSILON) || \ + within_epsilon (a, F (-1), EPSILON) || \ + IS_ZERO (a)) +#define IS_INT(a) (IS_ZERO (pixman_fixed_frac (a))) + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_is_identity (const struct pixman_transform *t) +{ + return (IS_SAME (t->matrix[0][0], t->matrix[1][1]) && + IS_SAME (t->matrix[0][0], t->matrix[2][2]) && + !IS_ZERO (t->matrix[0][0]) && + IS_ZERO (t->matrix[0][1]) && + IS_ZERO (t->matrix[0][2]) && + IS_ZERO (t->matrix[1][0]) && + IS_ZERO (t->matrix[1][2]) && + IS_ZERO (t->matrix[2][0]) && + IS_ZERO (t->matrix[2][1])); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_is_scale (const struct pixman_transform *t) +{ + return (!IS_ZERO (t->matrix[0][0]) && + IS_ZERO (t->matrix[0][1]) && + IS_ZERO (t->matrix[0][2]) && + + IS_ZERO (t->matrix[1][0]) && + !IS_ZERO (t->matrix[1][1]) && + IS_ZERO (t->matrix[1][2]) && + + IS_ZERO (t->matrix[2][0]) && + IS_ZERO (t->matrix[2][1]) && + !IS_ZERO (t->matrix[2][2])); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_is_int_translate (const struct pixman_transform *t) +{ + return (IS_ONE (t->matrix[0][0]) && + IS_ZERO (t->matrix[0][1]) && + IS_INT (t->matrix[0][2]) && + + IS_ZERO (t->matrix[1][0]) && + IS_ONE (t->matrix[1][1]) && + IS_INT (t->matrix[1][2]) && + + IS_ZERO (t->matrix[2][0]) && + IS_ZERO (t->matrix[2][1]) && + IS_ONE (t->matrix[2][2])); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_is_inverse (const struct pixman_transform *a, + const struct pixman_transform *b) +{ + struct pixman_transform t; + + if (!pixman_transform_multiply (&t, a, b)) + return FALSE; + + return pixman_transform_is_identity (&t); +} + +PIXMAN_EXPORT void +pixman_f_transform_from_pixman_transform (struct pixman_f_transform * ft, + const struct pixman_transform *t) +{ + int i, j; + + for (j = 0; j < 3; j++) + { + for (i = 0; i < 3; i++) + ft->m[j][i] = pixman_fixed_to_double (t->matrix[j][i]); + } +} + +PIXMAN_EXPORT pixman_bool_t +pixman_transform_from_pixman_f_transform (struct pixman_transform * t, + const struct pixman_f_transform *ft) +{ + int i, j; + + for (j = 0; j < 3; j++) + { + for (i = 0; i < 3; i++) + { + double d = ft->m[j][i]; + if (d < -32767.0 || d > 32767.0) + return FALSE; + d = d * 65536.0 + 0.5; + t->matrix[j][i] = (pixman_fixed_t) floor (d); + } + } + + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_f_transform_invert (struct pixman_f_transform * dst, + const struct pixman_f_transform *src) +{ + static const int a[3] = { 2, 2, 1 }; + static const int b[3] = { 1, 0, 0 }; + pixman_f_transform_t d; + double det; + int i, j; + + det = 0; + for (i = 0; i < 3; i++) + { + double p; + int ai = a[i]; + int bi = b[i]; + p = src->m[i][0] * (src->m[ai][2] * src->m[bi][1] - + src->m[ai][1] * src->m[bi][2]); + if (i == 1) + p = -p; + det += p; + } + + if (det == 0) + return FALSE; + + det = 1 / det; + for (j = 0; j < 3; j++) + { + for (i = 0; i < 3; i++) + { + double p; + int ai = a[i]; + int aj = a[j]; + int bi = b[i]; + int bj = b[j]; + + p = (src->m[ai][aj] * src->m[bi][bj] - + src->m[ai][bj] * src->m[bi][aj]); + + if (((i + j) & 1) != 0) + p = -p; + + d.m[j][i] = det * p; + } + } + + *dst = d; + + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_f_transform_point (const struct pixman_f_transform *t, + struct pixman_f_vector * v) +{ + struct pixman_f_vector result; + int i, j; + double a; + + for (j = 0; j < 3; j++) + { + a = 0; + for (i = 0; i < 3; i++) + a += t->m[j][i] * v->v[i]; + result.v[j] = a; + } + + if (!result.v[2]) + return FALSE; + + for (j = 0; j < 2; j++) + v->v[j] = result.v[j] / result.v[2]; + + v->v[2] = 1; + + return TRUE; +} + +PIXMAN_EXPORT void +pixman_f_transform_point_3d (const struct pixman_f_transform *t, + struct pixman_f_vector * v) +{ + struct pixman_f_vector result; + int i, j; + double a; + + for (j = 0; j < 3; j++) + { + a = 0; + for (i = 0; i < 3; i++) + a += t->m[j][i] * v->v[i]; + result.v[j] = a; + } + + *v = result; +} + +PIXMAN_EXPORT void +pixman_f_transform_multiply (struct pixman_f_transform * dst, + const struct pixman_f_transform *l, + const struct pixman_f_transform *r) +{ + struct pixman_f_transform d; + int dx, dy; + int o; + + for (dy = 0; dy < 3; dy++) + { + for (dx = 0; dx < 3; dx++) + { + double v = 0; + for (o = 0; o < 3; o++) + v += l->m[dy][o] * r->m[o][dx]; + d.m[dy][dx] = v; + } + } + + *dst = d; +} + +PIXMAN_EXPORT void +pixman_f_transform_init_scale (struct pixman_f_transform *t, + double sx, + double sy) +{ + t->m[0][0] = sx; + t->m[0][1] = 0; + t->m[0][2] = 0; + t->m[1][0] = 0; + t->m[1][1] = sy; + t->m[1][2] = 0; + t->m[2][0] = 0; + t->m[2][1] = 0; + t->m[2][2] = 1; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_f_transform_scale (struct pixman_f_transform *forward, + struct pixman_f_transform *reverse, + double sx, + double sy) +{ + struct pixman_f_transform t; + + if (sx == 0 || sy == 0) + return FALSE; + + if (forward) + { + pixman_f_transform_init_scale (&t, sx, sy); + pixman_f_transform_multiply (forward, &t, forward); + } + + if (reverse) + { + pixman_f_transform_init_scale (&t, 1 / sx, 1 / sy); + pixman_f_transform_multiply (reverse, reverse, &t); + } + + return TRUE; +} + +PIXMAN_EXPORT void +pixman_f_transform_init_rotate (struct pixman_f_transform *t, + double c, + double s) +{ + t->m[0][0] = c; + t->m[0][1] = -s; + t->m[0][2] = 0; + t->m[1][0] = s; + t->m[1][1] = c; + t->m[1][2] = 0; + t->m[2][0] = 0; + t->m[2][1] = 0; + t->m[2][2] = 1; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_f_transform_rotate (struct pixman_f_transform *forward, + struct pixman_f_transform *reverse, + double c, + double s) +{ + struct pixman_f_transform t; + + if (forward) + { + pixman_f_transform_init_rotate (&t, c, s); + pixman_f_transform_multiply (forward, &t, forward); + } + + if (reverse) + { + pixman_f_transform_init_rotate (&t, c, -s); + pixman_f_transform_multiply (reverse, reverse, &t); + } + + return TRUE; +} + +PIXMAN_EXPORT void +pixman_f_transform_init_translate (struct pixman_f_transform *t, + double tx, + double ty) +{ + t->m[0][0] = 1; + t->m[0][1] = 0; + t->m[0][2] = tx; + t->m[1][0] = 0; + t->m[1][1] = 1; + t->m[1][2] = ty; + t->m[2][0] = 0; + t->m[2][1] = 0; + t->m[2][2] = 1; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_f_transform_translate (struct pixman_f_transform *forward, + struct pixman_f_transform *reverse, + double tx, + double ty) +{ + struct pixman_f_transform t; + + if (forward) + { + pixman_f_transform_init_translate (&t, tx, ty); + pixman_f_transform_multiply (forward, &t, forward); + } + + if (reverse) + { + pixman_f_transform_init_translate (&t, -tx, -ty); + pixman_f_transform_multiply (reverse, reverse, &t); + } + + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_f_transform_bounds (const struct pixman_f_transform *t, + struct pixman_box16 * b) +{ + struct pixman_f_vector v[4]; + int i; + int x1, y1, x2, y2; + + v[0].v[0] = b->x1; + v[0].v[1] = b->y1; + v[0].v[2] = 1; + v[1].v[0] = b->x2; + v[1].v[1] = b->y1; + v[1].v[2] = 1; + v[2].v[0] = b->x2; + v[2].v[1] = b->y2; + v[2].v[2] = 1; + v[3].v[0] = b->x1; + v[3].v[1] = b->y2; + v[3].v[2] = 1; + + for (i = 0; i < 4; i++) + { + if (!pixman_f_transform_point (t, &v[i])) + return FALSE; + + x1 = floor (v[i].v[0]); + y1 = floor (v[i].v[1]); + x2 = ceil (v[i].v[0]); + y2 = ceil (v[i].v[1]); + + if (i == 0) + { + b->x1 = x1; + b->y1 = y1; + b->x2 = x2; + b->y2 = y2; + } + else + { + if (x1 < b->x1) b->x1 = x1; + if (y1 < b->y1) b->y1 = y1; + if (x2 > b->x2) b->x2 = x2; + if (y2 > b->y2) b->y2 = y2; + } + } + + return TRUE; +} + +PIXMAN_EXPORT void +pixman_f_transform_init_identity (struct pixman_f_transform *t) +{ + int i, j; + + for (j = 0; j < 3; j++) + { + for (i = 0; i < 3; i++) + t->m[j][i] = i == j ? 1 : 0; + } +} diff --git a/gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.S b/gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.S new file mode 100644 index 0000000000..9dad163b79 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.S @@ -0,0 +1,4283 @@ +/* + * Copyright (c) 2012 + * MIPS Technologies, Inc., California. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Nemanja Lukic (nemanja.lukic@rt-rk.com) + */ + +#include "pixman-private.h" +#include "pixman-mips-dspr2-asm.h" + +LEAF_MIPS_DSPR2(pixman_fill_buff16_mips) +/* + * a0 - *dest + * a1 - count (bytes) + * a2 - value to fill buffer with + */ + + beqz a1, 3f + andi t1, a0, 0x0002 + beqz t1, 0f /* check if address is 4-byte aligned */ + nop + sh a2, 0(a0) + addiu a0, a0, 2 + addiu a1, a1, -2 +0: + srl t1, a1, 5 /* t1 how many multiples of 32 bytes */ + replv.ph a2, a2 /* replicate fill value (16bit) in a2 */ + beqz t1, 2f + nop +1: + addiu t1, t1, -1 + beqz t1, 11f + addiu a1, a1, -32 + pref 30, 32(a0) + sw a2, 0(a0) + sw a2, 4(a0) + sw a2, 8(a0) + sw a2, 12(a0) + sw a2, 16(a0) + sw a2, 20(a0) + sw a2, 24(a0) + sw a2, 28(a0) + b 1b + addiu a0, a0, 32 +11: + sw a2, 0(a0) + sw a2, 4(a0) + sw a2, 8(a0) + sw a2, 12(a0) + sw a2, 16(a0) + sw a2, 20(a0) + sw a2, 24(a0) + sw a2, 28(a0) + addiu a0, a0, 32 +2: + blez a1, 3f + addiu a1, a1, -2 + sh a2, 0(a0) + b 2b + addiu a0, a0, 2 +3: + jr ra + nop + +END(pixman_fill_buff16_mips) + +LEAF_MIPS32R2(pixman_fill_buff32_mips) +/* + * a0 - *dest + * a1 - count (bytes) + * a2 - value to fill buffer with + */ + + beqz a1, 3f + nop + srl t1, a1, 5 /* t1 how many multiples of 32 bytes */ + beqz t1, 2f + nop +1: + addiu t1, t1, -1 + beqz t1, 11f + addiu a1, a1, -32 + pref 30, 32(a0) + sw a2, 0(a0) + sw a2, 4(a0) + sw a2, 8(a0) + sw a2, 12(a0) + sw a2, 16(a0) + sw a2, 20(a0) + sw a2, 24(a0) + sw a2, 28(a0) + b 1b + addiu a0, a0, 32 +11: + sw a2, 0(a0) + sw a2, 4(a0) + sw a2, 8(a0) + sw a2, 12(a0) + sw a2, 16(a0) + sw a2, 20(a0) + sw a2, 24(a0) + sw a2, 28(a0) + addiu a0, a0, 32 +2: + blez a1, 3f + addiu a1, a1, -4 + sw a2, 0(a0) + b 2b + addiu a0, a0, 4 +3: + jr ra + nop + +END(pixman_fill_buff32_mips) + +LEAF_MIPS_DSPR2(pixman_composite_src_8888_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (a8r8g8b8) + * a2 - w + */ + + beqz a2, 3f + nop + addiu t1, a2, -1 + beqz t1, 2f + nop + li t4, 0xf800f800 + li t5, 0x07e007e0 + li t6, 0x001f001f +1: + lw t0, 0(a1) + lw t1, 4(a1) + addiu a1, a1, 8 + addiu a2, a2, -2 + + CONVERT_2x8888_TO_2x0565 t0, t1, t2, t3, t4, t5, t6, t7, t8 + + sh t2, 0(a0) + sh t3, 2(a0) + + addiu t2, a2, -1 + bgtz t2, 1b + addiu a0, a0, 4 +2: + beqz a2, 3f + nop + lw t0, 0(a1) + + CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 + + sh t1, 0(a0) +3: + j ra + nop + +END(pixman_composite_src_8888_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_src_0565_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (r5g6b5) + * a2 - w + */ + + beqz a2, 3f + nop + addiu t1, a2, -1 + beqz t1, 2f + nop + li t4, 0x07e007e0 + li t5, 0x001F001F +1: + lhu t0, 0(a1) + lhu t1, 2(a1) + addiu a1, a1, 4 + addiu a2, a2, -2 + + CONVERT_2x0565_TO_2x8888 t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 + + sw t2, 0(a0) + sw t3, 4(a0) + + addiu t2, a2, -1 + bgtz t2, 1b + addiu a0, a0, 8 +2: + beqz a2, 3f + nop + lhu t0, 0(a1) + + CONVERT_1x0565_TO_1x8888 t0, t1, t2, t3 + + sw t1, 0(a0) +3: + j ra + nop + +END(pixman_composite_src_0565_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_src_x888_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (x8r8g8b8) + * a2 - w + */ + + beqz a2, 4f + nop + li t9, 0xff000000 + srl t8, a2, 3 /* t1 = how many multiples of 8 src pixels */ + beqz t8, 3f /* branch if less than 8 src pixels */ + nop +1: + addiu t8, t8, -1 + beqz t8, 2f + addiu a2, a2, -8 + pref 0, 32(a1) + lw t0, 0(a1) + lw t1, 4(a1) + lw t2, 8(a1) + lw t3, 12(a1) + lw t4, 16(a1) + lw t5, 20(a1) + lw t6, 24(a1) + lw t7, 28(a1) + addiu a1, a1, 32 + or t0, t0, t9 + or t1, t1, t9 + or t2, t2, t9 + or t3, t3, t9 + or t4, t4, t9 + or t5, t5, t9 + or t6, t6, t9 + or t7, t7, t9 + pref 30, 32(a0) + sw t0, 0(a0) + sw t1, 4(a0) + sw t2, 8(a0) + sw t3, 12(a0) + sw t4, 16(a0) + sw t5, 20(a0) + sw t6, 24(a0) + sw t7, 28(a0) + b 1b + addiu a0, a0, 32 +2: + lw t0, 0(a1) + lw t1, 4(a1) + lw t2, 8(a1) + lw t3, 12(a1) + lw t4, 16(a1) + lw t5, 20(a1) + lw t6, 24(a1) + lw t7, 28(a1) + addiu a1, a1, 32 + or t0, t0, t9 + or t1, t1, t9 + or t2, t2, t9 + or t3, t3, t9 + or t4, t4, t9 + or t5, t5, t9 + or t6, t6, t9 + or t7, t7, t9 + sw t0, 0(a0) + sw t1, 4(a0) + sw t2, 8(a0) + sw t3, 12(a0) + sw t4, 16(a0) + sw t5, 20(a0) + sw t6, 24(a0) + sw t7, 28(a0) + beqz a2, 4f + addiu a0, a0, 32 +3: + lw t0, 0(a1) + addiu a1, a1, 4 + addiu a2, a2, -1 + or t1, t0, t9 + sw t1, 0(a0) + bnez a2, 3b + addiu a0, a0, 4 +4: + jr ra + nop + +END(pixman_composite_src_x888_8888_asm_mips) + +#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) +LEAF_MIPS_DSPR2(pixman_composite_src_0888_8888_rev_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (b8g8r8) + * a2 - w + */ + + beqz a2, 6f + nop + + lui t8, 0xff00; + srl t9, a2, 2 /* t9 = how many multiples of 4 src pixels */ + beqz t9, 4f /* branch if less than 4 src pixels */ + nop + + li t0, 0x1 + li t1, 0x2 + li t2, 0x3 + andi t3, a1, 0x3 + beq t3, t0, 1f + nop + beq t3, t1, 2f + nop + beq t3, t2, 3f + nop + +0: + beqz t9, 4f + addiu t9, t9, -1 + lw t0, 0(a1) /* t0 = R2 | B1 | G1 | R1 */ + lw t1, 4(a1) /* t1 = G3 | R3 | B2 | G2 */ + lw t2, 8(a1) /* t2 = B4 | G4 | R4 | B3 */ + + addiu a1, a1, 12 + addiu a2, a2, -4 + + wsbh t0, t0 /* t0 = B1 | R2 | R1 | G1 */ + wsbh t1, t1 /* t1 = R3 | G3 | G2 | B2 */ + wsbh t2, t2 /* t2 = G4 | B4 | B3 | R4 */ + + packrl.ph t3, t1, t0 /* t3 = G2 | B2 | B1 | R2 */ + packrl.ph t4, t0, t0 /* t4 = R1 | G1 | B1 | R2 */ + rotr t3, t3, 16 /* t3 = B1 | R2 | G2 | B2 */ + or t3, t3, t8 /* t3 = FF | R2 | G2 | B2 */ + srl t4, t4, 8 /* t4 = 0 | R1 | G1 | B1 */ + or t4, t4, t8 /* t4 = FF | R1 | G1 | B1 */ + packrl.ph t5, t2, t1 /* t5 = B3 | R4 | R3 | G3 */ + rotr t5, t5, 24 /* t5 = R4 | R3 | G3 | B3 */ + or t5, t5, t8 /* t5 = FF | R3 | G3 | B3 */ + rotr t2, t2, 16 /* t2 = B3 | R4 | G4 | B4 */ + or t2, t2, t8 /* t5 = FF | R3 | G3 | B3 */ + + sw t4, 0(a0) + sw t3, 4(a0) + sw t5, 8(a0) + sw t2, 12(a0) + b 0b + addiu a0, a0, 16 + +1: + lbu t6, 0(a1) /* t6 = 0 | 0 | 0 | R1 */ + lhu t7, 1(a1) /* t7 = 0 | 0 | B1 | G1 */ + sll t6, t6, 16 /* t6 = 0 | R1 | 0 | 0 */ + wsbh t7, t7 /* t7 = 0 | 0 | G1 | B1 */ + or t7, t6, t7 /* t7 = 0 | R1 | G1 | B1 */ +11: + beqz t9, 4f + addiu t9, t9, -1 + lw t0, 3(a1) /* t0 = R3 | B2 | G2 | R2 */ + lw t1, 7(a1) /* t1 = G4 | R4 | B3 | G3 */ + lw t2, 11(a1) /* t2 = B5 | G5 | R5 | B4 */ + + addiu a1, a1, 12 + addiu a2, a2, -4 + + wsbh t0, t0 /* t0 = B2 | R3 | R2 | G2 */ + wsbh t1, t1 /* t1 = R4 | G4 | G3 | B3 */ + wsbh t2, t2 /* t2 = G5 | B5 | B4 | R5 */ + + packrl.ph t3, t1, t0 /* t3 = G3 | B3 | B2 | R3 */ + packrl.ph t4, t2, t1 /* t4 = B4 | R5 | R4 | G4 */ + rotr t0, t0, 24 /* t0 = R3 | R2 | G2 | B2 */ + rotr t3, t3, 16 /* t3 = B2 | R3 | G3 | B3 */ + rotr t4, t4, 24 /* t4 = R5 | R4 | G4 | B4 */ + or t7, t7, t8 /* t7 = FF | R1 | G1 | B1 */ + or t0, t0, t8 /* t0 = FF | R2 | G2 | B2 */ + or t3, t3, t8 /* t1 = FF | R3 | G3 | B3 */ + or t4, t4, t8 /* t3 = FF | R4 | G4 | B4 */ + + sw t7, 0(a0) + sw t0, 4(a0) + sw t3, 8(a0) + sw t4, 12(a0) + rotr t7, t2, 16 /* t7 = xx | R5 | G5 | B5 */ + b 11b + addiu a0, a0, 16 + +2: + lhu t7, 0(a1) /* t7 = 0 | 0 | G1 | R1 */ + wsbh t7, t7 /* t7 = 0 | 0 | R1 | G1 */ +21: + beqz t9, 4f + addiu t9, t9, -1 + lw t0, 2(a1) /* t0 = B2 | G2 | R2 | B1 */ + lw t1, 6(a1) /* t1 = R4 | B3 | G3 | R3 */ + lw t2, 10(a1) /* t2 = G5 | R5 | B4 | G4 */ + + addiu a1, a1, 12 + addiu a2, a2, -4 + + wsbh t0, t0 /* t0 = G2 | B2 | B1 | R2 */ + wsbh t1, t1 /* t1 = B3 | R4 | R3 | G3 */ + wsbh t2, t2 /* t2 = R5 | G5 | G4 | B4 */ + + precr_sra.ph.w t7, t0, 0 /* t7 = R1 | G1 | B1 | R2 */ + rotr t0, t0, 16 /* t0 = B1 | R2 | G2 | B2 */ + packrl.ph t3, t2, t1 /* t3 = G4 | B4 | B3 | R4 */ + rotr t1, t1, 24 /* t1 = R4 | R3 | G3 | B3 */ + srl t7, t7, 8 /* t7 = 0 | R1 | G1 | B1 */ + rotr t3, t3, 16 /* t3 = B3 | R4 | G4 | B4 */ + or t7, t7, t8 /* t7 = FF | R1 | G1 | B1 */ + or t0, t0, t8 /* t0 = FF | R2 | G2 | B2 */ + or t1, t1, t8 /* t1 = FF | R3 | G3 | B3 */ + or t3, t3, t8 /* t3 = FF | R4 | G4 | B4 */ + + sw t7, 0(a0) + sw t0, 4(a0) + sw t1, 8(a0) + sw t3, 12(a0) + srl t7, t2, 16 /* t7 = 0 | 0 | R5 | G5 */ + b 21b + addiu a0, a0, 16 + +3: + lbu t7, 0(a1) /* t7 = 0 | 0 | 0 | R1 */ +31: + beqz t9, 4f + addiu t9, t9, -1 + lw t0, 1(a1) /* t0 = G2 | R2 | B1 | G1 */ + lw t1, 5(a1) /* t1 = B3 | G3 | R3 | B2 */ + lw t2, 9(a1) /* t2 = R5 | B4 | G4 | R4 */ + + addiu a1, a1, 12 + addiu a2, a2, -4 + + wsbh t0, t0 /* t0 = R2 | G2 | G1 | B1 */ + wsbh t1, t1 /* t1 = G3 | B3 | B2 | R3 */ + wsbh t2, t2 /* t2 = B4 | R5 | R4 | G4 */ + + precr_sra.ph.w t7, t0, 0 /* t7 = xx | R1 | G1 | B1 */ + packrl.ph t3, t1, t0 /* t3 = B2 | R3 | R2 | G2 */ + rotr t1, t1, 16 /* t1 = B2 | R3 | G3 | B3 */ + rotr t4, t2, 24 /* t4 = R5 | R4 | G4 | B4 */ + rotr t3, t3, 24 /* t3 = R3 | R2 | G2 | B2 */ + or t7, t7, t8 /* t7 = FF | R1 | G1 | B1 */ + or t3, t3, t8 /* t3 = FF | R2 | G2 | B2 */ + or t1, t1, t8 /* t1 = FF | R3 | G3 | B3 */ + or t4, t4, t8 /* t4 = FF | R4 | G4 | B4 */ + + sw t7, 0(a0) + sw t3, 4(a0) + sw t1, 8(a0) + sw t4, 12(a0) + srl t7, t2, 16 /* t7 = 0 | 0 | xx | R5 */ + b 31b + addiu a0, a0, 16 + +4: + beqz a2, 6f + nop +5: + lbu t0, 0(a1) /* t0 = 0 | 0 | 0 | R */ + lbu t1, 1(a1) /* t1 = 0 | 0 | 0 | G */ + lbu t2, 2(a1) /* t2 = 0 | 0 | 0 | B */ + addiu a1, a1, 3 + + sll t0, t0, 16 /* t2 = 0 | R | 0 | 0 */ + sll t1, t1, 8 /* t1 = 0 | 0 | G | 0 */ + + or t2, t2, t1 /* t2 = 0 | 0 | G | B */ + or t2, t2, t0 /* t2 = 0 | R | G | B */ + or t2, t2, t8 /* t2 = FF | R | G | B */ + + sw t2, 0(a0) + addiu a2, a2, -1 + bnez a2, 5b + addiu a0, a0, 4 +6: + j ra + nop + +END(pixman_composite_src_0888_8888_rev_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_src_0888_0565_rev_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (b8g8r8) + * a2 - w + */ + + SAVE_REGS_ON_STACK 0, v0, v1 + beqz a2, 6f + nop + + li t6, 0xf800f800 + li t7, 0x07e007e0 + li t8, 0x001F001F + srl t9, a2, 2 /* t9 = how many multiples of 4 src pixels */ + beqz t9, 4f /* branch if less than 4 src pixels */ + nop + + li t0, 0x1 + li t1, 0x2 + li t2, 0x3 + andi t3, a1, 0x3 + beq t3, t0, 1f + nop + beq t3, t1, 2f + nop + beq t3, t2, 3f + nop + +0: + beqz t9, 4f + addiu t9, t9, -1 + lw t0, 0(a1) /* t0 = R2 | B1 | G1 | R1 */ + lw t1, 4(a1) /* t1 = G3 | R3 | B2 | G2 */ + lw t2, 8(a1) /* t2 = B4 | G4 | R4 | B3 */ + + addiu a1, a1, 12 + addiu a2, a2, -4 + + wsbh t0, t0 /* t0 = B1 | R2 | R1 | G1 */ + wsbh t1, t1 /* t1 = R3 | G3 | G2 | B2 */ + wsbh t2, t2 /* t2 = G4 | B4 | B3 | R4 */ + + packrl.ph t3, t1, t0 /* t3 = G2 | B2 | B1 | R2 */ + packrl.ph t4, t0, t0 /* t4 = R1 | G1 | B1 | R2 */ + rotr t3, t3, 16 /* t3 = B1 | R2 | G2 | B2 */ + srl t4, t4, 8 /* t4 = 0 | R1 | G1 | B1 */ + packrl.ph t5, t2, t1 /* t5 = B3 | R4 | R3 | G3 */ + rotr t5, t5, 24 /* t5 = R4 | R3 | G3 | B3 */ + rotr t2, t2, 16 /* t2 = B3 | R4 | G4 | B4 */ + + CONVERT_2x8888_TO_2x0565 t4, t3, t4, t3, t6, t7, t8, v0, v1 + CONVERT_2x8888_TO_2x0565 t5, t2, t5, t2, t6, t7, t8, v0, v1 + + sh t4, 0(a0) + sh t3, 2(a0) + sh t5, 4(a0) + sh t2, 6(a0) + b 0b + addiu a0, a0, 8 + +1: + lbu t4, 0(a1) /* t4 = 0 | 0 | 0 | R1 */ + lhu t5, 1(a1) /* t5 = 0 | 0 | B1 | G1 */ + sll t4, t4, 16 /* t4 = 0 | R1 | 0 | 0 */ + wsbh t5, t5 /* t5 = 0 | 0 | G1 | B1 */ + or t5, t4, t5 /* t5 = 0 | R1 | G1 | B1 */ +11: + beqz t9, 4f + addiu t9, t9, -1 + lw t0, 3(a1) /* t0 = R3 | B2 | G2 | R2 */ + lw t1, 7(a1) /* t1 = G4 | R4 | B3 | G3 */ + lw t2, 11(a1) /* t2 = B5 | G5 | R5 | B4 */ + + addiu a1, a1, 12 + addiu a2, a2, -4 + + wsbh t0, t0 /* t0 = B2 | R3 | R2 | G2 */ + wsbh t1, t1 /* t1 = R4 | G4 | G3 | B3 */ + wsbh t2, t2 /* t2 = G5 | B5 | B4 | R5 */ + + packrl.ph t3, t1, t0 /* t3 = G3 | B3 | B2 | R3 */ + packrl.ph t4, t2, t1 /* t4 = B4 | R5 | R4 | G4 */ + rotr t0, t0, 24 /* t0 = R3 | R2 | G2 | B2 */ + rotr t3, t3, 16 /* t3 = B2 | R3 | G3 | B3 */ + rotr t4, t4, 24 /* t4 = R5 | R4 | G4 | B4 */ + + CONVERT_2x8888_TO_2x0565 t5, t0, t5, t0, t6, t7, t8, v0, v1 + CONVERT_2x8888_TO_2x0565 t3, t4, t3, t4, t6, t7, t8, v0, v1 + + sh t5, 0(a0) + sh t0, 2(a0) + sh t3, 4(a0) + sh t4, 6(a0) + rotr t5, t2, 16 /* t5 = xx | R5 | G5 | B5 */ + b 11b + addiu a0, a0, 8 + +2: + lhu t5, 0(a1) /* t5 = 0 | 0 | G1 | R1 */ + wsbh t5, t5 /* t5 = 0 | 0 | R1 | G1 */ +21: + beqz t9, 4f + addiu t9, t9, -1 + lw t0, 2(a1) /* t0 = B2 | G2 | R2 | B1 */ + lw t1, 6(a1) /* t1 = R4 | B3 | G3 | R3 */ + lw t2, 10(a1) /* t2 = G5 | R5 | B4 | G4 */ + + addiu a1, a1, 12 + addiu a2, a2, -4 + + wsbh t0, t0 /* t0 = G2 | B2 | B1 | R2 */ + wsbh t1, t1 /* t1 = B3 | R4 | R3 | G3 */ + wsbh t2, t2 /* t2 = R5 | G5 | G4 | B4 */ + + precr_sra.ph.w t5, t0, 0 /* t5 = R1 | G1 | B1 | R2 */ + rotr t0, t0, 16 /* t0 = B1 | R2 | G2 | B2 */ + packrl.ph t3, t2, t1 /* t3 = G4 | B4 | B3 | R4 */ + rotr t1, t1, 24 /* t1 = R4 | R3 | G3 | B3 */ + srl t5, t5, 8 /* t5 = 0 | R1 | G1 | B1 */ + rotr t3, t3, 16 /* t3 = B3 | R4 | G4 | B4 */ + + CONVERT_2x8888_TO_2x0565 t5, t0, t5, t0, t6, t7, t8, v0, v1 + CONVERT_2x8888_TO_2x0565 t1, t3, t1, t3, t6, t7, t8, v0, v1 + + sh t5, 0(a0) + sh t0, 2(a0) + sh t1, 4(a0) + sh t3, 6(a0) + srl t5, t2, 16 /* t5 = 0 | 0 | R5 | G5 */ + b 21b + addiu a0, a0, 8 + +3: + lbu t5, 0(a1) /* t5 = 0 | 0 | 0 | R1 */ +31: + beqz t9, 4f + addiu t9, t9, -1 + lw t0, 1(a1) /* t0 = G2 | R2 | B1 | G1 */ + lw t1, 5(a1) /* t1 = B3 | G3 | R3 | B2 */ + lw t2, 9(a1) /* t2 = R5 | B4 | G4 | R4 */ + + addiu a1, a1, 12 + addiu a2, a2, -4 + + wsbh t0, t0 /* t0 = R2 | G2 | G1 | B1 */ + wsbh t1, t1 /* t1 = G3 | B3 | B2 | R3 */ + wsbh t2, t2 /* t2 = B4 | R5 | R4 | G4 */ + + precr_sra.ph.w t5, t0, 0 /* t5 = xx | R1 | G1 | B1 */ + packrl.ph t3, t1, t0 /* t3 = B2 | R3 | R2 | G2 */ + rotr t1, t1, 16 /* t1 = B2 | R3 | G3 | B3 */ + rotr t4, t2, 24 /* t4 = R5 | R4 | G4 | B4 */ + rotr t3, t3, 24 /* t3 = R3 | R2 | G2 | B2 */ + + CONVERT_2x8888_TO_2x0565 t5, t3, t5, t3, t6, t7, t8, v0, v1 + CONVERT_2x8888_TO_2x0565 t1, t4, t1, t4, t6, t7, t8, v0, v1 + + sh t5, 0(a0) + sh t3, 2(a0) + sh t1, 4(a0) + sh t4, 6(a0) + srl t5, t2, 16 /* t5 = 0 | 0 | xx | R5 */ + b 31b + addiu a0, a0, 8 + +4: + beqz a2, 6f + nop +5: + lbu t0, 0(a1) /* t0 = 0 | 0 | 0 | R */ + lbu t1, 1(a1) /* t1 = 0 | 0 | 0 | G */ + lbu t2, 2(a1) /* t2 = 0 | 0 | 0 | B */ + addiu a1, a1, 3 + + sll t0, t0, 16 /* t2 = 0 | R | 0 | 0 */ + sll t1, t1, 8 /* t1 = 0 | 0 | G | 0 */ + + or t2, t2, t1 /* t2 = 0 | 0 | G | B */ + or t2, t2, t0 /* t2 = 0 | R | G | B */ + + CONVERT_1x8888_TO_1x0565 t2, t3, t4, t5 + + sh t3, 0(a0) + addiu a2, a2, -1 + bnez a2, 5b + addiu a0, a0, 2 +6: + RESTORE_REGS_FROM_STACK 0, v0, v1 + j ra + nop + +END(pixman_composite_src_0888_0565_rev_asm_mips) +#endif + +LEAF_MIPS_DSPR2(pixman_composite_src_pixbuf_8888_asm_mips) +/* + * a0 - dst (a8b8g8r8) + * a1 - src (a8r8g8b8) + * a2 - w + */ + + SAVE_REGS_ON_STACK 0, v0 + li v0, 0x00ff00ff + + beqz a2, 3f + nop + addiu t1, a2, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) + lw t1, 4(a1) + addiu a1, a1, 8 + addiu a2, a2, -2 + srl t2, t0, 24 + srl t3, t1, 24 + + MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t2, t3, t0, t1, v0, t4, t5, t6, t7, t8, t9 + + sll t0, t0, 8 + sll t1, t1, 8 + andi t2, t2, 0xff + andi t3, t3, 0xff + or t0, t0, t2 + or t1, t1, t3 + wsbh t0, t0 + wsbh t1, t1 + rotr t0, t0, 16 + rotr t1, t1, 16 + sw t0, 0(a0) + sw t1, 4(a0) + + addiu t2, a2, -1 + bgtz t2, 1b + addiu a0, a0, 8 +2: + beqz a2, 3f + nop + lw t0, 0(a1) + srl t1, t0, 24 + + MIPS_UN8x4_MUL_UN8 t0, t1, t0, v0, t3, t4, t5 + + sll t0, t0, 8 + andi t1, t1, 0xff + or t0, t0, t1 + wsbh t0, t0 + rotr t0, t0, 16 + sw t0, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, v0 + j ra + nop + +END(pixman_composite_src_pixbuf_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_src_rpixbuf_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - w + */ + + SAVE_REGS_ON_STACK 0, v0 + li v0, 0x00ff00ff + + beqz a2, 3f + nop + addiu t1, a2, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) + lw t1, 4(a1) + addiu a1, a1, 8 + addiu a2, a2, -2 + srl t2, t0, 24 + srl t3, t1, 24 + + MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t2, t3, t0, t1, v0, t4, t5, t6, t7, t8, t9 + + sll t0, t0, 8 + sll t1, t1, 8 + andi t2, t2, 0xff + andi t3, t3, 0xff + or t0, t0, t2 + or t1, t1, t3 + rotr t0, t0, 8 + rotr t1, t1, 8 + sw t0, 0(a0) + sw t1, 4(a0) + + addiu t2, a2, -1 + bgtz t2, 1b + addiu a0, a0, 8 +2: + beqz a2, 3f + nop + lw t0, 0(a1) + srl t1, t0, 24 + + MIPS_UN8x4_MUL_UN8 t0, t1, t0, v0, t3, t4, t5 + + sll t0, t0, 8 + andi t1, t1, 0xff + or t0, t0, t1 + rotr t0, t0, 8 + sw t0, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, v0 + j ra + nop + +END(pixman_composite_src_rpixbuf_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_src_n_8_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (32bit constant) + * a2 - mask (a8) + * a3 - w + */ + + + SAVE_REGS_ON_STACK 0, v0 + li v0, 0x00ff00ff + + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop + +1: + /* a1 = source (32bit constant) */ + lbu t0, 0(a2) /* t2 = mask (a8) */ + lbu t1, 1(a2) /* t3 = mask (a8) */ + addiu a2, a2, 2 + + MIPS_2xUN8x4_MUL_2xUN8 a1, a1, t0, t1, t2, t3, v0, t4, t5, t6, t7, t8, t9 + + sw t2, 0(a0) + sw t3, 4(a0) + addiu a3, a3, -2 + addiu t2, a3, -1 + bgtz t2, 1b + addiu a0, a0, 8 + + beqz a3, 3f + nop + +2: + lbu t0, 0(a2) + addiu a2, a2, 1 + + MIPS_UN8x4_MUL_UN8 a1, t0, t1, v0, t3, t4, t5 + + sw t1, 0(a0) + addiu a3, a3, -1 + addiu a0, a0, 4 + +3: + RESTORE_REGS_FROM_STACK 0, v0 + j ra + nop + +END(pixman_composite_src_n_8_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_src_n_8_8_asm_mips) +/* + * a0 - dst (a8) + * a1 - src (32bit constant) + * a2 - mask (a8) + * a3 - w + */ + + li t9, 0x00ff00ff + beqz a3, 3f + nop + srl t7, a3, 2 /* t7 = how many multiples of 4 dst pixels */ + beqz t7, 1f /* branch if less than 4 src pixels */ + nop + + srl t8, a1, 24 + replv.ph t8, t8 + +0: + beqz t7, 1f + addiu t7, t7, -1 + lbu t0, 0(a2) + lbu t1, 1(a2) + lbu t2, 2(a2) + lbu t3, 3(a2) + + addiu a2, a2, 4 + + precr_sra.ph.w t1, t0, 0 + precr_sra.ph.w t3, t2, 0 + precr.qb.ph t0, t3, t1 + + muleu_s.ph.qbl t2, t0, t8 + muleu_s.ph.qbr t3, t0, t8 + shra_r.ph t4, t2, 8 + shra_r.ph t5, t3, 8 + and t4, t4, t9 + and t5, t5, t9 + addq.ph t2, t2, t4 + addq.ph t3, t3, t5 + shra_r.ph t2, t2, 8 + shra_r.ph t3, t3, 8 + precr.qb.ph t2, t2, t3 + + sb t2, 0(a0) + srl t2, t2, 8 + sb t2, 1(a0) + srl t2, t2, 8 + sb t2, 2(a0) + srl t2, t2, 8 + sb t2, 3(a0) + addiu a3, a3, -4 + b 0b + addiu a0, a0, 4 + +1: + beqz a3, 3f + nop + srl t8, a1, 24 +2: + lbu t0, 0(a2) + addiu a2, a2, 1 + + mul t2, t0, t8 + shra_r.ph t3, t2, 8 + andi t3, t3, 0x00ff + addq.ph t2, t2, t3 + shra_r.ph t2, t2, 8 + + sb t2, 0(a0) + addiu a3, a3, -1 + bnez a3, 2b + addiu a0, a0, 1 + +3: + j ra + nop + +END(pixman_composite_src_n_8_8_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_n_8888_8888_ca_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (32bit constant) + * a2 - mask (a8r8g8b8) + * a3 - w + */ + + beqz a3, 8f + nop + SAVE_REGS_ON_STACK 8, s0, s1, s2, s3, s4, s5 + + li t6, 0xff + addiu t7, zero, -1 /* t7 = 0xffffffff */ + srl t8, a1, 24 /* t8 = srca */ + li t9, 0x00ff00ff + + addiu t1, a3, -1 + beqz t1, 4f /* last pixel */ + nop + +0: + lw t0, 0(a2) /* t0 = mask */ + lw t1, 4(a2) /* t1 = mask */ + addiu a3, a3, -2 /* w = w - 2 */ + or t2, t0, t1 + beqz t2, 3f /* if (t0 == 0) && (t1 == 0) */ + addiu a2, a2, 8 + and t2, t0, t1 + beq t2, t7, 1f /* if (t0 == 0xffffffff) && (t1 == 0xffffffff) */ + nop + +//if(ma) + lw t2, 0(a0) /* t2 = dst */ + lw t3, 4(a0) /* t3 = dst */ + MIPS_2xUN8x4_MUL_2xUN8x4 a1, a1, t0, t1, t4, t5, t9, s0, s1, s2, s3, s4, s5 + MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t8, t8, t0, t1, t9, s0, s1, s2, s3, s4, s5 + not t0, t0 + not t1, t1 + MIPS_2xUN8x4_MUL_2xUN8x4 t2, t3, t0, t1, t2, t3, t9, s0, s1, s2, s3, s4, s5 + addu_s.qb t2, t4, t2 + addu_s.qb t3, t5, t3 + sw t2, 0(a0) + sw t3, 4(a0) + addiu t1, a3, -1 + bgtz t1, 0b + addiu a0, a0, 8 + b 4f + nop +1: +//if (t0 == 0xffffffff) && (t1 == 0xffffffff): + beq t8, t6, 2f /* if (srca == 0xff) */ + nop + lw t2, 0(a0) /* t2 = dst */ + lw t3, 4(a0) /* t3 = dst */ + not t0, a1 + not t1, a1 + srl t0, t0, 24 + srl t1, t1, 24 + MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t0, t1, t2, t3, t9, s0, s1, s2, s3, s4, s5 + addu_s.qb t2, a1, t2 + addu_s.qb t3, a1, t3 + sw t2, 0(a0) + sw t3, 4(a0) + addiu t1, a3, -1 + bgtz t1, 0b + addiu a0, a0, 8 + b 4f + nop +2: + sw a1, 0(a0) + sw a1, 4(a0) +3: + addiu t1, a3, -1 + bgtz t1, 0b + addiu a0, a0, 8 + +4: + beqz a3, 7f + nop + /* a1 = src */ + lw t0, 0(a2) /* t0 = mask */ + beqz t0, 7f /* if (t0 == 0) */ + nop + beq t0, t7, 5f /* if (t0 == 0xffffffff) */ + nop +//if(ma) + lw t1, 0(a0) /* t1 = dst */ + MIPS_UN8x4_MUL_UN8x4 a1, t0, t2, t9, t3, t4, t5, s0 + MIPS_UN8x4_MUL_UN8 t0, t8, t0, t9, t3, t4, t5 + not t0, t0 + MIPS_UN8x4_MUL_UN8x4 t1, t0, t1, t9, t3, t4, t5, s0 + addu_s.qb t1, t2, t1 + sw t1, 0(a0) + RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5 + j ra + nop +5: +//if (t0 == 0xffffffff) + beq t8, t6, 6f /* if (srca == 0xff) */ + nop + lw t1, 0(a0) /* t1 = dst */ + not t0, a1 + srl t0, t0, 24 + MIPS_UN8x4_MUL_UN8 t1, t0, t1, t9, t2, t3, t4 + addu_s.qb t1, a1, t1 + sw t1, 0(a0) + RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5 + j ra + nop +6: + sw a1, 0(a0) +7: + RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5 +8: + j ra + nop + +END(pixman_composite_over_n_8888_8888_ca_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_n_8888_0565_ca_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (32bit constant) + * a2 - mask (a8r8g8b8) + * a3 - w + */ + + beqz a3, 8f + nop + SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8 + + li t6, 0xff + addiu t7, zero, -1 /* t7 = 0xffffffff */ + srl t8, a1, 24 /* t8 = srca */ + li t9, 0x00ff00ff + li s6, 0xf800f800 + li s7, 0x07e007e0 + li s8, 0x001F001F + + addiu t1, a3, -1 + beqz t1, 4f /* last pixel */ + nop + +0: + lw t0, 0(a2) /* t0 = mask */ + lw t1, 4(a2) /* t1 = mask */ + addiu a3, a3, -2 /* w = w - 2 */ + or t2, t0, t1 + beqz t2, 3f /* if (t0 == 0) && (t1 == 0) */ + addiu a2, a2, 8 + and t2, t0, t1 + beq t2, t7, 1f /* if (t0 == 0xffffffff) && (t1 == 0xffffffff) */ + nop + +//if(ma) + lhu t2, 0(a0) /* t2 = dst */ + lhu t3, 2(a0) /* t3 = dst */ + MIPS_2xUN8x4_MUL_2xUN8x4 a1, a1, t0, t1, t4, t5, t9, s0, s1, s2, s3, s4, s5 + MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t8, t8, t0, t1, t9, s0, s1, s2, s3, s4, s5 + not t0, t0 + not t1, t1 + CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, s7, s8, s0, s1, s2, s3 + MIPS_2xUN8x4_MUL_2xUN8x4 t2, t3, t0, t1, t2, t3, t9, s0, s1, s2, s3, s4, s5 + addu_s.qb t2, t4, t2 + addu_s.qb t3, t5, t3 + CONVERT_2x8888_TO_2x0565 t2, t3, t2, t3, s6, s7, s8, s0, s1 + sh t2, 0(a0) + sh t3, 2(a0) + addiu t1, a3, -1 + bgtz t1, 0b + addiu a0, a0, 4 + b 4f + nop +1: +//if (t0 == 0xffffffff) && (t1 == 0xffffffff): + beq t8, t6, 2f /* if (srca == 0xff) */ + nop + lhu t2, 0(a0) /* t2 = dst */ + lhu t3, 2(a0) /* t3 = dst */ + not t0, a1 + not t1, a1 + srl t0, t0, 24 + srl t1, t1, 24 + CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, s7, s8, s0, s1, s2, s3 + MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t0, t1, t2, t3, t9, s0, s1, s2, s3, s4, s5 + addu_s.qb t2, a1, t2 + addu_s.qb t3, a1, t3 + CONVERT_2x8888_TO_2x0565 t2, t3, t2, t3, s6, s7, s8, s0, s1 + sh t2, 0(a0) + sh t3, 2(a0) + addiu t1, a3, -1 + bgtz t1, 0b + addiu a0, a0, 4 + b 4f + nop +2: + CONVERT_1x8888_TO_1x0565 a1, t2, s0, s1 + sh t2, 0(a0) + sh t2, 2(a0) +3: + addiu t1, a3, -1 + bgtz t1, 0b + addiu a0, a0, 4 + +4: + beqz a3, 7f + nop + /* a1 = src */ + lw t0, 0(a2) /* t0 = mask */ + beqz t0, 7f /* if (t0 == 0) */ + nop + beq t0, t7, 5f /* if (t0 == 0xffffffff) */ + nop +//if(ma) + lhu t1, 0(a0) /* t1 = dst */ + MIPS_UN8x4_MUL_UN8x4 a1, t0, t2, t9, t3, t4, t5, s0 + MIPS_UN8x4_MUL_UN8 t0, t8, t0, t9, t3, t4, t5 + not t0, t0 + CONVERT_1x0565_TO_1x8888 t1, s1, s2, s3 + MIPS_UN8x4_MUL_UN8x4 s1, t0, s1, t9, t3, t4, t5, s0 + addu_s.qb s1, t2, s1 + CONVERT_1x8888_TO_1x0565 s1, t1, s0, s2 + sh t1, 0(a0) + RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8 + j ra + nop +5: +//if (t0 == 0xffffffff) + beq t8, t6, 6f /* if (srca == 0xff) */ + nop + lhu t1, 0(a0) /* t1 = dst */ + not t0, a1 + srl t0, t0, 24 + CONVERT_1x0565_TO_1x8888 t1, s1, s2, s3 + MIPS_UN8x4_MUL_UN8 s1, t0, s1, t9, t2, t3, t4 + addu_s.qb s1, a1, s1 + CONVERT_1x8888_TO_1x0565 s1, t1, s0, s2 + sh t1, 0(a0) + RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8 + j ra + nop +6: + CONVERT_1x8888_TO_1x0565 a1, t1, s0, s2 + sh t1, 0(a0) +7: + RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8 +8: + j ra + nop + +END(pixman_composite_over_n_8888_0565_ca_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_n_8_8_asm_mips) +/* + * a0 - dst (a8) + * a1 - src (32bit constant) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, v0 + li t9, 0x00ff00ff + beqz a3, 3f + nop + srl v0, a3, 2 /* v0 = how many multiples of 4 dst pixels */ + beqz v0, 1f /* branch if less than 4 src pixels */ + nop + + srl t8, a1, 24 + replv.ph t8, t8 + +0: + beqz v0, 1f + addiu v0, v0, -1 + lbu t0, 0(a2) + lbu t1, 1(a2) + lbu t2, 2(a2) + lbu t3, 3(a2) + lbu t4, 0(a0) + lbu t5, 1(a0) + lbu t6, 2(a0) + lbu t7, 3(a0) + + addiu a2, a2, 4 + + precr_sra.ph.w t1, t0, 0 + precr_sra.ph.w t3, t2, 0 + precr_sra.ph.w t5, t4, 0 + precr_sra.ph.w t7, t6, 0 + + precr.qb.ph t0, t3, t1 + precr.qb.ph t1, t7, t5 + + muleu_s.ph.qbl t2, t0, t8 + muleu_s.ph.qbr t3, t0, t8 + shra_r.ph t4, t2, 8 + shra_r.ph t5, t3, 8 + and t4, t4, t9 + and t5, t5, t9 + addq.ph t2, t2, t4 + addq.ph t3, t3, t5 + shra_r.ph t2, t2, 8 + shra_r.ph t3, t3, 8 + precr.qb.ph t0, t2, t3 + not t6, t0 + + preceu.ph.qbl t7, t6 + preceu.ph.qbr t6, t6 + + muleu_s.ph.qbl t2, t1, t7 + muleu_s.ph.qbr t3, t1, t6 + shra_r.ph t4, t2, 8 + shra_r.ph t5, t3, 8 + and t4, t4, t9 + and t5, t5, t9 + addq.ph t2, t2, t4 + addq.ph t3, t3, t5 + shra_r.ph t2, t2, 8 + shra_r.ph t3, t3, 8 + precr.qb.ph t1, t2, t3 + + addu_s.qb t2, t0, t1 + + sb t2, 0(a0) + srl t2, t2, 8 + sb t2, 1(a0) + srl t2, t2, 8 + sb t2, 2(a0) + srl t2, t2, 8 + sb t2, 3(a0) + addiu a3, a3, -4 + b 0b + addiu a0, a0, 4 + +1: + beqz a3, 3f + nop + srl t8, a1, 24 +2: + lbu t0, 0(a2) + lbu t1, 0(a0) + addiu a2, a2, 1 + + mul t2, t0, t8 + shra_r.ph t3, t2, 8 + andi t3, t3, 0x00ff + addq.ph t2, t2, t3 + shra_r.ph t2, t2, 8 + not t3, t2 + andi t3, t3, 0x00ff + + + mul t4, t1, t3 + shra_r.ph t5, t4, 8 + andi t5, t5, 0x00ff + addq.ph t4, t4, t5 + shra_r.ph t4, t4, 8 + andi t4, t4, 0x00ff + + addu_s.qb t2, t2, t4 + sb t2, 0(a0) + addiu a3, a3, -1 + bnez a3, 2b + addiu a0, a0, 1 + +3: + RESTORE_REGS_FROM_STACK 0, v0 + j ra + nop + +END(pixman_composite_over_n_8_8_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_n_8_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (32bit constant) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 4, s0, s1, s2, s3, s4 + beqz a3, 4f + nop + li t4, 0x00ff00ff + li t5, 0xff + addiu t0, a3, -1 + beqz t0, 3f /* last pixel */ + srl t6, a1, 24 /* t6 = srca */ + not s4, a1 + beq t5, t6, 2f /* if (srca == 0xff) */ + srl s4, s4, 24 +1: + /* a1 = src */ + lbu t0, 0(a2) /* t0 = mask */ + lbu t1, 1(a2) /* t1 = mask */ + or t2, t0, t1 + beqz t2, 111f /* if (t0 == 0) && (t1 == 0) */ + addiu a2, a2, 2 + and t3, t0, t1 + + lw t2, 0(a0) /* t2 = dst */ + beq t3, t5, 11f /* if (t0 == 0xff) && (t1 == 0xff) */ + lw t3, 4(a0) /* t3 = dst */ + + MIPS_2xUN8x4_MUL_2xUN8 a1, a1, t0, t1, s0, s1, t4, t6, t7, t8, t9, s2, s3 + not s2, s0 + not s3, s1 + srl s2, s2, 24 + srl s3, s3, 24 + MIPS_2xUN8x4_MUL_2xUN8 t2, t3, s2, s3, t2, t3, t4, t0, t1, t6, t7, t8, t9 + addu_s.qb s2, t2, s0 + addu_s.qb s3, t3, s1 + sw s2, 0(a0) + b 111f + sw s3, 4(a0) +11: + MIPS_2xUN8x4_MUL_2xUN8 t2, t3, s4, s4, t2, t3, t4, t0, t1, t6, t7, t8, t9 + addu_s.qb s2, t2, a1 + addu_s.qb s3, t3, a1 + sw s2, 0(a0) + sw s3, 4(a0) + +111: + addiu a3, a3, -2 + addiu t0, a3, -1 + bgtz t0, 1b + addiu a0, a0, 8 + b 3f + nop +2: + /* a1 = src */ + lbu t0, 0(a2) /* t0 = mask */ + lbu t1, 1(a2) /* t1 = mask */ + or t2, t0, t1 + beqz t2, 222f /* if (t0 == 0) && (t1 == 0) */ + addiu a2, a2, 2 + and t3, t0, t1 + beq t3, t5, 22f /* if (t0 == 0xff) && (t1 == 0xff) */ + nop + lw t2, 0(a0) /* t2 = dst */ + lw t3, 4(a0) /* t3 = dst */ + + OVER_2x8888_2x8_2x8888 a1, a1, t0, t1, t2, t3, \ + t6, t7, t4, t8, t9, s0, s1, s2, s3 + sw t6, 0(a0) + b 222f + sw t7, 4(a0) +22: + sw a1, 0(a0) + sw a1, 4(a0) +222: + addiu a3, a3, -2 + addiu t0, a3, -1 + bgtz t0, 2b + addiu a0, a0, 8 +3: + blez a3, 4f + nop + /* a1 = src */ + lbu t0, 0(a2) /* t0 = mask */ + beqz t0, 4f /* if (t0 == 0) */ + addiu a2, a2, 1 + move t3, a1 + beq t0, t5, 31f /* if (t0 == 0xff) */ + lw t1, 0(a0) /* t1 = dst */ + + MIPS_UN8x4_MUL_UN8 a1, t0, t3, t4, t6, t7, t8 +31: + not t2, t3 + srl t2, t2, 24 + MIPS_UN8x4_MUL_UN8 t1, t2, t1, t4, t6, t7, t8 + addu_s.qb t2, t1, t3 + sw t2, 0(a0) +4: + RESTORE_REGS_FROM_STACK 4, s0, s1, s2, s3, s4 + j ra + nop + +END(pixman_composite_over_n_8_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_n_8_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (32bit constant) + * a2 - mask (a8) + * a3 - w + */ + SAVE_REGS_ON_STACK 24, v0, s0, s1, s2, s3, s4, s5, s6, s7, s8 + beqz a3, 4f + nop + li t4, 0x00ff00ff + li t5, 0xff + li t6, 0xf800f800 + li t7, 0x07e007e0 + li t8, 0x001F001F + addiu t1, a3, -1 + beqz t1, 3f /* last pixel */ + srl t0, a1, 24 /* t0 = srca */ + not v0, a1 + beq t0, t5, 2f /* if (srca == 0xff) */ + srl v0, v0, 24 +1: + /* a1 = src */ + lbu t0, 0(a2) /* t0 = mask */ + lbu t1, 1(a2) /* t1 = mask */ + or t2, t0, t1 + beqz t2, 111f /* if (t0 == 0) && (t1 == 0) */ + addiu a2, a2, 2 + lhu t2, 0(a0) /* t2 = dst */ + lhu t3, 2(a0) /* t3 = dst */ + CONVERT_2x0565_TO_2x8888 t2, t3, s0, s1, t7, t8, t9, s2, s3, s4 + and t9, t0, t1 + beq t9, t5, 11f /* if (t0 == 0xff) && (t1 == 0xff) */ + nop + + MIPS_2xUN8x4_MUL_2xUN8 a1, a1, t0, t1, s2, s3, t4, t9, s4, s5, s6, s7, s8 + not s4, s2 + not s5, s3 + srl s4, s4, 24 + srl s5, s5, 24 + MIPS_2xUN8x4_MUL_2xUN8 s0, s1, s4, s5, s0, s1, t4, t9, t0, t1, s6, s7, s8 + addu_s.qb s4, s2, s0 + addu_s.qb s5, s3, s1 + CONVERT_2x8888_TO_2x0565 s4, s5, t2, t3, t6, t7, t8, s0, s1 + sh t2, 0(a0) + b 111f + sh t3, 2(a0) +11: + MIPS_2xUN8x4_MUL_2xUN8 s0, s1, v0, v0, s0, s1, t4, t9, t0, t1, s6, s7, s8 + addu_s.qb s4, a1, s0 + addu_s.qb s5, a1, s1 + CONVERT_2x8888_TO_2x0565 s4, s5, t2, t3, t6, t7, t8, s0, s1 + sh t2, 0(a0) + sh t3, 2(a0) +111: + addiu a3, a3, -2 + addiu t0, a3, -1 + bgtz t0, 1b + addiu a0, a0, 4 + b 3f + nop +2: + CONVERT_1x8888_TO_1x0565 a1, s0, s1, s2 +21: + /* a1 = src */ + lbu t0, 0(a2) /* t0 = mask */ + lbu t1, 1(a2) /* t1 = mask */ + or t2, t0, t1 + beqz t2, 222f /* if (t0 == 0) && (t1 == 0) */ + addiu a2, a2, 2 + and t9, t0, t1 + move s2, s0 + beq t9, t5, 22f /* if (t0 == 0xff) && (t2 == 0xff) */ + move s3, s0 + lhu t2, 0(a0) /* t2 = dst */ + lhu t3, 2(a0) /* t3 = dst */ + + CONVERT_2x0565_TO_2x8888 t2, t3, s2, s3, t7, t8, s4, s5, s6, s7 + OVER_2x8888_2x8_2x8888 a1, a1, t0, t1, s2, s3, \ + t2, t3, t4, t9, s4, s5, s6, s7, s8 + CONVERT_2x8888_TO_2x0565 t2, t3, s2, s3, t6, t7, t8, s4, s5 +22: + sh s2, 0(a0) + sh s3, 2(a0) +222: + addiu a3, a3, -2 + addiu t0, a3, -1 + bgtz t0, 21b + addiu a0, a0, 4 +3: + blez a3, 4f + nop + /* a1 = src */ + lbu t0, 0(a2) /* t0 = mask */ + beqz t0, 4f /* if (t0 == 0) */ + nop + lhu t1, 0(a0) /* t1 = dst */ + CONVERT_1x0565_TO_1x8888 t1, t2, t3, t7 + beq t0, t5, 31f /* if (t0 == 0xff) */ + move t3, a1 + + MIPS_UN8x4_MUL_UN8 a1, t0, t3, t4, t7, t8, t9 +31: + not t6, t3 + srl t6, t6, 24 + MIPS_UN8x4_MUL_UN8 t2, t6, t2, t4, t7, t8, t9 + addu_s.qb t1, t2, t3 + CONVERT_1x8888_TO_1x0565 t1, t2, t3, t7 + sh t2, 0(a0) +4: + RESTORE_REGS_FROM_STACK 24, v0, s0, s1, s2, s3, s4, s5, s6, s7, s8 + j ra + nop + +END(pixman_composite_over_n_8_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_8888_n_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - mask (32bit constant) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, s0 + li t4, 0x00ff00ff + beqz a3, 3f + nop + addiu t1, a3, -1 + srl a2, a2, 24 + beqz t1, 2f + nop + +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + /* a2 = mask (32bit constant) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ + addiu a1, a1, 8 + + OVER_2x8888_2x8_2x8888 t0, t1, a2, a2, t2, t3, \ + t5, t6, t4, t7, t8, t9, t0, t1, s0 + + sw t5, 0(a0) + sw t6, 4(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a3, 3f + nop + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + /* a2 = mask (32bit constant) */ + lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ + + OVER_8888_8_8888 t0, a2, t1, t3, t4, t5, t6, t7, t8 + + sw t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0 + j ra + nop + +END(pixman_composite_over_8888_n_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_8888_n_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (a8r8g8b8) + * a2 - mask (32bit constant) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, s0, s1, s2, s3 + li t6, 0x00ff00ff + li t7, 0xf800f800 + li t8, 0x07e007e0 + li t9, 0x001F001F + beqz a3, 3f + nop + srl a2, a2, 24 + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + /* a2 = mask (32bit constant) */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + lhu t3, 2(a0) /* t2 = destination (r5g6b5) */ + addiu a1, a1, 8 + + CONVERT_2x0565_TO_2x8888 t2, t3, t4, t5, t8, t9, s0, s1, t2, t3 + OVER_2x8888_2x8_2x8888 t0, t1, a2, a2, t4, t5, \ + t2, t3, t6, t0, t1, s0, s1, s2, s3 + CONVERT_2x8888_TO_2x0565 t2, t3, t4, t5, t7, t8, t9, s0, s1 + + sh t4, 0(a0) + sh t5, 2(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a3, 3f + nop + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + /* a2 = mask (32bit constant) */ + lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ + + CONVERT_1x0565_TO_1x8888 t1, t2, t4, t5 + OVER_8888_8_8888 t0, a2, t2, t1, t6, t3, t4, t5, t7 + CONVERT_1x8888_TO_1x0565 t1, t3, t4, t5 + + sh t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2, s3 + j ra + nop + +END(pixman_composite_over_8888_n_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_0565_n_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (r5g6b5) + * a2 - mask (32bit constant) + * a3 - w + */ + + SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5 + li t6, 0x00ff00ff + li t7, 0xf800f800 + li t8, 0x07e007e0 + li t9, 0x001F001F + beqz a3, 3f + nop + srl a2, a2, 24 + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lhu t0, 0(a1) /* t0 = source (r5g6b5) */ + lhu t1, 2(a1) /* t1 = source (r5g6b5) */ + /* a2 = mask (32bit constant) */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + lhu t3, 2(a0) /* t3 = destination (r5g6b5) */ + addiu a1, a1, 4 + + CONVERT_2x0565_TO_2x8888 t0, t1, t4, t5, t8, t9, s0, s1, s2, s3 + CONVERT_2x0565_TO_2x8888 t2, t3, s0, s1, t8, t9, s2, s3, s4, s5 + OVER_2x8888_2x8_2x8888 t4, t5, a2, a2, s0, s1, \ + t0, t1, t6, s2, s3, s4, s5, t4, t5 + CONVERT_2x8888_TO_2x0565 t0, t1, s0, s1, t7, t8, t9, s2, s3 + + sh s0, 0(a0) + sh s1, 2(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a3, 3f + nop + lhu t0, 0(a1) /* t0 = source (r5g6b5) */ + /* a2 = mask (32bit constant) */ + lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ + + CONVERT_1x0565_TO_1x8888 t0, t2, t4, t5 + CONVERT_1x0565_TO_1x8888 t1, t3, t4, t5 + OVER_8888_8_8888 t2, a2, t3, t0, t6, t1, t4, t5, t7 + CONVERT_1x8888_TO_1x0565 t0, t3, t4, t5 + + sh t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5 + j ra + nop + +END(pixman_composite_over_0565_n_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_8888_8_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, s0, s1 + li t4, 0x00ff00ff + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + lbu t2, 0(a2) /* t2 = mask (a8) */ + lbu t3, 1(a2) /* t3 = mask (a8) */ + lw t5, 0(a0) /* t5 = destination (a8r8g8b8) */ + lw t6, 4(a0) /* t6 = destination (a8r8g8b8) */ + addiu a1, a1, 8 + addiu a2, a2, 2 + + OVER_2x8888_2x8_2x8888 t0, t1, t2, t3, t5, t6, \ + t7, t8, t4, t9, s0, s1, t0, t1, t2 + + sw t7, 0(a0) + sw t8, 4(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a3, 3f + nop + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lbu t1, 0(a2) /* t1 = mask (a8) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + + OVER_8888_8_8888 t0, t1, t2, t3, t4, t5, t6, t7, t8 + + sw t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1 + j ra + nop + +END(pixman_composite_over_8888_8_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_8888_8_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (a8r8g8b8) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5 + li t6, 0x00ff00ff + li t7, 0xf800f800 + li t8, 0x07e007e0 + li t9, 0x001F001F + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + lbu t2, 0(a2) /* t2 = mask (a8) */ + lbu t3, 1(a2) /* t3 = mask (a8) */ + lhu t4, 0(a0) /* t4 = destination (r5g6b5) */ + lhu t5, 2(a0) /* t5 = destination (r5g6b5) */ + addiu a1, a1, 8 + addiu a2, a2, 2 + + CONVERT_2x0565_TO_2x8888 t4, t5, s0, s1, t8, t9, s2, s3, s4, s5 + OVER_2x8888_2x8_2x8888 t0, t1, t2, t3, s0, s1, \ + t4, t5, t6, s2, s3, s4, s5, t0, t1 + CONVERT_2x8888_TO_2x0565 t4, t5, s0, s1, t7, t8, t9, s2, s3 + + sh s0, 0(a0) + sh s1, 2(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a3, 3f + nop + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lbu t1, 0(a2) /* t1 = mask (a8) */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + + CONVERT_1x0565_TO_1x8888 t2, t3, t4, t5 + OVER_8888_8_8888 t0, t1, t3, t2, t6, t4, t5, t7, t8 + CONVERT_1x8888_TO_1x0565 t2, t3, t4, t5 + + sh t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5 + j ra + nop + +END(pixman_composite_over_8888_8_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_0565_8_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (r5g6b5) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5 + li t4, 0xf800f800 + li t5, 0x07e007e0 + li t6, 0x001F001F + li t7, 0x00ff00ff + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lhu t0, 0(a1) /* t0 = source (r5g6b5) */ + lhu t1, 2(a1) /* t1 = source (r5g6b5) */ + lbu t2, 0(a2) /* t2 = mask (a8) */ + lbu t3, 1(a2) /* t3 = mask (a8) */ + lhu t8, 0(a0) /* t8 = destination (r5g6b5) */ + lhu t9, 2(a0) /* t9 = destination (r5g6b5) */ + addiu a1, a1, 4 + addiu a2, a2, 2 + + CONVERT_2x0565_TO_2x8888 t0, t1, s0, s1, t5, t6, s2, s3, s4, s5 + CONVERT_2x0565_TO_2x8888 t8, t9, s2, s3, t5, t6, s4, s5, t0, t1 + OVER_2x8888_2x8_2x8888 s0, s1, t2, t3, s2, s3, \ + t0, t1, t7, s4, s5, t8, t9, s0, s1 + CONVERT_2x8888_TO_2x0565 t0, t1, s0, s1, t4, t5, t6, s2, s3 + + sh s0, 0(a0) + sh s1, 2(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a3, 3f + nop + lhu t0, 0(a1) /* t0 = source (r5g6b5) */ + lbu t1, 0(a2) /* t1 = mask (a8) */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + + CONVERT_1x0565_TO_1x8888 t0, t3, t4, t5 + CONVERT_1x0565_TO_1x8888 t2, t4, t5, t6 + OVER_8888_8_8888 t3, t1, t4, t0, t7, t2, t5, t6, t8 + CONVERT_1x8888_TO_1x0565 t0, t3, t4, t5 + + sh t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5 + j ra + nop + +END(pixman_composite_over_0565_8_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_8888_8888_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - mask (a8r8g8b8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, s0, s1, s2 + li t4, 0x00ff00ff + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + lw t2, 0(a2) /* t2 = mask (a8r8g8b8) */ + lw t3, 4(a2) /* t3 = mask (a8r8g8b8) */ + lw t5, 0(a0) /* t5 = destination (a8r8g8b8) */ + lw t6, 4(a0) /* t6 = destination (a8r8g8b8) */ + addiu a1, a1, 8 + addiu a2, a2, 8 + srl t2, t2, 24 + srl t3, t3, 24 + + OVER_2x8888_2x8_2x8888 t0, t1, t2, t3, t5, t6, t7, t8, t4, t9, s0, s1, s2, t0, t1 + + sw t7, 0(a0) + sw t8, 4(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a3, 3f + nop + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 0(a2) /* t1 = mask (a8r8g8b8) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + srl t1, t1, 24 + + OVER_8888_8_8888 t0, t1, t2, t3, t4, t5, t6, t7, t8 + + sw t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2 + j ra + nop + +END(pixman_composite_over_8888_8888_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_8888_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - w + */ + + SAVE_REGS_ON_STACK 0, s0, s1, s2 + li t4, 0x00ff00ff + beqz a2, 3f + nop + addiu t1, a2, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ + addiu a1, a1, 8 + + not t5, t0 + srl t5, t5, 24 + not t6, t1 + srl t6, t6, 24 + + or t7, t5, t6 + beqz t7, 11f + or t8, t0, t1 + beqz t8, 12f + + MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t5, t6, t7, t8, t4, t9, s0, s1, s2, t2, t3 + + addu_s.qb t0, t7, t0 + addu_s.qb t1, t8, t1 +11: + sw t0, 0(a0) + sw t1, 4(a0) +12: + addiu a2, a2, -2 + addiu t1, a2, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a2, 3f + nop + + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ + addiu a1, a1, 4 + + not t2, t0 + srl t2, t2, 24 + + beqz t2, 21f + nop + beqz t0, 3f + + MIPS_UN8x4_MUL_UN8 t1, t2, t3, t4, t5, t6, t7 + + addu_s.qb t0, t3, t0 +21: + sw t0, 0(a0) + +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2 + j ra + nop + +END(pixman_composite_over_8888_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_8888_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (a8r8g8b8) + * a2 - w + */ + + SAVE_REGS_ON_STACK 8, s0, s1, s2, s3, s4, s5 + li t4, 0x00ff00ff + li s3, 0xf800f800 + li s4, 0x07e007e0 + li s5, 0x001F001F + beqz a2, 3f + nop + addiu t1, a2, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + lhu t3, 2(a0) /* t3 = destination (r5g6b5) */ + addiu a1, a1, 8 + + not t5, t0 + srl t5, t5, 24 + not t6, t1 + srl t6, t6, 24 + + or t7, t5, t6 + beqz t7, 11f + or t8, t0, t1 + beqz t8, 12f + + CONVERT_2x0565_TO_2x8888 t2, t3, s0, s1, s4, s5, t7, t8, t9, s2 + MIPS_2xUN8x4_MUL_2xUN8 s0, s1, t5, t6, t7, t8, t4, t9, t2, t3, s2, s0, s1 + + addu_s.qb t0, t7, t0 + addu_s.qb t1, t8, t1 +11: + CONVERT_2x8888_TO_2x0565 t0, t1, t7, t8, s3, s4, s5, t2, t3 + sh t7, 0(a0) + sh t8, 2(a0) +12: + addiu a2, a2, -2 + addiu t1, a2, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a2, 3f + nop + + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ + addiu a1, a1, 4 + + not t2, t0 + srl t2, t2, 24 + + beqz t2, 21f + nop + beqz t0, 3f + + CONVERT_1x0565_TO_1x8888 t1, s0, t8, t9 + MIPS_UN8x4_MUL_UN8 s0, t2, t3, t4, t5, t6, t7 + + addu_s.qb t0, t3, t0 +21: + CONVERT_1x8888_TO_1x0565 t0, s0, t8, t9 + sh s0, 0(a0) + +3: + RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5 + j ra + nop + +END(pixman_composite_over_8888_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_n_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (32bit constant) + * a2 - w + */ + + beqz a2, 5f + nop + + not t0, a1 + srl t0, t0, 24 + bgtz t0, 1f + nop + CONVERT_1x8888_TO_1x0565 a1, t1, t2, t3 +0: + sh t1, 0(a0) + addiu a2, a2, -1 + bgtz a2, 0b + addiu a0, a0, 2 + j ra + nop + +1: + SAVE_REGS_ON_STACK 0, s0, s1, s2 + li t4, 0x00ff00ff + li t5, 0xf800f800 + li t6, 0x07e007e0 + li t7, 0x001F001F + addiu t1, a2, -1 + beqz t1, 3f + nop +2: + lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ + lhu t2, 2(a0) /* t2 = destination (r5g6b5) */ + + CONVERT_2x0565_TO_2x8888 t1, t2, t3, t8, t6, t7, t9, s0, s1, s2 + MIPS_2xUN8x4_MUL_2xUN8 t3, t8, t0, t0, t1, t2, t4, t9, s0, s1, s2, t3, t8 + addu_s.qb t1, t1, a1 + addu_s.qb t2, t2, a1 + CONVERT_2x8888_TO_2x0565 t1, t2, t3, t8, t5, t6, t7, s0, s1 + + sh t3, 0(a0) + sh t8, 2(a0) + + addiu a2, a2, -2 + addiu t1, a2, -1 + bgtz t1, 2b + addiu a0, a0, 4 +3: + beqz a2, 4f + nop + + lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ + + CONVERT_1x0565_TO_1x8888 t1, t2, s0, s1 + MIPS_UN8x4_MUL_UN8 t2, t0, t1, t4, s0, s1, s2 + addu_s.qb t1, t1, a1 + CONVERT_1x8888_TO_1x0565 t1, t2, s0, s1 + + sh t2, 0(a0) + +4: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2 +5: + j ra + nop + +END(pixman_composite_over_n_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_n_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (32bit constant) + * a2 - w + */ + + beqz a2, 5f + nop + + not t0, a1 + srl t0, t0, 24 + bgtz t0, 1f + nop +0: + sw a1, 0(a0) + addiu a2, a2, -1 + bgtz a2, 0b + addiu a0, a0, 4 + j ra + nop + +1: + SAVE_REGS_ON_STACK 0, s0, s1, s2 + li t4, 0x00ff00ff + addiu t1, a2, -1 + beqz t1, 3f + nop +2: + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ + + MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t0, t0, t7, t8, t4, t9, s0, s1, s2, t2, t3 + + addu_s.qb t7, t7, a1 + addu_s.qb t8, t8, a1 + + sw t7, 0(a0) + sw t8, 4(a0) + + addiu a2, a2, -2 + addiu t1, a2, -1 + bgtz t1, 2b + addiu a0, a0, 8 +3: + beqz a2, 4f + nop + + lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ + + MIPS_UN8x4_MUL_UN8 t1, t0, t3, t4, t5, t6, t7 + + addu_s.qb t3, t3, a1 + + sw t3, 0(a0) + +4: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2 +5: + j ra + nop + +END(pixman_composite_over_n_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_8_8_8_asm_mips) +/* + * a0 - dst (a8) + * a1 - src (a8) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, v0, v1 + li t9, 0x00ff00ff + beqz a3, 3f + nop + + srl v0, a3, 2 /* v0 = how many multiples of 4 dst pixels */ + beqz v0, 1f /* branch if less than 4 src pixels */ + nop + +0: + beqz v0, 1f + addiu v0, v0, -1 + lbu t0, 0(a2) + lbu t1, 1(a2) + lbu t2, 2(a2) + lbu t3, 3(a2) + lbu t4, 0(a0) + lbu t5, 1(a0) + lbu t6, 2(a0) + lbu t7, 3(a0) + + addiu a2, a2, 4 + + precr_sra.ph.w t1, t0, 0 + precr_sra.ph.w t3, t2, 0 + precr_sra.ph.w t5, t4, 0 + precr_sra.ph.w t7, t6, 0 + + precr.qb.ph t0, t3, t1 + precr.qb.ph t1, t7, t5 + + lbu t4, 0(a1) + lbu v1, 1(a1) + lbu t7, 2(a1) + lbu t8, 3(a1) + + addiu a1, a1, 4 + + precr_sra.ph.w v1, t4, 0 + precr_sra.ph.w t8, t7, 0 + + muleu_s.ph.qbl t2, t0, t8 + muleu_s.ph.qbr t3, t0, v1 + shra_r.ph t4, t2, 8 + shra_r.ph t5, t3, 8 + and t4, t4, t9 + and t5, t5, t9 + addq.ph t2, t2, t4 + addq.ph t3, t3, t5 + shra_r.ph t2, t2, 8 + shra_r.ph t3, t3, 8 + precr.qb.ph t0, t2, t3 + + addu_s.qb t2, t0, t1 + + sb t2, 0(a0) + srl t2, t2, 8 + sb t2, 1(a0) + srl t2, t2, 8 + sb t2, 2(a0) + srl t2, t2, 8 + sb t2, 3(a0) + addiu a3, a3, -4 + b 0b + addiu a0, a0, 4 + +1: + beqz a3, 3f + nop +2: + lbu t8, 0(a1) + lbu t0, 0(a2) + lbu t1, 0(a0) + addiu a1, a1, 1 + addiu a2, a2, 1 + + mul t2, t0, t8 + shra_r.ph t3, t2, 8 + andi t3, t3, 0xff + addq.ph t2, t2, t3 + shra_r.ph t2, t2, 8 + andi t2, t2, 0xff + + addu_s.qb t2, t2, t1 + sb t2, 0(a0) + addiu a3, a3, -1 + bnez a3, 2b + addiu a0, a0, 1 + +3: + RESTORE_REGS_FROM_STACK 0, v0, v1 + j ra + nop + +END(pixman_composite_add_8_8_8_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_n_8_8_asm_mips) +/* + * a0 - dst (a8) + * a1 - src (32bit constant) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, v0 + li t9, 0x00ff00ff + beqz a3, 3f + nop + + srl v0, a3, 2 /* v0 = how many multiples of 4 dst pixels */ + beqz v0, 1f /* branch if less than 4 src pixels */ + nop + + srl t8, a1, 24 + replv.ph t8, t8 + +0: + beqz v0, 1f + addiu v0, v0, -1 + lbu t0, 0(a2) + lbu t1, 1(a2) + lbu t2, 2(a2) + lbu t3, 3(a2) + lbu t4, 0(a0) + lbu t5, 1(a0) + lbu t6, 2(a0) + lbu t7, 3(a0) + + addiu a2, a2, 4 + + precr_sra.ph.w t1, t0, 0 + precr_sra.ph.w t3, t2, 0 + precr_sra.ph.w t5, t4, 0 + precr_sra.ph.w t7, t6, 0 + + precr.qb.ph t0, t3, t1 + precr.qb.ph t1, t7, t5 + + muleu_s.ph.qbl t2, t0, t8 + muleu_s.ph.qbr t3, t0, t8 + shra_r.ph t4, t2, 8 + shra_r.ph t5, t3, 8 + and t4, t4, t9 + and t5, t5, t9 + addq.ph t2, t2, t4 + addq.ph t3, t3, t5 + shra_r.ph t2, t2, 8 + shra_r.ph t3, t3, 8 + precr.qb.ph t0, t2, t3 + + addu_s.qb t2, t0, t1 + + sb t2, 0(a0) + srl t2, t2, 8 + sb t2, 1(a0) + srl t2, t2, 8 + sb t2, 2(a0) + srl t2, t2, 8 + sb t2, 3(a0) + addiu a3, a3, -4 + b 0b + addiu a0, a0, 4 + +1: + beqz a3, 3f + nop + srl t8, a1, 24 +2: + lbu t0, 0(a2) + lbu t1, 0(a0) + addiu a2, a2, 1 + + mul t2, t0, t8 + shra_r.ph t3, t2, 8 + andi t3, t3, 0xff + addq.ph t2, t2, t3 + shra_r.ph t2, t2, 8 + andi t2, t2, 0xff + + addu_s.qb t2, t2, t1 + sb t2, 0(a0) + addiu a3, a3, -1 + bnez a3, 2b + addiu a0, a0, 1 + +3: + RESTORE_REGS_FROM_STACK 0, v0 + j ra + nop + +END(pixman_composite_add_n_8_8_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_n_8_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (32bit constant) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, s0, s1, s2 + li t4, 0x00ff00ff + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + /* a1 = source (32bit constant) */ + lbu t0, 0(a2) /* t0 = mask (a8) */ + lbu t1, 1(a2) /* t1 = mask (a8) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ + addiu a2, a2, 2 + + MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 a1, a1, \ + t0, t1, \ + t2, t3, \ + t5, t6, \ + t4, t7, t8, t9, s0, s1, s2 + + sw t5, 0(a0) + sw t6, 4(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a3, 3f + nop + /* a1 = source (32bit constant) */ + lbu t0, 0(a2) /* t0 = mask (a8) */ + lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ + + MIPS_UN8x4_MUL_UN8_ADD_UN8x4 a1, t0, t1, t2, t4, t3, t5, t6 + + sw t2, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2 + j ra + nop + +END(pixman_composite_add_n_8_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_0565_8_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (r5g6b5) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7 + li t4, 0xf800f800 + li t5, 0x07e007e0 + li t6, 0x001F001F + li t7, 0x00ff00ff + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lhu t0, 0(a1) /* t0 = source (r5g6b5) */ + lhu t1, 2(a1) /* t1 = source (r5g6b5) */ + lbu t2, 0(a2) /* t2 = mask (a8) */ + lbu t3, 1(a2) /* t3 = mask (a8) */ + lhu t8, 0(a0) /* t8 = destination (r5g6b5) */ + lhu t9, 2(a0) /* t9 = destination (r5g6b5) */ + addiu a1, a1, 4 + addiu a2, a2, 2 + + CONVERT_2x0565_TO_2x8888 t0, t1, s0, s1, t5, t6, s2, s3, s4, s5 + CONVERT_2x0565_TO_2x8888 t8, t9, s2, s3, t5, t6, s4, s5, s6, s7 + MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 s0, s1, \ + t2, t3, \ + s2, s3, \ + t0, t1, \ + t7, s4, s5, s6, s7, t8, t9 + CONVERT_2x8888_TO_2x0565 t0, t1, s0, s1, t4, t5, t6, s2, s3 + + sh s0, 0(a0) + sh s1, 2(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a3, 3f + nop + lhu t0, 0(a1) /* t0 = source (r5g6b5) */ + lbu t1, 0(a2) /* t1 = mask (a8) */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + + CONVERT_1x0565_TO_1x8888 t0, t3, t4, t5 + CONVERT_1x0565_TO_1x8888 t2, t4, t5, t6 + MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t3, t1, t4, t0, t7, t2, t5, t6 + CONVERT_1x8888_TO_1x0565 t0, t3, t4, t5 + + sh t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7 + j ra + nop + +END(pixman_composite_add_0565_8_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_8888_8_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - mask (a8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, s0, s1, s2 + li t4, 0x00ff00ff + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + lbu t2, 0(a2) /* t2 = mask (a8) */ + lbu t3, 1(a2) /* t3 = mask (a8) */ + lw t5, 0(a0) /* t5 = destination (a8r8g8b8) */ + lw t6, 4(a0) /* t6 = destination (a8r8g8b8) */ + addiu a1, a1, 8 + addiu a2, a2, 2 + + MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 t0, t1, \ + t2, t3, \ + t5, t6, \ + t7, t8, \ + t4, t9, s0, s1, s2, t0, t1 + + sw t7, 0(a0) + sw t8, 4(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a3, 3f + nop + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lbu t1, 0(a2) /* t1 = mask (a8) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + + MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t0, t1, t2, t3, t4, t5, t6, t7 + + sw t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2 + j ra + nop + +END(pixman_composite_add_8888_8_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_8888_n_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - mask (32bit constant) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, s0, s1, s2 + li t4, 0x00ff00ff + beqz a3, 3f + nop + srl a2, a2, 24 + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + /* a2 = mask (32bit constant) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ + addiu a1, a1, 8 + + MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 t0, t1, \ + a2, a2, \ + t2, t3, \ + t5, t6, \ + t4, t7, t8, t9, s0, s1, s2 + + sw t5, 0(a0) + sw t6, 4(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a3, 3f + nop + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + /* a2 = mask (32bit constant) */ + lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ + + MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t0, a2, t1, t3, t4, t5, t6, t7 + + sw t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2 + j ra + nop + +END(pixman_composite_add_8888_n_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_8888_8888_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - mask (a8r8g8b8) + * a3 - w + */ + + SAVE_REGS_ON_STACK 0, s0, s1, s2 + li t4, 0x00ff00ff + beqz a3, 3f + nop + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 4(a1) /* t1 = source (a8r8g8b8) */ + lw t2, 0(a2) /* t2 = mask (a8r8g8b8) */ + lw t3, 4(a2) /* t3 = mask (a8r8g8b8) */ + lw t5, 0(a0) /* t5 = destination (a8r8g8b8) */ + lw t6, 4(a0) /* t6 = destination (a8r8g8b8) */ + addiu a1, a1, 8 + addiu a2, a2, 8 + srl t2, t2, 24 + srl t3, t3, 24 + + MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 t0, t1, \ + t2, t3, \ + t5, t6, \ + t7, t8, \ + t4, t9, s0, s1, s2, t0, t1 + + sw t7, 0(a0) + sw t8, 4(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a3, 3f + nop + lw t0, 0(a1) /* t0 = source (a8r8g8b8) */ + lw t1, 0(a2) /* t1 = mask (a8r8g8b8) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + srl t1, t1, 24 + + MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t0, t1, t2, t3, t4, t5, t6, t7 + + sw t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2 + j ra + nop + +END(pixman_composite_add_8888_8888_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_8_8_asm_mips) +/* + * a0 - dst (a8) + * a1 - src (a8) + * a2 - w + */ + + beqz a2, 3f + nop + srl t9, a2, 2 /* t9 = how many multiples of 4 dst pixels */ + beqz t9, 1f /* branch if less than 4 src pixels */ + nop + +0: + beqz t9, 1f + addiu t9, t9, -1 + lbu t0, 0(a1) + lbu t1, 1(a1) + lbu t2, 2(a1) + lbu t3, 3(a1) + lbu t4, 0(a0) + lbu t5, 1(a0) + lbu t6, 2(a0) + lbu t7, 3(a0) + + addiu a1, a1, 4 + + precr_sra.ph.w t1, t0, 0 + precr_sra.ph.w t3, t2, 0 + precr_sra.ph.w t5, t4, 0 + precr_sra.ph.w t7, t6, 0 + + precr.qb.ph t0, t3, t1 + precr.qb.ph t1, t7, t5 + + addu_s.qb t2, t0, t1 + + sb t2, 0(a0) + srl t2, t2, 8 + sb t2, 1(a0) + srl t2, t2, 8 + sb t2, 2(a0) + srl t2, t2, 8 + sb t2, 3(a0) + addiu a2, a2, -4 + b 0b + addiu a0, a0, 4 + +1: + beqz a2, 3f + nop +2: + lbu t0, 0(a1) + lbu t1, 0(a0) + addiu a1, a1, 1 + + addu_s.qb t2, t0, t1 + sb t2, 0(a0) + addiu a2, a2, -1 + bnez a2, 2b + addiu a0, a0, 1 + +3: + j ra + nop + +END(pixman_composite_add_8_8_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_add_8888_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - w + */ + + beqz a2, 4f + nop + + srl t9, a2, 2 /* t1 = how many multiples of 4 src pixels */ + beqz t9, 3f /* branch if less than 4 src pixels */ + nop +1: + addiu t9, t9, -1 + beqz t9, 2f + addiu a2, a2, -4 + + lw t0, 0(a1) + lw t1, 4(a1) + lw t2, 8(a1) + lw t3, 12(a1) + lw t4, 0(a0) + lw t5, 4(a0) + lw t6, 8(a0) + lw t7, 12(a0) + addiu a1, a1, 16 + + addu_s.qb t4, t4, t0 + addu_s.qb t5, t5, t1 + addu_s.qb t6, t6, t2 + addu_s.qb t7, t7, t3 + + sw t4, 0(a0) + sw t5, 4(a0) + sw t6, 8(a0) + sw t7, 12(a0) + b 1b + addiu a0, a0, 16 +2: + lw t0, 0(a1) + lw t1, 4(a1) + lw t2, 8(a1) + lw t3, 12(a1) + lw t4, 0(a0) + lw t5, 4(a0) + lw t6, 8(a0) + lw t7, 12(a0) + addiu a1, a1, 16 + + addu_s.qb t4, t4, t0 + addu_s.qb t5, t5, t1 + addu_s.qb t6, t6, t2 + addu_s.qb t7, t7, t3 + + sw t4, 0(a0) + sw t5, 4(a0) + sw t6, 8(a0) + sw t7, 12(a0) + + beqz a2, 4f + addiu a0, a0, 16 +3: + lw t0, 0(a1) + lw t1, 0(a0) + addiu a1, a1, 4 + addiu a2, a2, -1 + addu_s.qb t1, t1, t0 + sw t1, 0(a0) + bnez a2, 3b + addiu a0, a0, 4 +4: + jr ra + nop + +END(pixman_composite_add_8888_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_out_reverse_8_0565_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (a8) + * a2 - w + */ + + beqz a2, 4f + nop + + SAVE_REGS_ON_STACK 0, s0, s1, s2, s3 + li t2, 0xf800f800 + li t3, 0x07e007e0 + li t4, 0x001F001F + li t5, 0x00ff00ff + + addiu t1, a2, -1 + beqz t1, 2f + nop +1: + lbu t0, 0(a1) /* t0 = source (a8) */ + lbu t1, 1(a1) /* t1 = source (a8) */ + lhu t6, 0(a0) /* t6 = destination (r5g6b5) */ + lhu t7, 2(a0) /* t7 = destination (r5g6b5) */ + addiu a1, a1, 2 + + not t0, t0 + not t1, t1 + andi t0, 0xff /* t0 = neg source1 */ + andi t1, 0xff /* t1 = neg source2 */ + CONVERT_2x0565_TO_2x8888 t6, t7, t8, t9, t3, t4, s0, s1, s2, s3 + MIPS_2xUN8x4_MUL_2xUN8 t8, t9, t0, t1, t6, t7, t5, s0, s1, s2, s3, t8, t9 + CONVERT_2x8888_TO_2x0565 t6, t7, t8, t9, t2, t3, t4, s0, s1 + + sh t8, 0(a0) + sh t9, 2(a0) + addiu a2, a2, -2 + addiu t1, a2, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a2, 3f + nop + lbu t0, 0(a1) /* t0 = source (a8) */ + lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ + + not t0, t0 + andi t0, 0xff /* t0 = neg source */ + CONVERT_1x0565_TO_1x8888 t1, t2, t3, t4 + MIPS_UN8x4_MUL_UN8 t2, t0, t1, t5, t3, t4, t6 + CONVERT_1x8888_TO_1x0565 t1, t2, t3, t4 + + sh t2, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2, s3 +4: + j ra + nop + +END(pixman_composite_out_reverse_8_0565_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_out_reverse_8_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8) + * a2 - w + */ + + beqz a2, 3f + nop + li t4, 0x00ff00ff + addiu t1, a2, -1 + beqz t1, 2f + nop +1: + lbu t0, 0(a1) /* t0 = source (a8) */ + lbu t1, 1(a1) /* t1 = source (a8) */ + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ + addiu a1, a1, 2 + not t0, t0 + not t1, t1 + andi t0, 0xff /* t0 = neg source */ + andi t1, 0xff /* t1 = neg source */ + + MIPS_2xUN8x4_MUL_2xUN8 t2, t3, t0, t1, t5, t6, t4, t7, t8, t9, t2, t3, t0 + + sw t5, 0(a0) + sw t6, 4(a0) + addiu a2, a2, -2 + addiu t1, a2, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a2, 3f + nop + lbu t0, 0(a1) /* t0 = source (a8) */ + lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ + not t0, t0 + andi t0, 0xff /* t0 = neg source */ + + MIPS_UN8x4_MUL_UN8 t1, t0, t2, t4, t3, t5, t6 + + sw t2, 0(a0) +3: + j ra + nop + +END(pixman_composite_out_reverse_8_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_over_reverse_n_8888_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (32bit constant) + * a2 - w + */ + + beqz a2, 5f + nop + + SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7 + li t0, 0x00ff00ff + srl t9, a2, 2 /* t9 = how many multiples of 4 src pixels */ + beqz t9, 2f /* branch if less than 4 src pixels */ + nop +1: + beqz t9, 2f + addiu t9, t9, -1 + + lw t1, 0(a0) + lw t2, 4(a0) + lw t3, 8(a0) + lw t4, 12(a0) + + addiu a2, a2, -4 + + not t5, t1 + not t6, t2 + not t7, t3 + not t8, t4 + srl t5, t5, 24 + srl t6, t6, 24 + srl t7, t7, 24 + srl t8, t8, 24 + replv.ph t5, t5 + replv.ph t6, t6 + replv.ph t7, t7 + replv.ph t8, t8 + muleu_s.ph.qbl s0, a1, t5 + muleu_s.ph.qbr s1, a1, t5 + muleu_s.ph.qbl s2, a1, t6 + muleu_s.ph.qbr s3, a1, t6 + muleu_s.ph.qbl s4, a1, t7 + muleu_s.ph.qbr s5, a1, t7 + muleu_s.ph.qbl s6, a1, t8 + muleu_s.ph.qbr s7, a1, t8 + + shra_r.ph t5, s0, 8 + shra_r.ph t6, s1, 8 + shra_r.ph t7, s2, 8 + shra_r.ph t8, s3, 8 + and t5, t5, t0 + and t6, t6, t0 + and t7, t7, t0 + and t8, t8, t0 + addq.ph s0, s0, t5 + addq.ph s1, s1, t6 + addq.ph s2, s2, t7 + addq.ph s3, s3, t8 + shra_r.ph s0, s0, 8 + shra_r.ph s1, s1, 8 + shra_r.ph s2, s2, 8 + shra_r.ph s3, s3, 8 + shra_r.ph t5, s4, 8 + shra_r.ph t6, s5, 8 + shra_r.ph t7, s6, 8 + shra_r.ph t8, s7, 8 + and t5, t5, t0 + and t6, t6, t0 + and t7, t7, t0 + and t8, t8, t0 + addq.ph s4, s4, t5 + addq.ph s5, s5, t6 + addq.ph s6, s6, t7 + addq.ph s7, s7, t8 + shra_r.ph s4, s4, 8 + shra_r.ph s5, s5, 8 + shra_r.ph s6, s6, 8 + shra_r.ph s7, s7, 8 + + precr.qb.ph t5, s0, s1 + precr.qb.ph t6, s2, s3 + precr.qb.ph t7, s4, s5 + precr.qb.ph t8, s6, s7 + addu_s.qb t5, t1, t5 + addu_s.qb t6, t2, t6 + addu_s.qb t7, t3, t7 + addu_s.qb t8, t4, t8 + + sw t5, 0(a0) + sw t6, 4(a0) + sw t7, 8(a0) + sw t8, 12(a0) + b 1b + addiu a0, a0, 16 + +2: + beqz a2, 4f + nop +3: + lw t1, 0(a0) + + not t2, t1 + srl t2, t2, 24 + replv.ph t2, t2 + + muleu_s.ph.qbl t4, a1, t2 + muleu_s.ph.qbr t5, a1, t2 + shra_r.ph t6, t4, 8 + shra_r.ph t7, t5, 8 + + and t6,t6,t0 + and t7,t7,t0 + + addq.ph t8, t4, t6 + addq.ph t9, t5, t7 + + shra_r.ph t8, t8, 8 + shra_r.ph t9, t9, 8 + + precr.qb.ph t9, t8, t9 + + addu_s.qb t9, t1, t9 + sw t9, 0(a0) + + addiu a2, a2, -1 + bnez a2, 3b + addiu a0, a0, 4 +4: + RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7 +5: + j ra + nop + +END(pixman_composite_over_reverse_n_8888_asm_mips) + +LEAF_MIPS_DSPR2(pixman_composite_in_n_8_asm_mips) +/* + * a0 - dst (a8) + * a1 - src (32bit constant) + * a2 - w + */ + + li t9, 0x00ff00ff + beqz a2, 3f + nop + srl t7, a2, 2 /* t7 = how many multiples of 4 dst pixels */ + beqz t7, 1f /* branch if less than 4 src pixels */ + nop + + srl t8, a1, 24 + replv.ph t8, t8 + +0: + beqz t7, 1f + addiu t7, t7, -1 + lbu t0, 0(a0) + lbu t1, 1(a0) + lbu t2, 2(a0) + lbu t3, 3(a0) + + precr_sra.ph.w t1, t0, 0 + precr_sra.ph.w t3, t2, 0 + precr.qb.ph t0, t3, t1 + + muleu_s.ph.qbl t2, t0, t8 + muleu_s.ph.qbr t3, t0, t8 + shra_r.ph t4, t2, 8 + shra_r.ph t5, t3, 8 + and t4, t4, t9 + and t5, t5, t9 + addq.ph t2, t2, t4 + addq.ph t3, t3, t5 + shra_r.ph t2, t2, 8 + shra_r.ph t3, t3, 8 + precr.qb.ph t2, t2, t3 + + sb t2, 0(a0) + srl t2, t2, 8 + sb t2, 1(a0) + srl t2, t2, 8 + sb t2, 2(a0) + srl t2, t2, 8 + sb t2, 3(a0) + addiu a2, a2, -4 + b 0b + addiu a0, a0, 4 + +1: + beqz a2, 3f + nop + srl t8, a1, 24 +2: + lbu t0, 0(a0) + + mul t2, t0, t8 + shra_r.ph t3, t2, 8 + andi t3, t3, 0x00ff + addq.ph t2, t2, t3 + shra_r.ph t2, t2, 8 + + sb t2, 0(a0) + addiu a2, a2, -1 + bnez a2, 2b + addiu a0, a0, 1 + +3: + j ra + nop + +END(pixman_composite_in_n_8_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_8888_8888_OVER_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (a8r8g8b8) + * a2 - w + * a3 - vx + * 16(sp) - unit_x + */ + + SAVE_REGS_ON_STACK 0, s0, s1, s2, s3 + lw t8, 16(sp) /* t8 = unit_x */ + li t6, 0x00ff00ff + beqz a2, 3f + nop + addiu t1, a2, -1 + beqz t1, 2f + nop +1: + sra t0, a3, 16 /* t0 = vx >> 16 */ + sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ + addu t0, a1, t0 + lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ + addu a3, a3, t8 /* a3 = vx + unit_x */ + + sra t1, a3, 16 /* t0 = vx >> 16 */ + sll t1, t1, 2 /* t0 = t0 * 4 (a8r8g8b8) */ + addu t1, a1, t1 + lw t1, 0(t1) /* t1 = source (a8r8g8b8) */ + addu a3, a3, t8 /* a3 = vx + unit_x */ + + lw t2, 0(a0) /* t2 = destination (a8r8g8b8) */ + lw t3, 4(a0) /* t3 = destination (a8r8g8b8) */ + + OVER_2x8888_2x8888 t0, t1, t2, t3, t4, t5, t6, t7, t9, s0, s1, s2, s3 + + sw t4, 0(a0) + sw t5, 4(a0) + addiu a2, a2, -2 + addiu t1, a2, -1 + bgtz t1, 1b + addiu a0, a0, 8 +2: + beqz a2, 3f + nop + sra t0, a3, 16 /* t0 = vx >> 16 */ + sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ + addu t0, a1, t0 + lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ + lw t1, 0(a0) /* t1 = destination (a8r8g8b8) */ + addu a3, a3, t8 /* a3 = vx + unit_x */ + + OVER_8888_8888 t0, t1, t2, t6, t4, t5, t3, t7 + + sw t2, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, s0, s1, s2, s3 + j ra + nop + +END(pixman_scaled_nearest_scanline_8888_8888_OVER_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_8888_0565_OVER_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (a8r8g8b8) + * a2 - w + * a3 - vx + * 16(sp) - unit_x + */ + + SAVE_REGS_ON_STACK 24, s0, s1, s2, s3, s4, v0, v1 + lw t8, 40(sp) /* t8 = unit_x */ + li t4, 0x00ff00ff + li t5, 0xf800f800 + li t6, 0x07e007e0 + li t7, 0x001F001F + beqz a2, 3f + nop + addiu t1, a2, -1 + beqz t1, 2f + nop +1: + sra t0, a3, 16 /* t0 = vx >> 16 */ + sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ + addu t0, a1, t0 + lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ + addu a3, a3, t8 /* a3 = vx + unit_x */ + sra t1, a3, 16 /* t0 = vx >> 16 */ + sll t1, t1, 2 /* t0 = t0 * 4 (a8r8g8b8) */ + addu t1, a1, t1 + lw t1, 0(t1) /* t1 = source (a8r8g8b8) */ + addu a3, a3, t8 /* a3 = vx + unit_x */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + lhu t3, 2(a0) /* t3 = destination (r5g6b5) */ + + CONVERT_2x0565_TO_2x8888 t2, t3, v0, v1, t6, t7, s0, s1, s2, s3 + OVER_2x8888_2x8888 t0, t1, v0, v1, t2, t3, t4, t9, s0, s1, s2, s3, s4 + CONVERT_2x8888_TO_2x0565 t2, t3, v0, v1, t5, t6, t7, t9, s2 + + sh v0, 0(a0) + sh v1, 2(a0) + addiu a2, a2, -2 + addiu t1, a2, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a2, 3f + nop + sra t0, a3, 16 /* t0 = vx >> 16 */ + sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ + addu t0, a1, t0 + lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ + lhu t1, 0(a0) /* t1 = destination (r5g6b5) */ + addu a3, a3, t8 /* a3 = vx + unit_x */ + + CONVERT_1x0565_TO_1x8888 t1, t2, t5, t6 + OVER_8888_8888 t0, t2, t1, t4, t3, t5, t6, t7 + CONVERT_1x8888_TO_1x0565 t1, t2, t5, t6 + + sh t2, 0(a0) +3: + RESTORE_REGS_FROM_STACK 24, s0, s1, s2, s3, s4, v0, v1 + j ra + nop + +END(pixman_scaled_nearest_scanline_8888_0565_OVER_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_0565_8888_SRC_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - src (r5g6b5) + * a2 - w + * a3 - vx + * 16(sp) - unit_x + */ + + SAVE_REGS_ON_STACK 0, v0 + beqz a2, 3f + nop + + lw v0, 16(sp) /* v0 = unit_x */ + addiu t1, a2, -1 + beqz t1, 2f + nop + + li t4, 0x07e007e0 + li t5, 0x001F001F +1: + sra t0, a3, 16 /* t0 = vx >> 16 */ + sll t0, t0, 1 /* t0 = t0 * 2 ((r5g6b5)) */ + addu t0, a1, t0 + lhu t0, 0(t0) /* t0 = source ((r5g6b5)) */ + addu a3, a3, v0 /* a3 = vx + unit_x */ + sra t1, a3, 16 /* t1 = vx >> 16 */ + sll t1, t1, 1 /* t1 = t1 * 2 ((r5g6b5)) */ + addu t1, a1, t1 + lhu t1, 0(t1) /* t1 = source ((r5g6b5)) */ + addu a3, a3, v0 /* a3 = vx + unit_x */ + addiu a2, a2, -2 + + CONVERT_2x0565_TO_2x8888 t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 + + sw t2, 0(a0) + sw t3, 4(a0) + + addiu t2, a2, -1 + bgtz t2, 1b + addiu a0, a0, 8 +2: + beqz a2, 3f + nop + sra t0, a3, 16 /* t0 = vx >> 16 */ + sll t0, t0, 1 /* t0 = t0 * 2 ((r5g6b5)) */ + addu t0, a1, t0 + lhu t0, 0(t0) /* t0 = source ((r5g6b5)) */ + + CONVERT_1x0565_TO_1x8888 t0, t1, t2, t3 + + sw t1, 0(a0) +3: + RESTORE_REGS_FROM_STACK 0, v0 + j ra + nop + +END(pixman_scaled_nearest_scanline_0565_8888_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_8888_8_0565_OVER_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (a8r8g8b8) + * a2 - mask (a8) + * a3 - w + * 16(sp) - vx + * 20(sp) - unit_x + */ + beqz a3, 4f + nop + + SAVE_REGS_ON_STACK 20, v0, v1, s0, s1, s2, s3, s4, s5 + lw v0, 36(sp) /* v0 = vx */ + lw v1, 40(sp) /* v1 = unit_x */ + li t6, 0x00ff00ff + li t7, 0xf800f800 + li t8, 0x07e007e0 + li t9, 0x001F001F + + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + sra t0, v0, 16 /* t0 = vx >> 16 */ + sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ + addu t0, a1, t0 + lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ + addu v0, v0, v1 /* v0 = vx + unit_x */ + sra t1, v0, 16 /* t1 = vx >> 16 */ + sll t1, t1, 2 /* t1 = t1 * 4 (a8r8g8b8) */ + addu t1, a1, t1 + lw t1, 0(t1) /* t1 = source (a8r8g8b8) */ + addu v0, v0, v1 /* v0 = vx + unit_x */ + lbu t2, 0(a2) /* t2 = mask (a8) */ + lbu t3, 1(a2) /* t3 = mask (a8) */ + lhu t4, 0(a0) /* t4 = destination (r5g6b5) */ + lhu t5, 2(a0) /* t5 = destination (r5g6b5) */ + addiu a2, a2, 2 + + CONVERT_2x0565_TO_2x8888 t4, t5, s0, s1, t8, t9, s2, s3, s4, s5 + OVER_2x8888_2x8_2x8888 t0, t1, \ + t2, t3, \ + s0, s1, \ + t4, t5, \ + t6, s2, s3, s4, s5, t2, t3 + CONVERT_2x8888_TO_2x0565 t4, t5, s0, s1, t7, t8, t9, s2, s3 + + sh s0, 0(a0) + sh s1, 2(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a3, 3f + nop + sra t0, v0, 16 /* t0 = vx >> 16 */ + sll t0, t0, 2 /* t0 = t0 * 4 (a8r8g8b8) */ + addu t0, a1, t0 + lw t0, 0(t0) /* t0 = source (a8r8g8b8) */ + lbu t1, 0(a2) /* t1 = mask (a8) */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + + CONVERT_1x0565_TO_1x8888 t2, t3, t4, t5 + OVER_8888_8_8888 t0, t1, t3, t2, t6, t4, t5, t7, t8 + CONVERT_1x8888_TO_1x0565 t2, t3, t4, t5 + + sh t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 20, v0, v1, s0, s1, s2, s3, s4, s5 +4: + j ra + nop + +END(pixman_scaled_nearest_scanline_8888_8_0565_OVER_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_nearest_scanline_0565_8_0565_OVER_asm_mips) +/* + * a0 - dst (r5g6b5) + * a1 - src (r5g6b5) + * a2 - mask (a8) + * a3 - w + * 16(sp) - vx + * 20(sp) - unit_x + */ + + beqz a3, 4f + nop + SAVE_REGS_ON_STACK 20, v0, v1, s0, s1, s2, s3, s4, s5 + lw v0, 36(sp) /* v0 = vx */ + lw v1, 40(sp) /* v1 = unit_x */ + li t4, 0xf800f800 + li t5, 0x07e007e0 + li t6, 0x001F001F + li t7, 0x00ff00ff + + addiu t1, a3, -1 + beqz t1, 2f + nop +1: + sra t0, v0, 16 /* t0 = vx >> 16 */ + sll t0, t0, 1 /* t0 = t0 * 2 (r5g6b5) */ + addu t0, a1, t0 + lhu t0, 0(t0) /* t0 = source (r5g6b5) */ + addu v0, v0, v1 /* v0 = vx + unit_x */ + sra t1, v0, 16 /* t1 = vx >> 16 */ + sll t1, t1, 1 /* t1 = t1 * 2 (r5g6b5) */ + addu t1, a1, t1 + lhu t1, 0(t1) /* t1 = source (r5g6b5) */ + addu v0, v0, v1 /* v0 = vx + unit_x */ + lbu t2, 0(a2) /* t2 = mask (a8) */ + lbu t3, 1(a2) /* t3 = mask (a8) */ + lhu t8, 0(a0) /* t8 = destination (r5g6b5) */ + lhu t9, 2(a0) /* t9 = destination (r5g6b5) */ + addiu a2, a2, 2 + + CONVERT_2x0565_TO_2x8888 t0, t1, s0, s1, t5, t6, s2, s3, s4, s5 + CONVERT_2x0565_TO_2x8888 t8, t9, s2, s3, t5, t6, s4, s5, t0, t1 + OVER_2x8888_2x8_2x8888 s0, s1, \ + t2, t3, \ + s2, s3, \ + t0, t1, \ + t7, t8, t9, s4, s5, s0, s1 + CONVERT_2x8888_TO_2x0565 t0, t1, s0, s1, t4, t5, t6, s2, s3 + + sh s0, 0(a0) + sh s1, 2(a0) + addiu a3, a3, -2 + addiu t1, a3, -1 + bgtz t1, 1b + addiu a0, a0, 4 +2: + beqz a3, 3f + nop + sra t0, v0, 16 /* t0 = vx >> 16 */ + sll t0, t0, 1 /* t0 = t0 * 2 (r5g6b5) */ + addu t0, a1, t0 + + lhu t0, 0(t0) /* t0 = source (r5g6b5) */ + lbu t1, 0(a2) /* t1 = mask (a8) */ + lhu t2, 0(a0) /* t2 = destination (r5g6b5) */ + + CONVERT_1x0565_TO_1x8888 t0, t3, t4, t5 + CONVERT_1x0565_TO_1x8888 t2, t4, t5, t6 + OVER_8888_8_8888 t3, t1, t4, t0, t7, t2, t5, t6, t8 + CONVERT_1x8888_TO_1x0565 t0, t3, t4, t5 + + sh t3, 0(a0) +3: + RESTORE_REGS_FROM_STACK 20, v0, v1, s0, s1, s2, s3, s4, s5 +4: + j ra + nop + +END(pixman_scaled_nearest_scanline_0565_8_0565_OVER_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_mips) +/* + * a0 - *dst + * a1 - *src_top + * a2 - *src_bottom + * a3 - w + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + */ + + beqz a3, 1f + nop + + SAVE_REGS_ON_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 + + lw s0, 36(sp) /* s0 = wt */ + lw s1, 40(sp) /* s1 = wb */ + lw s2, 44(sp) /* s2 = vx */ + lw s3, 48(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 2 + addiu t8, t9, 4 + lwx t0, t9(a1) /* t0 = tl */ + lwx t1, t8(a1) /* t1 = tr */ + addiu a3, a3, -1 + lwx t2, t9(a2) /* t2 = bl */ + lwx t3, t8(a2) /* t3 = br */ + + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + + addu s2, s2, s3 /* vx += unit_x; */ + sw t0, 0(a0) + bnez a3, 0b + addiu a0, a0, 4 + + RESTORE_REGS_FROM_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_0565_SRC_asm_mips) +/* + * a0 - *dst + * a1 - *src_top + * a2 - *src_bottom + * a3 - w + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + */ + + beqz a3, 1f + nop + + SAVE_REGS_ON_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 + + lw s0, 36(sp) /* s0 = wt */ + lw s1, 40(sp) /* s1 = wb */ + lw s2, 44(sp) /* s2 = vx */ + lw s3, 48(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 2 + addiu t8, t9, 4 + lwx t0, t9(a1) /* t0 = tl */ + lwx t1, t8(a1) /* t1 = tr */ + addiu a3, a3, -1 + lwx t2, t9(a2) /* t2 = bl */ + lwx t3, t8(a2) /* t3 = br */ + + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 + + addu s2, s2, s3 /* vx += unit_x; */ + sh t1, 0(a0) + bnez a3, 0b + addiu a0, a0, 2 + + RESTORE_REGS_FROM_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_8888_0565_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_0565_8888_SRC_asm_mips) +/* + * a0 - *dst + * a1 - *src_top + * a2 - *src_bottom + * a3 - w + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + */ + + beqz a3, 1f + nop + + SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 + + lw s0, 44(sp) /* s0 = wt */ + lw s1, 48(sp) /* s1 = wb */ + lw s2, 52(sp) /* s2 = vx */ + lw s3, 56(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + li v1, 0x07e007e0 + li s8, 0x001f001f + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 1 + addiu t8, t9, 2 + lhx t0, t9(a1) /* t0 = tl */ + lhx t1, t8(a1) /* t1 = tr */ + andi t1, t1, 0xffff + addiu a3, a3, -1 + lhx t2, t9(a2) /* t2 = bl */ + lhx t3, t8(a2) /* t3 = br */ + andi t3, t3, 0xffff + + CONVERT_2x0565_TO_2x8888 t0, t1, t0, t1, v1, s8, t4, t5, t6, t7 + CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, v1, s8, t4, t5, t6, t7 + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + + addu s2, s2, s3 /* vx += unit_x; */ + sw t0, 0(a0) + bnez a3, 0b + addiu a0, a0, 4 + + RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_0565_8888_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_0565_0565_SRC_asm_mips) +/* + * a0 - *dst + * a1 - *src_top + * a2 - *src_bottom + * a3 - w + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + */ + + beqz a3, 1f + nop + + SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 + + lw s0, 44(sp) /* s0 = wt */ + lw s1, 48(sp) /* s1 = wb */ + lw s2, 52(sp) /* s2 = vx */ + lw s3, 56(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + li v1, 0x07e007e0 + li s8, 0x001f001f + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 1 + addiu t8, t9, 2 + lhx t0, t9(a1) /* t0 = tl */ + lhx t1, t8(a1) /* t1 = tr */ + andi t1, t1, 0xffff + addiu a3, a3, -1 + lhx t2, t9(a2) /* t2 = bl */ + lhx t3, t8(a2) /* t3 = br */ + andi t3, t3, 0xffff + + CONVERT_2x0565_TO_2x8888 t0, t1, t0, t1, v1, s8, t4, t5, t6, t7 + CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, v1, s8, t4, t5, t6, t7 + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 + + addu s2, s2, s3 /* vx += unit_x; */ + sh t1, 0(a0) + bnez a3, 0b + addiu a0, a0, 2 + + RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_0565_0565_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8888_OVER_asm_mips) +/* + * a0 - *dst + * a1 - *src_top + * a2 - *src_bottom + * a3 - w + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + */ + + beqz a3, 1f + nop + + SAVE_REGS_ON_STACK 24, v0, s0, s1, s2, s3, s4, s5, s6, s7, s8 + + lw s0, 40(sp) /* s0 = wt */ + lw s1, 44(sp) /* s1 = wb */ + lw s2, 48(sp) /* s2 = vx */ + lw s3, 52(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + li s8, 0x00ff00ff + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 2 + addiu t8, t9, 4 + lwx t0, t9(a1) /* t0 = tl */ + lwx t1, t8(a1) /* t1 = tr */ + addiu a3, a3, -1 + lwx t2, t9(a2) /* t2 = bl */ + lwx t3, t8(a2) /* t3 = br */ + + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + lw t1, 0(a0) /* t1 = dest */ + OVER_8888_8888 t0, t1, t2, s8, t3, t4, t5, t6 + + addu s2, s2, s3 /* vx += unit_x; */ + sw t2, 0(a0) + bnez a3, 0b + addiu a0, a0, 4 + + RESTORE_REGS_FROM_STACK 24, v0, s0, s1, s2, s3, s4, s5, s6, s7, s8 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_8888_8888_OVER_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8888_ADD_asm_mips) +/* + * a0 - *dst + * a1 - *src_top + * a2 - *src_bottom + * a3 - w + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + */ + + beqz a3, 1f + nop + + SAVE_REGS_ON_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 + + lw s0, 36(sp) /* s0 = wt */ + lw s1, 40(sp) /* s1 = wb */ + lw s2, 44(sp) /* s2 = vx */ + lw s3, 48(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 2 + addiu t8, t9, 4 + lwx t0, t9(a1) /* t0 = tl */ + lwx t1, t8(a1) /* t1 = tr */ + addiu a3, a3, -1 + lwx t2, t9(a2) /* t2 = bl */ + lwx t3, t8(a2) /* t3 = br */ + + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + lw t1, 0(a0) + addu_s.qb t2, t0, t1 + + addu s2, s2, s3 /* vx += unit_x; */ + sw t2, 0(a0) + bnez a3, 0b + addiu a0, a0, 4 + + RESTORE_REGS_FROM_STACK 20, v0, s0, s1, s2, s3, s4, s5, s6, s7 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_8888_8888_ADD_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8_8888_SRC_asm_mips) +/* + * a0 - *dst + * a1 - *mask + * a2 - *src_top + * a3 - *src_bottom + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + * 32(sp) - w + */ + + lw v1, 32(sp) + beqz v1, 1f + nop + + SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 + + lw s0, 44(sp) /* s0 = wt */ + lw s1, 48(sp) /* s1 = wb */ + lw s2, 52(sp) /* s2 = vx */ + lw s3, 56(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + li s8, 0x00ff00ff + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 2 + addiu t8, t9, 4 + lwx t0, t9(a2) /* t0 = tl */ + lwx t1, t8(a2) /* t1 = tr */ + addiu v1, v1, -1 + lwx t2, t9(a3) /* t2 = bl */ + lwx t3, t8(a3) /* t3 = br */ + + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + lbu t1, 0(a1) /* t1 = mask */ + addiu a1, a1, 1 + MIPS_UN8x4_MUL_UN8 t0, t1, t0, s8, t2, t3, t4 + + addu s2, s2, s3 /* vx += unit_x; */ + sw t0, 0(a0) + bnez v1, 0b + addiu a0, a0, 4 + + RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_8888_8_8888_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8_0565_SRC_asm_mips) +/* + * a0 - *dst + * a1 - *mask + * a2 - *src_top + * a3 - *src_bottom + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + * 32(sp) - w + */ + + lw v1, 32(sp) + beqz v1, 1f + nop + + SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 + + lw s0, 44(sp) /* s0 = wt */ + lw s1, 48(sp) /* s1 = wb */ + lw s2, 52(sp) /* s2 = vx */ + lw s3, 56(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + li s8, 0x00ff00ff + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 2 + addiu t8, t9, 4 + lwx t0, t9(a2) /* t0 = tl */ + lwx t1, t8(a2) /* t1 = tr */ + addiu v1, v1, -1 + lwx t2, t9(a3) /* t2 = bl */ + lwx t3, t8(a3) /* t3 = br */ + + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + lbu t1, 0(a1) /* t1 = mask */ + addiu a1, a1, 1 + MIPS_UN8x4_MUL_UN8 t0, t1, t0, s8, t2, t3, t4 + CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 + + addu s2, s2, s3 /* vx += unit_x; */ + sh t1, 0(a0) + bnez v1, 0b + addiu a0, a0, 2 + + RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_8888_8_0565_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_0565_8_x888_SRC_asm_mips) +/* + * a0 - *dst + * a1 - *mask + * a2 - *src_top + * a3 - *src_bottom + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + * 32(sp) - w + */ + + lw t0, 32(sp) + beqz t0, 1f + nop + + SAVE_REGS_ON_STACK 32, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8, ra + + lw s0, 48(sp) /* s0 = wt */ + lw s1, 52(sp) /* s1 = wb */ + lw s2, 56(sp) /* s2 = vx */ + lw s3, 60(sp) /* s3 = unit_x */ + lw ra, 64(sp) /* ra = w */ + li v0, 0x00ff00ff + li v1, 0x07e007e0 + li s8, 0x001f001f + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + li t5, BILINEAR_INTERPOLATION_RANGE + subu t5, t5, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 1 + addiu t8, t9, 2 + lhx t0, t9(a2) /* t0 = tl */ + lhx t1, t8(a2) /* t1 = tr */ + andi t1, t1, 0xffff + addiu ra, ra, -1 + lhx t2, t9(a3) /* t2 = bl */ + lhx t3, t8(a3) /* t3 = br */ + andi t3, t3, 0xffff + + CONVERT_2x0565_TO_2x8888 t0, t1, t0, t1, v1, s8, t4, t5, t6, t7 + CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, v1, s8, t4, t5, t6, t7 + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + lbu t1, 0(a1) /* t1 = mask */ + addiu a1, a1, 1 + MIPS_UN8x4_MUL_UN8 t0, t1, t0, v0, t2, t3, t4 + + addu s2, s2, s3 /* vx += unit_x; */ + sw t0, 0(a0) + bnez ra, 0b + addiu a0, a0, 4 + + RESTORE_REGS_FROM_STACK 32, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8, ra +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_0565_8_x888_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_0565_8_0565_SRC_asm_mips) +/* + * a0 - *dst + * a1 - *mask + * a2 - *src_top + * a3 - *src_bottom + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + * 32(sp) - w + */ + + lw t0, 32(sp) + beqz t0, 1f + nop + + SAVE_REGS_ON_STACK 32, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8, ra + + lw s0, 48(sp) /* s0 = wt */ + lw s1, 52(sp) /* s1 = wb */ + lw s2, 56(sp) /* s2 = vx */ + lw s3, 60(sp) /* s3 = unit_x */ + lw ra, 64(sp) /* ra = w */ + li v0, 0x00ff00ff + li v1, 0x07e007e0 + li s8, 0x001f001f + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + li t5, BILINEAR_INTERPOLATION_RANGE + subu t5, t5, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 1 + addiu t8, t9, 2 + lhx t0, t9(a2) /* t0 = tl */ + lhx t1, t8(a2) /* t1 = tr */ + andi t1, t1, 0xffff + addiu ra, ra, -1 + lhx t2, t9(a3) /* t2 = bl */ + lhx t3, t8(a3) /* t3 = br */ + andi t3, t3, 0xffff + + CONVERT_2x0565_TO_2x8888 t0, t1, t0, t1, v1, s8, t4, t5, t6, t7 + CONVERT_2x0565_TO_2x8888 t2, t3, t2, t3, v1, s8, t4, t5, t6, t7 + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + lbu t1, 0(a1) /* t1 = mask */ + addiu a1, a1, 1 + MIPS_UN8x4_MUL_UN8 t0, t1, t0, v0, t2, t3, t4 + CONVERT_1x8888_TO_1x0565 t0, t1, t2, t3 + + addu s2, s2, s3 /* vx += unit_x; */ + sh t1, 0(a0) + bnez ra, 0b + addiu a0, a0, 2 + + RESTORE_REGS_FROM_STACK 32, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8, ra +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_0565_8_0565_SRC_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8_8888_OVER_asm_mips) +/* + * a0 - dst (a8r8g8b8) + * a1 - mask (a8) + * a2 - src_top (a8r8g8b8) + * a3 - src_bottom (a8r8g8b8) + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + * 32(sp) - w + */ + + SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 + + lw v1, 60(sp) /* v1 = w(sp + 32 + 28 save regs stack offset)*/ + beqz v1, 1f + nop + + lw s0, 44(sp) /* s0 = wt */ + lw s1, 48(sp) /* s1 = wb */ + lw s2, 52(sp) /* s2 = vx */ + lw s3, 56(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + li s8, 0x00ff00ff + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 2 + addiu t8, t9, 4 + lwx t0, t9(a2) /* t0 = tl */ + lwx t1, t8(a2) /* t1 = tr */ + addiu v1, v1, -1 + lwx t2, t9(a3) /* t2 = bl */ + lwx t3, t8(a3) /* t3 = br */ + + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, \ + t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + lbu t1, 0(a1) /* t1 = mask */ + lw t2, 0(a0) /* t2 = dst */ + addiu a1, a1, 1 + OVER_8888_8_8888 t0, t1, t2, t0, s8, t3, t4, t5, t6 + + addu s2, s2, s3 /* vx += unit_x; */ + sw t0, 0(a0) + bnez v1, 0b + addiu a0, a0, 4 + +1: + RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 + j ra + nop + +END(pixman_scaled_bilinear_scanline_8888_8_8888_OVER_asm_mips) + +LEAF_MIPS_DSPR2(pixman_scaled_bilinear_scanline_8888_8_8888_ADD_asm_mips) +/* + * a0 - *dst + * a1 - *mask + * a2 - *src_top + * a3 - *src_bottom + * 16(sp) - wt + * 20(sp) - wb + * 24(sp) - vx + * 28(sp) - unit_x + * 32(sp) - w + */ + + lw v1, 32(sp) + beqz v1, 1f + nop + + SAVE_REGS_ON_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 + + lw s0, 44(sp) /* s0 = wt */ + lw s1, 48(sp) /* s1 = wb */ + lw s2, 52(sp) /* s2 = vx */ + lw s3, 56(sp) /* s3 = unit_x */ + li v0, BILINEAR_INTERPOLATION_RANGE + li s8, 0x00ff00ff + + sll s0, s0, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) + sll s1, s1, (2 * (8 - BILINEAR_INTERPOLATION_BITS)) +0: + andi t4, s2, 0xffff /* t4 = (short)vx */ + srl t4, t4, (16 - BILINEAR_INTERPOLATION_BITS) /* t4 = vx >> 8 */ + subu t5, v0, t4 /* t5 = ( 256 - (vx>>8)) */ + + mul s4, s0, t5 /* s4 = wt*(256-(vx>>8)) */ + mul s5, s0, t4 /* s5 = wt*(vx>>8) */ + mul s6, s1, t5 /* s6 = wb*(256-(vx>>8)) */ + mul s7, s1, t4 /* s7 = wb*(vx>>8) */ + + sra t9, s2, 16 + sll t9, t9, 2 + addiu t8, t9, 4 + lwx t0, t9(a2) /* t0 = tl */ + lwx t1, t8(a2) /* t1 = tr */ + addiu v1, v1, -1 + lwx t2, t9(a3) /* t2 = bl */ + lwx t3, t8(a3) /* t3 = br */ + + BILINEAR_INTERPOLATE_SINGLE_PIXEL t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, s4, s5, s6, s7 + lbu t1, 0(a1) /* t1 = mask */ + lw t2, 0(a0) /* t2 = dst */ + addiu a1, a1, 1 + MIPS_UN8x4_MUL_UN8_ADD_UN8x4 t0, t1, t2, t0, s8, t3, t4, t5 + + addu s2, s2, s3 /* vx += unit_x; */ + sw t0, 0(a0) + bnez v1, 0b + addiu a0, a0, 4 + + RESTORE_REGS_FROM_STACK 28, v0, v1, s0, s1, s2, s3, s4, s5, s6, s7, s8 +1: + j ra + nop + +END(pixman_scaled_bilinear_scanline_8888_8_8888_ADD_asm_mips) diff --git a/gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.h b/gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.h new file mode 100644 index 0000000000..e238566196 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.h @@ -0,0 +1,711 @@ +/* + * Copyright (c) 2012 + * MIPS Technologies, Inc., California. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Nemanja Lukic (nemanja.lukic@rt-rk.com) + */ + +#ifndef PIXMAN_MIPS_DSPR2_ASM_H +#define PIXMAN_MIPS_DSPR2_ASM_H + +#define zero $0 +#define AT $1 +#define v0 $2 +#define v1 $3 +#define a0 $4 +#define a1 $5 +#define a2 $6 +#define a3 $7 +#define t0 $8 +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 +#define s0 $16 +#define s1 $17 +#define s2 $18 +#define s3 $19 +#define s4 $20 +#define s5 $21 +#define s6 $22 +#define s7 $23 +#define t8 $24 +#define t9 $25 +#define k0 $26 +#define k1 $27 +#define gp $28 +#define sp $29 +#define fp $30 +#define s8 $30 +#define ra $31 + +/* + * LEAF_MIPS32R2 - declare leaf routine for MIPS32r2 + */ +#define LEAF_MIPS32R2(symbol) \ + .globl symbol; \ + .align 2; \ + .hidden symbol; \ + .type symbol, @function; \ + .ent symbol, 0; \ +symbol: .frame sp, 0, ra; \ + .set push; \ + .set arch=mips32r2; \ + .set noreorder; \ + .set noat; + +/* + * LEAF_MIPS32R2 - declare leaf routine for MIPS DSPr2 + */ +#define LEAF_MIPS_DSPR2(symbol) \ +LEAF_MIPS32R2(symbol) \ + .set dspr2; + +/* + * END - mark end of function + */ +#define END(function) \ + .set pop; \ + .end function; \ + .size function,.-function + +/* + * Checks if stack offset is big enough for storing/restoring regs_num + * number of register to/from stack. Stack offset must be greater than + * or equal to the number of bytes needed for storing registers (regs_num*4). + * Since MIPS ABI allows usage of first 16 bytes of stack frame (this is + * preserved for input arguments of the functions, already stored in a0-a3), + * stack size can be further optimized by utilizing this space. + */ +.macro CHECK_STACK_OFFSET regs_num, stack_offset +.if \stack_offset < \regs_num * 4 - 16 +.error "Stack offset too small." +.endif +.endm + +/* + * Saves set of registers on stack. Maximum number of registers that + * can be saved on stack is limitted to 14 (a0-a3, v0-v1 and s0-s7). + * Stack offset is number of bytes that are added to stack pointer (sp) + * before registers are pushed in order to provide enough space on stack + * (offset must be multiple of 4, and must be big enough, as described by + * CHECK_STACK_OFFSET macro). This macro is intended to be used in + * combination with RESTORE_REGS_FROM_STACK macro. Example: + * SAVE_REGS_ON_STACK 4, v0, v1, s0, s1 + * RESTORE_REGS_FROM_STACK 4, v0, v1, s0, s1 + */ +.macro SAVE_REGS_ON_STACK stack_offset = 0, r1, \ + r2 = 0, r3 = 0, r4 = 0, \ + r5 = 0, r6 = 0, r7 = 0, \ + r8 = 0, r9 = 0, r10 = 0, \ + r11 = 0, r12 = 0, r13 = 0, \ + r14 = 0 + .if (\stack_offset < 0) || (\stack_offset - (\stack_offset / 4) * 4) + .error "Stack offset must be pozitive and multiple of 4." + .endif + .if \stack_offset != 0 + addiu sp, sp, -\stack_offset + .endif + sw \r1, 0(sp) + .if \r2 != 0 + sw \r2, 4(sp) + .endif + .if \r3 != 0 + sw \r3, 8(sp) + .endif + .if \r4 != 0 + sw \r4, 12(sp) + .endif + .if \r5 != 0 + CHECK_STACK_OFFSET 5, \stack_offset + sw \r5, 16(sp) + .endif + .if \r6 != 0 + CHECK_STACK_OFFSET 6, \stack_offset + sw \r6, 20(sp) + .endif + .if \r7 != 0 + CHECK_STACK_OFFSET 7, \stack_offset + sw \r7, 24(sp) + .endif + .if \r8 != 0 + CHECK_STACK_OFFSET 8, \stack_offset + sw \r8, 28(sp) + .endif + .if \r9 != 0 + CHECK_STACK_OFFSET 9, \stack_offset + sw \r9, 32(sp) + .endif + .if \r10 != 0 + CHECK_STACK_OFFSET 10, \stack_offset + sw \r10, 36(sp) + .endif + .if \r11 != 0 + CHECK_STACK_OFFSET 11, \stack_offset + sw \r11, 40(sp) + .endif + .if \r12 != 0 + CHECK_STACK_OFFSET 12, \stack_offset + sw \r12, 44(sp) + .endif + .if \r13 != 0 + CHECK_STACK_OFFSET 13, \stack_offset + sw \r13, 48(sp) + .endif + .if \r14 != 0 + CHECK_STACK_OFFSET 14, \stack_offset + sw \r14, 52(sp) + .endif +.endm + +/* + * Restores set of registers from stack. Maximum number of registers that + * can be restored from stack is limitted to 14 (a0-a3, v0-v1 and s0-s7). + * Stack offset is number of bytes that are added to stack pointer (sp) + * after registers are restored (offset must be multiple of 4, and must + * be big enough, as described by CHECK_STACK_OFFSET macro). This macro is + * intended to be used in combination with RESTORE_REGS_FROM_STACK macro. + * Example: + * SAVE_REGS_ON_STACK 4, v0, v1, s0, s1 + * RESTORE_REGS_FROM_STACK 4, v0, v1, s0, s1 + */ +.macro RESTORE_REGS_FROM_STACK stack_offset = 0, r1, \ + r2 = 0, r3 = 0, r4 = 0, \ + r5 = 0, r6 = 0, r7 = 0, \ + r8 = 0, r9 = 0, r10 = 0, \ + r11 = 0, r12 = 0, r13 = 0, \ + r14 = 0 + .if (\stack_offset < 0) || (\stack_offset - (\stack_offset/4)*4) + .error "Stack offset must be pozitive and multiple of 4." + .endif + lw \r1, 0(sp) + .if \r2 != 0 + lw \r2, 4(sp) + .endif + .if \r3 != 0 + lw \r3, 8(sp) + .endif + .if \r4 != 0 + lw \r4, 12(sp) + .endif + .if \r5 != 0 + CHECK_STACK_OFFSET 5, \stack_offset + lw \r5, 16(sp) + .endif + .if \r6 != 0 + CHECK_STACK_OFFSET 6, \stack_offset + lw \r6, 20(sp) + .endif + .if \r7 != 0 + CHECK_STACK_OFFSET 7, \stack_offset + lw \r7, 24(sp) + .endif + .if \r8 != 0 + CHECK_STACK_OFFSET 8, \stack_offset + lw \r8, 28(sp) + .endif + .if \r9 != 0 + CHECK_STACK_OFFSET 9, \stack_offset + lw \r9, 32(sp) + .endif + .if \r10 != 0 + CHECK_STACK_OFFSET 10, \stack_offset + lw \r10, 36(sp) + .endif + .if \r11 != 0 + CHECK_STACK_OFFSET 11, \stack_offset + lw \r11, 40(sp) + .endif + .if \r12 != 0 + CHECK_STACK_OFFSET 12, \stack_offset + lw \r12, 44(sp) + .endif + .if \r13 != 0 + CHECK_STACK_OFFSET 13, \stack_offset + lw \r13, 48(sp) + .endif + .if \r14 != 0 + CHECK_STACK_OFFSET 14, \stack_offset + lw \r14, 52(sp) + .endif + .if \stack_offset != 0 + addiu sp, sp, \stack_offset + .endif +.endm + +/* + * Conversion of single r5g6b5 pixel (in_565) to single a8r8g8b8 pixel + * returned in (out_8888) register. Requires two temporary registers + * (scratch1 and scratch2). + */ +.macro CONVERT_1x0565_TO_1x8888 in_565, \ + out_8888, \ + scratch1, scratch2 + lui \out_8888, 0xff00 + sll \scratch1, \in_565, 0x3 + andi \scratch2, \scratch1, 0xff + ext \scratch1, \in_565, 0x2, 0x3 + or \scratch1, \scratch2, \scratch1 + or \out_8888, \out_8888, \scratch1 + + sll \scratch1, \in_565, 0x5 + andi \scratch1, \scratch1, 0xfc00 + srl \scratch2, \in_565, 0x1 + andi \scratch2, \scratch2, 0x300 + or \scratch2, \scratch1, \scratch2 + or \out_8888, \out_8888, \scratch2 + + andi \scratch1, \in_565, 0xf800 + srl \scratch2, \scratch1, 0x5 + andi \scratch2, \scratch2, 0xff00 + or \scratch1, \scratch1, \scratch2 + sll \scratch1, \scratch1, 0x8 + or \out_8888, \out_8888, \scratch1 +.endm + +/* + * Conversion of two r5g6b5 pixels (in1_565 and in2_565) to two a8r8g8b8 pixels + * returned in (out1_8888 and out2_8888) registers. Requires four scratch + * registers (scratch1 ... scratch4). It also requires maskG and maskB for + * color component extractions. These masks must have following values: + * li maskG, 0x07e007e0 + * li maskB, 0x001F001F + */ +.macro CONVERT_2x0565_TO_2x8888 in1_565, in2_565, \ + out1_8888, out2_8888, \ + maskG, maskB, \ + scratch1, scratch2, scratch3, scratch4 + sll \scratch1, \in1_565, 16 + or \scratch1, \scratch1, \in2_565 + lui \out2_8888, 0xff00 + ori \out2_8888, \out2_8888, 0xff00 + shrl.ph \scratch2, \scratch1, 11 + and \scratch3, \scratch1, \maskG + shra.ph \scratch4, \scratch2, 2 + shll.ph \scratch2, \scratch2, 3 + shll.ph \scratch3, \scratch3, 5 + or \scratch2, \scratch2, \scratch4 + shrl.qb \scratch4, \scratch3, 6 + or \out2_8888, \out2_8888, \scratch2 + or \scratch3, \scratch3, \scratch4 + and \scratch1, \scratch1, \maskB + shll.ph \scratch2, \scratch1, 3 + shra.ph \scratch4, \scratch1, 2 + or \scratch2, \scratch2, \scratch4 + or \scratch3, \scratch2, \scratch3 + precrq.ph.w \out1_8888, \out2_8888, \scratch3 + precr_sra.ph.w \out2_8888, \scratch3, 0 +.endm + +/* + * Conversion of single a8r8g8b8 pixel (in_8888) to single r5g6b5 pixel + * returned in (out_565) register. Requires two temporary registers + * (scratch1 and scratch2). + */ +.macro CONVERT_1x8888_TO_1x0565 in_8888, \ + out_565, \ + scratch1, scratch2 + ext \out_565, \in_8888, 0x3, 0x5 + srl \scratch1, \in_8888, 0x5 + andi \scratch1, \scratch1, 0x07e0 + srl \scratch2, \in_8888, 0x8 + andi \scratch2, \scratch2, 0xf800 + or \out_565, \out_565, \scratch1 + or \out_565, \out_565, \scratch2 +.endm + +/* + * Conversion of two a8r8g8b8 pixels (in1_8888 and in2_8888) to two r5g6b5 + * pixels returned in (out1_565 and out2_565) registers. Requires two temporary + * registers (scratch1 and scratch2). It also requires maskR, maskG and maskB + * for color component extractions. These masks must have following values: + * li maskR, 0xf800f800 + * li maskG, 0x07e007e0 + * li maskB, 0x001F001F + * Value of input register in2_8888 is lost. + */ +.macro CONVERT_2x8888_TO_2x0565 in1_8888, in2_8888, \ + out1_565, out2_565, \ + maskR, maskG, maskB, \ + scratch1, scratch2 + precr.qb.ph \scratch1, \in2_8888, \in1_8888 + precrq.qb.ph \in2_8888, \in2_8888, \in1_8888 + and \out1_565, \scratch1, \maskR + shrl.ph \scratch1, \scratch1, 3 + shll.ph \in2_8888, \in2_8888, 3 + and \scratch1, \scratch1, \maskB + or \out1_565, \out1_565, \scratch1 + and \in2_8888, \in2_8888, \maskG + or \out1_565, \out1_565, \in2_8888 + srl \out2_565, \out1_565, 16 +.endm + +/* + * Multiply pixel (a8) with single pixel (a8r8g8b8). It requires maskLSR needed + * for rounding process. maskLSR must have following value: + * li maskLSR, 0x00ff00ff + */ +.macro MIPS_UN8x4_MUL_UN8 s_8888, \ + m_8, \ + d_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3 + replv.ph \m_8, \m_8 /* 0 | M | 0 | M */ + muleu_s.ph.qbl \scratch1, \s_8888, \m_8 /* A*M | R*M */ + muleu_s.ph.qbr \scratch2, \s_8888, \m_8 /* G*M | B*M */ + shra_r.ph \scratch3, \scratch1, 8 + shra_r.ph \d_8888, \scratch2, 8 + and \scratch3, \scratch3, \maskLSR /* 0 |A*M| 0 |R*M */ + and \d_8888, \d_8888, \maskLSR /* 0 |G*M| 0 |B*M */ + addq.ph \scratch1, \scratch1, \scratch3 /* A*M+A*M | R*M+R*M */ + addq.ph \scratch2, \scratch2, \d_8888 /* G*M+G*M | B*M+B*M */ + shra_r.ph \scratch1, \scratch1, 8 + shra_r.ph \scratch2, \scratch2, 8 + precr.qb.ph \d_8888, \scratch1, \scratch2 +.endm + +/* + * Multiply two pixels (a8) with two pixels (a8r8g8b8). It requires maskLSR + * needed for rounding process. maskLSR must have following value: + * li maskLSR, 0x00ff00ff + */ +.macro MIPS_2xUN8x4_MUL_2xUN8 s1_8888, \ + s2_8888, \ + m1_8, \ + m2_8, \ + d1_8888, \ + d2_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3, \ + scratch4, scratch5, scratch6 + replv.ph \m1_8, \m1_8 /* 0 | M1 | 0 | M1 */ + replv.ph \m2_8, \m2_8 /* 0 | M2 | 0 | M2 */ + muleu_s.ph.qbl \scratch1, \s1_8888, \m1_8 /* A1*M1 | R1*M1 */ + muleu_s.ph.qbr \scratch2, \s1_8888, \m1_8 /* G1*M1 | B1*M1 */ + muleu_s.ph.qbl \scratch3, \s2_8888, \m2_8 /* A2*M2 | R2*M2 */ + muleu_s.ph.qbr \scratch4, \s2_8888, \m2_8 /* G2*M2 | B2*M2 */ + shra_r.ph \scratch5, \scratch1, 8 + shra_r.ph \d1_8888, \scratch2, 8 + shra_r.ph \scratch6, \scratch3, 8 + shra_r.ph \d2_8888, \scratch4, 8 + and \scratch5, \scratch5, \maskLSR /* 0 |A1*M1| 0 |R1*M1 */ + and \d1_8888, \d1_8888, \maskLSR /* 0 |G1*M1| 0 |B1*M1 */ + and \scratch6, \scratch6, \maskLSR /* 0 |A2*M2| 0 |R2*M2 */ + and \d2_8888, \d2_8888, \maskLSR /* 0 |G2*M2| 0 |B2*M2 */ + addq.ph \scratch1, \scratch1, \scratch5 + addq.ph \scratch2, \scratch2, \d1_8888 + addq.ph \scratch3, \scratch3, \scratch6 + addq.ph \scratch4, \scratch4, \d2_8888 + shra_r.ph \scratch1, \scratch1, 8 + shra_r.ph \scratch2, \scratch2, 8 + shra_r.ph \scratch3, \scratch3, 8 + shra_r.ph \scratch4, \scratch4, 8 + precr.qb.ph \d1_8888, \scratch1, \scratch2 + precr.qb.ph \d2_8888, \scratch3, \scratch4 +.endm + +/* + * Multiply pixel (a8r8g8b8) with single pixel (a8r8g8b8). It requires maskLSR + * needed for rounding process. maskLSR must have following value: + * li maskLSR, 0x00ff00ff + */ +.macro MIPS_UN8x4_MUL_UN8x4 s_8888, \ + m_8888, \ + d_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3, scratch4 + preceu.ph.qbl \scratch1, \m_8888 /* 0 | A | 0 | R */ + preceu.ph.qbr \scratch2, \m_8888 /* 0 | G | 0 | B */ + muleu_s.ph.qbl \scratch3, \s_8888, \scratch1 /* A*A | R*R */ + muleu_s.ph.qbr \scratch4, \s_8888, \scratch2 /* G*G | B*B */ + shra_r.ph \scratch1, \scratch3, 8 + shra_r.ph \scratch2, \scratch4, 8 + and \scratch1, \scratch1, \maskLSR /* 0 |A*A| 0 |R*R */ + and \scratch2, \scratch2, \maskLSR /* 0 |G*G| 0 |B*B */ + addq.ph \scratch1, \scratch1, \scratch3 + addq.ph \scratch2, \scratch2, \scratch4 + shra_r.ph \scratch1, \scratch1, 8 + shra_r.ph \scratch2, \scratch2, 8 + precr.qb.ph \d_8888, \scratch1, \scratch2 +.endm + +/* + * Multiply two pixels (a8r8g8b8) with two pixels (a8r8g8b8). It requires + * maskLSR needed for rounding process. maskLSR must have following value: + * li maskLSR, 0x00ff00ff + */ + +.macro MIPS_2xUN8x4_MUL_2xUN8x4 s1_8888, \ + s2_8888, \ + m1_8888, \ + m2_8888, \ + d1_8888, \ + d2_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3, \ + scratch4, scratch5, scratch6 + preceu.ph.qbl \scratch1, \m1_8888 /* 0 | A | 0 | R */ + preceu.ph.qbr \scratch2, \m1_8888 /* 0 | G | 0 | B */ + preceu.ph.qbl \scratch3, \m2_8888 /* 0 | A | 0 | R */ + preceu.ph.qbr \scratch4, \m2_8888 /* 0 | G | 0 | B */ + muleu_s.ph.qbl \scratch5, \s1_8888, \scratch1 /* A*A | R*R */ + muleu_s.ph.qbr \scratch6, \s1_8888, \scratch2 /* G*G | B*B */ + muleu_s.ph.qbl \scratch1, \s2_8888, \scratch3 /* A*A | R*R */ + muleu_s.ph.qbr \scratch2, \s2_8888, \scratch4 /* G*G | B*B */ + shra_r.ph \scratch3, \scratch5, 8 + shra_r.ph \scratch4, \scratch6, 8 + shra_r.ph \d1_8888, \scratch1, 8 + shra_r.ph \d2_8888, \scratch2, 8 + and \scratch3, \scratch3, \maskLSR /* 0 |A*A| 0 |R*R */ + and \scratch4, \scratch4, \maskLSR /* 0 |G*G| 0 |B*B */ + and \d1_8888, \d1_8888, \maskLSR /* 0 |A*A| 0 |R*R */ + and \d2_8888, \d2_8888, \maskLSR /* 0 |G*G| 0 |B*B */ + addq.ph \scratch3, \scratch3, \scratch5 + addq.ph \scratch4, \scratch4, \scratch6 + addq.ph \d1_8888, \d1_8888, \scratch1 + addq.ph \d2_8888, \d2_8888, \scratch2 + shra_r.ph \scratch3, \scratch3, 8 + shra_r.ph \scratch4, \scratch4, 8 + shra_r.ph \scratch5, \d1_8888, 8 + shra_r.ph \scratch6, \d2_8888, 8 + precr.qb.ph \d1_8888, \scratch3, \scratch4 + precr.qb.ph \d2_8888, \scratch5, \scratch6 +.endm + +/* + * OVER operation on single a8r8g8b8 source pixel (s_8888) and single a8r8g8b8 + * destination pixel (d_8888) using a8 mask (m_8). It also requires maskLSR + * needed for rounding process. maskLSR must have following value: + * li maskLSR, 0x00ff00ff + */ +.macro OVER_8888_8_8888 s_8888, \ + m_8, \ + d_8888, \ + out_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3, scratch4 + MIPS_UN8x4_MUL_UN8 \s_8888, \m_8, \ + \scratch1, \maskLSR, \ + \scratch2, \scratch3, \scratch4 + + not \scratch2, \scratch1 + srl \scratch2, \scratch2, 24 + + MIPS_UN8x4_MUL_UN8 \d_8888, \scratch2, \ + \d_8888, \maskLSR, \ + \scratch3, \scratch4, \out_8888 + + addu_s.qb \out_8888, \d_8888, \scratch1 +.endm + +/* + * OVER operation on two a8r8g8b8 source pixels (s1_8888 and s2_8888) and two + * a8r8g8b8 destination pixels (d1_8888 and d2_8888) using a8 masks (m1_8 and + * m2_8). It also requires maskLSR needed for rounding process. maskLSR must + * have following value: + * li maskLSR, 0x00ff00ff + */ +.macro OVER_2x8888_2x8_2x8888 s1_8888, \ + s2_8888, \ + m1_8, \ + m2_8, \ + d1_8888, \ + d2_8888, \ + out1_8888, \ + out2_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3, \ + scratch4, scratch5, scratch6 + MIPS_2xUN8x4_MUL_2xUN8 \s1_8888, \s2_8888, \ + \m1_8, \m2_8, \ + \scratch1, \scratch2, \ + \maskLSR, \ + \scratch3, \scratch4, \out1_8888, \ + \out2_8888, \scratch5, \scratch6 + + not \scratch3, \scratch1 + srl \scratch3, \scratch3, 24 + not \scratch4, \scratch2 + srl \scratch4, \scratch4, 24 + + MIPS_2xUN8x4_MUL_2xUN8 \d1_8888, \d2_8888, \ + \scratch3, \scratch4, \ + \d1_8888, \d2_8888, \ + \maskLSR, \ + \scratch5, \scratch6, \out1_8888, \ + \out2_8888, \scratch3, \scratch4 + + addu_s.qb \out1_8888, \d1_8888, \scratch1 + addu_s.qb \out2_8888, \d2_8888, \scratch2 +.endm + +/* + * OVER operation on single a8r8g8b8 source pixel (s_8888) and single a8r8g8b8 + * destination pixel (d_8888). It also requires maskLSR needed for rounding + * process. maskLSR must have following value: + * li maskLSR, 0x00ff00ff + */ +.macro OVER_8888_8888 s_8888, \ + d_8888, \ + out_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3, scratch4 + not \scratch1, \s_8888 + srl \scratch1, \scratch1, 24 + + MIPS_UN8x4_MUL_UN8 \d_8888, \scratch1, \ + \out_8888, \maskLSR, \ + \scratch2, \scratch3, \scratch4 + + addu_s.qb \out_8888, \out_8888, \s_8888 +.endm + +/* + * OVER operation on two a8r8g8b8 source pixels (s1_8888 and s2_8888) and two + * a8r8g8b8 destination pixels (d1_8888 and d2_8888). It also requires maskLSR + * needed for rounding process. maskLSR must have following value: + * li maskLSR, 0x00ff00ff + */ +.macro OVER_2x8888_2x8888 s1_8888, \ + s2_8888, \ + d1_8888, \ + d2_8888, \ + out1_8888, \ + out2_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3, \ + scratch4, scratch5, scratch6 + not \scratch1, \s1_8888 + srl \scratch1, \scratch1, 24 + not \scratch2, \s2_8888 + srl \scratch2, \scratch2, 24 + MIPS_2xUN8x4_MUL_2xUN8 \d1_8888, \d2_8888, \ + \scratch1, \scratch2, \ + \out1_8888, \out2_8888, \ + \maskLSR, \ + \scratch3, \scratch4, \scratch5, \ + \scratch6, \d1_8888, \d2_8888 + + addu_s.qb \out1_8888, \out1_8888, \s1_8888 + addu_s.qb \out2_8888, \out2_8888, \s2_8888 +.endm + +.macro MIPS_UN8x4_MUL_UN8_ADD_UN8x4 s_8888, \ + m_8, \ + d_8888, \ + out_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3 + MIPS_UN8x4_MUL_UN8 \s_8888, \m_8, \ + \out_8888, \maskLSR, \ + \scratch1, \scratch2, \scratch3 + + addu_s.qb \out_8888, \out_8888, \d_8888 +.endm + +.macro MIPS_2xUN8x4_MUL_2xUN8_ADD_2xUN8x4 s1_8888, \ + s2_8888, \ + m1_8, \ + m2_8, \ + d1_8888, \ + d2_8888, \ + out1_8888, \ + out2_8888, \ + maskLSR, \ + scratch1, scratch2, scratch3, \ + scratch4, scratch5, scratch6 + MIPS_2xUN8x4_MUL_2xUN8 \s1_8888, \s2_8888, \ + \m1_8, \m2_8, \ + \out1_8888, \out2_8888, \ + \maskLSR, \ + \scratch1, \scratch2, \scratch3, \ + \scratch4, \scratch5, \scratch6 + + addu_s.qb \out1_8888, \out1_8888, \d1_8888 + addu_s.qb \out2_8888, \out2_8888, \d2_8888 +.endm + +.macro BILINEAR_INTERPOLATE_SINGLE_PIXEL tl, tr, bl, br, \ + scratch1, scratch2, \ + alpha, red, green, blue \ + wt1, wt2, wb1, wb2 + andi \scratch1, \tl, 0xff + andi \scratch2, \tr, 0xff + andi \alpha, \bl, 0xff + andi \red, \br, 0xff + + multu $ac0, \wt1, \scratch1 + maddu $ac0, \wt2, \scratch2 + maddu $ac0, \wb1, \alpha + maddu $ac0, \wb2, \red + + ext \scratch1, \tl, 8, 8 + ext \scratch2, \tr, 8, 8 + ext \alpha, \bl, 8, 8 + ext \red, \br, 8, 8 + + multu $ac1, \wt1, \scratch1 + maddu $ac1, \wt2, \scratch2 + maddu $ac1, \wb1, \alpha + maddu $ac1, \wb2, \red + + ext \scratch1, \tl, 16, 8 + ext \scratch2, \tr, 16, 8 + ext \alpha, \bl, 16, 8 + ext \red, \br, 16, 8 + + mflo \blue, $ac0 + + multu $ac2, \wt1, \scratch1 + maddu $ac2, \wt2, \scratch2 + maddu $ac2, \wb1, \alpha + maddu $ac2, \wb2, \red + + ext \scratch1, \tl, 24, 8 + ext \scratch2, \tr, 24, 8 + ext \alpha, \bl, 24, 8 + ext \red, \br, 24, 8 + + mflo \green, $ac1 + + multu $ac3, \wt1, \scratch1 + maddu $ac3, \wt2, \scratch2 + maddu $ac3, \wb1, \alpha + maddu $ac3, \wb2, \red + + mflo \red, $ac2 + mflo \alpha, $ac3 + + precr.qb.ph \alpha, \alpha, \red + precr.qb.ph \scratch1, \green, \blue + precrq.qb.ph \tl, \alpha, \scratch1 +.endm + +#endif //PIXMAN_MIPS_DSPR2_ASM_H diff --git a/gfx/cairo/libpixman/src/pixman-mips-dspr2.c b/gfx/cairo/libpixman/src/pixman-mips-dspr2.c new file mode 100644 index 0000000000..87969ae704 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-mips-dspr2.c @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2012 + * MIPS Technologies, Inc., California. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Nemanja Lukic (nemanja.lukic@rt-rk.com) + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pixman-private.h" +#include "pixman-mips-dspr2.h" + +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_x888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_8888_0565, + uint32_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_0565_8888, + uint16_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (DO_FAST_MEMCPY, src_0565_0565, + uint16_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (DO_FAST_MEMCPY, src_8888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (DO_FAST_MEMCPY, src_0888_0888, + uint8_t, 3, uint8_t, 3) +#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_0888_8888_rev, + uint8_t, 3, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_0888_0565_rev, + uint8_t, 3, uint16_t, 1) +#endif +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_pixbuf_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, src_rpixbuf_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, over_8888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, over_8888_0565, + uint32_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, add_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, add_8888_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, out_reverse_8_0565, + uint8_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST (0, out_reverse_8_8888, + uint8_t, 1, uint32_t, 1) + +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (0, src_n_8_8888, + uint8_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (0, src_n_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8888_8888_ca, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8888_0565_ca, + uint32_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8_8888, + uint8_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, over_n_8_0565, + uint8_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, add_n_8_8, + uint8_t, 1, uint8_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST (SKIP_ZERO_SRC, add_n_8_8888, + uint8_t, 1, uint32_t, 1) + +PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, over_8888_n_8888, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, over_8888_n_0565, + uint32_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, over_0565_n_0565, + uint16_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST (SKIP_ZERO_MASK, add_8888_n_8888, + uint32_t, 1, uint32_t, 1) + +PIXMAN_MIPS_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, over_n_0565, + uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, over_n_8888, + uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_DST (SKIP_ZERO_SRC, over_reverse_n_8888, + uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_N_DST (0, in_n_8, + uint8_t, 1) + +PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (add_8_8_8, uint8_t, 1, + uint8_t, 1, uint8_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (add_8888_8_8888, uint32_t, 1, + uint8_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (add_8888_8888_8888, uint32_t, 1, + uint32_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (add_0565_8_0565, uint16_t, 1, + uint8_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (over_8888_8_8888, uint32_t, 1, + uint8_t, 1, uint32_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (over_8888_8_0565, uint32_t, 1, + uint8_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (over_0565_8_0565, uint16_t, 1, + uint8_t, 1, uint16_t, 1) +PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST (over_8888_8888_8888, uint32_t, 1, + uint32_t, 1, uint32_t, 1) + +PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_DST (8888_8888, OVER, + uint32_t, uint32_t) +PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_DST (8888_0565, OVER, + uint32_t, uint16_t) +PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_DST (0565_8888, SRC, + uint16_t, uint32_t) + +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (0, 8888_8888, SRC, + uint32_t, uint32_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (0, 8888_0565, SRC, + uint32_t, uint16_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (0, 0565_8888, SRC, + uint16_t, uint32_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (0, 0565_0565, SRC, + uint16_t, uint16_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (SKIP_ZERO_SRC, 8888_8888, OVER, + uint32_t, uint32_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST (SKIP_ZERO_SRC, 8888_8888, ADD, + uint32_t, uint32_t) + +PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_A8_DST (SKIP_ZERO_SRC, 8888_8_0565, + OVER, uint32_t, uint16_t) +PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_A8_DST (SKIP_ZERO_SRC, 0565_8_0565, + OVER, uint16_t, uint16_t) + +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (0, 8888_8_8888, SRC, + uint32_t, uint32_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (0, 8888_8_0565, SRC, + uint32_t, uint16_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (0, 0565_8_x888, SRC, + uint16_t, uint32_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (0, 0565_8_0565, SRC, + uint16_t, uint16_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (SKIP_ZERO_SRC, 8888_8_8888, OVER, + uint32_t, uint32_t) +PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST (SKIP_ZERO_SRC, 8888_8_8888, ADD, + uint32_t, uint32_t) + +static pixman_bool_t +mips_dspr2_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t _xor) +{ + uint8_t *byte_line; + uint32_t byte_width; + switch (bpp) + { + case 16: + stride = stride * (int) sizeof (uint32_t) / 2; + byte_line = (uint8_t *)(((uint16_t *)bits) + stride * y + x); + byte_width = width * 2; + stride *= 2; + + while (height--) + { + uint8_t *dst = byte_line; + byte_line += stride; + pixman_fill_buff16_mips (dst, byte_width, _xor & 0xffff); + } + return TRUE; + case 32: + stride = stride * (int) sizeof (uint32_t) / 4; + byte_line = (uint8_t *)(((uint32_t *)bits) + stride * y + x); + byte_width = width * 4; + stride *= 4; + + while (height--) + { + uint8_t *dst = byte_line; + byte_line += stride; + pixman_fill_buff32_mips (dst, byte_width, _xor); + } + return TRUE; + default: + return FALSE; + } +} + +static pixman_bool_t +mips_dspr2_blt (pixman_implementation_t *imp, + uint32_t * src_bits, + uint32_t * dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height) +{ + if (src_bpp != dst_bpp) + return FALSE; + + uint8_t *src_bytes; + uint8_t *dst_bytes; + uint32_t byte_width; + + switch (src_bpp) + { + case 16: + src_stride = src_stride * (int) sizeof (uint32_t) / 2; + dst_stride = dst_stride * (int) sizeof (uint32_t) / 2; + src_bytes =(uint8_t *)(((uint16_t *)src_bits) + + src_stride * (src_y) + (src_x)); + dst_bytes = (uint8_t *)(((uint16_t *)dst_bits) + + dst_stride * (dest_y) + (dest_x)); + byte_width = width * 2; + src_stride *= 2; + dst_stride *= 2; + + while (height--) + { + uint8_t *src = src_bytes; + uint8_t *dst = dst_bytes; + src_bytes += src_stride; + dst_bytes += dst_stride; + pixman_mips_fast_memcpy (dst, src, byte_width); + } + return TRUE; + case 32: + src_stride = src_stride * (int) sizeof (uint32_t) / 4; + dst_stride = dst_stride * (int) sizeof (uint32_t) / 4; + src_bytes = (uint8_t *)(((uint32_t *)src_bits) + + src_stride * (src_y) + (src_x)); + dst_bytes = (uint8_t *)(((uint32_t *)dst_bits) + + dst_stride * (dest_y) + (dest_x)); + byte_width = width * 4; + src_stride *= 4; + dst_stride *= 4; + + while (height--) + { + uint8_t *src = src_bytes; + uint8_t *dst = dst_bytes; + src_bytes += src_stride; + dst_bytes += dst_stride; + pixman_mips_fast_memcpy (dst, src, byte_width); + } + return TRUE; + default: + return FALSE; + } +} + +static const pixman_fast_path_t mips_dspr2_fast_paths[] = +{ + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, mips_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, mips_composite_src_0565_0565), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, mips_composite_src_8888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, mips_composite_src_8888_0565), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, mips_composite_src_8888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, mips_composite_src_8888_0565), + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, a8r8g8b8, mips_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, x8r8g8b8, mips_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, a8b8g8r8, mips_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, x8b8g8r8, mips_composite_src_0565_8888), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, mips_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, mips_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, mips_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, mips_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, mips_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, mips_composite_src_8888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, mips_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, mips_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, r8g8b8, null, r8g8b8, mips_composite_src_0888_0888), +#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, x8r8g8b8, mips_composite_src_0888_8888_rev), + PIXMAN_STD_FAST_PATH (SRC, b8g8r8, null, r5g6b5, mips_composite_src_0888_0565_rev), +#endif + PIXMAN_STD_FAST_PATH (SRC, pixbuf, pixbuf, a8r8g8b8, mips_composite_src_pixbuf_8888), + PIXMAN_STD_FAST_PATH (SRC, pixbuf, pixbuf, a8b8g8r8, mips_composite_src_rpixbuf_8888), + PIXMAN_STD_FAST_PATH (SRC, rpixbuf, rpixbuf, a8r8g8b8, mips_composite_src_rpixbuf_8888), + PIXMAN_STD_FAST_PATH (SRC, rpixbuf, rpixbuf, a8b8g8r8, mips_composite_src_pixbuf_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8r8g8b8, mips_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, mips_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, mips_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, mips_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8, mips_composite_src_n_8_8), + + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, mips_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, mips_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, mips_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, mips_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, mips_composite_over_n_8888_0565_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, mips_composite_over_n_8888_0565_ca), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8, mips_composite_over_n_8_8), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, mips_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, mips_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, mips_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, mips_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, mips_composite_over_n_8_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, mips_composite_over_n_8_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, null, r5g6b5, mips_composite_over_n_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, mips_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, mips_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, mips_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, mips_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, r5g6b5, mips_composite_over_8888_n_0565), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, b5g6r5, mips_composite_over_8888_n_0565), + PIXMAN_STD_FAST_PATH (OVER, r5g6b5, solid, r5g6b5, mips_composite_over_0565_n_0565), + PIXMAN_STD_FAST_PATH (OVER, b5g6r5, solid, b5g6r5, mips_composite_over_0565_n_0565), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, a8r8g8b8, mips_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, x8r8g8b8, mips_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, a8b8g8r8, mips_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, x8b8g8r8, mips_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, r5g6b5, mips_composite_over_8888_8_0565), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, b5g6r5, mips_composite_over_8888_8_0565), + PIXMAN_STD_FAST_PATH (OVER, r5g6b5, a8, r5g6b5, mips_composite_over_0565_8_0565), + PIXMAN_STD_FAST_PATH (OVER, b5g6r5, a8, b5g6r5, mips_composite_over_0565_8_0565), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, a8r8g8b8, mips_composite_over_8888_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, mips_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, mips_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, mips_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, mips_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, mips_composite_over_8888_0565), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, mips_composite_over_8888_0565), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, mips_composite_add_n_8_8), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8r8g8b8, mips_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8b8g8r8, mips_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, a8, a8, a8, mips_composite_add_8_8_8), + PIXMAN_STD_FAST_PATH (ADD, r5g6b5, a8, r5g6b5, mips_composite_add_0565_8_0565), + PIXMAN_STD_FAST_PATH (ADD, b5g6r5, a8, b5g6r5, mips_composite_add_0565_8_0565), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8, a8r8g8b8, mips_composite_add_8888_8_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, a8, a8b8g8r8, mips_composite_add_8888_8_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, a8r8g8b8, mips_composite_add_8888_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, solid, a8r8g8b8, mips_composite_add_8888_n_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, solid, a8b8g8r8, mips_composite_add_8888_n_8888), + PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, mips_composite_add_8_8), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, mips_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, mips_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, r5g6b5, mips_composite_out_reverse_8_0565), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, b5g6r5, mips_composite_out_reverse_8_0565), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8r8g8b8, mips_composite_out_reverse_8_8888), + PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8b8g8r8, mips_composite_out_reverse_8_8888), + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, mips_composite_over_reverse_n_8888), + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, mips_composite_over_reverse_n_8888), + PIXMAN_STD_FAST_PATH (IN, solid, null, a8, mips_composite_in_n_8), + + PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mips_8888_8888), + PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, mips_8888_8888), + PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mips_8888_8888), + PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, mips_8888_8888), + + PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, r5g6b5, mips_8888_0565), + PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, b5g6r5, mips_8888_0565), + + PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (SRC, b5g6r5, x8b8g8r8, mips_0565_8888), + PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, x8r8g8b8, mips_0565_8888), + /* Note: NONE repeat is not supported yet */ + SIMPLE_NEAREST_FAST_PATH_COVER (SRC, r5g6b5, a8r8g8b8, mips_0565_8888), + SIMPLE_NEAREST_FAST_PATH_COVER (SRC, b5g6r5, a8b8g8r8, mips_0565_8888), + SIMPLE_NEAREST_FAST_PATH_PAD (SRC, r5g6b5, a8r8g8b8, mips_0565_8888), + SIMPLE_NEAREST_FAST_PATH_PAD (SRC, b5g6r5, a8b8g8r8, mips_0565_8888), + + SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8r8g8b8, r5g6b5, mips_8888_8_0565), + SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8b8g8r8, b5g6r5, mips_8888_8_0565), + + SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, r5g6b5, r5g6b5, mips_0565_8_0565), + SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, b5g6r5, b5g6r5, mips_0565_8_0565), + + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, mips_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, mips_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, mips_8888_8888), + + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, r5g6b5, mips_8888_0565), + SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, r5g6b5, mips_8888_0565), + + SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, x8r8g8b8, mips_0565_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, r5g6b5, mips_0565_0565), + + SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mips_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mips_8888_8888), + + SIMPLE_BILINEAR_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, mips_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (ADD, a8r8g8b8, x8r8g8b8, mips_8888_8888), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, mips_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, mips_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, mips_8888_8_8888), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, a8r8g8b8, r5g6b5, mips_8888_8_0565), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, x8r8g8b8, r5g6b5, mips_8888_8_0565), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, r5g6b5, x8r8g8b8, mips_0565_8_x888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (SRC, r5g6b5, r5g6b5, mips_0565_8_0565), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mips_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mips_8888_8_8888), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, a8r8g8b8, a8r8g8b8, mips_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (ADD, a8r8g8b8, x8r8g8b8, mips_8888_8_8888), + { PIXMAN_OP_NONE }, +}; + +static void +mips_dspr2_combine_over_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + pixman_composite_over_8888_8888_8888_asm_mips ( + dest, (uint32_t *)src, (uint32_t *)mask, width); + else + pixman_composite_over_8888_8888_asm_mips ( + dest, (uint32_t *)src, width); +} + +pixman_implementation_t * +_pixman_implementation_create_mips_dspr2 (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = + _pixman_implementation_create (fallback, mips_dspr2_fast_paths); + + imp->combine_32[PIXMAN_OP_OVER] = mips_dspr2_combine_over_u; + + imp->blt = mips_dspr2_blt; + imp->fill = mips_dspr2_fill; + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-mips-dspr2.h b/gfx/cairo/libpixman/src/pixman-mips-dspr2.h new file mode 100644 index 0000000000..57b38359e3 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-mips-dspr2.h @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2012 + * MIPS Technologies, Inc., California. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Author: Nemanja Lukic (nemanja.lukic@rt-rk.com) + */ + +#ifndef PIXMAN_MIPS_DSPR2_H +#define PIXMAN_MIPS_DSPR2_H + +#include "pixman-private.h" +#include "pixman-inlines.h" + +#define SKIP_ZERO_SRC 1 +#define SKIP_ZERO_MASK 2 +#define DO_FAST_MEMCPY 3 + +void +pixman_mips_fast_memcpy (void *dst, void *src, uint32_t n_bytes); +void +pixman_fill_buff16_mips (void *dst, uint32_t n_bytes, uint16_t value); +void +pixman_fill_buff32_mips (void *dst, uint32_t n_bytes, uint32_t value); + +/****************************************************************/ + +#define PIXMAN_MIPS_BIND_FAST_PATH_SRC_DST(flags, name, \ + src_type, src_cnt, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_mips (dst_type *dst, \ + src_type *src, \ + int32_t w); \ + \ +static void \ +mips_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line, *dst; \ + src_type *src_line, *src; \ + int32_t dst_stride, src_stride; \ + int bpp = PIXMAN_FORMAT_BPP (dest_image->bits.format) / 8; \ + \ + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ + src_stride, src_line, src_cnt); \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + \ + while (height--) \ + { \ + dst = dst_line; \ + dst_line += dst_stride; \ + src = src_line; \ + src_line += src_stride; \ + \ + if (flags == DO_FAST_MEMCPY) \ + pixman_mips_fast_memcpy (dst, src, width * bpp); \ + else \ + pixman_composite_##name##_asm_mips (dst, src, width); \ + } \ +} + +/****************************************************************/ + +#define PIXMAN_MIPS_BIND_FAST_PATH_N_DST(flags, name, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_mips (dst_type *dst, \ + uint32_t src, \ + int32_t w); \ + \ +static void \ +mips_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line, *dst; \ + int32_t dst_stride; \ + uint32_t src; \ + \ + src = _pixman_image_get_solid ( \ + imp, src_image, dest_image->bits.format); \ + \ + if ((flags & SKIP_ZERO_SRC) && src == 0) \ + return; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + \ + while (height--) \ + { \ + dst = dst_line; \ + dst_line += dst_stride; \ + \ + pixman_composite_##name##_asm_mips (dst, src, width); \ + } \ +} + +/*******************************************************************/ + +#define PIXMAN_MIPS_BIND_FAST_PATH_N_MASK_DST(flags, name, \ + mask_type, mask_cnt, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_mips (dst_type *dst, \ + uint32_t src, \ + mask_type *mask, \ + int32_t w); \ + \ +static void \ +mips_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line, *dst; \ + mask_type *mask_line, *mask; \ + int32_t dst_stride, mask_stride; \ + uint32_t src; \ + \ + src = _pixman_image_get_solid ( \ + imp, src_image, dest_image->bits.format); \ + \ + if ((flags & SKIP_ZERO_SRC) && src == 0) \ + return; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type, \ + mask_stride, mask_line, mask_cnt); \ + \ + while (height--) \ + { \ + dst = dst_line; \ + dst_line += dst_stride; \ + mask = mask_line; \ + mask_line += mask_stride; \ + pixman_composite_##name##_asm_mips (dst, src, mask, width); \ + } \ +} + +/*******************************************************************/ + +#define PIXMAN_MIPS_BIND_FAST_PATH_SRC_N_DST(flags, name, \ + src_type, src_cnt, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_mips (dst_type *dst, \ + src_type *src, \ + uint32_t mask, \ + int32_t w); \ + \ +static void \ +mips_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line, *dst; \ + src_type *src_line, *src; \ + int32_t dst_stride, src_stride; \ + uint32_t mask; \ + \ + mask = _pixman_image_get_solid ( \ + imp, mask_image, dest_image->bits.format); \ + \ + if ((flags & SKIP_ZERO_MASK) && mask == 0) \ + return; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ + src_stride, src_line, src_cnt); \ + \ + while (height--) \ + { \ + dst = dst_line; \ + dst_line += dst_stride; \ + src = src_line; \ + src_line += src_stride; \ + \ + pixman_composite_##name##_asm_mips (dst, src, mask, width); \ + } \ +} + +/************************************************************************/ + +#define PIXMAN_MIPS_BIND_FAST_PATH_SRC_MASK_DST(name, src_type, src_cnt, \ + mask_type, mask_cnt, \ + dst_type, dst_cnt) \ +void \ +pixman_composite_##name##_asm_mips (dst_type *dst, \ + src_type *src, \ + mask_type *mask, \ + int32_t w); \ + \ +static void \ +mips_composite_##name (pixman_implementation_t *imp, \ + pixman_composite_info_t *info) \ +{ \ + PIXMAN_COMPOSITE_ARGS (info); \ + dst_type *dst_line, *dst; \ + src_type *src_line, *src; \ + mask_type *mask_line, *mask; \ + int32_t dst_stride, src_stride, mask_stride; \ + \ + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, dst_type, \ + dst_stride, dst_line, dst_cnt); \ + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, src_type, \ + src_stride, src_line, src_cnt); \ + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, mask_type, \ + mask_stride, mask_line, mask_cnt); \ + \ + while (height--) \ + { \ + dst = dst_line; \ + dst_line += dst_stride; \ + mask = mask_line; \ + mask_line += mask_stride; \ + src = src_line; \ + src_line += src_stride; \ + pixman_composite_##name##_asm_mips (dst, src, mask, width); \ + } \ +} + +/****************************************************************************/ + +#define PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_DST(name, op, \ + src_type, dst_type) \ +void \ +pixman_scaled_nearest_scanline_##name##_##op##_asm_mips ( \ + dst_type * dst, \ + const src_type * src, \ + int32_t w, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x); \ + \ +static force_inline void \ +scaled_nearest_scanline_mips_##name##_##op (dst_type * pd, \ + const src_type * ps, \ + int32_t w, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t zero_src) \ +{ \ + pixman_scaled_nearest_scanline_##name##_##op##_asm_mips (pd, ps, w, \ + vx, unit_x); \ +} \ + \ +FAST_NEAREST_MAINLOOP (mips_##name##_cover_##op, \ + scaled_nearest_scanline_mips_##name##_##op, \ + src_type, dst_type, COVER) \ +FAST_NEAREST_MAINLOOP (mips_##name##_none_##op, \ + scaled_nearest_scanline_mips_##name##_##op, \ + src_type, dst_type, NONE) \ +FAST_NEAREST_MAINLOOP (mips_##name##_pad_##op, \ + scaled_nearest_scanline_mips_##name##_##op, \ + src_type, dst_type, PAD) + +/* Provide entries for the fast path table */ +#define PIXMAN_MIPS_SIMPLE_NEAREST_FAST_PATH(op,s,d,func) \ + SIMPLE_NEAREST_FAST_PATH_COVER (op,s,d,func), \ + SIMPLE_NEAREST_FAST_PATH_NONE (op,s,d,func), \ + SIMPLE_NEAREST_FAST_PATH_PAD (op,s,d,func) + + +/*****************************************************************************/ + +#define PIXMAN_MIPS_BIND_SCALED_NEAREST_SRC_A8_DST(flags, name, op, \ + src_type, dst_type) \ +void \ +pixman_scaled_nearest_scanline_##name##_##op##_asm_mips ( \ + dst_type * dst, \ + const src_type * src, \ + const uint8_t * mask, \ + int32_t w, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x); \ + \ +static force_inline void \ +scaled_nearest_scanline_mips_##name##_##op (const uint8_t * mask, \ + dst_type * pd, \ + const src_type * ps, \ + int32_t w, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t zero_src) \ +{ \ + if ((flags & SKIP_ZERO_SRC) && zero_src) \ + return; \ + pixman_scaled_nearest_scanline_##name##_##op##_asm_mips (pd, ps, \ + mask, w, \ + vx, unit_x); \ +} \ + \ +FAST_NEAREST_MAINLOOP_COMMON (mips_##name##_cover_##op, \ + scaled_nearest_scanline_mips_##name##_##op, \ + src_type, uint8_t, dst_type, COVER, TRUE, FALSE)\ +FAST_NEAREST_MAINLOOP_COMMON (mips_##name##_none_##op, \ + scaled_nearest_scanline_mips_##name##_##op, \ + src_type, uint8_t, dst_type, NONE, TRUE, FALSE) \ +FAST_NEAREST_MAINLOOP_COMMON (mips_##name##_pad_##op, \ + scaled_nearest_scanline_mips_##name##_##op, \ + src_type, uint8_t, dst_type, PAD, TRUE, FALSE) + +/****************************************************************************/ + +#define PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_DST(flags, name, op, \ + src_type, dst_type) \ +void \ +pixman_scaled_bilinear_scanline_##name##_##op##_asm_mips( \ + dst_type * dst, \ + const src_type * src_top, \ + const src_type * src_bottom, \ + int32_t w, \ + int wt, \ + int wb, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x); \ +static force_inline void \ +scaled_bilinear_scanline_mips_##name##_##op (dst_type * dst, \ + const uint32_t * mask, \ + const src_type * src_top, \ + const src_type * src_bottom, \ + int32_t w, \ + int wt, \ + int wb, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t zero_src) \ +{ \ + if ((flags & SKIP_ZERO_SRC) && zero_src) \ + return; \ + pixman_scaled_bilinear_scanline_##name##_##op##_asm_mips (dst, src_top, \ + src_bottom, w, \ + wt, wb, \ + vx, unit_x); \ +} \ + \ +FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_cover_##op, \ + scaled_bilinear_scanline_mips_##name##_##op, \ + src_type, uint32_t, dst_type, COVER, FLAG_NONE) \ +FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_none_##op, \ + scaled_bilinear_scanline_mips_##name##_##op, \ + src_type, uint32_t, dst_type, NONE, FLAG_NONE) \ +FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_pad_##op, \ + scaled_bilinear_scanline_mips_##name##_##op, \ + src_type, uint32_t, dst_type, PAD, FLAG_NONE) \ +FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_normal_##op, \ + scaled_bilinear_scanline_mips_##name##_##op, \ + src_type, uint32_t, dst_type, NORMAL, \ + FLAG_NONE) + +/*****************************************************************************/ + +#define PIXMAN_MIPS_BIND_SCALED_BILINEAR_SRC_A8_DST(flags, name, op, \ + src_type, dst_type) \ +void \ +pixman_scaled_bilinear_scanline_##name##_##op##_asm_mips ( \ + dst_type * dst, \ + const uint8_t * mask, \ + const src_type * top, \ + const src_type * bottom, \ + int wt, \ + int wb, \ + pixman_fixed_t x, \ + pixman_fixed_t ux, \ + int width); \ + \ +static force_inline void \ +scaled_bilinear_scanline_mips_##name##_##op (dst_type * dst, \ + const uint8_t * mask, \ + const src_type * src_top, \ + const src_type * src_bottom, \ + int32_t w, \ + int wt, \ + int wb, \ + pixman_fixed_t vx, \ + pixman_fixed_t unit_x, \ + pixman_fixed_t max_vx, \ + pixman_bool_t zero_src) \ +{ \ + if ((flags & SKIP_ZERO_SRC) && zero_src) \ + return; \ + pixman_scaled_bilinear_scanline_##name##_##op##_asm_mips ( \ + dst, mask, src_top, src_bottom, wt, wb, vx, unit_x, w); \ +} \ + \ +FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_cover_##op, \ + scaled_bilinear_scanline_mips_##name##_##op, \ + src_type, uint8_t, dst_type, COVER, \ + FLAG_HAVE_NON_SOLID_MASK) \ +FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_none_##op, \ + scaled_bilinear_scanline_mips_##name##_##op, \ + src_type, uint8_t, dst_type, NONE, \ + FLAG_HAVE_NON_SOLID_MASK) \ +FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_pad_##op, \ + scaled_bilinear_scanline_mips_##name##_##op, \ + src_type, uint8_t, dst_type, PAD, \ + FLAG_HAVE_NON_SOLID_MASK) \ +FAST_BILINEAR_MAINLOOP_COMMON (mips_##name##_normal_##op, \ + scaled_bilinear_scanline_mips_##name##_##op, \ + src_type, uint8_t, dst_type, NORMAL, \ + FLAG_HAVE_NON_SOLID_MASK) + +#endif //PIXMAN_MIPS_DSPR2_H diff --git a/gfx/cairo/libpixman/src/pixman-mips-memcpy-asm.S b/gfx/cairo/libpixman/src/pixman-mips-memcpy-asm.S new file mode 100644 index 0000000000..9ad6da5378 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-mips-memcpy-asm.S @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2012 + * MIPS Technologies, Inc., California. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "pixman-mips-dspr2-asm.h" + +/* + * This routine could be optimized for MIPS64. The current code only + * uses MIPS32 instructions. + */ + +#ifdef EB +# define LWHI lwl /* high part is left in big-endian */ +# define SWHI swl /* high part is left in big-endian */ +# define LWLO lwr /* low part is right in big-endian */ +# define SWLO swr /* low part is right in big-endian */ +#else +# define LWHI lwr /* high part is right in little-endian */ +# define SWHI swr /* high part is right in little-endian */ +# define LWLO lwl /* low part is left in big-endian */ +# define SWLO swl /* low part is left in big-endian */ +#endif + +LEAF_MIPS32R2(pixman_mips_fast_memcpy) + + slti AT, a2, 8 + bne AT, zero, $last8 + move v0, a0 /* memcpy returns the dst pointer */ + +/* Test if the src and dst are word-aligned, or can be made word-aligned */ + xor t8, a1, a0 + andi t8, t8, 0x3 /* t8 is a0/a1 word-displacement */ + + bne t8, zero, $unaligned + negu a3, a0 + + andi a3, a3, 0x3 /* we need to copy a3 bytes to make a0/a1 aligned */ + beq a3, zero, $chk16w /* when a3=0 then the dst (a0) is word-aligned */ + subu a2, a2, a3 /* now a2 is the remining bytes count */ + + LWHI t8, 0(a1) + addu a1, a1, a3 + SWHI t8, 0(a0) + addu a0, a0, a3 + +/* Now the dst/src are mutually word-aligned with word-aligned addresses */ +$chk16w: andi t8, a2, 0x3f /* any whole 64-byte chunks? */ + /* t8 is the byte count after 64-byte chunks */ + + beq a2, t8, $chk8w /* if a2==t8, no 64-byte chunks */ + /* There will be at most 1 32-byte chunk after it */ + subu a3, a2, t8 /* subtract from a2 the reminder */ + /* Here a3 counts bytes in 16w chunks */ + addu a3, a0, a3 /* Now a3 is the final dst after 64-byte chunks */ + + addu t0, a0, a2 /* t0 is the "past the end" address */ + +/* + * When in the loop we exercise "pref 30, x(a0)", the a0+x should not be past + * the "t0-32" address + * This means: for x=128 the last "safe" a0 address is "t0-160" + * Alternatively, for x=64 the last "safe" a0 address is "t0-96" + * In the current version we use "pref 30, 128(a0)", so "t0-160" is the limit + */ + subu t9, t0, 160 /* t9 is the "last safe pref 30, 128(a0)" address */ + + pref 0, 0(a1) /* bring the first line of src, addr 0 */ + pref 0, 32(a1) /* bring the second line of src, addr 32 */ + pref 0, 64(a1) /* bring the third line of src, addr 64 */ + pref 30, 32(a0) /* safe, as we have at least 64 bytes ahead */ +/* In case the a0 > t9 don't use "pref 30" at all */ + sgtu v1, a0, t9 + bgtz v1, $loop16w /* skip "pref 30, 64(a0)" for too short arrays */ + nop +/* otherwise, start with using pref30 */ + pref 30, 64(a0) +$loop16w: + pref 0, 96(a1) + lw t0, 0(a1) + bgtz v1, $skip_pref30_96 /* skip "pref 30, 96(a0)" */ + lw t1, 4(a1) + pref 30, 96(a0) /* continue setting up the dest, addr 96 */ +$skip_pref30_96: + lw t2, 8(a1) + lw t3, 12(a1) + lw t4, 16(a1) + lw t5, 20(a1) + lw t6, 24(a1) + lw t7, 28(a1) + pref 0, 128(a1) /* bring the next lines of src, addr 128 */ + + sw t0, 0(a0) + sw t1, 4(a0) + sw t2, 8(a0) + sw t3, 12(a0) + sw t4, 16(a0) + sw t5, 20(a0) + sw t6, 24(a0) + sw t7, 28(a0) + + lw t0, 32(a1) + bgtz v1, $skip_pref30_128 /* skip "pref 30, 128(a0)" */ + lw t1, 36(a1) + pref 30, 128(a0) /* continue setting up the dest, addr 128 */ +$skip_pref30_128: + lw t2, 40(a1) + lw t3, 44(a1) + lw t4, 48(a1) + lw t5, 52(a1) + lw t6, 56(a1) + lw t7, 60(a1) + pref 0, 160(a1) /* bring the next lines of src, addr 160 */ + + sw t0, 32(a0) + sw t1, 36(a0) + sw t2, 40(a0) + sw t3, 44(a0) + sw t4, 48(a0) + sw t5, 52(a0) + sw t6, 56(a0) + sw t7, 60(a0) + + addiu a0, a0, 64 /* adding 64 to dest */ + sgtu v1, a0, t9 + bne a0, a3, $loop16w + addiu a1, a1, 64 /* adding 64 to src */ + move a2, t8 + +/* Here we have src and dest word-aligned but less than 64-bytes to go */ + +$chk8w: + pref 0, 0x0(a1) + andi t8, a2, 0x1f /* is there a 32-byte chunk? */ + /* the t8 is the reminder count past 32-bytes */ + beq a2, t8, $chk1w /* when a2=t8, no 32-byte chunk */ + nop + + lw t0, 0(a1) + lw t1, 4(a1) + lw t2, 8(a1) + lw t3, 12(a1) + lw t4, 16(a1) + lw t5, 20(a1) + lw t6, 24(a1) + lw t7, 28(a1) + addiu a1, a1, 32 + + sw t0, 0(a0) + sw t1, 4(a0) + sw t2, 8(a0) + sw t3, 12(a0) + sw t4, 16(a0) + sw t5, 20(a0) + sw t6, 24(a0) + sw t7, 28(a0) + addiu a0, a0, 32 + +$chk1w: + andi a2, t8, 0x3 /* now a2 is the reminder past 1w chunks */ + beq a2, t8, $last8 + subu a3, t8, a2 /* a3 is count of bytes in 1w chunks */ + addu a3, a0, a3 /* now a3 is the dst address past the 1w chunks */ + +/* copying in words (4-byte chunks) */ +$wordCopy_loop: + lw t3, 0(a1) /* the first t3 may be equal t0 ... optimize? */ + addiu a1, a1, 4 + addiu a0, a0, 4 + bne a0, a3, $wordCopy_loop + sw t3, -4(a0) + +/* For the last (<8) bytes */ +$last8: + blez a2, leave + addu a3, a0, a2 /* a3 is the last dst address */ +$last8loop: + lb v1, 0(a1) + addiu a1, a1, 1 + addiu a0, a0, 1 + bne a0, a3, $last8loop + sb v1, -1(a0) + +leave: j ra + nop + +/* + * UNALIGNED case + */ + +$unaligned: + /* got here with a3="negu a0" */ + andi a3, a3, 0x3 /* test if the a0 is word aligned */ + beqz a3, $ua_chk16w + subu a2, a2, a3 /* bytes left after initial a3 bytes */ + + LWHI v1, 0(a1) + LWLO v1, 3(a1) + addu a1, a1, a3 /* a3 may be here 1, 2 or 3 */ + SWHI v1, 0(a0) + addu a0, a0, a3 /* below the dst will be word aligned (NOTE1) */ + +$ua_chk16w: andi t8, a2, 0x3f /* any whole 64-byte chunks? */ + /* t8 is the byte count after 64-byte chunks */ + beq a2, t8, $ua_chk8w /* if a2==t8, no 64-byte chunks */ + /* There will be at most 1 32-byte chunk after it */ + subu a3, a2, t8 /* subtract from a2 the reminder */ + /* Here a3 counts bytes in 16w chunks */ + addu a3, a0, a3 /* Now a3 is the final dst after 64-byte chunks */ + + addu t0, a0, a2 /* t0 is the "past the end" address */ + + subu t9, t0, 160 /* t9 is the "last safe pref 30, 128(a0)" address */ + + pref 0, 0(a1) /* bring the first line of src, addr 0 */ + pref 0, 32(a1) /* bring the second line of src, addr 32 */ + pref 0, 64(a1) /* bring the third line of src, addr 64 */ + pref 30, 32(a0) /* safe, as we have at least 64 bytes ahead */ +/* In case the a0 > t9 don't use "pref 30" at all */ + sgtu v1, a0, t9 + bgtz v1, $ua_loop16w /* skip "pref 30, 64(a0)" for too short arrays */ + nop +/* otherwise, start with using pref30 */ + pref 30, 64(a0) +$ua_loop16w: + pref 0, 96(a1) + LWHI t0, 0(a1) + LWLO t0, 3(a1) + LWHI t1, 4(a1) + bgtz v1, $ua_skip_pref30_96 + LWLO t1, 7(a1) + pref 30, 96(a0) /* continue setting up the dest, addr 96 */ +$ua_skip_pref30_96: + LWHI t2, 8(a1) + LWLO t2, 11(a1) + LWHI t3, 12(a1) + LWLO t3, 15(a1) + LWHI t4, 16(a1) + LWLO t4, 19(a1) + LWHI t5, 20(a1) + LWLO t5, 23(a1) + LWHI t6, 24(a1) + LWLO t6, 27(a1) + LWHI t7, 28(a1) + LWLO t7, 31(a1) + pref 0, 128(a1) /* bring the next lines of src, addr 128 */ + + sw t0, 0(a0) + sw t1, 4(a0) + sw t2, 8(a0) + sw t3, 12(a0) + sw t4, 16(a0) + sw t5, 20(a0) + sw t6, 24(a0) + sw t7, 28(a0) + + LWHI t0, 32(a1) + LWLO t0, 35(a1) + LWHI t1, 36(a1) + bgtz v1, $ua_skip_pref30_128 + LWLO t1, 39(a1) + pref 30, 128(a0) /* continue setting up the dest, addr 128 */ +$ua_skip_pref30_128: + LWHI t2, 40(a1) + LWLO t2, 43(a1) + LWHI t3, 44(a1) + LWLO t3, 47(a1) + LWHI t4, 48(a1) + LWLO t4, 51(a1) + LWHI t5, 52(a1) + LWLO t5, 55(a1) + LWHI t6, 56(a1) + LWLO t6, 59(a1) + LWHI t7, 60(a1) + LWLO t7, 63(a1) + pref 0, 160(a1) /* bring the next lines of src, addr 160 */ + + sw t0, 32(a0) + sw t1, 36(a0) + sw t2, 40(a0) + sw t3, 44(a0) + sw t4, 48(a0) + sw t5, 52(a0) + sw t6, 56(a0) + sw t7, 60(a0) + + addiu a0, a0, 64 /* adding 64 to dest */ + sgtu v1, a0, t9 + bne a0, a3, $ua_loop16w + addiu a1, a1, 64 /* adding 64 to src */ + move a2, t8 + +/* Here we have src and dest word-aligned but less than 64-bytes to go */ + +$ua_chk8w: + pref 0, 0x0(a1) + andi t8, a2, 0x1f /* is there a 32-byte chunk? */ + /* the t8 is the reminder count */ + beq a2, t8, $ua_chk1w /* when a2=t8, no 32-byte chunk */ + + LWHI t0, 0(a1) + LWLO t0, 3(a1) + LWHI t1, 4(a1) + LWLO t1, 7(a1) + LWHI t2, 8(a1) + LWLO t2, 11(a1) + LWHI t3, 12(a1) + LWLO t3, 15(a1) + LWHI t4, 16(a1) + LWLO t4, 19(a1) + LWHI t5, 20(a1) + LWLO t5, 23(a1) + LWHI t6, 24(a1) + LWLO t6, 27(a1) + LWHI t7, 28(a1) + LWLO t7, 31(a1) + addiu a1, a1, 32 + + sw t0, 0(a0) + sw t1, 4(a0) + sw t2, 8(a0) + sw t3, 12(a0) + sw t4, 16(a0) + sw t5, 20(a0) + sw t6, 24(a0) + sw t7, 28(a0) + addiu a0, a0, 32 + +$ua_chk1w: + andi a2, t8, 0x3 /* now a2 is the reminder past 1w chunks */ + beq a2, t8, $ua_smallCopy + subu a3, t8, a2 /* a3 is count of bytes in 1w chunks */ + addu a3, a0, a3 /* now a3 is the dst address past the 1w chunks */ + +/* copying in words (4-byte chunks) */ +$ua_wordCopy_loop: + LWHI v1, 0(a1) + LWLO v1, 3(a1) + addiu a1, a1, 4 + addiu a0, a0, 4 /* note: dst=a0 is word aligned here, see NOTE1 */ + bne a0, a3, $ua_wordCopy_loop + sw v1, -4(a0) + +/* Now less than 4 bytes (value in a2) left to copy */ +$ua_smallCopy: + beqz a2, leave + addu a3, a0, a2 /* a3 is the last dst address */ +$ua_smallCopy_loop: + lb v1, 0(a1) + addiu a1, a1, 1 + addiu a0, a0, 1 + bne a0, a3, $ua_smallCopy_loop + sb v1, -1(a0) + + j ra + nop + +END(pixman_mips_fast_memcpy) diff --git a/gfx/cairo/libpixman/src/pixman-mips.c b/gfx/cairo/libpixman/src/pixman-mips.c new file mode 100644 index 0000000000..304881383b --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-mips.c @@ -0,0 +1,94 @@ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pixman-private.h" + +#if defined(USE_MIPS_DSPR2) || defined(USE_LOONGSON_MMI) + +#include +#include + +static pixman_bool_t +have_feature (const char *search_string) +{ +#if defined (__linux__) /* linux ELF */ + /* Simple detection of MIPS features at runtime for Linux. + * It is based on /proc/cpuinfo, which reveals hardware configuration + * to user-space applications. According to MIPS (early 2010), no similar + * facility is universally available on the MIPS architectures, so it's up + * to individual OSes to provide such. + */ + const char *file_name = "/proc/cpuinfo"; + char cpuinfo_line[256]; + FILE *f = NULL; + + if ((f = fopen (file_name, "r")) == NULL) + return FALSE; + + while (fgets (cpuinfo_line, sizeof (cpuinfo_line), f) != NULL) + { + if (strstr (cpuinfo_line, search_string) != NULL) + { + fclose (f); + return TRUE; + } + } + + fclose (f); +#endif + + /* Did not find string in the proc file, or not Linux ELF. */ + return FALSE; +} + +#endif + +pixman_implementation_t * +_pixman_mips_get_implementations (pixman_implementation_t *imp) +{ +#ifdef USE_LOONGSON_MMI + /* I really don't know if some Loongson CPUs don't have MMI. */ + if (!_pixman_disabled ("loongson-mmi") && have_feature ("Loongson")) + imp = _pixman_implementation_create_mmx (imp); +#endif + +#ifdef USE_MIPS_DSPR2 + if (!_pixman_disabled ("mips-dspr2")) + { + int already_compiling_everything_for_dspr2 = 0; +#if defined(__mips_dsp) && (__mips_dsp_rev >= 2) + already_compiling_everything_for_dspr2 = 1; +#endif + if (already_compiling_everything_for_dspr2 || + /* Only currently available MIPS core that supports DSPr2 is 74K. */ + have_feature ("MIPS 74K")) + { + imp = _pixman_implementation_create_mips_dspr2 (imp); + } + } +#endif + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-mmx.c b/gfx/cairo/libpixman/src/pixman-mmx.c new file mode 100644 index 0000000000..80c3ad42a5 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-mmx.c @@ -0,0 +1,4153 @@ +/* + * Copyright © 2004, 2005 Red Hat, Inc. + * Copyright © 2004 Nicholas Miell + * Copyright © 2005 Trolltech AS + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes 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. + * + * Author: Søren Sandmann (sandmann@redhat.com) + * Minor Improvements: Nicholas Miell (nmiell@gmail.com) + * MMX code paths for fbcompose.c by Lars Knoll (lars@trolltech.com) + * + * Based on work by Owen Taylor + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#if defined USE_X86_MMX || defined USE_ARM_IWMMXT || defined USE_LOONGSON_MMI + +#ifdef USE_LOONGSON_MMI +#include +#else +#include +#endif +#include "pixman-private.h" +#include "pixman-combine32.h" +#include "pixman-inlines.h" + +#ifdef VERBOSE +#define CHECKPOINT() error_f ("at %s %d\n", __FUNCTION__, __LINE__) +#else +#define CHECKPOINT() +#endif + +#if defined USE_ARM_IWMMXT && __GNUC__ == 4 && __GNUC_MINOR__ < 8 +/* Empty the multimedia state. For some reason, ARM's mmintrin.h doesn't provide this. */ +extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_empty (void) +{ + +} +#endif + +#ifdef USE_X86_MMX +# if (defined(__SUNPRO_C) || defined(_MSC_VER) || defined(_WIN64)) || defined(__MINGW32__) +# include +# else +/* We have to compile with -msse to use xmmintrin.h, but that causes SSE + * instructions to be generated that we don't want. Just duplicate the + * functions we want to use. */ +extern __inline int __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_movemask_pi8 (__m64 __A) +{ + int ret; + + asm ("pmovmskb %1, %0\n\t" + : "=r" (ret) + : "y" (__A) + ); + + return ret; +} + +extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__)) +_mm_mulhi_pu16 (__m64 __A, __m64 __B) +{ + asm ("pmulhuw %1, %0\n\t" + : "+y" (__A) + : "y" (__B) + ); + return __A; +} + +# define _mm_shuffle_pi16(A, N) \ + ({ \ + __m64 ret; \ + \ + asm ("pshufw %2, %1, %0\n\t" \ + : "=y" (ret) \ + : "y" (A), "K" ((const int8_t)N) \ + ); \ + \ + ret; \ + }) +# endif +#endif + +#ifndef _MSC_VER +#define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \ + (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | (fp0)) +#endif + +/* Notes about writing mmx code + * + * give memory operands as the second operand. If you give it as the + * first, gcc will first load it into a register, then use that + * register + * + * ie. use + * + * _mm_mullo_pi16 (x, mmx_constant); + * + * not + * + * _mm_mullo_pi16 (mmx_constant, x); + * + * Also try to minimize dependencies. i.e. when you need a value, try + * to calculate it from a value that was calculated as early as + * possible. + */ + +/* --------------- MMX primitives ------------------------------------- */ + +/* If __m64 is defined as a struct or union, then define M64_MEMBER to be + * the name of the member used to access the data. + * If __m64 requires using mm_cvt* intrinsics functions to convert between + * uint64_t and __m64 values, then define USE_CVT_INTRINSICS. + * If __m64 and uint64_t values can just be cast to each other directly, + * then define USE_M64_CASTS. + * If __m64 is a double datatype, then define USE_M64_DOUBLE. + */ +#if defined(_MSC_VER) && !defined(__clang__) +# define M64_MEMBER m64_u64 +#elif defined(__ICC) +# define USE_CVT_INTRINSICS +#elif defined(USE_LOONGSON_MMI) +# define USE_M64_DOUBLE +#elif defined(__GNUC__) || defined(__clang__) +# define USE_M64_CASTS +#elif defined(__SUNPRO_C) +# if (__SUNPRO_C >= 0x5120) && !defined(__NOVECTORSIZE__) +/* Solaris Studio 12.3 (Sun C 5.12) introduces __attribute__(__vector_size__) + * support, and defaults to using it to define __m64, unless __NOVECTORSIZE__ + * is defined. If it is used, then the mm_cvt* intrinsics must be used. + */ +# define USE_CVT_INTRINSICS +# else +/* For Studio 12.2 or older, or when __attribute__(__vector_size__) is + * disabled, __m64 is defined as a struct containing "unsigned long long l_". + */ +# define M64_MEMBER l_ +# endif +#endif + +#if defined(USE_M64_CASTS) || defined(USE_CVT_INTRINSICS) || defined(USE_M64_DOUBLE) +typedef uint64_t mmxdatafield; +#else +typedef __m64 mmxdatafield; +#endif + +typedef struct +{ + mmxdatafield mmx_4x00ff; + mmxdatafield mmx_4x0080; + mmxdatafield mmx_565_rgb; + mmxdatafield mmx_565_unpack_multiplier; + mmxdatafield mmx_565_pack_multiplier; + mmxdatafield mmx_565_r; + mmxdatafield mmx_565_g; + mmxdatafield mmx_565_b; + mmxdatafield mmx_packed_565_rb; + mmxdatafield mmx_packed_565_g; + mmxdatafield mmx_expand_565_g; + mmxdatafield mmx_expand_565_b; + mmxdatafield mmx_expand_565_r; +#ifndef USE_LOONGSON_MMI + mmxdatafield mmx_mask_0; + mmxdatafield mmx_mask_1; + mmxdatafield mmx_mask_2; + mmxdatafield mmx_mask_3; +#endif + mmxdatafield mmx_full_alpha; + mmxdatafield mmx_4x0101; + mmxdatafield mmx_ff000000; +} mmx_data_t; + +#if defined(_MSC_VER) +# define MMXDATA_INIT(field, val) { val ## UI64 } +#elif defined(M64_MEMBER) /* __m64 is a struct, not an integral type */ +# define MMXDATA_INIT(field, val) field = { val ## ULL } +#else /* mmxdatafield is an integral type */ +# define MMXDATA_INIT(field, val) field = val ## ULL +#endif + +static const mmx_data_t c = +{ + MMXDATA_INIT (.mmx_4x00ff, 0x00ff00ff00ff00ff), + MMXDATA_INIT (.mmx_4x0080, 0x0080008000800080), + MMXDATA_INIT (.mmx_565_rgb, 0x000001f0003f001f), + MMXDATA_INIT (.mmx_565_unpack_multiplier, 0x0000008404100840), + MMXDATA_INIT (.mmx_565_pack_multiplier, 0x2000000420000004), + MMXDATA_INIT (.mmx_565_r, 0x000000f800000000), + MMXDATA_INIT (.mmx_565_g, 0x0000000000fc0000), + MMXDATA_INIT (.mmx_565_b, 0x00000000000000f8), + MMXDATA_INIT (.mmx_packed_565_rb, 0x00f800f800f800f8), + MMXDATA_INIT (.mmx_packed_565_g, 0x0000fc000000fc00), + MMXDATA_INIT (.mmx_expand_565_g, 0x07e007e007e007e0), + MMXDATA_INIT (.mmx_expand_565_b, 0x001f001f001f001f), + MMXDATA_INIT (.mmx_expand_565_r, 0xf800f800f800f800), +#ifndef USE_LOONGSON_MMI + MMXDATA_INIT (.mmx_mask_0, 0xffffffffffff0000), + MMXDATA_INIT (.mmx_mask_1, 0xffffffff0000ffff), + MMXDATA_INIT (.mmx_mask_2, 0xffff0000ffffffff), + MMXDATA_INIT (.mmx_mask_3, 0x0000ffffffffffff), +#endif + MMXDATA_INIT (.mmx_full_alpha, 0x00ff000000000000), + MMXDATA_INIT (.mmx_4x0101, 0x0101010101010101), + MMXDATA_INIT (.mmx_ff000000, 0xff000000ff000000), +}; + +#ifdef USE_CVT_INTRINSICS +# define MC(x) to_m64 (c.mmx_ ## x) +#elif defined(USE_M64_CASTS) +# define MC(x) ((__m64)c.mmx_ ## x) +#elif defined(USE_M64_DOUBLE) +# define MC(x) (*(__m64 *)&c.mmx_ ## x) +#else +# define MC(x) c.mmx_ ## x +#endif + +static force_inline __m64 +to_m64 (uint64_t x) +{ +#ifdef USE_CVT_INTRINSICS + return _mm_cvtsi64_m64 (x); +#elif defined M64_MEMBER /* __m64 is a struct, not an integral type */ + __m64 res; + + res.M64_MEMBER = x; + return res; +#elif defined USE_M64_DOUBLE + return *(__m64 *)&x; +#else /* USE_M64_CASTS */ + return (__m64)x; +#endif +} + +static force_inline uint64_t +to_uint64 (__m64 x) +{ +#ifdef USE_CVT_INTRINSICS + return _mm_cvtm64_si64 (x); +#elif defined M64_MEMBER /* __m64 is a struct, not an integral type */ + uint64_t res = x.M64_MEMBER; + return res; +#elif defined USE_M64_DOUBLE + return *(uint64_t *)&x; +#else /* USE_M64_CASTS */ + return (uint64_t)x; +#endif +} + +static force_inline __m64 +shift (__m64 v, + int s) +{ + if (s > 0) + return _mm_slli_si64 (v, s); + else if (s < 0) + return _mm_srli_si64 (v, -s); + else + return v; +} + +static force_inline __m64 +negate (__m64 mask) +{ + return _mm_xor_si64 (mask, MC (4x00ff)); +} + +/* Computes the product of two unsigned fixed-point 8-bit values from 0 to 1 + * and maps its result to the same range. + * + * Jim Blinn gives multiple ways to compute this in "Jim Blinn's Corner: + * Notation, Notation, Notation", the first of which is + * + * prod(a, b) = (a * b + 128) / 255. + * + * By approximating the division by 255 as 257/65536 it can be replaced by a + * multiply and a right shift. This is the implementation that we use in + * pix_multiply(), but we _mm_mulhi_pu16() by 257 (part of SSE1 or Extended + * 3DNow!, and unavailable at the time of the book's publication) to perform + * the multiplication and right shift in a single operation. + * + * prod(a, b) = ((a * b + 128) * 257) >> 16. + * + * A third way (how pix_multiply() was implemented prior to 14208344) exists + * also that performs the multiplication by 257 with adds and shifts. + * + * Where temp = a * b + 128 + * + * prod(a, b) = (temp + (temp >> 8)) >> 8. + */ +static force_inline __m64 +pix_multiply (__m64 a, __m64 b) +{ + __m64 res; + + res = _mm_mullo_pi16 (a, b); + res = _mm_adds_pu16 (res, MC (4x0080)); + res = _mm_mulhi_pu16 (res, MC (4x0101)); + + return res; +} + +static force_inline __m64 +pix_add (__m64 a, __m64 b) +{ + return _mm_adds_pu8 (a, b); +} + +static force_inline __m64 +expand_alpha (__m64 pixel) +{ + return _mm_shuffle_pi16 (pixel, _MM_SHUFFLE (3, 3, 3, 3)); +} + +static force_inline __m64 +expand_alpha_rev (__m64 pixel) +{ + return _mm_shuffle_pi16 (pixel, _MM_SHUFFLE (0, 0, 0, 0)); +} + +static force_inline __m64 +invert_colors (__m64 pixel) +{ + return _mm_shuffle_pi16 (pixel, _MM_SHUFFLE (3, 0, 1, 2)); +} + +static force_inline __m64 +over (__m64 src, + __m64 srca, + __m64 dest) +{ + return _mm_adds_pu8 (src, pix_multiply (dest, negate (srca))); +} + +static force_inline __m64 +over_rev_non_pre (__m64 src, __m64 dest) +{ + __m64 srca = expand_alpha (src); + __m64 srcfaaa = _mm_or_si64 (srca, MC (full_alpha)); + + return over (pix_multiply (invert_colors (src), srcfaaa), srca, dest); +} + +static force_inline __m64 +in (__m64 src, __m64 mask) +{ + return pix_multiply (src, mask); +} + +#ifndef _MSC_VER +static force_inline __m64 +in_over (__m64 src, __m64 srca, __m64 mask, __m64 dest) +{ + return over (in (src, mask), pix_multiply (srca, mask), dest); +} + +#else + +#define in_over(src, srca, mask, dest) \ + over (in (src, mask), pix_multiply (srca, mask), dest) + +#endif + +/* Elemental unaligned loads */ + +static force_inline __m64 ldq_u(__m64 *p) +{ +#ifdef USE_X86_MMX + /* x86's alignment restrictions are very relaxed, but that's no excuse */ + __m64 r; + memcpy(&r, p, sizeof(__m64)); + return r; +#elif defined USE_ARM_IWMMXT + int align = (uintptr_t)p & 7; + __m64 *aligned_p; + if (align == 0) + return *p; + aligned_p = (__m64 *)((uintptr_t)p & ~7); + return (__m64) _mm_align_si64 (aligned_p[0], aligned_p[1], align); +#else + struct __una_u64 { __m64 x __attribute__((packed)); }; + const struct __una_u64 *ptr = (const struct __una_u64 *) p; + return (__m64) ptr->x; +#endif +} + +static force_inline uint32_t ldl_u(const uint32_t *p) +{ +#ifdef USE_X86_MMX + /* x86's alignment restrictions are very relaxed. */ + uint32_t r; + memcpy(&r, p, sizeof(uint32_t)); + return r; +#else + struct __una_u32 { uint32_t x __attribute__((packed)); }; + const struct __una_u32 *ptr = (const struct __una_u32 *) p; + return ptr->x; +#endif +} + +static force_inline __m64 +load (const uint32_t *v) +{ +#ifdef USE_LOONGSON_MMI + __m64 ret; + asm ("lwc1 %0, %1\n\t" + : "=f" (ret) + : "m" (*v) + ); + return ret; +#else + return _mm_cvtsi32_si64 (*v); +#endif +} + +static force_inline __m64 +load8888 (const uint32_t *v) +{ +#ifdef USE_LOONGSON_MMI + return _mm_unpacklo_pi8_f (*(__m32 *)v, _mm_setzero_si64 ()); +#else + return _mm_unpacklo_pi8 (load (v), _mm_setzero_si64 ()); +#endif +} + +static force_inline __m64 +load8888u (const uint32_t *v) +{ + uint32_t l = ldl_u (v); + return load8888 (&l); +} + +static force_inline __m64 +pack8888 (__m64 lo, __m64 hi) +{ + return _mm_packs_pu16 (lo, hi); +} + +static force_inline void +store (uint32_t *dest, __m64 v) +{ +#ifdef USE_LOONGSON_MMI + asm ("swc1 %1, %0\n\t" + : "=m" (*dest) + : "f" (v) + : "memory" + ); +#else + *dest = _mm_cvtsi64_si32 (v); +#endif +} + +static force_inline void +store8888 (uint32_t *dest, __m64 v) +{ + v = pack8888 (v, _mm_setzero_si64 ()); + store (dest, v); +} + +static force_inline pixman_bool_t +is_equal (__m64 a, __m64 b) +{ +#ifdef USE_LOONGSON_MMI + /* __m64 is double, we can compare directly. */ + return a == b; +#else + return _mm_movemask_pi8 (_mm_cmpeq_pi8 (a, b)) == 0xff; +#endif +} + +static force_inline pixman_bool_t +is_opaque (__m64 v) +{ +#ifdef USE_LOONGSON_MMI + return is_equal (_mm_and_si64 (v, MC (full_alpha)), MC (full_alpha)); +#else + __m64 ffs = _mm_cmpeq_pi8 (v, v); + return (_mm_movemask_pi8 (_mm_cmpeq_pi8 (v, ffs)) & 0x40); +#endif +} + +static force_inline pixman_bool_t +is_zero (__m64 v) +{ + return is_equal (v, _mm_setzero_si64 ()); +} + +/* Expand 16 bits positioned at @pos (0-3) of a mmx register into + * + * 00RR00GG00BB + * + * --- Expanding 565 in the low word --- + * + * m = (m << (32 - 3)) | (m << (16 - 5)) | m; + * m = m & (01f0003f001f); + * m = m * (008404100840); + * m = m >> 8; + * + * Note the trick here - the top word is shifted by another nibble to + * avoid it bumping into the middle word + */ +static force_inline __m64 +expand565 (__m64 pixel, int pos) +{ + __m64 p = pixel; + __m64 t1, t2; + + /* move pixel to low 16 bit and zero the rest */ +#ifdef USE_LOONGSON_MMI + p = loongson_extract_pi16 (p, pos); +#else + p = shift (shift (p, (3 - pos) * 16), -48); +#endif + + t1 = shift (p, 36 - 11); + t2 = shift (p, 16 - 5); + + p = _mm_or_si64 (t1, p); + p = _mm_or_si64 (t2, p); + p = _mm_and_si64 (p, MC (565_rgb)); + + pixel = _mm_mullo_pi16 (p, MC (565_unpack_multiplier)); + return _mm_srli_pi16 (pixel, 8); +} + +/* Expand 4 16 bit pixels in an mmx register into two mmx registers of + * + * AARRGGBBRRGGBB + */ +static force_inline void +expand_4xpacked565 (__m64 vin, __m64 *vout0, __m64 *vout1, int full_alpha) +{ + __m64 t0, t1, alpha = _mm_setzero_si64 (); + __m64 r = _mm_and_si64 (vin, MC (expand_565_r)); + __m64 g = _mm_and_si64 (vin, MC (expand_565_g)); + __m64 b = _mm_and_si64 (vin, MC (expand_565_b)); + if (full_alpha) + alpha = _mm_cmpeq_pi32 (alpha, alpha); + + /* Replicate high bits into empty low bits. */ + r = _mm_or_si64 (_mm_srli_pi16 (r, 8), _mm_srli_pi16 (r, 13)); + g = _mm_or_si64 (_mm_srli_pi16 (g, 3), _mm_srli_pi16 (g, 9)); + b = _mm_or_si64 (_mm_slli_pi16 (b, 3), _mm_srli_pi16 (b, 2)); + + r = _mm_packs_pu16 (r, _mm_setzero_si64 ()); /* 00 00 00 00 R3 R2 R1 R0 */ + g = _mm_packs_pu16 (g, _mm_setzero_si64 ()); /* 00 00 00 00 G3 G2 G1 G0 */ + b = _mm_packs_pu16 (b, _mm_setzero_si64 ()); /* 00 00 00 00 B3 B2 B1 B0 */ + + t1 = _mm_unpacklo_pi8 (r, alpha); /* A3 R3 A2 R2 A1 R1 A0 R0 */ + t0 = _mm_unpacklo_pi8 (b, g); /* G3 B3 G2 B2 G1 B1 G0 B0 */ + + *vout0 = _mm_unpacklo_pi16 (t0, t1); /* A1 R1 G1 B1 A0 R0 G0 B0 */ + *vout1 = _mm_unpackhi_pi16 (t0, t1); /* A3 R3 G3 B3 A2 R2 G2 B2 */ +} + +static force_inline __m64 +expand8888 (__m64 in, int pos) +{ + if (pos == 0) + return _mm_unpacklo_pi8 (in, _mm_setzero_si64 ()); + else + return _mm_unpackhi_pi8 (in, _mm_setzero_si64 ()); +} + +static force_inline __m64 +expandx888 (__m64 in, int pos) +{ + return _mm_or_si64 (expand8888 (in, pos), MC (full_alpha)); +} + +static force_inline void +expand_4x565 (__m64 vin, __m64 *vout0, __m64 *vout1, __m64 *vout2, __m64 *vout3, int full_alpha) +{ + __m64 v0, v1; + expand_4xpacked565 (vin, &v0, &v1, full_alpha); + *vout0 = expand8888 (v0, 0); + *vout1 = expand8888 (v0, 1); + *vout2 = expand8888 (v1, 0); + *vout3 = expand8888 (v1, 1); +} + +static force_inline __m64 +pack_565 (__m64 pixel, __m64 target, int pos) +{ + __m64 p = pixel; + __m64 t = target; + __m64 r, g, b; + + r = _mm_and_si64 (p, MC (565_r)); + g = _mm_and_si64 (p, MC (565_g)); + b = _mm_and_si64 (p, MC (565_b)); + +#ifdef USE_LOONGSON_MMI + r = shift (r, -(32 - 8)); + g = shift (g, -(16 - 3)); + b = shift (b, -(0 + 3)); + + p = _mm_or_si64 (r, g); + p = _mm_or_si64 (p, b); + return loongson_insert_pi16 (t, p, pos); +#else + r = shift (r, -(32 - 8) + pos * 16); + g = shift (g, -(16 - 3) + pos * 16); + b = shift (b, -(0 + 3) + pos * 16); + + if (pos == 0) + t = _mm_and_si64 (t, MC (mask_0)); + else if (pos == 1) + t = _mm_and_si64 (t, MC (mask_1)); + else if (pos == 2) + t = _mm_and_si64 (t, MC (mask_2)); + else if (pos == 3) + t = _mm_and_si64 (t, MC (mask_3)); + + p = _mm_or_si64 (r, t); + p = _mm_or_si64 (g, p); + + return _mm_or_si64 (b, p); +#endif +} + +static force_inline __m64 +pack_4xpacked565 (__m64 a, __m64 b) +{ + __m64 rb0 = _mm_and_si64 (a, MC (packed_565_rb)); + __m64 rb1 = _mm_and_si64 (b, MC (packed_565_rb)); + + __m64 t0 = _mm_madd_pi16 (rb0, MC (565_pack_multiplier)); + __m64 t1 = _mm_madd_pi16 (rb1, MC (565_pack_multiplier)); + + __m64 g0 = _mm_and_si64 (a, MC (packed_565_g)); + __m64 g1 = _mm_and_si64 (b, MC (packed_565_g)); + + t0 = _mm_or_si64 (t0, g0); + t1 = _mm_or_si64 (t1, g1); + + t0 = shift(t0, -5); +#ifdef USE_ARM_IWMMXT + t1 = shift(t1, -5); + return _mm_packs_pu32 (t0, t1); +#else + t1 = shift(t1, -5 + 16); + return _mm_shuffle_pi16 (_mm_or_si64 (t0, t1), _MM_SHUFFLE (3, 1, 2, 0)); +#endif +} + +#ifndef _MSC_VER + +static force_inline __m64 +pack_4x565 (__m64 v0, __m64 v1, __m64 v2, __m64 v3) +{ + return pack_4xpacked565 (pack8888 (v0, v1), pack8888 (v2, v3)); +} + +static force_inline __m64 +pix_add_mul (__m64 x, __m64 a, __m64 y, __m64 b) +{ + x = pix_multiply (x, a); + y = pix_multiply (y, b); + + return pix_add (x, y); +} + +#else + +/* MSVC only handles a "pass by register" of up to three SSE intrinsics */ + +#define pack_4x565(v0, v1, v2, v3) \ + pack_4xpacked565 (pack8888 (v0, v1), pack8888 (v2, v3)) + +#define pix_add_mul(x, a, y, b) \ + ( x = pix_multiply (x, a), \ + y = pix_multiply (y, b), \ + pix_add (x, y) ) + +#endif + +/* --------------- MMX code patch for fbcompose.c --------------------- */ + +static force_inline __m64 +combine (const uint32_t *src, const uint32_t *mask) +{ + __m64 vsrc = load8888 (src); + + if (mask) + { + __m64 m = load8888 (mask); + + m = expand_alpha (m); + vsrc = pix_multiply (vsrc, m); + } + + return vsrc; +} + +static force_inline __m64 +core_combine_over_u_pixel_mmx (__m64 vsrc, __m64 vdst) +{ + vsrc = _mm_unpacklo_pi8 (vsrc, _mm_setzero_si64 ()); + + if (is_opaque (vsrc)) + { + return vsrc; + } + else if (!is_zero (vsrc)) + { + return over (vsrc, expand_alpha (vsrc), + _mm_unpacklo_pi8 (vdst, _mm_setzero_si64 ())); + } + + return _mm_unpacklo_pi8 (vdst, _mm_setzero_si64 ()); +} + +static void +mmx_combine_over_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 vsrc = combine (src, mask); + + if (is_opaque (vsrc)) + { + store8888 (dest, vsrc); + } + else if (!is_zero (vsrc)) + { + __m64 sa = expand_alpha (vsrc); + store8888 (dest, over (vsrc, sa, load8888 (dest))); + } + + ++dest; + ++src; + if (mask) + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_over_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 d, da; + __m64 s = combine (src, mask); + + d = load8888 (dest); + da = expand_alpha (d); + store8888 (dest, over (d, da, s)); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_in_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 a; + __m64 x = combine (src, mask); + + a = load8888 (dest); + a = expand_alpha (a); + x = pix_multiply (x, a); + + store8888 (dest, x); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_in_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 a = combine (src, mask); + __m64 x; + + x = load8888 (dest); + a = expand_alpha (a); + x = pix_multiply (x, a); + store8888 (dest, x); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_out_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 a; + __m64 x = combine (src, mask); + + a = load8888 (dest); + a = expand_alpha (a); + a = negate (a); + x = pix_multiply (x, a); + store8888 (dest, x); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_out_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 a = combine (src, mask); + __m64 x; + + x = load8888 (dest); + a = expand_alpha (a); + a = negate (a); + x = pix_multiply (x, a); + + store8888 (dest, x); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_atop_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 da, d, sia; + __m64 s = combine (src, mask); + + d = load8888 (dest); + sia = expand_alpha (s); + sia = negate (sia); + da = expand_alpha (d); + s = pix_add_mul (s, da, d, sia); + store8888 (dest, s); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_atop_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end; + + end = dest + width; + + while (dest < end) + { + __m64 dia, d, sa; + __m64 s = combine (src, mask); + + d = load8888 (dest); + sa = expand_alpha (s); + dia = expand_alpha (d); + dia = negate (dia); + s = pix_add_mul (s, dia, d, sa); + store8888 (dest, s); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_xor_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 dia, d, sia; + __m64 s = combine (src, mask); + + d = load8888 (dest); + sia = expand_alpha (s); + dia = expand_alpha (d); + sia = negate (sia); + dia = negate (dia); + s = pix_add_mul (s, dia, d, sia); + store8888 (dest, s); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_add_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + __m64 d; + __m64 s = combine (src, mask); + + d = load8888 (dest); + s = pix_add (s, d); + store8888 (dest, s); + + ++dest; + ++src; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_saturate_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = dest + width; + + while (dest < end) + { + uint32_t s, sa, da; + uint32_t d = *dest; + __m64 ms = combine (src, mask); + __m64 md = load8888 (dest); + + store8888(&s, ms); + da = ~d >> 24; + sa = s >> 24; + + if (sa > da) + { + uint32_t quot = DIV_UN8 (da, sa) << 24; + __m64 msa = load8888 ("); + msa = expand_alpha (msa); + ms = pix_multiply (ms, msa); + } + + md = pix_add (md, ms); + store8888 (dest, md); + + ++src; + ++dest; + if (mask) + mask++; + } + _mm_empty (); +} + +static void +mmx_combine_src_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + + s = pix_multiply (s, a); + store8888 (dest, s); + + ++src; + ++mask; + ++dest; + } + _mm_empty (); +} + +static void +mmx_combine_over_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 sa = expand_alpha (s); + + store8888 (dest, in_over (s, sa, a, d)); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_over_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 da = expand_alpha (d); + + store8888 (dest, over (d, da, in (s, a))); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_in_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 da = expand_alpha (d); + + s = pix_multiply (s, a); + s = pix_multiply (s, da); + store8888 (dest, s); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_in_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 sa = expand_alpha (s); + + a = pix_multiply (a, sa); + d = pix_multiply (d, a); + store8888 (dest, d); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_out_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 da = expand_alpha (d); + + da = negate (da); + s = pix_multiply (s, a); + s = pix_multiply (s, da); + store8888 (dest, s); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_out_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 sa = expand_alpha (s); + + a = pix_multiply (a, sa); + a = negate (a); + d = pix_multiply (d, a); + store8888 (dest, d); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_atop_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 da = expand_alpha (d); + __m64 sa = expand_alpha (s); + + s = pix_multiply (s, a); + a = pix_multiply (a, sa); + a = negate (a); + d = pix_add_mul (d, a, s, da); + store8888 (dest, d); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_atop_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 da = expand_alpha (d); + __m64 sa = expand_alpha (s); + + s = pix_multiply (s, a); + a = pix_multiply (a, sa); + da = negate (da); + d = pix_add_mul (d, a, s, da); + store8888 (dest, d); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_xor_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + __m64 da = expand_alpha (d); + __m64 sa = expand_alpha (s); + + s = pix_multiply (s, a); + a = pix_multiply (a, sa); + da = negate (da); + a = negate (a); + d = pix_add_mul (d, a, s, da); + store8888 (dest, d); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +static void +mmx_combine_add_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + const uint32_t *end = src + width; + + while (src < end) + { + __m64 a = load8888 (mask); + __m64 s = load8888 (src); + __m64 d = load8888 (dest); + + s = pix_multiply (s, a); + d = pix_add (s, d); + store8888 (dest, d); + + ++src; + ++dest; + ++mask; + } + _mm_empty (); +} + +/* ------------- MMX code paths called from fbpict.c -------------------- */ + +static void +mmx_composite_over_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint32_t *dst_line, *dst; + int32_t w; + int dst_stride; + __m64 vsrc, vsrca; + + CHECKPOINT (); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + vsrc = load8888 (&src); + vsrca = expand_alpha (vsrc); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + w = width; + + CHECKPOINT (); + + while (w && (uintptr_t)dst & 7) + { + store8888 (dst, over (vsrc, vsrca, load8888 (dst))); + + w--; + dst++; + } + + while (w >= 2) + { + __m64 vdest; + __m64 dest0, dest1; + + vdest = *(__m64 *)dst; + + dest0 = over (vsrc, vsrca, expand8888 (vdest, 0)); + dest1 = over (vsrc, vsrca, expand8888 (vdest, 1)); + + *(__m64 *)dst = pack8888 (dest0, dest1); + + dst += 2; + w -= 2; + } + + CHECKPOINT (); + + if (w) + { + store8888 (dst, over (vsrc, vsrca, load8888 (dst))); + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_n_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint16_t *dst_line, *dst; + int32_t w; + int dst_stride; + __m64 vsrc, vsrca; + + CHECKPOINT (); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + + vsrc = load8888 (&src); + vsrca = expand_alpha (vsrc); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + w = width; + + CHECKPOINT (); + + while (w && (uintptr_t)dst & 7) + { + uint64_t d = *dst; + __m64 vdest = expand565 (to_m64 (d), 0); + + vdest = pack_565 (over (vsrc, vsrca, vdest), vdest, 0); + *dst = to_uint64 (vdest); + + w--; + dst++; + } + + while (w >= 4) + { + __m64 vdest = *(__m64 *)dst; + __m64 v0, v1, v2, v3; + + expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); + + v0 = over (vsrc, vsrca, v0); + v1 = over (vsrc, vsrca, v1); + v2 = over (vsrc, vsrca, v2); + v3 = over (vsrc, vsrca, v3); + + *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3); + + dst += 4; + w -= 4; + } + + CHECKPOINT (); + + while (w) + { + uint64_t d = *dst; + __m64 vdest = expand565 (to_m64 (d), 0); + + vdest = pack_565 (over (vsrc, vsrca, vdest), vdest, 0); + *dst = to_uint64 (vdest); + + w--; + dst++; + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_n_8888_8888_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint32_t *dst_line; + uint32_t *mask_line; + int dst_stride, mask_stride; + __m64 vsrc, vsrca; + + CHECKPOINT (); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + vsrc = load8888 (&src); + vsrca = expand_alpha (vsrc); + + while (height--) + { + int twidth = width; + uint32_t *p = (uint32_t *)mask_line; + uint32_t *q = (uint32_t *)dst_line; + + while (twidth && (uintptr_t)q & 7) + { + uint32_t m = *(uint32_t *)p; + + if (m) + { + __m64 vdest = load8888 (q); + vdest = in_over (vsrc, vsrca, load8888 (&m), vdest); + store8888 (q, vdest); + } + + twidth--; + p++; + q++; + } + + while (twidth >= 2) + { + uint32_t m0, m1; + m0 = *p; + m1 = *(p + 1); + + if (m0 | m1) + { + __m64 dest0, dest1; + __m64 vdest = *(__m64 *)q; + + dest0 = in_over (vsrc, vsrca, load8888 (&m0), + expand8888 (vdest, 0)); + dest1 = in_over (vsrc, vsrca, load8888 (&m1), + expand8888 (vdest, 1)); + + *(__m64 *)q = pack8888 (dest0, dest1); + } + + p += 2; + q += 2; + twidth -= 2; + } + + if (twidth) + { + uint32_t m = *(uint32_t *)p; + + if (m) + { + __m64 vdest = load8888 (q); + vdest = in_over (vsrc, vsrca, load8888 (&m), vdest); + store8888 (q, vdest); + } + + twidth--; + p++; + q++; + } + + dst_line += dst_stride; + mask_line += mask_stride; + } + + _mm_empty (); +} + +static void +mmx_composite_over_8888_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + uint32_t mask; + __m64 vmask; + int dst_stride, src_stride; + int32_t w; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + mask = _pixman_image_get_solid (imp, mask_image, dest_image->bits.format); + vmask = expand_alpha (load8888 (&mask)); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 7) + { + __m64 s = load8888 (src); + __m64 d = load8888 (dst); + + store8888 (dst, in_over (s, expand_alpha (s), vmask, d)); + + w--; + dst++; + src++; + } + + while (w >= 2) + { + __m64 vs = ldq_u ((__m64 *)src); + __m64 vd = *(__m64 *)dst; + __m64 vsrc0 = expand8888 (vs, 0); + __m64 vsrc1 = expand8888 (vs, 1); + + *(__m64 *)dst = pack8888 ( + in_over (vsrc0, expand_alpha (vsrc0), vmask, expand8888 (vd, 0)), + in_over (vsrc1, expand_alpha (vsrc1), vmask, expand8888 (vd, 1))); + + w -= 2; + dst += 2; + src += 2; + } + + if (w) + { + __m64 s = load8888 (src); + __m64 d = load8888 (dst); + + store8888 (dst, in_over (s, expand_alpha (s), vmask, d)); + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_x888_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + uint32_t mask; + __m64 vmask; + int dst_stride, src_stride; + int32_t w; + __m64 srca; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + mask = _pixman_image_get_solid (imp, mask_image, dest_image->bits.format); + + vmask = expand_alpha (load8888 (&mask)); + srca = MC (4x00ff); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 7) + { + uint32_t ssrc = *src | 0xff000000; + __m64 s = load8888 (&ssrc); + __m64 d = load8888 (dst); + + store8888 (dst, in_over (s, srca, vmask, d)); + + w--; + dst++; + src++; + } + + while (w >= 16) + { + __m64 vd0 = *(__m64 *)(dst + 0); + __m64 vd1 = *(__m64 *)(dst + 2); + __m64 vd2 = *(__m64 *)(dst + 4); + __m64 vd3 = *(__m64 *)(dst + 6); + __m64 vd4 = *(__m64 *)(dst + 8); + __m64 vd5 = *(__m64 *)(dst + 10); + __m64 vd6 = *(__m64 *)(dst + 12); + __m64 vd7 = *(__m64 *)(dst + 14); + + __m64 vs0 = ldq_u ((__m64 *)(src + 0)); + __m64 vs1 = ldq_u ((__m64 *)(src + 2)); + __m64 vs2 = ldq_u ((__m64 *)(src + 4)); + __m64 vs3 = ldq_u ((__m64 *)(src + 6)); + __m64 vs4 = ldq_u ((__m64 *)(src + 8)); + __m64 vs5 = ldq_u ((__m64 *)(src + 10)); + __m64 vs6 = ldq_u ((__m64 *)(src + 12)); + __m64 vs7 = ldq_u ((__m64 *)(src + 14)); + + vd0 = pack8888 ( + in_over (expandx888 (vs0, 0), srca, vmask, expand8888 (vd0, 0)), + in_over (expandx888 (vs0, 1), srca, vmask, expand8888 (vd0, 1))); + + vd1 = pack8888 ( + in_over (expandx888 (vs1, 0), srca, vmask, expand8888 (vd1, 0)), + in_over (expandx888 (vs1, 1), srca, vmask, expand8888 (vd1, 1))); + + vd2 = pack8888 ( + in_over (expandx888 (vs2, 0), srca, vmask, expand8888 (vd2, 0)), + in_over (expandx888 (vs2, 1), srca, vmask, expand8888 (vd2, 1))); + + vd3 = pack8888 ( + in_over (expandx888 (vs3, 0), srca, vmask, expand8888 (vd3, 0)), + in_over (expandx888 (vs3, 1), srca, vmask, expand8888 (vd3, 1))); + + vd4 = pack8888 ( + in_over (expandx888 (vs4, 0), srca, vmask, expand8888 (vd4, 0)), + in_over (expandx888 (vs4, 1), srca, vmask, expand8888 (vd4, 1))); + + vd5 = pack8888 ( + in_over (expandx888 (vs5, 0), srca, vmask, expand8888 (vd5, 0)), + in_over (expandx888 (vs5, 1), srca, vmask, expand8888 (vd5, 1))); + + vd6 = pack8888 ( + in_over (expandx888 (vs6, 0), srca, vmask, expand8888 (vd6, 0)), + in_over (expandx888 (vs6, 1), srca, vmask, expand8888 (vd6, 1))); + + vd7 = pack8888 ( + in_over (expandx888 (vs7, 0), srca, vmask, expand8888 (vd7, 0)), + in_over (expandx888 (vs7, 1), srca, vmask, expand8888 (vd7, 1))); + + *(__m64 *)(dst + 0) = vd0; + *(__m64 *)(dst + 2) = vd1; + *(__m64 *)(dst + 4) = vd2; + *(__m64 *)(dst + 6) = vd3; + *(__m64 *)(dst + 8) = vd4; + *(__m64 *)(dst + 10) = vd5; + *(__m64 *)(dst + 12) = vd6; + *(__m64 *)(dst + 14) = vd7; + + w -= 16; + dst += 16; + src += 16; + } + + while (w) + { + uint32_t ssrc = *src | 0xff000000; + __m64 s = load8888 (&ssrc); + __m64 d = load8888 (dst); + + store8888 (dst, in_over (s, srca, vmask, d)); + + w--; + dst++; + src++; + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + uint32_t s; + int dst_stride, src_stride; + uint8_t a; + int32_t w; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w--) + { + s = *src++; + a = s >> 24; + + if (a == 0xff) + { + *dst = s; + } + else if (s) + { + __m64 ms, sa; + ms = load8888 (&s); + sa = expand_alpha (ms); + store8888 (dst, over (ms, sa, load8888 (dst))); + } + + dst++; + } + } + _mm_empty (); +} + +static void +mmx_composite_over_8888_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + +#if 0 + /* FIXME */ + assert (src_image->drawable == mask_image->drawable); +#endif + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + CHECKPOINT (); + + while (w && (uintptr_t)dst & 7) + { + __m64 vsrc = load8888 (src); + uint64_t d = *dst; + __m64 vdest = expand565 (to_m64 (d), 0); + + vdest = pack_565 ( + over (vsrc, expand_alpha (vsrc), vdest), vdest, 0); + + *dst = to_uint64 (vdest); + + w--; + dst++; + src++; + } + + CHECKPOINT (); + + while (w >= 4) + { + __m64 vdest = *(__m64 *)dst; + __m64 v0, v1, v2, v3; + __m64 vsrc0, vsrc1, vsrc2, vsrc3; + + expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); + + vsrc0 = load8888 ((src + 0)); + vsrc1 = load8888 ((src + 1)); + vsrc2 = load8888 ((src + 2)); + vsrc3 = load8888 ((src + 3)); + + v0 = over (vsrc0, expand_alpha (vsrc0), v0); + v1 = over (vsrc1, expand_alpha (vsrc1), v1); + v2 = over (vsrc2, expand_alpha (vsrc2), v2); + v3 = over (vsrc3, expand_alpha (vsrc3), v3); + + *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3); + + w -= 4; + dst += 4; + src += 4; + } + + CHECKPOINT (); + + while (w) + { + __m64 vsrc = load8888 (src); + uint64_t d = *dst; + __m64 vdest = expand565 (to_m64 (d), 0); + + vdest = pack_565 (over (vsrc, expand_alpha (vsrc), vdest), vdest, 0); + + *dst = to_uint64 (vdest); + + w--; + dst++; + src++; + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_n_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint32_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + __m64 vsrc, vsrca; + uint64_t srcsrc; + + CHECKPOINT (); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + return; + + srcsrc = (uint64_t)src << 32 | src; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + vsrc = load8888 (&src); + vsrca = expand_alpha (vsrc); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + CHECKPOINT (); + + while (w && (uintptr_t)dst & 7) + { + uint64_t m = *mask; + + if (m) + { + __m64 vdest = in_over (vsrc, vsrca, + expand_alpha_rev (to_m64 (m)), + load8888 (dst)); + + store8888 (dst, vdest); + } + + w--; + mask++; + dst++; + } + + CHECKPOINT (); + + while (w >= 2) + { + uint64_t m0, m1; + + m0 = *mask; + m1 = *(mask + 1); + + if (srca == 0xff && (m0 & m1) == 0xff) + { + *(uint64_t *)dst = srcsrc; + } + else if (m0 | m1) + { + __m64 vdest; + __m64 dest0, dest1; + + vdest = *(__m64 *)dst; + + dest0 = in_over (vsrc, vsrca, expand_alpha_rev (to_m64 (m0)), + expand8888 (vdest, 0)); + dest1 = in_over (vsrc, vsrca, expand_alpha_rev (to_m64 (m1)), + expand8888 (vdest, 1)); + + *(__m64 *)dst = pack8888 (dest0, dest1); + } + + mask += 2; + dst += 2; + w -= 2; + } + + CHECKPOINT (); + + if (w) + { + uint64_t m = *mask; + + if (m) + { + __m64 vdest = load8888 (dst); + + vdest = in_over ( + vsrc, vsrca, expand_alpha_rev (to_m64 (m)), vdest); + store8888 (dst, vdest); + } + } + } + + _mm_empty (); +} + +static pixman_bool_t +mmx_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + uint64_t fill; + __m64 vfill; + uint32_t byte_width; + uint8_t *byte_line; + +#if defined __GNUC__ && defined USE_X86_MMX + __m64 v1, v2, v3, v4, v5, v6, v7; +#endif + + if (bpp != 16 && bpp != 32 && bpp != 8) + return FALSE; + + if (bpp == 8) + { + stride = stride * (int) sizeof (uint32_t) / 1; + byte_line = (uint8_t *)(((uint8_t *)bits) + stride * y + x); + byte_width = width; + stride *= 1; + filler = (filler & 0xff) * 0x01010101; + } + else if (bpp == 16) + { + stride = stride * (int) sizeof (uint32_t) / 2; + byte_line = (uint8_t *)(((uint16_t *)bits) + stride * y + x); + byte_width = 2 * width; + stride *= 2; + filler = (filler & 0xffff) * 0x00010001; + } + else + { + stride = stride * (int) sizeof (uint32_t) / 4; + byte_line = (uint8_t *)(((uint32_t *)bits) + stride * y + x); + byte_width = 4 * width; + stride *= 4; + } + + fill = ((uint64_t)filler << 32) | filler; + vfill = to_m64 (fill); + +#if defined __GNUC__ && defined USE_X86_MMX + __asm__ ( + "movq %7, %0\n" + "movq %7, %1\n" + "movq %7, %2\n" + "movq %7, %3\n" + "movq %7, %4\n" + "movq %7, %5\n" + "movq %7, %6\n" + : "=&y" (v1), "=&y" (v2), "=&y" (v3), + "=&y" (v4), "=&y" (v5), "=&y" (v6), "=y" (v7) + : "y" (vfill)); +#endif + + while (height--) + { + int w; + uint8_t *d = byte_line; + + byte_line += stride; + w = byte_width; + + if (w >= 1 && ((uintptr_t)d & 1)) + { + *(uint8_t *)d = (filler & 0xff); + w--; + d++; + } + + if (w >= 2 && ((uintptr_t)d & 3)) + { + *(uint16_t *)d = filler; + w -= 2; + d += 2; + } + + while (w >= 4 && ((uintptr_t)d & 7)) + { + *(uint32_t *)d = filler; + + w -= 4; + d += 4; + } + + while (w >= 64) + { +#if defined __GNUC__ && defined USE_X86_MMX + __asm__ ( + "movq %1, (%0)\n" + "movq %2, 8(%0)\n" + "movq %3, 16(%0)\n" + "movq %4, 24(%0)\n" + "movq %5, 32(%0)\n" + "movq %6, 40(%0)\n" + "movq %7, 48(%0)\n" + "movq %8, 56(%0)\n" + : + : "r" (d), + "y" (vfill), "y" (v1), "y" (v2), "y" (v3), + "y" (v4), "y" (v5), "y" (v6), "y" (v7) + : "memory"); +#else + *(__m64*) (d + 0) = vfill; + *(__m64*) (d + 8) = vfill; + *(__m64*) (d + 16) = vfill; + *(__m64*) (d + 24) = vfill; + *(__m64*) (d + 32) = vfill; + *(__m64*) (d + 40) = vfill; + *(__m64*) (d + 48) = vfill; + *(__m64*) (d + 56) = vfill; +#endif + w -= 64; + d += 64; + } + + while (w >= 4) + { + *(uint32_t *)d = filler; + + w -= 4; + d += 4; + } + if (w >= 2) + { + *(uint16_t *)d = filler; + w -= 2; + d += 2; + } + if (w >= 1) + { + *(uint8_t *)d = (filler & 0xff); + w--; + d++; + } + + } + + _mm_empty (); + return TRUE; +} + +static void +mmx_composite_src_x888_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst; + uint32_t *src_line, *src, s; + int dst_stride, src_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 7) + { + s = *src++; + *dst = convert_8888_to_0565 (s); + dst++; + w--; + } + + while (w >= 4) + { + __m64 vdest; + __m64 vsrc0 = ldq_u ((__m64 *)(src + 0)); + __m64 vsrc1 = ldq_u ((__m64 *)(src + 2)); + + vdest = pack_4xpacked565 (vsrc0, vsrc1); + + *(__m64 *)dst = vdest; + + w -= 4; + src += 4; + dst += 4; + } + + while (w) + { + s = *src++; + *dst = convert_8888_to_0565 (s); + dst++; + w--; + } + } + + _mm_empty (); +} + +static void +mmx_composite_src_n_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint32_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + __m64 vsrc; + uint64_t srcsrc; + + CHECKPOINT (); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + { + mmx_fill (imp, dest_image->bits.bits, dest_image->bits.rowstride, + PIXMAN_FORMAT_BPP (dest_image->bits.format), + dest_x, dest_y, width, height, 0); + return; + } + + srcsrc = (uint64_t)src << 32 | src; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + vsrc = load8888 (&src); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + CHECKPOINT (); + + while (w && (uintptr_t)dst & 7) + { + uint64_t m = *mask; + + if (m) + { + __m64 vdest = in (vsrc, expand_alpha_rev (to_m64 (m))); + + store8888 (dst, vdest); + } + else + { + *dst = 0; + } + + w--; + mask++; + dst++; + } + + CHECKPOINT (); + + while (w >= 2) + { + uint64_t m0, m1; + m0 = *mask; + m1 = *(mask + 1); + + if (srca == 0xff && (m0 & m1) == 0xff) + { + *(uint64_t *)dst = srcsrc; + } + else if (m0 | m1) + { + __m64 dest0, dest1; + + dest0 = in (vsrc, expand_alpha_rev (to_m64 (m0))); + dest1 = in (vsrc, expand_alpha_rev (to_m64 (m1))); + + *(__m64 *)dst = pack8888 (dest0, dest1); + } + else + { + *(uint64_t *)dst = 0; + } + + mask += 2; + dst += 2; + w -= 2; + } + + CHECKPOINT (); + + if (w) + { + uint64_t m = *mask; + + if (m) + { + __m64 vdest = load8888 (dst); + + vdest = in (vsrc, expand_alpha_rev (to_m64 (m))); + store8888 (dst, vdest); + } + else + { + *dst = 0; + } + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_n_8_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint16_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + __m64 vsrc, vsrca, tmp; + __m64 srcsrcsrcsrc; + + CHECKPOINT (); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + vsrc = load8888 (&src); + vsrca = expand_alpha (vsrc); + + tmp = pack_565 (vsrc, _mm_setzero_si64 (), 0); + srcsrcsrcsrc = expand_alpha_rev (tmp); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + CHECKPOINT (); + + while (w && (uintptr_t)dst & 7) + { + uint64_t m = *mask; + + if (m) + { + uint64_t d = *dst; + __m64 vd = to_m64 (d); + __m64 vdest = in_over ( + vsrc, vsrca, expand_alpha_rev (to_m64 (m)), expand565 (vd, 0)); + + vd = pack_565 (vdest, _mm_setzero_si64 (), 0); + *dst = to_uint64 (vd); + } + + w--; + mask++; + dst++; + } + + CHECKPOINT (); + + while (w >= 4) + { + uint64_t m0, m1, m2, m3; + m0 = *mask; + m1 = *(mask + 1); + m2 = *(mask + 2); + m3 = *(mask + 3); + + if (srca == 0xff && (m0 & m1 & m2 & m3) == 0xff) + { + *(__m64 *)dst = srcsrcsrcsrc; + } + else if (m0 | m1 | m2 | m3) + { + __m64 vdest = *(__m64 *)dst; + __m64 v0, v1, v2, v3; + __m64 vm0, vm1, vm2, vm3; + + expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); + + vm0 = to_m64 (m0); + v0 = in_over (vsrc, vsrca, expand_alpha_rev (vm0), v0); + + vm1 = to_m64 (m1); + v1 = in_over (vsrc, vsrca, expand_alpha_rev (vm1), v1); + + vm2 = to_m64 (m2); + v2 = in_over (vsrc, vsrca, expand_alpha_rev (vm2), v2); + + vm3 = to_m64 (m3); + v3 = in_over (vsrc, vsrca, expand_alpha_rev (vm3), v3); + + *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3);; + } + + w -= 4; + mask += 4; + dst += 4; + } + + CHECKPOINT (); + + while (w) + { + uint64_t m = *mask; + + if (m) + { + uint64_t d = *dst; + __m64 vd = to_m64 (d); + __m64 vdest = in_over (vsrc, vsrca, expand_alpha_rev (to_m64 (m)), + expand565 (vd, 0)); + vd = pack_565 (vdest, _mm_setzero_si64 (), 0); + *dst = to_uint64 (vd); + } + + w--; + mask++; + dst++; + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_pixbuf_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + +#if 0 + /* FIXME */ + assert (src_image->drawable == mask_image->drawable); +#endif + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + CHECKPOINT (); + + while (w && (uintptr_t)dst & 7) + { + __m64 vsrc = load8888 (src); + uint64_t d = *dst; + __m64 vdest = expand565 (to_m64 (d), 0); + + vdest = pack_565 (over_rev_non_pre (vsrc, vdest), vdest, 0); + + *dst = to_uint64 (vdest); + + w--; + dst++; + src++; + } + + CHECKPOINT (); + + while (w >= 4) + { + uint32_t s0, s1, s2, s3; + unsigned char a0, a1, a2, a3; + + s0 = *src; + s1 = *(src + 1); + s2 = *(src + 2); + s3 = *(src + 3); + + a0 = (s0 >> 24); + a1 = (s1 >> 24); + a2 = (s2 >> 24); + a3 = (s3 >> 24); + + if ((a0 & a1 & a2 & a3) == 0xFF) + { + __m64 v0 = invert_colors (load8888 (&s0)); + __m64 v1 = invert_colors (load8888 (&s1)); + __m64 v2 = invert_colors (load8888 (&s2)); + __m64 v3 = invert_colors (load8888 (&s3)); + + *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3); + } + else if (s0 | s1 | s2 | s3) + { + __m64 vdest = *(__m64 *)dst; + __m64 v0, v1, v2, v3; + + __m64 vsrc0 = load8888 (&s0); + __m64 vsrc1 = load8888 (&s1); + __m64 vsrc2 = load8888 (&s2); + __m64 vsrc3 = load8888 (&s3); + + expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); + + v0 = over_rev_non_pre (vsrc0, v0); + v1 = over_rev_non_pre (vsrc1, v1); + v2 = over_rev_non_pre (vsrc2, v2); + v3 = over_rev_non_pre (vsrc3, v3); + + *(__m64 *)dst = pack_4x565 (v0, v1, v2, v3); + } + + w -= 4; + dst += 4; + src += 4; + } + + CHECKPOINT (); + + while (w) + { + __m64 vsrc = load8888 (src); + uint64_t d = *dst; + __m64 vdest = expand565 (to_m64 (d), 0); + + vdest = pack_565 (over_rev_non_pre (vsrc, vdest), vdest, 0); + + *dst = to_uint64 (vdest); + + w--; + dst++; + src++; + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_pixbuf_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + +#if 0 + /* FIXME */ + assert (src_image->drawable == mask_image->drawable); +#endif + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 7) + { + __m64 s = load8888 (src); + __m64 d = load8888 (dst); + + store8888 (dst, over_rev_non_pre (s, d)); + + w--; + dst++; + src++; + } + + while (w >= 2) + { + uint32_t s0, s1; + unsigned char a0, a1; + __m64 d0, d1; + + s0 = *src; + s1 = *(src + 1); + + a0 = (s0 >> 24); + a1 = (s1 >> 24); + + if ((a0 & a1) == 0xFF) + { + d0 = invert_colors (load8888 (&s0)); + d1 = invert_colors (load8888 (&s1)); + + *(__m64 *)dst = pack8888 (d0, d1); + } + else if (s0 | s1) + { + __m64 vdest = *(__m64 *)dst; + + d0 = over_rev_non_pre (load8888 (&s0), expand8888 (vdest, 0)); + d1 = over_rev_non_pre (load8888 (&s1), expand8888 (vdest, 1)); + + *(__m64 *)dst = pack8888 (d0, d1); + } + + w -= 2; + dst += 2; + src += 2; + } + + if (w) + { + __m64 s = load8888 (src); + __m64 d = load8888 (dst); + + store8888 (dst, over_rev_non_pre (s, d)); + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_n_8888_0565_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint16_t *dst_line; + uint32_t *mask_line; + int dst_stride, mask_stride; + __m64 vsrc, vsrca; + + CHECKPOINT (); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + vsrc = load8888 (&src); + vsrca = expand_alpha (vsrc); + + while (height--) + { + int twidth = width; + uint32_t *p = (uint32_t *)mask_line; + uint16_t *q = (uint16_t *)dst_line; + + while (twidth && ((uintptr_t)q & 7)) + { + uint32_t m = *(uint32_t *)p; + + if (m) + { + uint64_t d = *q; + __m64 vdest = expand565 (to_m64 (d), 0); + vdest = pack_565 (in_over (vsrc, vsrca, load8888 (&m), vdest), vdest, 0); + *q = to_uint64 (vdest); + } + + twidth--; + p++; + q++; + } + + while (twidth >= 4) + { + uint32_t m0, m1, m2, m3; + + m0 = *p; + m1 = *(p + 1); + m2 = *(p + 2); + m3 = *(p + 3); + + if ((m0 | m1 | m2 | m3)) + { + __m64 vdest = *(__m64 *)q; + __m64 v0, v1, v2, v3; + + expand_4x565 (vdest, &v0, &v1, &v2, &v3, 0); + + v0 = in_over (vsrc, vsrca, load8888 (&m0), v0); + v1 = in_over (vsrc, vsrca, load8888 (&m1), v1); + v2 = in_over (vsrc, vsrca, load8888 (&m2), v2); + v3 = in_over (vsrc, vsrca, load8888 (&m3), v3); + + *(__m64 *)q = pack_4x565 (v0, v1, v2, v3); + } + twidth -= 4; + p += 4; + q += 4; + } + + while (twidth) + { + uint32_t m; + + m = *(uint32_t *)p; + if (m) + { + uint64_t d = *q; + __m64 vdest = expand565 (to_m64 (d), 0); + vdest = pack_565 (in_over (vsrc, vsrca, load8888 (&m), vdest), vdest, 0); + *q = to_uint64 (vdest); + } + + twidth--; + p++; + q++; + } + + mask_line += mask_stride; + dst_line += dst_stride; + } + + _mm_empty (); +} + +static void +mmx_composite_in_n_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + uint32_t src; + uint8_t sa; + __m64 vsrc, vsrca; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + sa = src >> 24; + + vsrc = load8888 (&src); + vsrca = expand_alpha (vsrc); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w && (uintptr_t)dst & 7) + { + uint16_t tmp; + uint8_t a; + uint32_t m, d; + + a = *mask++; + d = *dst; + + m = MUL_UN8 (sa, a, tmp); + d = MUL_UN8 (m, d, tmp); + + *dst++ = d; + w--; + } + + while (w >= 4) + { + __m64 vmask; + __m64 vdest; + + vmask = load8888u ((uint32_t *)mask); + vdest = load8888 ((uint32_t *)dst); + + store8888 ((uint32_t *)dst, in (in (vsrca, vmask), vdest)); + + dst += 4; + mask += 4; + w -= 4; + } + + while (w--) + { + uint16_t tmp; + uint8_t a; + uint32_t m, d; + + a = *mask++; + d = *dst; + + m = MUL_UN8 (sa, a, tmp); + d = MUL_UN8 (m, d, tmp); + + *dst++ = d; + } + } + + _mm_empty (); +} + +static void +mmx_composite_in_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *src_line, *src; + int src_stride, dst_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 3) + { + uint8_t s, d; + uint16_t tmp; + + s = *src; + d = *dst; + + *dst = MUL_UN8 (s, d, tmp); + + src++; + dst++; + w--; + } + + while (w >= 4) + { + uint32_t *s = (uint32_t *)src; + uint32_t *d = (uint32_t *)dst; + + store8888 (d, in (load8888u (s), load8888 (d))); + + w -= 4; + dst += 4; + src += 4; + } + + while (w--) + { + uint8_t s, d; + uint16_t tmp; + + s = *src; + d = *dst; + + *dst = MUL_UN8 (s, d, tmp); + + src++; + dst++; + } + } + + _mm_empty (); +} + +static void +mmx_composite_add_n_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + uint32_t src; + uint8_t sa; + __m64 vsrc, vsrca; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + sa = src >> 24; + + if (src == 0) + return; + + vsrc = load8888 (&src); + vsrca = expand_alpha (vsrc); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w && (uintptr_t)dst & 3) + { + uint16_t tmp; + uint16_t a; + uint32_t m, d; + uint32_t r; + + a = *mask++; + d = *dst; + + m = MUL_UN8 (sa, a, tmp); + r = ADD_UN8 (m, d, tmp); + + *dst++ = r; + w--; + } + + while (w >= 4) + { + __m64 vmask; + __m64 vdest; + + vmask = load8888u ((uint32_t *)mask); + vdest = load8888 ((uint32_t *)dst); + + store8888 ((uint32_t *)dst, _mm_adds_pu8 (in (vsrca, vmask), vdest)); + + dst += 4; + mask += 4; + w -= 4; + } + + while (w--) + { + uint16_t tmp; + uint16_t a; + uint32_t m, d; + uint32_t r; + + a = *mask++; + d = *dst; + + m = MUL_UN8 (sa, a, tmp); + r = ADD_UN8 (m, d, tmp); + + *dst++ = r; + } + } + + _mm_empty (); +} + +static void +mmx_composite_add_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + uint8_t s, d; + uint16_t t; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 7) + { + s = *src; + d = *dst; + t = d + s; + s = t | (0 - (t >> 8)); + *dst = s; + + dst++; + src++; + w--; + } + + while (w >= 8) + { + *(__m64*)dst = _mm_adds_pu8 (ldq_u ((__m64 *)src), *(__m64*)dst); + dst += 8; + src += 8; + w -= 8; + } + + while (w) + { + s = *src; + d = *dst; + t = d + s; + s = t | (0 - (t >> 8)); + *dst = s; + + dst++; + src++; + w--; + } + } + + _mm_empty (); +} + +static void +mmx_composite_add_0565_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst; + uint32_t d; + uint16_t *src_line, *src; + uint32_t s; + int dst_stride, src_stride; + int32_t w; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint16_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 7) + { + s = *src++; + if (s) + { + d = *dst; + s = convert_0565_to_8888 (s); + if (d) + { + d = convert_0565_to_8888 (d); + UN8x4_ADD_UN8x4 (s, d); + } + *dst = convert_8888_to_0565 (s); + } + dst++; + w--; + } + + while (w >= 4) + { + __m64 vdest = *(__m64 *)dst; + __m64 vsrc = ldq_u ((__m64 *)src); + __m64 vd0, vd1; + __m64 vs0, vs1; + + expand_4xpacked565 (vdest, &vd0, &vd1, 0); + expand_4xpacked565 (vsrc, &vs0, &vs1, 0); + + vd0 = _mm_adds_pu8 (vd0, vs0); + vd1 = _mm_adds_pu8 (vd1, vs1); + + *(__m64 *)dst = pack_4xpacked565 (vd0, vd1); + + dst += 4; + src += 4; + w -= 4; + } + + while (w--) + { + s = *src++; + if (s) + { + d = *dst; + s = convert_0565_to_8888 (s); + if (d) + { + d = convert_0565_to_8888 (d); + UN8x4_ADD_UN8x4 (s, d); + } + *dst = convert_8888_to_0565 (s); + } + dst++; + } + } + + _mm_empty (); +} + +static void +mmx_composite_add_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + + CHECKPOINT (); + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 7) + { + store (dst, _mm_adds_pu8 (load ((const uint32_t *)src), + load ((const uint32_t *)dst))); + dst++; + src++; + w--; + } + + while (w >= 2) + { + *(__m64 *)dst = _mm_adds_pu8 (ldq_u ((__m64 *)src), *(__m64*)dst); + dst += 2; + src += 2; + w -= 2; + } + + if (w) + { + store (dst, _mm_adds_pu8 (load ((const uint32_t *)src), + load ((const uint32_t *)dst))); + + } + } + + _mm_empty (); +} + +static pixman_bool_t +mmx_blt (pixman_implementation_t *imp, + uint32_t * src_bits, + uint32_t * dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height) +{ + uint8_t * src_bytes; + uint8_t * dst_bytes; + int byte_width; + + if (src_bpp != dst_bpp) + return FALSE; + + if (src_bpp == 16) + { + src_stride = src_stride * (int) sizeof (uint32_t) / 2; + dst_stride = dst_stride * (int) sizeof (uint32_t) / 2; + src_bytes = (uint8_t *)(((uint16_t *)src_bits) + src_stride * (src_y) + (src_x)); + dst_bytes = (uint8_t *)(((uint16_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); + byte_width = 2 * width; + src_stride *= 2; + dst_stride *= 2; + } + else if (src_bpp == 32) + { + src_stride = src_stride * (int) sizeof (uint32_t) / 4; + dst_stride = dst_stride * (int) sizeof (uint32_t) / 4; + src_bytes = (uint8_t *)(((uint32_t *)src_bits) + src_stride * (src_y) + (src_x)); + dst_bytes = (uint8_t *)(((uint32_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); + byte_width = 4 * width; + src_stride *= 4; + dst_stride *= 4; + } + else + { + return FALSE; + } + + while (height--) + { + int w; + uint8_t *s = src_bytes; + uint8_t *d = dst_bytes; + src_bytes += src_stride; + dst_bytes += dst_stride; + w = byte_width; + + if (w >= 1 && ((uintptr_t)d & 1)) + { + *(uint8_t *)d = *(uint8_t *)s; + w -= 1; + s += 1; + d += 1; + } + + if (w >= 2 && ((uintptr_t)d & 3)) + { + *(uint16_t *)d = *(uint16_t *)s; + w -= 2; + s += 2; + d += 2; + } + + while (w >= 4 && ((uintptr_t)d & 7)) + { + *(uint32_t *)d = ldl_u ((uint32_t *)s); + + w -= 4; + s += 4; + d += 4; + } + + while (w >= 64) + { +#if (defined (__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))) && defined USE_X86_MMX + __asm__ ( + "movq (%1), %%mm0\n" + "movq 8(%1), %%mm1\n" + "movq 16(%1), %%mm2\n" + "movq 24(%1), %%mm3\n" + "movq 32(%1), %%mm4\n" + "movq 40(%1), %%mm5\n" + "movq 48(%1), %%mm6\n" + "movq 56(%1), %%mm7\n" + + "movq %%mm0, (%0)\n" + "movq %%mm1, 8(%0)\n" + "movq %%mm2, 16(%0)\n" + "movq %%mm3, 24(%0)\n" + "movq %%mm4, 32(%0)\n" + "movq %%mm5, 40(%0)\n" + "movq %%mm6, 48(%0)\n" + "movq %%mm7, 56(%0)\n" + : + : "r" (d), "r" (s) + : "memory", + "%mm0", "%mm1", "%mm2", "%mm3", + "%mm4", "%mm5", "%mm6", "%mm7"); +#else + __m64 v0 = ldq_u ((__m64 *)(s + 0)); + __m64 v1 = ldq_u ((__m64 *)(s + 8)); + __m64 v2 = ldq_u ((__m64 *)(s + 16)); + __m64 v3 = ldq_u ((__m64 *)(s + 24)); + __m64 v4 = ldq_u ((__m64 *)(s + 32)); + __m64 v5 = ldq_u ((__m64 *)(s + 40)); + __m64 v6 = ldq_u ((__m64 *)(s + 48)); + __m64 v7 = ldq_u ((__m64 *)(s + 56)); + *(__m64 *)(d + 0) = v0; + *(__m64 *)(d + 8) = v1; + *(__m64 *)(d + 16) = v2; + *(__m64 *)(d + 24) = v3; + *(__m64 *)(d + 32) = v4; + *(__m64 *)(d + 40) = v5; + *(__m64 *)(d + 48) = v6; + *(__m64 *)(d + 56) = v7; +#endif + + w -= 64; + s += 64; + d += 64; + } + while (w >= 4) + { + *(uint32_t *)d = ldl_u ((uint32_t *)s); + + w -= 4; + s += 4; + d += 4; + } + if (w >= 2) + { + *(uint16_t *)d = *(uint16_t *)s; + w -= 2; + s += 2; + d += 2; + } + } + + _mm_empty (); + + return TRUE; +} + +static void +mmx_composite_copy_area (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + + mmx_blt (imp, src_image->bits.bits, + dest_image->bits.bits, + src_image->bits.rowstride, + dest_image->bits.rowstride, + PIXMAN_FORMAT_BPP (src_image->bits.format), + PIXMAN_FORMAT_BPP (dest_image->bits.format), + src_x, src_y, dest_x, dest_y, width, height); +} + +static void +mmx_composite_over_x888_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *src, *src_line; + uint32_t *dst, *dst_line; + uint8_t *mask, *mask_line; + int src_stride, mask_stride, dst_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE (mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + src = src_line; + src_line += src_stride; + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + + w = width; + + while (w--) + { + uint64_t m = *mask; + + if (m) + { + uint32_t ssrc = *src | 0xff000000; + __m64 s = load8888 (&ssrc); + + if (m == 0xff) + { + store8888 (dst, s); + } + else + { + __m64 sa = expand_alpha (s); + __m64 vm = expand_alpha_rev (to_m64 (m)); + __m64 vdest = in_over (s, sa, vm, load8888 (dst)); + + store8888 (dst, vdest); + } + } + + mask++; + dst++; + src++; + } + } + + _mm_empty (); +} + +static void +mmx_composite_over_reverse_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint32_t *dst_line, *dst; + int32_t w; + int dst_stride; + __m64 vsrc; + + CHECKPOINT (); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + vsrc = load8888 (&src); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + w = width; + + CHECKPOINT (); + + while (w && (uintptr_t)dst & 7) + { + __m64 vdest = load8888 (dst); + + store8888 (dst, over (vdest, expand_alpha (vdest), vsrc)); + + w--; + dst++; + } + + while (w >= 2) + { + __m64 vdest = *(__m64 *)dst; + __m64 dest0 = expand8888 (vdest, 0); + __m64 dest1 = expand8888 (vdest, 1); + + + dest0 = over (dest0, expand_alpha (dest0), vsrc); + dest1 = over (dest1, expand_alpha (dest1), vsrc); + + *(__m64 *)dst = pack8888 (dest0, dest1); + + dst += 2; + w -= 2; + } + + CHECKPOINT (); + + if (w) + { + __m64 vdest = load8888 (dst); + + store8888 (dst, over (vdest, expand_alpha (vdest), vsrc)); + } + } + + _mm_empty (); +} + +static force_inline void +scaled_nearest_scanline_mmx_8888_8888_OVER (uint32_t* pd, + const uint32_t* ps, + int32_t w, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t src_width_fixed, + pixman_bool_t fully_transparent_src) +{ + if (fully_transparent_src) + return; + + while (w) + { + __m64 d = load (pd); + __m64 s = load (ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + store8888 (pd, core_combine_over_u_pixel_mmx (s, d)); + pd++; + + w--; + } + + _mm_empty (); +} + +FAST_NEAREST_MAINLOOP (mmx_8888_8888_cover_OVER, + scaled_nearest_scanline_mmx_8888_8888_OVER, + uint32_t, uint32_t, COVER) +FAST_NEAREST_MAINLOOP (mmx_8888_8888_none_OVER, + scaled_nearest_scanline_mmx_8888_8888_OVER, + uint32_t, uint32_t, NONE) +FAST_NEAREST_MAINLOOP (mmx_8888_8888_pad_OVER, + scaled_nearest_scanline_mmx_8888_8888_OVER, + uint32_t, uint32_t, PAD) +FAST_NEAREST_MAINLOOP (mmx_8888_8888_normal_OVER, + scaled_nearest_scanline_mmx_8888_8888_OVER, + uint32_t, uint32_t, NORMAL) + +static force_inline void +scaled_nearest_scanline_mmx_8888_n_8888_OVER (const uint32_t * mask, + uint32_t * dst, + const uint32_t * src, + int32_t w, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t src_width_fixed, + pixman_bool_t zero_src) +{ + __m64 mm_mask; + + if (zero_src || (*mask >> 24) == 0) + { + /* A workaround for https://gcc.gnu.org/PR47759 */ + _mm_empty (); + return; + } + + mm_mask = expand_alpha (load8888 (mask)); + + while (w) + { + uint32_t s = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + if (s) + { + __m64 ms = load8888 (&s); + __m64 alpha = expand_alpha (ms); + __m64 dest = load8888 (dst); + + store8888 (dst, (in_over (ms, alpha, mm_mask, dest))); + } + + dst++; + w--; + } + + _mm_empty (); +} + +FAST_NEAREST_MAINLOOP_COMMON (mmx_8888_n_8888_cover_OVER, + scaled_nearest_scanline_mmx_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, COVER, TRUE, TRUE) +FAST_NEAREST_MAINLOOP_COMMON (mmx_8888_n_8888_pad_OVER, + scaled_nearest_scanline_mmx_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, PAD, TRUE, TRUE) +FAST_NEAREST_MAINLOOP_COMMON (mmx_8888_n_8888_none_OVER, + scaled_nearest_scanline_mmx_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, NONE, TRUE, TRUE) +FAST_NEAREST_MAINLOOP_COMMON (mmx_8888_n_8888_normal_OVER, + scaled_nearest_scanline_mmx_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, NORMAL, TRUE, TRUE) + +#define BSHIFT ((1 << BILINEAR_INTERPOLATION_BITS)) +#define BMSK (BSHIFT - 1) + +#define BILINEAR_DECLARE_VARIABLES \ + const __m64 mm_wt = _mm_set_pi16 (wt, wt, wt, wt); \ + const __m64 mm_wb = _mm_set_pi16 (wb, wb, wb, wb); \ + const __m64 mm_addc7 = _mm_set_pi16 (0, 1, 0, 1); \ + const __m64 mm_xorc7 = _mm_set_pi16 (0, BMSK, 0, BMSK); \ + const __m64 mm_ux = _mm_set_pi16 (unit_x, unit_x, unit_x, unit_x); \ + const __m64 mm_zero = _mm_setzero_si64 (); \ + __m64 mm_x = _mm_set_pi16 (vx, vx, vx, vx) + +#define BILINEAR_INTERPOLATE_ONE_PIXEL(pix) \ +do { \ + /* fetch 2x2 pixel block into 2 mmx registers */ \ + __m64 t = ldq_u ((__m64 *)&src_top [pixman_fixed_to_int (vx)]); \ + __m64 b = ldq_u ((__m64 *)&src_bottom [pixman_fixed_to_int (vx)]); \ + /* vertical interpolation */ \ + __m64 t_hi = _mm_mullo_pi16 (_mm_unpackhi_pi8 (t, mm_zero), mm_wt); \ + __m64 t_lo = _mm_mullo_pi16 (_mm_unpacklo_pi8 (t, mm_zero), mm_wt); \ + __m64 b_hi = _mm_mullo_pi16 (_mm_unpackhi_pi8 (b, mm_zero), mm_wb); \ + __m64 b_lo = _mm_mullo_pi16 (_mm_unpacklo_pi8 (b, mm_zero), mm_wb); \ + __m64 hi = _mm_add_pi16 (t_hi, b_hi); \ + __m64 lo = _mm_add_pi16 (t_lo, b_lo); \ + /* calculate horizontal weights */ \ + __m64 mm_wh = _mm_add_pi16 (mm_addc7, _mm_xor_si64 (mm_xorc7, \ + _mm_srli_pi16 (mm_x, \ + 16 - BILINEAR_INTERPOLATION_BITS))); \ + /* horizontal interpolation */ \ + __m64 p = _mm_unpacklo_pi16 (lo, hi); \ + __m64 q = _mm_unpackhi_pi16 (lo, hi); \ + vx += unit_x; \ + lo = _mm_madd_pi16 (p, mm_wh); \ + hi = _mm_madd_pi16 (q, mm_wh); \ + mm_x = _mm_add_pi16 (mm_x, mm_ux); \ + /* shift and pack the result */ \ + hi = _mm_srli_pi32 (hi, BILINEAR_INTERPOLATION_BITS * 2); \ + lo = _mm_srli_pi32 (lo, BILINEAR_INTERPOLATION_BITS * 2); \ + lo = _mm_packs_pi32 (lo, hi); \ + lo = _mm_packs_pu16 (lo, lo); \ + pix = lo; \ +} while (0) + +#define BILINEAR_SKIP_ONE_PIXEL() \ +do { \ + vx += unit_x; \ + mm_x = _mm_add_pi16 (mm_x, mm_ux); \ +} while(0) + +static force_inline void +scaled_bilinear_scanline_mmx_8888_8888_SRC (uint32_t * dst, + const uint32_t * mask, + const uint32_t * src_top, + const uint32_t * src_bottom, + int32_t w, + int wt, + int wb, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t max_vx, + pixman_bool_t zero_src) +{ + BILINEAR_DECLARE_VARIABLES; + __m64 pix; + + while (w--) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix); + store (dst, pix); + dst++; + } + + _mm_empty (); +} + +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_cover_SRC, + scaled_bilinear_scanline_mmx_8888_8888_SRC, + uint32_t, uint32_t, uint32_t, + COVER, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_pad_SRC, + scaled_bilinear_scanline_mmx_8888_8888_SRC, + uint32_t, uint32_t, uint32_t, + PAD, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_none_SRC, + scaled_bilinear_scanline_mmx_8888_8888_SRC, + uint32_t, uint32_t, uint32_t, + NONE, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_normal_SRC, + scaled_bilinear_scanline_mmx_8888_8888_SRC, + uint32_t, uint32_t, uint32_t, + NORMAL, FLAG_NONE) + +static force_inline void +scaled_bilinear_scanline_mmx_8888_8888_OVER (uint32_t * dst, + const uint32_t * mask, + const uint32_t * src_top, + const uint32_t * src_bottom, + int32_t w, + int wt, + int wb, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t max_vx, + pixman_bool_t zero_src) +{ + BILINEAR_DECLARE_VARIABLES; + __m64 pix1, pix2; + + while (w) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + + if (!is_zero (pix1)) + { + pix2 = load (dst); + store8888 (dst, core_combine_over_u_pixel_mmx (pix1, pix2)); + } + + w--; + dst++; + } + + _mm_empty (); +} + +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_cover_OVER, + scaled_bilinear_scanline_mmx_8888_8888_OVER, + uint32_t, uint32_t, uint32_t, + COVER, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_pad_OVER, + scaled_bilinear_scanline_mmx_8888_8888_OVER, + uint32_t, uint32_t, uint32_t, + PAD, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_none_OVER, + scaled_bilinear_scanline_mmx_8888_8888_OVER, + uint32_t, uint32_t, uint32_t, + NONE, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8888_normal_OVER, + scaled_bilinear_scanline_mmx_8888_8888_OVER, + uint32_t, uint32_t, uint32_t, + NORMAL, FLAG_NONE) + +static force_inline void +scaled_bilinear_scanline_mmx_8888_8_8888_OVER (uint32_t * dst, + const uint8_t * mask, + const uint32_t * src_top, + const uint32_t * src_bottom, + int32_t w, + int wt, + int wb, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t max_vx, + pixman_bool_t zero_src) +{ + BILINEAR_DECLARE_VARIABLES; + __m64 pix1, pix2; + uint32_t m; + + while (w) + { + m = (uint32_t) *mask++; + + if (m) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + + if (m == 0xff && is_opaque (pix1)) + { + store (dst, pix1); + } + else + { + __m64 ms, md, ma, msa; + + pix2 = load (dst); + ma = expand_alpha_rev (to_m64 (m)); + ms = _mm_unpacklo_pi8 (pix1, _mm_setzero_si64 ()); + md = _mm_unpacklo_pi8 (pix2, _mm_setzero_si64 ()); + + msa = expand_alpha (ms); + + store8888 (dst, (in_over (ms, msa, ma, md))); + } + } + else + { + BILINEAR_SKIP_ONE_PIXEL (); + } + + w--; + dst++; + } + + _mm_empty (); +} + +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8_8888_cover_OVER, + scaled_bilinear_scanline_mmx_8888_8_8888_OVER, + uint32_t, uint8_t, uint32_t, + COVER, FLAG_HAVE_NON_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8_8888_pad_OVER, + scaled_bilinear_scanline_mmx_8888_8_8888_OVER, + uint32_t, uint8_t, uint32_t, + PAD, FLAG_HAVE_NON_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8_8888_none_OVER, + scaled_bilinear_scanline_mmx_8888_8_8888_OVER, + uint32_t, uint8_t, uint32_t, + NONE, FLAG_HAVE_NON_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (mmx_8888_8_8888_normal_OVER, + scaled_bilinear_scanline_mmx_8888_8_8888_OVER, + uint32_t, uint8_t, uint32_t, + NORMAL, FLAG_HAVE_NON_SOLID_MASK) + +static uint32_t * +mmx_fetch_x8r8g8b8 (pixman_iter_t *iter, const uint32_t *mask) +{ + int w = iter->width; + uint32_t *dst = iter->buffer; + uint32_t *src = (uint32_t *)iter->bits; + + iter->bits += iter->stride; + + while (w && ((uintptr_t)dst) & 7) + { + *dst++ = (*src++) | 0xff000000; + w--; + } + + while (w >= 8) + { + __m64 vsrc1 = ldq_u ((__m64 *)(src + 0)); + __m64 vsrc2 = ldq_u ((__m64 *)(src + 2)); + __m64 vsrc3 = ldq_u ((__m64 *)(src + 4)); + __m64 vsrc4 = ldq_u ((__m64 *)(src + 6)); + + *(__m64 *)(dst + 0) = _mm_or_si64 (vsrc1, MC (ff000000)); + *(__m64 *)(dst + 2) = _mm_or_si64 (vsrc2, MC (ff000000)); + *(__m64 *)(dst + 4) = _mm_or_si64 (vsrc3, MC (ff000000)); + *(__m64 *)(dst + 6) = _mm_or_si64 (vsrc4, MC (ff000000)); + + dst += 8; + src += 8; + w -= 8; + } + + while (w) + { + *dst++ = (*src++) | 0xff000000; + w--; + } + + _mm_empty (); + return iter->buffer; +} + +static uint32_t * +mmx_fetch_r5g6b5 (pixman_iter_t *iter, const uint32_t *mask) +{ + int w = iter->width; + uint32_t *dst = iter->buffer; + uint16_t *src = (uint16_t *)iter->bits; + + iter->bits += iter->stride; + + while (w && ((uintptr_t)dst) & 0x0f) + { + uint16_t s = *src++; + + *dst++ = convert_0565_to_8888 (s); + w--; + } + + while (w >= 4) + { + __m64 vsrc = ldq_u ((__m64 *)src); + __m64 mm0, mm1; + + expand_4xpacked565 (vsrc, &mm0, &mm1, 1); + + *(__m64 *)(dst + 0) = mm0; + *(__m64 *)(dst + 2) = mm1; + + dst += 4; + src += 4; + w -= 4; + } + + while (w) + { + uint16_t s = *src++; + + *dst++ = convert_0565_to_8888 (s); + w--; + } + + _mm_empty (); + return iter->buffer; +} + +static uint32_t * +mmx_fetch_a8 (pixman_iter_t *iter, const uint32_t *mask) +{ + int w = iter->width; + uint32_t *dst = iter->buffer; + uint8_t *src = iter->bits; + + iter->bits += iter->stride; + + while (w && (((uintptr_t)dst) & 15)) + { + *dst++ = (uint32_t)*(src++) << 24; + w--; + } + + while (w >= 8) + { + __m64 mm0 = ldq_u ((__m64 *)src); + + __m64 mm1 = _mm_unpacklo_pi8 (_mm_setzero_si64(), mm0); + __m64 mm2 = _mm_unpackhi_pi8 (_mm_setzero_si64(), mm0); + __m64 mm3 = _mm_unpacklo_pi16 (_mm_setzero_si64(), mm1); + __m64 mm4 = _mm_unpackhi_pi16 (_mm_setzero_si64(), mm1); + __m64 mm5 = _mm_unpacklo_pi16 (_mm_setzero_si64(), mm2); + __m64 mm6 = _mm_unpackhi_pi16 (_mm_setzero_si64(), mm2); + + *(__m64 *)(dst + 0) = mm3; + *(__m64 *)(dst + 2) = mm4; + *(__m64 *)(dst + 4) = mm5; + *(__m64 *)(dst + 6) = mm6; + + dst += 8; + src += 8; + w -= 8; + } + + while (w) + { + *dst++ = (uint32_t)*(src++) << 24; + w--; + } + + _mm_empty (); + return iter->buffer; +} + +#define IMAGE_FLAGS \ + (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \ + FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) + +static const pixman_iter_info_t mmx_iters[] = +{ + { PIXMAN_x8r8g8b8, IMAGE_FLAGS, ITER_NARROW, + _pixman_iter_init_bits_stride, mmx_fetch_x8r8g8b8, NULL + }, + { PIXMAN_r5g6b5, IMAGE_FLAGS, ITER_NARROW, + _pixman_iter_init_bits_stride, mmx_fetch_r5g6b5, NULL + }, + { PIXMAN_a8, IMAGE_FLAGS, ITER_NARROW, + _pixman_iter_init_bits_stride, mmx_fetch_a8, NULL + }, + { PIXMAN_null }, +}; + +static const pixman_fast_path_t mmx_fast_paths[] = +{ + PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, mmx_composite_over_n_8_0565 ), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, mmx_composite_over_n_8_0565 ), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, mmx_composite_over_n_8_8888 ), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, mmx_composite_over_n_8_8888 ), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, mmx_composite_over_n_8_8888 ), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, mmx_composite_over_n_8_8888 ), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, mmx_composite_over_n_8888_8888_ca ), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, mmx_composite_over_n_8888_8888_ca ), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, mmx_composite_over_n_8888_0565_ca ), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, mmx_composite_over_n_8888_8888_ca ), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, mmx_composite_over_n_8888_8888_ca ), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, mmx_composite_over_n_8888_0565_ca ), + PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, a8r8g8b8, mmx_composite_over_pixbuf_8888 ), + PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, x8r8g8b8, mmx_composite_over_pixbuf_8888 ), + PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, r5g6b5, mmx_composite_over_pixbuf_0565 ), + PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, a8b8g8r8, mmx_composite_over_pixbuf_8888 ), + PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, x8b8g8r8, mmx_composite_over_pixbuf_8888 ), + PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, b5g6r5, mmx_composite_over_pixbuf_0565 ), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, solid, a8r8g8b8, mmx_composite_over_x888_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, solid, x8r8g8b8, mmx_composite_over_x888_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, solid, a8b8g8r8, mmx_composite_over_x888_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, solid, x8b8g8r8, mmx_composite_over_x888_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, mmx_composite_over_8888_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, mmx_composite_over_8888_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, a8b8g8r8, mmx_composite_over_8888_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, x8b8g8r8, mmx_composite_over_8888_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, x8r8g8b8, mmx_composite_over_x888_8_8888 ), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, a8r8g8b8, mmx_composite_over_x888_8_8888 ), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, x8b8g8r8, mmx_composite_over_x888_8_8888 ), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, a8b8g8r8, mmx_composite_over_x888_8_8888 ), + PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, mmx_composite_over_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, mmx_composite_over_n_8888 ), + PIXMAN_STD_FAST_PATH (OVER, solid, null, r5g6b5, mmx_composite_over_n_0565 ), + PIXMAN_STD_FAST_PATH (OVER, solid, null, b5g6r5, mmx_composite_over_n_0565 ), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, null, x8r8g8b8, mmx_composite_copy_area ), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, null, x8b8g8r8, mmx_composite_copy_area ), + + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, mmx_composite_over_8888_8888 ), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, mmx_composite_over_8888_8888 ), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, mmx_composite_over_8888_0565 ), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, mmx_composite_over_8888_8888 ), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, mmx_composite_over_8888_8888 ), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, mmx_composite_over_8888_0565 ), + + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, mmx_composite_over_reverse_n_8888), + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, mmx_composite_over_reverse_n_8888), + + PIXMAN_STD_FAST_PATH (ADD, r5g6b5, null, r5g6b5, mmx_composite_add_0565_0565 ), + PIXMAN_STD_FAST_PATH (ADD, b5g6r5, null, b5g6r5, mmx_composite_add_0565_0565 ), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, mmx_composite_add_8888_8888 ), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, mmx_composite_add_8888_8888 ), + PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, mmx_composite_add_8_8 ), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, mmx_composite_add_n_8_8 ), + + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, mmx_composite_src_x888_0565 ), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, mmx_composite_src_x888_0565 ), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, mmx_composite_src_x888_0565 ), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, mmx_composite_src_x888_0565 ), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8r8g8b8, mmx_composite_src_n_8_8888 ), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, mmx_composite_src_n_8_8888 ), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, mmx_composite_src_n_8_8888 ), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, mmx_composite_src_n_8_8888 ), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, mmx_composite_copy_area ), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, mmx_composite_copy_area ), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, mmx_composite_copy_area ), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, mmx_composite_copy_area ), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, mmx_composite_copy_area ), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, mmx_composite_copy_area ), + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, mmx_composite_copy_area ), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, mmx_composite_copy_area ), + + PIXMAN_STD_FAST_PATH (IN, a8, null, a8, mmx_composite_in_8_8 ), + PIXMAN_STD_FAST_PATH (IN, solid, a8, a8, mmx_composite_in_n_8_8 ), + + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mmx_8888_8888 ), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, mmx_8888_8888 ), + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mmx_8888_8888 ), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, mmx_8888_8888 ), + + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mmx_8888_n_8888 ), + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, mmx_8888_n_8888 ), + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mmx_8888_n_8888 ), + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, mmx_8888_n_8888 ), + + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, mmx_8888_8888 ), + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, mmx_8888_8888 ), + SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, mmx_8888_8888 ), + SIMPLE_BILINEAR_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, mmx_8888_8888 ), + SIMPLE_BILINEAR_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, mmx_8888_8888 ), + SIMPLE_BILINEAR_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8, mmx_8888_8888 ), + + SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mmx_8888_8888 ), + SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, mmx_8888_8888 ), + SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mmx_8888_8888 ), + SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, mmx_8888_8888 ), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, mmx_8888_8_8888 ), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, mmx_8888_8_8888 ), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, mmx_8888_8_8888 ), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, mmx_8888_8_8888 ), + + { PIXMAN_OP_NONE }, +}; + +pixman_implementation_t * +_pixman_implementation_create_mmx (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = _pixman_implementation_create (fallback, mmx_fast_paths); + + imp->combine_32[PIXMAN_OP_OVER] = mmx_combine_over_u; + imp->combine_32[PIXMAN_OP_OVER_REVERSE] = mmx_combine_over_reverse_u; + imp->combine_32[PIXMAN_OP_IN] = mmx_combine_in_u; + imp->combine_32[PIXMAN_OP_IN_REVERSE] = mmx_combine_in_reverse_u; + imp->combine_32[PIXMAN_OP_OUT] = mmx_combine_out_u; + imp->combine_32[PIXMAN_OP_OUT_REVERSE] = mmx_combine_out_reverse_u; + imp->combine_32[PIXMAN_OP_ATOP] = mmx_combine_atop_u; + imp->combine_32[PIXMAN_OP_ATOP_REVERSE] = mmx_combine_atop_reverse_u; + imp->combine_32[PIXMAN_OP_XOR] = mmx_combine_xor_u; + imp->combine_32[PIXMAN_OP_ADD] = mmx_combine_add_u; + imp->combine_32[PIXMAN_OP_SATURATE] = mmx_combine_saturate_u; + + imp->combine_32_ca[PIXMAN_OP_SRC] = mmx_combine_src_ca; + imp->combine_32_ca[PIXMAN_OP_OVER] = mmx_combine_over_ca; + imp->combine_32_ca[PIXMAN_OP_OVER_REVERSE] = mmx_combine_over_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_IN] = mmx_combine_in_ca; + imp->combine_32_ca[PIXMAN_OP_IN_REVERSE] = mmx_combine_in_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_OUT] = mmx_combine_out_ca; + imp->combine_32_ca[PIXMAN_OP_OUT_REVERSE] = mmx_combine_out_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_ATOP] = mmx_combine_atop_ca; + imp->combine_32_ca[PIXMAN_OP_ATOP_REVERSE] = mmx_combine_atop_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_XOR] = mmx_combine_xor_ca; + imp->combine_32_ca[PIXMAN_OP_ADD] = mmx_combine_add_ca; + + imp->blt = mmx_blt; + imp->fill = mmx_fill; + + imp->iter_info = mmx_iters; + + return imp; +} + +#endif /* USE_X86_MMX || USE_ARM_IWMMXT || USE_LOONGSON_MMI */ diff --git a/gfx/cairo/libpixman/src/pixman-noop.c b/gfx/cairo/libpixman/src/pixman-noop.c new file mode 100644 index 0000000000..e59890492f --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-noop.c @@ -0,0 +1,161 @@ +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ +/* + * Copyright © 2011 Red Hat, Inc. + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include "pixman-private.h" +#include "pixman-combine32.h" +#include "pixman-inlines.h" + +static void +noop_composite (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + return; +} + +static uint32_t * +noop_get_scanline (pixman_iter_t *iter, const uint32_t *mask) +{ + uint32_t *result = iter->buffer; + + iter->buffer += iter->image->bits.rowstride; + + return result; +} + +static void +noop_init_solid_narrow (pixman_iter_t *iter, + const pixman_iter_info_t *info) +{ + pixman_image_t *image = iter->image; + uint32_t *buffer = iter->buffer; + uint32_t *end = buffer + iter->width; + uint32_t color; + + if (iter->image->type == SOLID) + color = image->solid.color_32; + else + color = image->bits.fetch_pixel_32 (&image->bits, 0, 0); + + while (buffer < end) + *(buffer++) = color; +} + +static void +noop_init_solid_wide (pixman_iter_t *iter, + const pixman_iter_info_t *info) +{ + pixman_image_t *image = iter->image; + argb_t *buffer = (argb_t *)iter->buffer; + argb_t *end = buffer + iter->width; + argb_t color; + + if (iter->image->type == SOLID) + color = image->solid.color_float; + else + color = image->bits.fetch_pixel_float (&image->bits, 0, 0); + + while (buffer < end) + *(buffer++) = color; +} + +static void +noop_init_direct_buffer (pixman_iter_t *iter, const pixman_iter_info_t *info) +{ + pixman_image_t *image = iter->image; + + iter->buffer = + image->bits.bits + iter->y * image->bits.rowstride + iter->x; +} + +static void +dest_write_back_direct (pixman_iter_t *iter) +{ + iter->buffer += iter->image->bits.rowstride; +} + +static const pixman_iter_info_t noop_iters[] = +{ + /* Source iters */ + { PIXMAN_any, + 0, ITER_IGNORE_ALPHA | ITER_IGNORE_RGB | ITER_SRC, + NULL, + _pixman_iter_get_scanline_noop, + NULL + }, + { PIXMAN_solid, + FAST_PATH_NO_ALPHA_MAP, ITER_NARROW | ITER_SRC, + noop_init_solid_narrow, + _pixman_iter_get_scanline_noop, + NULL, + }, + { PIXMAN_solid, + FAST_PATH_NO_ALPHA_MAP, ITER_WIDE | ITER_SRC, + noop_init_solid_wide, + _pixman_iter_get_scanline_noop, + NULL + }, + { PIXMAN_a8r8g8b8, + FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | + FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST, + ITER_NARROW | ITER_SRC, + noop_init_direct_buffer, + noop_get_scanline, + NULL + }, + /* Dest iters */ + { PIXMAN_a8r8g8b8, + FAST_PATH_STD_DEST_FLAGS, ITER_NARROW | ITER_DEST, + noop_init_direct_buffer, + _pixman_iter_get_scanline_noop, + dest_write_back_direct + }, + { PIXMAN_x8r8g8b8, + FAST_PATH_STD_DEST_FLAGS, ITER_NARROW | ITER_DEST | ITER_LOCALIZED_ALPHA, + noop_init_direct_buffer, + _pixman_iter_get_scanline_noop, + dest_write_back_direct + }, + { PIXMAN_null }, +}; + +static const pixman_fast_path_t noop_fast_paths[] = +{ + { PIXMAN_OP_DST, PIXMAN_any, 0, PIXMAN_any, 0, PIXMAN_any, 0, noop_composite }, + { PIXMAN_OP_NONE }, +}; + +pixman_implementation_t * +_pixman_implementation_create_noop (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = + _pixman_implementation_create (fallback, noop_fast_paths); + + imp->iter_info = noop_iters; + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-ppc.c b/gfx/cairo/libpixman/src/pixman-ppc.c new file mode 100644 index 0000000000..4d5506d25d --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-ppc.c @@ -0,0 +1,173 @@ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pixman-private.h" + +#ifdef USE_VMX + +/* The CPU detection code needs to be in a file not compiled with + * "-maltivec -mabi=altivec", as gcc would try to save vector register + * across function calls causing SIGILL on cpus without Altivec/vmx. + */ +#ifdef __APPLE__ +#include + +static pixman_bool_t +pixman_have_vmx (void) +{ + int error, have_vmx; + size_t length = sizeof(have_vmx); + + error = sysctlbyname ("hw.optional.altivec", &have_vmx, &length, NULL, 0); + + if (error) + return FALSE; + + return have_vmx; +} + +#elif defined (__OpenBSD__) +#include +#include +#include + +static pixman_bool_t +pixman_have_vmx (void) +{ + int error, have_vmx; + int mib[2] = { CTL_MACHDEP, CPU_ALTIVEC }; + size_t length = sizeof(have_vmx); + + error = sysctl (mib, 2, &have_vmx, &length, NULL, 0); + + if (error != 0) + return FALSE; + + return have_vmx; +} + +#elif defined (__FreeBSD__) +#include +#include + +static pixman_bool_t +pixman_have_vmx (void) +{ + + unsigned long cpufeatures; + int have_vmx; + + if (elf_aux_info(AT_HWCAP, &cpufeatures, sizeof(cpufeatures))) + return FALSE; + + have_vmx = cpufeatures & PPC_FEATURE_HAS_ALTIVEC; + return have_vmx; +} + +#elif defined (__linux__) + +#include +#include +#include +#include +#include +#include +#include + +static pixman_bool_t +pixman_have_vmx (void) +{ + int have_vmx = FALSE; + int fd; + struct + { + unsigned long type; + unsigned long value; + } aux; + + fd = open ("/proc/self/auxv", O_RDONLY); + if (fd >= 0) + { + while (read (fd, &aux, sizeof (aux)) == sizeof (aux)) + { + if (aux.type == AT_HWCAP && (aux.value & PPC_FEATURE_HAS_ALTIVEC)) + { + have_vmx = TRUE; + break; + } + } + + close (fd); + } + + return have_vmx; +} + +#else /* !__APPLE__ && !__OpenBSD__ && !__linux__ */ +#include +#include + +static jmp_buf jump_env; + +static void +vmx_test (int sig, + siginfo_t *si, + void * unused) +{ + longjmp (jump_env, 1); +} + +static pixman_bool_t +pixman_have_vmx (void) +{ + struct sigaction sa, osa; + int jmp_result; + + sa.sa_flags = SA_SIGINFO; + sigemptyset (&sa.sa_mask); + sa.sa_sigaction = vmx_test; + sigaction (SIGILL, &sa, &osa); + jmp_result = setjmp (jump_env); + if (jmp_result == 0) + { + asm volatile ( "vor 0, 0, 0" ); + } + sigaction (SIGILL, &osa, NULL); + return (jmp_result == 0); +} + +#endif /* __APPLE__ */ +#endif /* USE_VMX */ + +pixman_implementation_t * +_pixman_ppc_get_implementations (pixman_implementation_t *imp) +{ +#ifdef USE_VMX + if (!_pixman_disabled ("vmx") && pixman_have_vmx ()) + imp = _pixman_implementation_create_vmx (imp); +#endif + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-private.h b/gfx/cairo/libpixman/src/pixman-private.h new file mode 100644 index 0000000000..0adf9fa067 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-private.h @@ -0,0 +1,1202 @@ +#ifndef PIXMAN_PRIVATE_H +#define PIXMAN_PRIVATE_H + +/* + * The defines which are shared between C and assembly code + */ + +/* bilinear interpolation precision (must be <= 8) */ +#ifndef MOZILLA_VERSION +#error "Need mozilla headers" +#endif +#ifdef MOZ_GFX_OPTIMIZE_MOBILE +#define LOW_QUALITY_INTERPOLATION +#define LOWER_QUALITY_INTERPOLATION +#define BILINEAR_INTERPOLATION_BITS 4 +#else +#define BILINEAR_INTERPOLATION_BITS 7 +#endif +#define BILINEAR_INTERPOLATION_RANGE (1 << BILINEAR_INTERPOLATION_BITS) + +/* + * C specific part + */ + +#ifndef __ASSEMBLER__ + +#ifndef PACKAGE +# error config.h must be included before pixman-private.h +#endif + +#define PIXMAN_DISABLE_DEPRECATED +#define PIXMAN_USE_INTERNAL_API + +#include "pixman.h" +#include +#include +#include +#include +#include +#include + +#include "pixman-compiler.h" + +/* + * Images + */ +typedef struct image_common image_common_t; +typedef struct solid_fill solid_fill_t; +typedef struct gradient gradient_t; +typedef struct linear_gradient linear_gradient_t; +typedef struct horizontal_gradient horizontal_gradient_t; +typedef struct vertical_gradient vertical_gradient_t; +typedef struct conical_gradient conical_gradient_t; +typedef struct radial_gradient radial_gradient_t; +typedef struct bits_image bits_image_t; +typedef struct circle circle_t; + +typedef struct argb_t argb_t; + +struct argb_t +{ + float a; + float r; + float g; + float b; +}; + +typedef void (*fetch_scanline_t) (bits_image_t *image, + int x, + int y, + int width, + uint32_t *buffer, + const uint32_t *mask); + +typedef uint32_t (*fetch_pixel_32_t) (bits_image_t *image, + int x, + int y); + +typedef argb_t (*fetch_pixel_float_t) (bits_image_t *image, + int x, + int y); + +typedef void (*store_scanline_t) (bits_image_t * image, + int x, + int y, + int width, + const uint32_t *values); + +typedef enum +{ + BITS, + LINEAR, + CONICAL, + RADIAL, + SOLID +} image_type_t; + +typedef void (*property_changed_func_t) (pixman_image_t *image); + +struct image_common +{ + image_type_t type; + int32_t ref_count; + pixman_region32_t clip_region; + int32_t alpha_count; /* How many times this image is being used as an alpha map */ + pixman_bool_t have_clip_region; /* FALSE if there is no clip */ + pixman_bool_t client_clip; /* Whether the source clip was + set by a client */ + pixman_bool_t clip_sources; /* Whether the clip applies when + * the image is used as a source + */ + pixman_bool_t dirty; + pixman_transform_t * transform; + pixman_repeat_t repeat; + pixman_filter_t filter; + pixman_fixed_t * filter_params; + int n_filter_params; + bits_image_t * alpha_map; + int alpha_origin_x; + int alpha_origin_y; + pixman_bool_t component_alpha; + property_changed_func_t property_changed; + + pixman_image_destroy_func_t destroy_func; + void * destroy_data; + + uint32_t flags; + pixman_format_code_t extended_format_code; +}; + +struct solid_fill +{ + image_common_t common; + pixman_color_t color; + + uint32_t color_32; + argb_t color_float; +}; + +struct gradient +{ + image_common_t common; + int n_stops; + pixman_gradient_stop_t *stops; +}; + +struct linear_gradient +{ + gradient_t common; + pixman_point_fixed_t p1; + pixman_point_fixed_t p2; +}; + +struct circle +{ + pixman_fixed_t x; + pixman_fixed_t y; + pixman_fixed_t radius; +}; + +struct radial_gradient +{ + gradient_t common; + + circle_t c1; + circle_t c2; + + circle_t delta; + double a; + double inva; + double mindr; +}; + +struct conical_gradient +{ + gradient_t common; + pixman_point_fixed_t center; + double angle; +}; + +struct bits_image +{ + image_common_t common; + pixman_format_code_t format; + const pixman_indexed_t * indexed; + int width; + int height; + uint32_t * bits; + uint32_t * free_me; + int rowstride; /* in number of uint32_t's */ + + pixman_dither_t dither; + uint32_t dither_offset_y; + uint32_t dither_offset_x; + + fetch_scanline_t fetch_scanline_32; + fetch_pixel_32_t fetch_pixel_32; + store_scanline_t store_scanline_32; + + fetch_scanline_t fetch_scanline_float; + fetch_pixel_float_t fetch_pixel_float; + store_scanline_t store_scanline_float; + + /* Used for indirect access to the bits */ + pixman_read_memory_func_t read_func; + pixman_write_memory_func_t write_func; +}; + +union pixman_image +{ + image_type_t type; + image_common_t common; + bits_image_t bits; + gradient_t gradient; + linear_gradient_t linear; + conical_gradient_t conical; + radial_gradient_t radial; + solid_fill_t solid; +}; + +typedef struct pixman_iter_t pixman_iter_t; +typedef uint32_t *(* pixman_iter_get_scanline_t) (pixman_iter_t *iter, const uint32_t *mask); +typedef void (* pixman_iter_write_back_t) (pixman_iter_t *iter); +typedef void (* pixman_iter_fini_t) (pixman_iter_t *iter); + +typedef enum +{ + ITER_NARROW = (1 << 0), + ITER_WIDE = (1 << 1), + + /* "Localized alpha" is when the alpha channel is used only to compute + * the alpha value of the destination. This means that the computation + * of the RGB values of the result is independent of the alpha value. + * + * For example, the OVER operator has localized alpha for the + * destination, because the RGB values of the result can be computed + * without knowing the destination alpha. Similarly, ADD has localized + * alpha for both source and destination because the RGB values of the + * result can be computed without knowing the alpha value of source or + * destination. + * + * When he destination is xRGB, this is useful knowledge, because then + * we can treat it as if it were ARGB, which means in some cases we can + * avoid copying it to a temporary buffer. + */ + ITER_LOCALIZED_ALPHA = (1 << 2), + ITER_IGNORE_ALPHA = (1 << 3), + ITER_IGNORE_RGB = (1 << 4), + + /* These indicate whether the iterator is for a source + * or a destination image + */ + ITER_SRC = (1 << 5), + ITER_DEST = (1 << 6) +} iter_flags_t; + +struct pixman_iter_t +{ + /* These are initialized by _pixman_implementation_{src,dest}_init */ + pixman_image_t * image; + uint32_t * buffer; + int x, y; + int width; + int height; + iter_flags_t iter_flags; + uint32_t image_flags; + + /* These function pointers are initialized by the implementation */ + pixman_iter_get_scanline_t get_scanline; + pixman_iter_write_back_t write_back; + pixman_iter_fini_t fini; + + /* These fields are scratch data that implementations can use */ + void * data; + uint8_t * bits; + int stride; +}; + +typedef struct pixman_iter_info_t pixman_iter_info_t; +typedef void (* pixman_iter_initializer_t) (pixman_iter_t *iter, + const pixman_iter_info_t *info); +struct pixman_iter_info_t +{ + pixman_format_code_t format; + uint32_t image_flags; + iter_flags_t iter_flags; + pixman_iter_initializer_t initializer; + pixman_iter_get_scanline_t get_scanline; + pixman_iter_write_back_t write_back; +}; + +void +_pixman_bits_image_setup_accessors (bits_image_t *image); + +void +_pixman_bits_image_src_iter_init (pixman_image_t *image, pixman_iter_t *iter); + +void +_pixman_bits_image_dest_iter_init (pixman_image_t *image, pixman_iter_t *iter); + +void +_pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter); + +void +_pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter); + +void +_pixman_conical_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter); + +void +_pixman_image_init (pixman_image_t *image); + +pixman_bool_t +_pixman_bits_image_init (pixman_image_t * image, + pixman_format_code_t format, + int width, + int height, + uint32_t * bits, + int rowstride, + pixman_bool_t clear); +pixman_bool_t +_pixman_image_fini (pixman_image_t *image); + +pixman_image_t * +_pixman_image_allocate (void); + +pixman_bool_t +_pixman_init_gradient (gradient_t * gradient, + const pixman_gradient_stop_t *stops, + int n_stops); +void +_pixman_image_reset_clip_region (pixman_image_t *image); + +void +_pixman_image_validate (pixman_image_t *image); + +#define PIXMAN_IMAGE_GET_LINE(image, x, y, type, out_stride, line, mul) \ + do \ + { \ + uint32_t *__bits__; \ + int __stride__; \ + \ + __bits__ = image->bits.bits; \ + __stride__ = image->bits.rowstride; \ + (out_stride) = \ + __stride__ * (int) sizeof (uint32_t) / (int) sizeof (type); \ + (line) = \ + ((type *) __bits__) + (out_stride) * (y) + (mul) * (x); \ + } while (0) + +/* + * Gradient walker + */ +typedef struct +{ + float a_s, a_b; + float r_s, r_b; + float g_s, g_b; + float b_s, b_b; + pixman_fixed_48_16_t left_x; + pixman_fixed_48_16_t right_x; + + pixman_gradient_stop_t *stops; + int num_stops; + pixman_repeat_t repeat; + + pixman_bool_t need_reset; +} pixman_gradient_walker_t; + +void +_pixman_gradient_walker_init (pixman_gradient_walker_t *walker, + gradient_t * gradient, + pixman_repeat_t repeat); + +void +_pixman_gradient_walker_reset (pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t pos); + +typedef void (*pixman_gradient_walker_write_t) ( + pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer); + +void +_pixman_gradient_walker_write_narrow(pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer); + +void +_pixman_gradient_walker_write_wide(pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer); + +typedef void (*pixman_gradient_walker_fill_t) ( + pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer, + uint32_t *end); + +void +_pixman_gradient_walker_fill_narrow(pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer, + uint32_t *end); + +void +_pixman_gradient_walker_fill_wide(pixman_gradient_walker_t *walker, + pixman_fixed_48_16_t x, + uint32_t *buffer, + uint32_t *end); + +/* + * Edges + */ + +#define MAX_ALPHA(n) ((1 << (n)) - 1) +#define N_Y_FRAC(n) ((n) == 1 ? 1 : (1 << ((n) / 2)) - 1) +#define N_X_FRAC(n) ((n) == 1 ? 1 : (1 << ((n) / 2)) + 1) + +#define STEP_Y_SMALL(n) (pixman_fixed_1 / N_Y_FRAC (n)) +#define STEP_Y_BIG(n) (pixman_fixed_1 - (N_Y_FRAC (n) - 1) * STEP_Y_SMALL (n)) + +#define Y_FRAC_FIRST(n) (STEP_Y_BIG (n) / 2) +#define Y_FRAC_LAST(n) (Y_FRAC_FIRST (n) + (N_Y_FRAC (n) - 1) * STEP_Y_SMALL (n)) + +#define STEP_X_SMALL(n) (pixman_fixed_1 / N_X_FRAC (n)) +#define STEP_X_BIG(n) (pixman_fixed_1 - (N_X_FRAC (n) - 1) * STEP_X_SMALL (n)) + +#define X_FRAC_FIRST(n) (STEP_X_BIG (n) / 2) +#define X_FRAC_LAST(n) (X_FRAC_FIRST (n) + (N_X_FRAC (n) - 1) * STEP_X_SMALL (n)) + +#define RENDER_SAMPLES_X(x, n) \ + ((n) == 1? 0 : (pixman_fixed_frac (x) + \ + X_FRAC_FIRST (n)) / STEP_X_SMALL (n)) + +void +pixman_rasterize_edges_accessors (pixman_image_t *image, + pixman_edge_t * l, + pixman_edge_t * r, + pixman_fixed_t t, + pixman_fixed_t b); + +/* + * Implementations + */ +typedef struct pixman_implementation_t pixman_implementation_t; + +typedef struct +{ + pixman_op_t op; + pixman_image_t * src_image; + pixman_image_t * mask_image; + pixman_image_t * dest_image; + int32_t src_x; + int32_t src_y; + int32_t mask_x; + int32_t mask_y; + int32_t dest_x; + int32_t dest_y; + int32_t width; + int32_t height; + + uint32_t src_flags; + uint32_t mask_flags; + uint32_t dest_flags; +} pixman_composite_info_t; + +#define PIXMAN_COMPOSITE_ARGS(info) \ + MAYBE_UNUSED pixman_op_t op = info->op; \ + MAYBE_UNUSED pixman_image_t * src_image = info->src_image; \ + MAYBE_UNUSED pixman_image_t * mask_image = info->mask_image; \ + MAYBE_UNUSED pixman_image_t * dest_image = info->dest_image; \ + MAYBE_UNUSED int32_t src_x = info->src_x; \ + MAYBE_UNUSED int32_t src_y = info->src_y; \ + MAYBE_UNUSED int32_t mask_x = info->mask_x; \ + MAYBE_UNUSED int32_t mask_y = info->mask_y; \ + MAYBE_UNUSED int32_t dest_x = info->dest_x; \ + MAYBE_UNUSED int32_t dest_y = info->dest_y; \ + MAYBE_UNUSED int32_t width = info->width; \ + MAYBE_UNUSED int32_t height = info->height + +typedef void (*pixman_combine_32_func_t) (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width); + +typedef void (*pixman_combine_float_func_t) (pixman_implementation_t *imp, + pixman_op_t op, + float * dest, + const float * src, + const float * mask, + int n_pixels); + +typedef void (*pixman_composite_func_t) (pixman_implementation_t *imp, + pixman_composite_info_t *info); +typedef pixman_bool_t (*pixman_blt_func_t) (pixman_implementation_t *imp, + uint32_t * src_bits, + uint32_t * dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height); +typedef pixman_bool_t (*pixman_fill_func_t) (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t filler); + +void _pixman_setup_combiner_functions_32 (pixman_implementation_t *imp); +void _pixman_setup_combiner_functions_float (pixman_implementation_t *imp); + +typedef struct +{ + pixman_op_t op; + pixman_format_code_t src_format; + uint32_t src_flags; + pixman_format_code_t mask_format; + uint32_t mask_flags; + pixman_format_code_t dest_format; + uint32_t dest_flags; + pixman_composite_func_t func; +} pixman_fast_path_t; + +struct pixman_implementation_t +{ + pixman_implementation_t * toplevel; + pixman_implementation_t * fallback; + const pixman_fast_path_t * fast_paths; + const pixman_iter_info_t * iter_info; + + pixman_blt_func_t blt; + pixman_fill_func_t fill; + + pixman_combine_32_func_t combine_32[PIXMAN_N_OPERATORS]; + pixman_combine_32_func_t combine_32_ca[PIXMAN_N_OPERATORS]; + pixman_combine_float_func_t combine_float[PIXMAN_N_OPERATORS]; + pixman_combine_float_func_t combine_float_ca[PIXMAN_N_OPERATORS]; +}; + +uint32_t +_pixman_image_get_solid (pixman_implementation_t *imp, + pixman_image_t * image, + pixman_format_code_t format); + +pixman_implementation_t * +_pixman_implementation_create (pixman_implementation_t *fallback, + const pixman_fast_path_t *fast_paths); + +void +_pixman_implementation_lookup_composite (pixman_implementation_t *toplevel, + pixman_op_t op, + pixman_format_code_t src_format, + uint32_t src_flags, + pixman_format_code_t mask_format, + uint32_t mask_flags, + pixman_format_code_t dest_format, + uint32_t dest_flags, + pixman_implementation_t **out_imp, + pixman_composite_func_t *out_func); + +pixman_combine_32_func_t +_pixman_implementation_lookup_combiner (pixman_implementation_t *imp, + pixman_op_t op, + pixman_bool_t component_alpha, + pixman_bool_t wide); + +pixman_bool_t +_pixman_implementation_blt (pixman_implementation_t *imp, + uint32_t * src_bits, + uint32_t * dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height); + +pixman_bool_t +_pixman_implementation_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t filler); + +void +_pixman_implementation_iter_init (pixman_implementation_t *imp, + pixman_iter_t *iter, + pixman_image_t *image, + int x, + int y, + int width, + int height, + uint8_t *buffer, + iter_flags_t flags, + uint32_t image_flags); + +/* Specific implementations */ +pixman_implementation_t * +_pixman_implementation_create_general (void); + +pixman_implementation_t * +_pixman_implementation_create_fast_path (pixman_implementation_t *fallback); + +pixman_implementation_t * +_pixman_implementation_create_noop (pixman_implementation_t *fallback); + +#if defined USE_X86_MMX || defined USE_ARM_IWMMXT || defined USE_LOONGSON_MMI +pixman_implementation_t * +_pixman_implementation_create_mmx (pixman_implementation_t *fallback); +#endif + +#ifdef USE_SSE2 +pixman_implementation_t * +_pixman_implementation_create_sse2 (pixman_implementation_t *fallback); +#endif + +#ifdef USE_SSSE3 +pixman_implementation_t * +_pixman_implementation_create_ssse3 (pixman_implementation_t *fallback); +#endif + +#ifdef USE_ARM_SIMD +pixman_implementation_t * +_pixman_implementation_create_arm_simd (pixman_implementation_t *fallback); +#endif + +#ifdef USE_ARM_NEON +pixman_implementation_t * +_pixman_implementation_create_arm_neon (pixman_implementation_t *fallback); +#endif + +#ifdef USE_ARM_A64_NEON +pixman_implementation_t * +_pixman_implementation_create_arm_neon (pixman_implementation_t *fallback); +#endif + +#ifdef USE_MIPS_DSPR2 +pixman_implementation_t * +_pixman_implementation_create_mips_dspr2 (pixman_implementation_t *fallback); +#endif + +#ifdef USE_VMX +pixman_implementation_t * +_pixman_implementation_create_vmx (pixman_implementation_t *fallback); +#endif + +pixman_bool_t +_pixman_implementation_disabled (const char *name); + +pixman_implementation_t * +_pixman_x86_get_implementations (pixman_implementation_t *imp); + +pixman_implementation_t * +_pixman_arm_get_implementations (pixman_implementation_t *imp); + +pixman_implementation_t * +_pixman_ppc_get_implementations (pixman_implementation_t *imp); + +pixman_implementation_t * +_pixman_mips_get_implementations (pixman_implementation_t *imp); + +pixman_implementation_t * +_pixman_choose_implementation (void); + +pixman_bool_t +_pixman_disabled (const char *name); + + +/* + * Utilities + */ +pixman_bool_t +_pixman_compute_composite_region32 (pixman_region32_t * region, + pixman_image_t * src_image, + pixman_image_t * mask_image, + pixman_image_t * dest_image, + int32_t src_x, + int32_t src_y, + int32_t mask_x, + int32_t mask_y, + int32_t dest_x, + int32_t dest_y, + int32_t width, + int32_t height); +uint32_t * +_pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask); + +void +_pixman_iter_init_bits_stride (pixman_iter_t *iter, const pixman_iter_info_t *info); + +/* These "formats" all have depth 0, so they + * will never clash with any real ones + */ +#define PIXMAN_null PIXMAN_FORMAT (0, 0, 0, 0, 0, 0) +#define PIXMAN_solid PIXMAN_FORMAT (0, 1, 0, 0, 0, 0) +#define PIXMAN_pixbuf PIXMAN_FORMAT (0, 2, 0, 0, 0, 0) +#define PIXMAN_rpixbuf PIXMAN_FORMAT (0, 3, 0, 0, 0, 0) +#define PIXMAN_unknown PIXMAN_FORMAT (0, 4, 0, 0, 0, 0) +#define PIXMAN_any PIXMAN_FORMAT (0, 5, 0, 0, 0, 0) + +#define PIXMAN_OP_any (PIXMAN_N_OPERATORS + 1) + +#define FAST_PATH_ID_TRANSFORM (1 << 0) +#define FAST_PATH_NO_ALPHA_MAP (1 << 1) +#define FAST_PATH_NO_CONVOLUTION_FILTER (1 << 2) +#define FAST_PATH_NO_PAD_REPEAT (1 << 3) +#define FAST_PATH_NO_REFLECT_REPEAT (1 << 4) +#define FAST_PATH_NO_ACCESSORS (1 << 5) +#define FAST_PATH_NARROW_FORMAT (1 << 6) +#define FAST_PATH_COMPONENT_ALPHA (1 << 8) +#define FAST_PATH_SAMPLES_OPAQUE (1 << 7) +#define FAST_PATH_UNIFIED_ALPHA (1 << 9) +#define FAST_PATH_SCALE_TRANSFORM (1 << 10) +#define FAST_PATH_NEAREST_FILTER (1 << 11) +#define FAST_PATH_HAS_TRANSFORM (1 << 12) +#define FAST_PATH_IS_OPAQUE (1 << 13) +#define FAST_PATH_NO_NORMAL_REPEAT (1 << 14) +#define FAST_PATH_NO_NONE_REPEAT (1 << 15) +#define FAST_PATH_X_UNIT_POSITIVE (1 << 16) +#define FAST_PATH_AFFINE_TRANSFORM (1 << 17) +#define FAST_PATH_Y_UNIT_ZERO (1 << 18) +#define FAST_PATH_BILINEAR_FILTER (1 << 19) +#define FAST_PATH_ROTATE_90_TRANSFORM (1 << 20) +#define FAST_PATH_ROTATE_180_TRANSFORM (1 << 21) +#define FAST_PATH_ROTATE_270_TRANSFORM (1 << 22) +#define FAST_PATH_SAMPLES_COVER_CLIP_NEAREST (1 << 23) +#define FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR (1 << 24) +#define FAST_PATH_BITS_IMAGE (1 << 25) +#define FAST_PATH_SEPARABLE_CONVOLUTION_FILTER (1 << 26) + +#define FAST_PATH_PAD_REPEAT \ + (FAST_PATH_NO_NONE_REPEAT | \ + FAST_PATH_NO_NORMAL_REPEAT | \ + FAST_PATH_NO_REFLECT_REPEAT) + +#define FAST_PATH_NORMAL_REPEAT \ + (FAST_PATH_NO_NONE_REPEAT | \ + FAST_PATH_NO_PAD_REPEAT | \ + FAST_PATH_NO_REFLECT_REPEAT) + +#define FAST_PATH_NONE_REPEAT \ + (FAST_PATH_NO_NORMAL_REPEAT | \ + FAST_PATH_NO_PAD_REPEAT | \ + FAST_PATH_NO_REFLECT_REPEAT) + +#define FAST_PATH_REFLECT_REPEAT \ + (FAST_PATH_NO_NONE_REPEAT | \ + FAST_PATH_NO_NORMAL_REPEAT | \ + FAST_PATH_NO_PAD_REPEAT) + +#define FAST_PATH_STANDARD_FLAGS \ + (FAST_PATH_NO_CONVOLUTION_FILTER | \ + FAST_PATH_NO_ACCESSORS | \ + FAST_PATH_NO_ALPHA_MAP | \ + FAST_PATH_NARROW_FORMAT) + +#define FAST_PATH_STD_DEST_FLAGS \ + (FAST_PATH_NO_ACCESSORS | \ + FAST_PATH_NO_ALPHA_MAP | \ + FAST_PATH_NARROW_FORMAT) + +#define SOURCE_FLAGS(format) \ + (FAST_PATH_STANDARD_FLAGS | \ + ((PIXMAN_ ## format == PIXMAN_solid) ? \ + 0 : (FAST_PATH_SAMPLES_COVER_CLIP_NEAREST | FAST_PATH_NEAREST_FILTER | FAST_PATH_ID_TRANSFORM))) + +#define MASK_FLAGS(format, extra) \ + ((PIXMAN_ ## format == PIXMAN_null) ? 0 : (SOURCE_FLAGS (format) | extra)) + +#define FAST_PATH(op, src, src_flags, mask, mask_flags, dest, dest_flags, func) \ + PIXMAN_OP_ ## op, \ + PIXMAN_ ## src, \ + src_flags, \ + PIXMAN_ ## mask, \ + mask_flags, \ + PIXMAN_ ## dest, \ + dest_flags, \ + func + +#define PIXMAN_STD_FAST_PATH(op, src, mask, dest, func) \ + { FAST_PATH ( \ + op, \ + src, SOURCE_FLAGS (src), \ + mask, MASK_FLAGS (mask, FAST_PATH_UNIFIED_ALPHA), \ + dest, FAST_PATH_STD_DEST_FLAGS, \ + func) } + +#define PIXMAN_STD_FAST_PATH_CA(op, src, mask, dest, func) \ + { FAST_PATH ( \ + op, \ + src, SOURCE_FLAGS (src), \ + mask, MASK_FLAGS (mask, FAST_PATH_COMPONENT_ALPHA), \ + dest, FAST_PATH_STD_DEST_FLAGS, \ + func) } + +extern pixman_implementation_t *global_implementation; + +static force_inline pixman_implementation_t * +get_implementation (void) +{ +#ifndef TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR + if (!global_implementation) + global_implementation = _pixman_choose_implementation (); +#endif + return global_implementation; +} + +/* This function is exported for the sake of the test suite and not part + * of the ABI. + */ +PIXMAN_EXPORT pixman_implementation_t * +_pixman_internal_only_get_implementation (void); + +/* Memory allocation helpers */ +void * +pixman_malloc_ab (unsigned int n, unsigned int b); + +void * +pixman_malloc_abc (unsigned int a, unsigned int b, unsigned int c); + +void * +pixman_malloc_ab_plus_c (unsigned int a, unsigned int b, unsigned int c); + +pixman_bool_t +_pixman_multiply_overflows_size (size_t a, size_t b); + +pixman_bool_t +_pixman_multiply_overflows_int (unsigned int a, unsigned int b); + +pixman_bool_t +_pixman_addition_overflows_int (unsigned int a, unsigned int b); + +/* Compositing utilities */ +void +pixman_expand_to_float (argb_t *dst, + const uint32_t *src, + pixman_format_code_t format, + int width); + +void +pixman_contract_from_float (uint32_t *dst, + const argb_t *src, + int width); + +/* Region Helpers */ +pixman_bool_t +pixman_region32_copy_from_region16 (pixman_region32_t *dst, + pixman_region16_t *src); + +pixman_bool_t +pixman_region16_copy_from_region32 (pixman_region16_t *dst, + pixman_region32_t *src); + +/* Doubly linked lists */ +typedef struct pixman_link_t pixman_link_t; +struct pixman_link_t +{ + pixman_link_t *next; + pixman_link_t *prev; +}; + +typedef struct pixman_list_t pixman_list_t; +struct pixman_list_t +{ + pixman_link_t *head; + pixman_link_t *tail; +}; + +static force_inline void +pixman_list_init (pixman_list_t *list) +{ + list->head = (pixman_link_t *)list; + list->tail = (pixman_link_t *)list; +} + +static force_inline void +pixman_list_prepend (pixman_list_t *list, pixman_link_t *link) +{ + link->next = list->head; + link->prev = (pixman_link_t *)list; + list->head->prev = link; + list->head = link; +} + +static force_inline void +pixman_list_unlink (pixman_link_t *link) +{ + link->prev->next = link->next; + link->next->prev = link->prev; +} + +static force_inline void +pixman_list_move_to_front (pixman_list_t *list, pixman_link_t *link) +{ + pixman_list_unlink (link); + pixman_list_prepend (list, link); +} + +/* Misc macros */ + +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef MIN +# define MIN(a, b) ((a < b) ? a : b) +#endif + +#ifndef MAX +# define MAX(a, b) ((a > b) ? a : b) +#endif + +/* Integer division that rounds towards -infinity */ +#define DIV(a, b) \ + ((((a) < 0) == ((b) < 0)) ? (a) / (b) : \ + ((a) - (b) + 1 - (((b) < 0) << 1)) / (b)) + +/* Modulus that produces the remainder wrt. DIV */ +#define MOD(a, b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b)) + +#define CLIP(v, low, high) ((v) < (low) ? (low) : ((v) > (high) ? (high) : (v))) + +#define FLOAT_IS_ZERO(f) (-FLT_MIN < (f) && (f) < FLT_MIN) + +/* Conversion between 8888 and 0565 */ + +static force_inline uint16_t +convert_8888_to_0565 (uint32_t s) +{ + /* The following code can be compiled into just 4 instructions on ARM */ + uint32_t a, b; + a = (s >> 3) & 0x1F001F; + b = s & 0xFC00; + a |= a >> 5; + a |= b >> 5; + return (uint16_t)a; +} + +static force_inline uint32_t +convert_0565_to_0888 (uint16_t s) +{ + return (((((s) << 3) & 0xf8) | (((s) >> 2) & 0x7)) | + ((((s) << 5) & 0xfc00) | (((s) >> 1) & 0x300)) | + ((((s) << 8) & 0xf80000) | (((s) << 3) & 0x70000))); +} + +static force_inline uint32_t +convert_0565_to_8888 (uint16_t s) +{ + return convert_0565_to_0888 (s) | 0xff000000; +} + +/* Trivial versions that are useful in macros */ + +static force_inline uint32_t +convert_8888_to_8888 (uint32_t s) +{ + return s; +} + +static force_inline uint32_t +convert_x888_to_8888 (uint32_t s) +{ + return s | 0xff000000; +} + +static force_inline uint16_t +convert_0565_to_0565 (uint16_t s) +{ + return s; +} + +#define PIXMAN_FORMAT_IS_WIDE(f) \ + (PIXMAN_FORMAT_A (f) > 8 || \ + PIXMAN_FORMAT_R (f) > 8 || \ + PIXMAN_FORMAT_G (f) > 8 || \ + PIXMAN_FORMAT_B (f) > 8 || \ + PIXMAN_FORMAT_TYPE (f) == PIXMAN_TYPE_ARGB_SRGB) + +#ifdef WORDS_BIGENDIAN +# define SCREEN_SHIFT_LEFT(x,n) ((x) << (n)) +# define SCREEN_SHIFT_RIGHT(x,n) ((x) >> (n)) +#else +# define SCREEN_SHIFT_LEFT(x,n) ((x) >> (n)) +# define SCREEN_SHIFT_RIGHT(x,n) ((x) << (n)) +#endif + +static force_inline uint32_t +unorm_to_unorm (uint32_t val, int from_bits, int to_bits) +{ + uint32_t result; + + if (from_bits == 0) + return 0; + + /* Delete any extra bits */ + val &= ((1 << from_bits) - 1); + + if (from_bits >= to_bits) + return val >> (from_bits - to_bits); + + /* Start out with the high bit of val in the high bit of result. */ + result = val << (to_bits - from_bits); + + /* Copy the bits in result, doubling the number of bits each time, until + * we fill all to_bits. Unrolled manually because from_bits and to_bits + * are usually known statically, so the compiler can turn all of this + * into a few shifts. + */ +#define REPLICATE() \ + do \ + { \ + if (from_bits < to_bits) \ + { \ + result |= result >> from_bits; \ + \ + from_bits *= 2; \ + } \ + } \ + while (0) + + REPLICATE(); + REPLICATE(); + REPLICATE(); + REPLICATE(); + REPLICATE(); + + return result; +} + +uint16_t pixman_float_to_unorm (float f, int n_bits); +float pixman_unorm_to_float (uint16_t u, int n_bits); + +/* + * Various debugging code + */ + +#undef DEBUG + +#define COMPILE_TIME_ASSERT(x) \ + do { typedef int compile_time_assertion [(x)?1:-1]; } while (0) + +/* Turn on debugging depending on what type of release this is + */ +#if (((PIXMAN_VERSION_MICRO % 2) == 0) && ((PIXMAN_VERSION_MINOR % 2) == 1)) + +/* Debugging gets turned on for development releases because these + * are the things that end up in bleeding edge distributions such + * as Rawhide etc. + * + * For performance reasons we don't turn it on for stable releases or + * random git checkouts. (Random git checkouts are often used for + * performance work). + */ + +# define DEBUG + +#endif + +void +_pixman_log_error (const char *function, const char *message); + +#define return_if_fail(expr) \ + do \ + { \ + if (unlikely (!(expr))) \ + { \ + _pixman_log_error (FUNC, "The expression " # expr " was false"); \ + return; \ + } \ + } \ + while (0) + +#define return_val_if_fail(expr, retval) \ + do \ + { \ + if (unlikely (!(expr))) \ + { \ + _pixman_log_error (FUNC, "The expression " # expr " was false"); \ + return (retval); \ + } \ + } \ + while (0) + +#define critical_if_fail(expr) \ + do \ + { \ + if (unlikely (!(expr))) \ + _pixman_log_error (FUNC, "The expression " # expr " was false"); \ + } \ + while (0) + +/* + * Matrix + */ + +typedef struct { pixman_fixed_48_16_t v[3]; } pixman_vector_48_16_t; + +PIXMAN_EXPORT +pixman_bool_t +pixman_transform_point_31_16 (const pixman_transform_t *t, + const pixman_vector_48_16_t *v, + pixman_vector_48_16_t *result); + +PIXMAN_EXPORT +void +pixman_transform_point_31_16_3d (const pixman_transform_t *t, + const pixman_vector_48_16_t *v, + pixman_vector_48_16_t *result); + +PIXMAN_EXPORT +void +pixman_transform_point_31_16_affine (const pixman_transform_t *t, + const pixman_vector_48_16_t *v, + pixman_vector_48_16_t *result); + +/* + * Timers + */ + +#ifdef PIXMAN_TIMERS + +static inline uint64_t +oil_profile_stamp_rdtsc (void) +{ + uint32_t hi, lo; + + __asm__ __volatile__ ("rdtsc\n" : "=a" (lo), "=d" (hi)); + + return lo | (((uint64_t)hi) << 32); +} + +#define OIL_STAMP oil_profile_stamp_rdtsc + +typedef struct pixman_timer_t pixman_timer_t; + +struct pixman_timer_t +{ + int initialized; + const char * name; + uint64_t n_times; + uint64_t total; + pixman_timer_t *next; +}; + +extern int timer_defined; + +void pixman_timer_register (pixman_timer_t *timer); + +#define TIMER_BEGIN(tname) \ + { \ + static pixman_timer_t timer ## tname; \ + uint64_t begin ## tname; \ + \ + if (!timer ## tname.initialized) \ + { \ + timer ## tname.initialized = 1; \ + timer ## tname.name = # tname; \ + pixman_timer_register (&timer ## tname); \ + } \ + \ + timer ## tname.n_times++; \ + begin ## tname = OIL_STAMP (); + +#define TIMER_END(tname) \ + timer ## tname.total += OIL_STAMP () - begin ## tname; \ + } + +#else + +#define TIMER_BEGIN(tname) +#define TIMER_END(tname) + +#endif /* PIXMAN_TIMERS */ + +#endif /* __ASSEMBLER__ */ + +#endif /* PIXMAN_PRIVATE_H */ diff --git a/gfx/cairo/libpixman/src/pixman-radial-gradient.c b/gfx/cairo/libpixman/src/pixman-radial-gradient.c new file mode 100644 index 0000000000..e8e99c98b9 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c @@ -0,0 +1,509 @@ +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ +/* + * + * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc. + * Copyright © 2000 SuSE, Inc. + * 2005 Lars Knoll & Zack Rusin, Trolltech + * Copyright © 2007 Red Hat, Inc. + * + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of 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. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include "pixman-private.h" + +static inline pixman_fixed_32_32_t +dot (pixman_fixed_48_16_t x1, + pixman_fixed_48_16_t y1, + pixman_fixed_48_16_t z1, + pixman_fixed_48_16_t x2, + pixman_fixed_48_16_t y2, + pixman_fixed_48_16_t z2) +{ + /* + * Exact computation, assuming that the input values can + * be represented as pixman_fixed_16_16_t + */ + return x1 * x2 + y1 * y2 + z1 * z2; +} + +static inline double +fdot (double x1, + double y1, + double z1, + double x2, + double y2, + double z2) +{ + /* + * Error can be unbound in some special cases. + * Using clever dot product algorithms (for example compensated + * dot product) would improve this but make the code much less + * obvious + */ + return x1 * x2 + y1 * y2 + z1 * z2; +} + +static void +radial_write_color (double a, + double b, + double c, + double inva, + double dr, + double mindr, + pixman_gradient_walker_t *walker, + pixman_repeat_t repeat, + int Bpp, + pixman_gradient_walker_write_t write_pixel, + uint32_t *buffer) +{ + /* + * In this function error propagation can lead to bad results: + * - discr can have an unbound error (if b*b-a*c is very small), + * potentially making it the opposite sign of what it should have been + * (thus clearing a pixel that would have been colored or vice-versa) + * or propagating the error to sqrtdiscr; + * if discr has the wrong sign or b is very small, this can lead to bad + * results + * + * - the algorithm used to compute the solutions of the quadratic + * equation is not numerically stable (but saves one division compared + * to the numerically stable one); + * this can be a problem if a*c is much smaller than b*b + * + * - the above problems are worse if a is small (as inva becomes bigger) + */ + double discr; + + if (a == 0) + { + double t; + + if (b == 0) + { + memset (buffer, 0, Bpp); + return; + } + + t = pixman_fixed_1 / 2 * c / b; + if (repeat == PIXMAN_REPEAT_NONE) + { + if (0 <= t && t <= pixman_fixed_1) + { + write_pixel (walker, t, buffer); + return; + } + } + else + { + if (t * dr >= mindr) + { + write_pixel (walker, t, buffer); + return; + } + } + + memset (buffer, 0, Bpp); + return; + } + + discr = fdot (b, a, 0, b, -c, 0); + if (discr >= 0) + { + double sqrtdiscr, t0, t1; + + sqrtdiscr = sqrt (discr); + t0 = (b + sqrtdiscr) * inva; + t1 = (b - sqrtdiscr) * inva; + + /* + * The root that must be used is the biggest one that belongs + * to the valid range ([0,1] for PIXMAN_REPEAT_NONE, any + * solution that results in a positive radius otherwise). + * + * If a > 0, t0 is the biggest solution, so if it is valid, it + * is the correct result. + * + * If a < 0, only one of the solutions can be valid, so the + * order in which they are tested is not important. + */ + if (repeat == PIXMAN_REPEAT_NONE) + { + if (0 <= t0 && t0 <= pixman_fixed_1) + { + write_pixel (walker, t0, buffer); + return; + } + else if (0 <= t1 && t1 <= pixman_fixed_1) + { + write_pixel (walker, t1, buffer); + return; + } + } + else + { + if (t0 * dr >= mindr) + { + write_pixel (walker, t0, buffer); + return; + } + else if (t1 * dr >= mindr) + { + write_pixel (walker, t1, buffer); + return; + } + } + } + + memset (buffer, 0, Bpp); + return; +} + +static uint32_t * +radial_get_scanline (pixman_iter_t *iter, + const uint32_t *mask, + int Bpp, + pixman_gradient_walker_write_t write_pixel) +{ + /* + * Implementation of radial gradients following the PDF specification. + * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference + * Manual (PDF 32000-1:2008 at the time of this writing). + * + * In the radial gradient problem we are given two circles (c₁,r₁) and + * (c₂,r₂) that define the gradient itself. + * + * Mathematically the gradient can be defined as the family of circles + * + * ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂) + * + * excluding those circles whose radius would be < 0. When a point + * belongs to more than one circle, the one with a bigger t is the only + * one that contributes to its color. When a point does not belong + * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0). + * Further limitations on the range of values for t are imposed when + * the gradient is not repeated, namely t must belong to [0,1]. + * + * The graphical result is the same as drawing the valid (radius > 0) + * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient + * is not repeated) using SOURCE operator composition. + * + * It looks like a cone pointing towards the viewer if the ending circle + * is smaller than the starting one, a cone pointing inside the page if + * the starting circle is the smaller one and like a cylinder if they + * have the same radius. + * + * What we actually do is, given the point whose color we are interested + * in, compute the t values for that point, solving for t in: + * + * length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂ + * + * Let's rewrite it in a simpler way, by defining some auxiliary + * variables: + * + * cd = c₂ - c₁ + * pd = p - c₁ + * dr = r₂ - r₁ + * length(t·cd - pd) = r₁ + t·dr + * + * which actually means + * + * hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr + * + * or + * + * ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr. + * + * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes: + * + * (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)² + * + * where we can actually expand the squares and solve for t: + * + * t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² = + * = r₁² + 2·r₁·t·dr + t²·dr² + * + * (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t + + * (pdx² + pdy² - r₁²) = 0 + * + * A = cdx² + cdy² - dr² + * B = pdx·cdx + pdy·cdy + r₁·dr + * C = pdx² + pdy² - r₁² + * At² - 2Bt + C = 0 + * + * The solutions (unless the equation degenerates because of A = 0) are: + * + * t = (B ± ⎷(B² - A·C)) / A + * + * The solution we are going to prefer is the bigger one, unless the + * radius associated to it is negative (or it falls outside the valid t + * range). + * + * Additional observations (useful for optimizations): + * A does not depend on p + * + * A < 0 <=> one of the two circles completely contains the other one + * <=> for every p, the radiuses associated with the two t solutions + * have opposite sign + */ + pixman_image_t *image = iter->image; + int x = iter->x; + int y = iter->y; + int width = iter->width; + uint32_t *buffer = iter->buffer; + + gradient_t *gradient = (gradient_t *)image; + radial_gradient_t *radial = (radial_gradient_t *)image; + uint32_t *end = buffer + width * (Bpp / 4); + pixman_gradient_walker_t walker; + pixman_vector_t v, unit; + + /* reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + _pixman_gradient_walker_init (&walker, gradient, image->common.repeat); + + if (image->common.transform) + { + if (!pixman_transform_point_3d (image->common.transform, &v)) + return iter->buffer; + + unit.vector[0] = image->common.transform->matrix[0][0]; + unit.vector[1] = image->common.transform->matrix[1][0]; + unit.vector[2] = image->common.transform->matrix[2][0]; + } + else + { + unit.vector[0] = pixman_fixed_1; + unit.vector[1] = 0; + unit.vector[2] = 0; + } + + if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1) + { + /* + * Given: + * + * t = (B ± ⎷(B² - A·C)) / A + * + * where + * + * A = cdx² + cdy² - dr² + * B = pdx·cdx + pdy·cdy + r₁·dr + * C = pdx² + pdy² - r₁² + * det = B² - A·C + * + * Since we have an affine transformation, we know that (pdx, pdy) + * increase linearly with each pixel, + * + * pdx = pdx₀ + n·ux, + * pdy = pdy₀ + n·uy, + * + * we can then express B, C and det through multiple differentiation. + */ + pixman_fixed_32_32_t b, db, c, dc, ddc; + + /* warning: this computation may overflow */ + v.vector[0] -= radial->c1.x; + v.vector[1] -= radial->c1.y; + + /* + * B and C are computed and updated exactly. + * If fdot was used instead of dot, in the worst case it would + * lose 11 bits of precision in each of the multiplication and + * summing up would zero out all the bit that were preserved, + * thus making the result 0 instead of the correct one. + * This would mean a worst case of unbound relative error or + * about 2^10 absolute error + */ + b = dot (v.vector[0], v.vector[1], radial->c1.radius, + radial->delta.x, radial->delta.y, radial->delta.radius); + db = dot (unit.vector[0], unit.vector[1], 0, + radial->delta.x, radial->delta.y, 0); + + c = dot (v.vector[0], v.vector[1], + -((pixman_fixed_48_16_t) radial->c1.radius), + v.vector[0], v.vector[1], radial->c1.radius); + dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0], + 2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1], + 0, + unit.vector[0], unit.vector[1], 0); + ddc = 2 * dot (unit.vector[0], unit.vector[1], 0, + unit.vector[0], unit.vector[1], 0); + + while (buffer < end) + { + if (!mask || *mask++) + { + radial_write_color (radial->a, b, c, + radial->inva, + radial->delta.radius, + radial->mindr, + &walker, + image->common.repeat, + Bpp, + write_pixel, + buffer); + } + + b += db; + c += dc; + dc += ddc; + buffer += (Bpp / 4); + } + } + else + { + /* projective */ + /* Warning: + * error propagation guarantees are much looser than in the affine case + */ + while (buffer < end) + { + if (!mask || *mask++) + { + if (v.vector[2] != 0) + { + double pdx, pdy, invv2, b, c; + + invv2 = 1. * pixman_fixed_1 / v.vector[2]; + + pdx = v.vector[0] * invv2 - radial->c1.x; + /* / pixman_fixed_1 */ + + pdy = v.vector[1] * invv2 - radial->c1.y; + /* / pixman_fixed_1 */ + + b = fdot (pdx, pdy, radial->c1.radius, + radial->delta.x, radial->delta.y, + radial->delta.radius); + /* / pixman_fixed_1 / pixman_fixed_1 */ + + c = fdot (pdx, pdy, -radial->c1.radius, + pdx, pdy, radial->c1.radius); + /* / pixman_fixed_1 / pixman_fixed_1 */ + + radial_write_color (radial->a, b, c, + radial->inva, + radial->delta.radius, + radial->mindr, + &walker, + image->common.repeat, + Bpp, + write_pixel, + buffer); + } + else + { + memset (buffer, 0, Bpp); + } + } + + buffer += (Bpp / 4); + + v.vector[0] += unit.vector[0]; + v.vector[1] += unit.vector[1]; + v.vector[2] += unit.vector[2]; + } + } + + iter->y++; + return iter->buffer; +} + +static uint32_t * +radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask) +{ + return radial_get_scanline (iter, mask, 4, + _pixman_gradient_walker_write_narrow); +} + +static uint32_t * +radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask) +{ + return radial_get_scanline (iter, NULL, 16, + _pixman_gradient_walker_write_wide); +} + +void +_pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) +{ + if (iter->iter_flags & ITER_NARROW) + iter->get_scanline = radial_get_scanline_narrow; + else + iter->get_scanline = radial_get_scanline_wide; +} + +PIXMAN_EXPORT pixman_image_t * +pixman_image_create_radial_gradient (const pixman_point_fixed_t * inner, + const pixman_point_fixed_t * outer, + pixman_fixed_t inner_radius, + pixman_fixed_t outer_radius, + const pixman_gradient_stop_t *stops, + int n_stops) +{ + pixman_image_t *image; + radial_gradient_t *radial; + + image = _pixman_image_allocate (); + + if (!image) + return NULL; + + radial = &image->radial; + + if (!_pixman_init_gradient (&radial->common, stops, n_stops)) + { + free (image); + return NULL; + } + + image->type = RADIAL; + + radial->c1.x = inner->x; + radial->c1.y = inner->y; + radial->c1.radius = inner_radius; + radial->c2.x = outer->x; + radial->c2.y = outer->y; + radial->c2.radius = outer_radius; + + /* warning: this computations may overflow */ + radial->delta.x = radial->c2.x - radial->c1.x; + radial->delta.y = radial->c2.y - radial->c1.y; + radial->delta.radius = radial->c2.radius - radial->c1.radius; + + /* computed exactly, then cast to double -> every bit of the double + representation is correct (53 bits) */ + radial->a = dot (radial->delta.x, radial->delta.y, -radial->delta.radius, + radial->delta.x, radial->delta.y, radial->delta.radius); + if (radial->a != 0) + radial->inva = 1. * pixman_fixed_1 / radial->a; + + radial->mindr = -1. * pixman_fixed_1 * radial->c1.radius; + + return image; +} diff --git a/gfx/cairo/libpixman/src/pixman-region.c b/gfx/cairo/libpixman/src/pixman-region.c new file mode 100644 index 0000000000..a2daa279fa --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-region.c @@ -0,0 +1,2792 @@ +/* + * Copyright 1987, 1988, 1989, 1998 The Open Group + * + * 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. + * + * 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 + * OPEN GROUP 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. + * + * Except as contained in this notice, the name of The Open Group shall not be + * used in advertising or otherwise to promote the sale, use or other dealings + * in this Software without prior written authorization from The Open Group. + * + * Copyright 1987, 1988, 1989 by + * Digital Equipment Corporation, Maynard, Massachusetts. + * + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * 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 Digital not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + * DIGITAL 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. + * + * Copyright © 1998 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 +#include +#include +#include +#include "pixman-private.h" + +#define PIXREGION_NIL(reg) ((reg)->data && !(reg)->data->numRects) +/* not a region */ +#define PIXREGION_NAR(reg) ((reg)->data == pixman_broken_data) +#define PIXREGION_NUMRECTS(reg) ((reg)->data ? (reg)->data->numRects : 1) +#define PIXREGION_SIZE(reg) ((reg)->data ? (reg)->data->size : 0) +#define PIXREGION_RECTS(reg) \ + ((reg)->data ? (box_type_t *)((reg)->data + 1) \ + : (box_type_t *)&(reg)->extents) +#define PIXREGION_BOXPTR(reg) ((box_type_t *)((reg)->data + 1)) +#define PIXREGION_BOX(reg, i) (&PIXREGION_BOXPTR (reg)[i]) +#define PIXREGION_TOP(reg) PIXREGION_BOX (reg, (reg)->data->numRects) +#define PIXREGION_END(reg) PIXREGION_BOX (reg, (reg)->data->numRects - 1) + +#define GOOD_RECT(rect) ((rect)->x1 < (rect)->x2 && (rect)->y1 < (rect)->y2) +#define BAD_RECT(rect) ((rect)->x1 > (rect)->x2 || (rect)->y1 > (rect)->y2) + +#ifdef DEBUG + +#define GOOD(reg) \ + do \ + { \ + if (!PREFIX (_selfcheck (reg))) \ + _pixman_log_error (FUNC, "Malformed region " # reg); \ + } while (0) + +#else + +#define GOOD(reg) + +#endif + +static const box_type_t PREFIX (_empty_box_) = { 0, 0, 0, 0 }; +static const region_data_type_t PREFIX (_empty_data_) = { 0, 0 }; +#if defined (__llvm__) && !defined (__clang__) +static const volatile region_data_type_t PREFIX (_broken_data_) = { 0, 0 }; +#else +static const region_data_type_t PREFIX (_broken_data_) = { 0, 0 }; +#endif + +static box_type_t *pixman_region_empty_box = + (box_type_t *)&PREFIX (_empty_box_); +static region_data_type_t *pixman_region_empty_data = + (region_data_type_t *)&PREFIX (_empty_data_); +static region_data_type_t *pixman_broken_data = + (region_data_type_t *)&PREFIX (_broken_data_); + +static pixman_bool_t +pixman_break (region_type_t *region); + +/* + * The functions in this file implement the Region abstraction used extensively + * throughout the X11 sample server. A Region is simply a set of disjoint + * (non-overlapping) rectangles, plus an "extent" rectangle which is the + * smallest single rectangle that contains all the non-overlapping rectangles. + * + * A Region is implemented as a "y-x-banded" array of rectangles. This array + * imposes two degrees of order. First, all rectangles are sorted by top side + * y coordinate first (y1), and then by left side x coordinate (x1). + * + * Furthermore, the rectangles are grouped into "bands". Each rectangle in a + * band has the same top y coordinate (y1), and each has the same bottom y + * coordinate (y2). Thus all rectangles in a band differ only in their left + * and right side (x1 and x2). Bands are implicit in the array of rectangles: + * there is no separate list of band start pointers. + * + * The y-x band representation does not minimize rectangles. In particular, + * if a rectangle vertically crosses a band (the rectangle has scanlines in + * the y1 to y2 area spanned by the band), then the rectangle may be broken + * down into two or more smaller rectangles stacked one atop the other. + * + * ----------- ----------- + * | | | | band 0 + * | | -------- ----------- -------- + * | | | | in y-x banded | | | | band 1 + * | | | | form is | | | | + * ----------- | | ----------- -------- + * | | | | band 2 + * -------- -------- + * + * An added constraint on the rectangles is that they must cover as much + * horizontal area as possible: no two rectangles within a band are allowed + * to touch. + * + * Whenever possible, bands will be merged together to cover a greater vertical + * distance (and thus reduce the number of rectangles). Two bands can be merged + * only if the bottom of one touches the top of the other and they have + * rectangles in the same places (of the same width, of course). + * + * Adam de Boor wrote most of the original region code. Joel McCormack + * substantially modified or rewrote most of the core arithmetic routines, and + * added pixman_region_validate in order to support several speed improvements + * to pixman_region_validate_tree. Bob Scheifler changed the representation + * to be more compact when empty or a single rectangle, and did a bunch of + * gratuitous reformatting. Carl Worth did further gratuitous reformatting + * while re-merging the server and client region code into libpixregion. + * Soren Sandmann did even more gratuitous reformatting. + */ + +/* true iff two Boxes overlap */ +#define EXTENTCHECK(r1, r2) \ + (!( ((r1)->x2 <= (r2)->x1) || \ + ((r1)->x1 >= (r2)->x2) || \ + ((r1)->y2 <= (r2)->y1) || \ + ((r1)->y1 >= (r2)->y2) ) ) + +/* true iff (x,y) is in Box */ +#define INBOX(r, x, y) \ + ( ((r)->x2 > x) && \ + ((r)->x1 <= x) && \ + ((r)->y2 > y) && \ + ((r)->y1 <= y) ) + +/* true iff Box r1 contains Box r2 */ +#define SUBSUMES(r1, r2) \ + ( ((r1)->x1 <= (r2)->x1) && \ + ((r1)->x2 >= (r2)->x2) && \ + ((r1)->y1 <= (r2)->y1) && \ + ((r1)->y2 >= (r2)->y2) ) + +static size_t +PIXREGION_SZOF (size_t n) +{ + size_t size = n * sizeof(box_type_t); + + if (n > UINT32_MAX / sizeof(box_type_t)) + return 0; + + if (sizeof(region_data_type_t) > UINT32_MAX - size) + return 0; + + return size + sizeof(region_data_type_t); +} + +static region_data_type_t * +alloc_data (size_t n) +{ + size_t sz = PIXREGION_SZOF (n); + + if (!sz) + return NULL; + + return malloc (sz); +} + +#define FREE_DATA(reg) if ((reg)->data && (reg)->data->size) free ((reg)->data) + +#define RECTALLOC_BAIL(region, n, bail) \ + do \ + { \ + if (!(region)->data || \ + (((region)->data->numRects + (n)) > (region)->data->size)) \ + { \ + if (!pixman_rect_alloc (region, n)) \ + goto bail; \ + } \ + } while (0) + +#define RECTALLOC(region, n) \ + do \ + { \ + if (!(region)->data || \ + (((region)->data->numRects + (n)) > (region)->data->size)) \ + { \ + if (!pixman_rect_alloc (region, n)) { \ + return FALSE; \ + } \ + } \ + } while (0) + +#define ADDRECT(next_rect, nx1, ny1, nx2, ny2) \ + do \ + { \ + next_rect->x1 = nx1; \ + next_rect->y1 = ny1; \ + next_rect->x2 = nx2; \ + next_rect->y2 = ny2; \ + next_rect++; \ + } \ + while (0) + +#define NEWRECT(region, next_rect, nx1, ny1, nx2, ny2) \ + do \ + { \ + if (!(region)->data || \ + ((region)->data->numRects == (region)->data->size)) \ + { \ + if (!pixman_rect_alloc (region, 1)) \ + return FALSE; \ + next_rect = PIXREGION_TOP (region); \ + } \ + ADDRECT (next_rect, nx1, ny1, nx2, ny2); \ + region->data->numRects++; \ + critical_if_fail (region->data->numRects <= region->data->size); \ + } while (0) + +#define DOWNSIZE(reg, numRects) \ + do \ + { \ + if (((numRects) < ((reg)->data->size >> 1)) && \ + ((reg)->data->size > 50)) \ + { \ + region_data_type_t * new_data; \ + size_t data_size = PIXREGION_SZOF (numRects); \ + \ + if (!data_size) \ + { \ + new_data = NULL; \ + } \ + else \ + { \ + new_data = (region_data_type_t *) \ + realloc ((reg)->data, data_size); \ + } \ + \ + if (new_data) \ + { \ + new_data->size = (numRects); \ + (reg)->data = new_data; \ + } \ + } \ + } while (0) + +PIXMAN_EXPORT pixman_bool_t +PREFIX (_equal) (const region_type_t *reg1, const region_type_t *reg2) +{ + int i; + box_type_t *rects1; + box_type_t *rects2; + + if (reg1->extents.x1 != reg2->extents.x1) + return FALSE; + + if (reg1->extents.x2 != reg2->extents.x2) + return FALSE; + + if (reg1->extents.y1 != reg2->extents.y1) + return FALSE; + + if (reg1->extents.y2 != reg2->extents.y2) + return FALSE; + + if (PIXREGION_NUMRECTS (reg1) != PIXREGION_NUMRECTS (reg2)) + return FALSE; + + rects1 = PIXREGION_RECTS (reg1); + rects2 = PIXREGION_RECTS (reg2); + + for (i = 0; i != PIXREGION_NUMRECTS (reg1); i++) + { + if (rects1[i].x1 != rects2[i].x1) + return FALSE; + + if (rects1[i].x2 != rects2[i].x2) + return FALSE; + + if (rects1[i].y1 != rects2[i].y1) + return FALSE; + + if (rects1[i].y2 != rects2[i].y2) + return FALSE; + } + + return TRUE; +} + +int +PREFIX (_print) (region_type_t *rgn) +{ + int num, size; + int i; + box_type_t * rects; + + num = PIXREGION_NUMRECTS (rgn); + size = PIXREGION_SIZE (rgn); + rects = PIXREGION_RECTS (rgn); + + fprintf (stderr, "num: %d size: %d\n", num, size); + fprintf (stderr, "extents: %d %d %d %d\n", + rgn->extents.x1, + rgn->extents.y1, + rgn->extents.x2, + rgn->extents.y2); + + for (i = 0; i < num; i++) + { + fprintf (stderr, "%d %d %d %d \n", + rects[i].x1, rects[i].y1, rects[i].x2, rects[i].y2); + } + + fprintf (stderr, "\n"); + + return(num); +} + + +PIXMAN_EXPORT void +PREFIX (_init) (region_type_t *region) +{ + region->extents = *pixman_region_empty_box; + region->data = pixman_region_empty_data; +} + +PIXMAN_EXPORT void +PREFIX (_init_rect) (region_type_t * region, + int x, + int y, + unsigned int width, + unsigned int height) +{ + region->extents.x1 = x; + region->extents.y1 = y; + region->extents.x2 = x + width; + region->extents.y2 = y + height; + + if (!GOOD_RECT (®ion->extents)) + { + if (BAD_RECT (®ion->extents)) + _pixman_log_error (FUNC, "Invalid rectangle passed"); + PREFIX (_init) (region); + return; + } + + region->data = NULL; +} + +PIXMAN_EXPORT void +PREFIX (_init_with_extents) (region_type_t *region, const box_type_t *extents) +{ + if (!GOOD_RECT (extents)) + { + if (BAD_RECT (extents)) + _pixman_log_error (FUNC, "Invalid rectangle passed"); + PREFIX (_init) (region); + return; + } + region->extents = *extents; + + region->data = NULL; +} + +PIXMAN_EXPORT void +PREFIX (_fini) (region_type_t *region) +{ + GOOD (region); + FREE_DATA (region); +} + +PIXMAN_EXPORT int +PREFIX (_n_rects) (const region_type_t *region) +{ + return PIXREGION_NUMRECTS (region); +} + +PIXMAN_EXPORT box_type_t * +PREFIX (_rectangles) (const region_type_t *region, + int *n_rects) +{ + if (n_rects) + *n_rects = PIXREGION_NUMRECTS (region); + + return PIXREGION_RECTS (region); +} + +static pixman_bool_t +pixman_break (region_type_t *region) +{ + FREE_DATA (region); + + region->extents = *pixman_region_empty_box; + region->data = pixman_broken_data; + + return FALSE; +} + +static pixman_bool_t +pixman_rect_alloc (region_type_t * region, + int n) +{ + region_data_type_t *data; + + if (!region->data) + { + n++; + region->data = alloc_data (n); + + if (!region->data) + return pixman_break (region); + + region->data->numRects = 1; + *PIXREGION_BOXPTR (region) = region->extents; + } + else if (!region->data->size) + { + region->data = alloc_data (n); + + if (!region->data) + return pixman_break (region); + + region->data->numRects = 0; + } + else + { + size_t data_size; + + if (n == 1) + { + n = region->data->numRects; + if (n > 500) /* XXX pick numbers out of a hat */ + n = 250; + } + + n += region->data->numRects; + data_size = PIXREGION_SZOF (n); + + if (!data_size) + { + data = NULL; + } + else + { + data = (region_data_type_t *) + realloc (region->data, PIXREGION_SZOF (n)); + } + + if (!data) + return pixman_break (region); + + region->data = data; + } + + region->data->size = n; + + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +PREFIX (_copy) (region_type_t *dst, const region_type_t *src) +{ + GOOD (dst); + GOOD (src); + + if (dst == src) + return TRUE; + + dst->extents = src->extents; + + if (!src->data || !src->data->size) + { + FREE_DATA (dst); + dst->data = src->data; + return TRUE; + } + + if (!dst->data || (dst->data->size < src->data->numRects)) + { + FREE_DATA (dst); + + dst->data = alloc_data (src->data->numRects); + + if (!dst->data) + return pixman_break (dst); + + dst->data->size = src->data->numRects; + } + + dst->data->numRects = src->data->numRects; + + memmove ((char *)PIXREGION_BOXPTR (dst), (char *)PIXREGION_BOXPTR (src), + dst->data->numRects * sizeof(box_type_t)); + + return TRUE; +} + +/*====================================================================== + * Generic Region Operator + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * pixman_coalesce -- + * Attempt to merge the boxes in the current band with those in the + * previous one. We are guaranteed that the current band extends to + * the end of the rects array. Used only by pixman_op. + * + * Results: + * The new index for the previous band. + * + * Side Effects: + * If coalescing takes place: + * - rectangles in the previous band will have their y2 fields + * altered. + * - region->data->numRects will be decreased. + * + *----------------------------------------------------------------------- + */ +static inline int +pixman_coalesce (region_type_t * region, /* Region to coalesce */ + int prev_start, /* Index of start of previous band */ + int cur_start) /* Index of start of current band */ +{ + box_type_t *prev_box; /* Current box in previous band */ + box_type_t *cur_box; /* Current box in current band */ + int numRects; /* Number rectangles in both bands */ + int y2; /* Bottom of current band */ + + /* + * Figure out how many rectangles are in the band. + */ + numRects = cur_start - prev_start; + critical_if_fail (numRects == region->data->numRects - cur_start); + + if (!numRects) return cur_start; + + /* + * The bands may only be coalesced if the bottom of the previous + * matches the top scanline of the current. + */ + prev_box = PIXREGION_BOX (region, prev_start); + cur_box = PIXREGION_BOX (region, cur_start); + if (prev_box->y2 != cur_box->y1) return cur_start; + + /* + * Make sure the bands have boxes in the same places. This + * assumes that boxes have been added in such a way that they + * cover the most area possible. I.e. two boxes in a band must + * have some horizontal space between them. + */ + y2 = cur_box->y2; + + do + { + if ((prev_box->x1 != cur_box->x1) || (prev_box->x2 != cur_box->x2)) + return (cur_start); + + prev_box++; + cur_box++; + numRects--; + } + while (numRects); + + /* + * The bands may be merged, so set the bottom y of each box + * in the previous band to the bottom y of the current band. + */ + numRects = cur_start - prev_start; + region->data->numRects -= numRects; + + do + { + prev_box--; + prev_box->y2 = y2; + numRects--; + } + while (numRects); + + return prev_start; +} + +/* Quicky macro to avoid trivial reject procedure calls to pixman_coalesce */ + +#define COALESCE(new_reg, prev_band, cur_band) \ + do \ + { \ + if (cur_band - prev_band == new_reg->data->numRects - cur_band) \ + prev_band = pixman_coalesce (new_reg, prev_band, cur_band); \ + else \ + prev_band = cur_band; \ + } while (0) + +/*- + *----------------------------------------------------------------------- + * pixman_region_append_non_o -- + * Handle a non-overlapping band for the union and subtract operations. + * Just adds the (top/bottom-clipped) rectangles into the region. + * Doesn't have to check for subsumption or anything. + * + * Results: + * None. + * + * Side Effects: + * region->data->numRects is incremented and the rectangles overwritten + * with the rectangles we're passed. + * + *----------------------------------------------------------------------- + */ +static inline pixman_bool_t +pixman_region_append_non_o (region_type_t * region, + box_type_t * r, + box_type_t * r_end, + int y1, + int y2) +{ + box_type_t *next_rect; + int new_rects; + + new_rects = r_end - r; + + critical_if_fail (y1 < y2); + critical_if_fail (new_rects != 0); + + /* Make sure we have enough space for all rectangles to be added */ + RECTALLOC (region, new_rects); + next_rect = PIXREGION_TOP (region); + region->data->numRects += new_rects; + + do + { + critical_if_fail (r->x1 < r->x2); + ADDRECT (next_rect, r->x1, y1, r->x2, y2); + r++; + } + while (r != r_end); + + return TRUE; +} + +#define FIND_BAND(r, r_band_end, r_end, ry1) \ + do \ + { \ + ry1 = r->y1; \ + r_band_end = r + 1; \ + while ((r_band_end != r_end) && (r_band_end->y1 == ry1)) { \ + r_band_end++; \ + } \ + } while (0) + +#define APPEND_REGIONS(new_reg, r, r_end) \ + do \ + { \ + int new_rects; \ + if ((new_rects = r_end - r)) { \ + RECTALLOC_BAIL (new_reg, new_rects, bail); \ + memmove ((char *)PIXREGION_TOP (new_reg), (char *)r, \ + new_rects * sizeof(box_type_t)); \ + new_reg->data->numRects += new_rects; \ + } \ + } while (0) + +/*- + *----------------------------------------------------------------------- + * pixman_op -- + * Apply an operation to two regions. Called by pixman_region_union, pixman_region_inverse, + * pixman_region_subtract, pixman_region_intersect.... Both regions MUST have at least one + * rectangle, and cannot be the same object. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * The new region is overwritten. + * overlap set to TRUE if overlap_func ever returns TRUE. + * + * Notes: + * The idea behind this function is to view the two regions as sets. + * Together they cover a rectangle of area that this function divides + * into horizontal bands where points are covered only by one region + * or by both. For the first case, the non_overlap_func is called with + * each the band and the band's upper and lower extents. For the + * second, the overlap_func is called to process the entire band. It + * is responsible for clipping the rectangles in the band, though + * this function provides the boundaries. + * At the end of each band, the new region is coalesced, if possible, + * to reduce the number of rectangles in the region. + * + *----------------------------------------------------------------------- + */ + +typedef pixman_bool_t (*overlap_proc_ptr) (region_type_t *region, + box_type_t * r1, + box_type_t * r1_end, + box_type_t * r2, + box_type_t * r2_end, + int y1, + int y2); + +static pixman_bool_t +pixman_op (region_type_t * new_reg, /* Place to store result */ + const region_type_t * reg1, /* First region in operation */ + const region_type_t * reg2, /* 2d region in operation */ + overlap_proc_ptr overlap_func, /* Function to call for over- + * lapping bands */ + int append_non1, /* Append non-overlapping bands + * in region 1 ? + */ + int append_non2 /* Append non-overlapping bands + * in region 2 ? + */ + ) +{ + box_type_t *r1; /* Pointer into first region */ + box_type_t *r2; /* Pointer into 2d region */ + box_type_t *r1_end; /* End of 1st region */ + box_type_t *r2_end; /* End of 2d region */ + int ybot; /* Bottom of intersection */ + int ytop; /* Top of intersection */ + region_data_type_t *old_data; /* Old data for new_reg */ + int prev_band; /* Index of start of + * previous band in new_reg */ + int cur_band; /* Index of start of current + * band in new_reg */ + box_type_t * r1_band_end; /* End of current band in r1 */ + box_type_t * r2_band_end; /* End of current band in r2 */ + int top; /* Top of non-overlapping band */ + int bot; /* Bottom of non-overlapping band*/ + int r1y1; /* Temps for r1->y1 and r2->y1 */ + int r2y1; + int new_size; + int numRects; + + /* + * Break any region computed from a broken region + */ + if (PIXREGION_NAR (reg1) || PIXREGION_NAR (reg2)) + return pixman_break (new_reg); + + /* + * Initialization: + * set r1, r2, r1_end and r2_end appropriately, save the rectangles + * of the destination region until the end in case it's one of + * the two source regions, then mark the "new" region empty, allocating + * another array of rectangles for it to use. + */ + + r1 = PIXREGION_RECTS (reg1); + new_size = PIXREGION_NUMRECTS (reg1); + r1_end = r1 + new_size; + + numRects = PIXREGION_NUMRECTS (reg2); + r2 = PIXREGION_RECTS (reg2); + r2_end = r2 + numRects; + + critical_if_fail (r1 != r1_end); + critical_if_fail (r2 != r2_end); + + old_data = (region_data_type_t *)NULL; + + if (((new_reg == reg1) && (new_size > 1)) || + ((new_reg == reg2) && (numRects > 1))) + { + old_data = new_reg->data; + new_reg->data = pixman_region_empty_data; + } + + /* guess at new size */ + if (numRects > new_size) + new_size = numRects; + + new_size <<= 1; + + if (!new_reg->data) + new_reg->data = pixman_region_empty_data; + else if (new_reg->data->size) + new_reg->data->numRects = 0; + + if (new_size > new_reg->data->size) + { + if (!pixman_rect_alloc (new_reg, new_size)) + { + free (old_data); + return FALSE; + } + } + + /* + * Initialize ybot. + * In the upcoming loop, ybot and ytop serve different functions depending + * on whether the band being handled is an overlapping or non-overlapping + * band. + * In the case of a non-overlapping band (only one of the regions + * has points in the band), ybot is the bottom of the most recent + * intersection and thus clips the top of the rectangles in that band. + * ytop is the top of the next intersection between the two regions and + * serves to clip the bottom of the rectangles in the current band. + * For an overlapping band (where the two regions intersect), ytop clips + * the top of the rectangles of both regions and ybot clips the bottoms. + */ + + ybot = MIN (r1->y1, r2->y1); + + /* + * prev_band serves to mark the start of the previous band so rectangles + * can be coalesced into larger rectangles. qv. pixman_coalesce, above. + * In the beginning, there is no previous band, so prev_band == cur_band + * (cur_band is set later on, of course, but the first band will always + * start at index 0). prev_band and cur_band must be indices because of + * the possible expansion, and resultant moving, of the new region's + * array of rectangles. + */ + prev_band = 0; + + do + { + /* + * This algorithm proceeds one source-band (as opposed to a + * destination band, which is determined by where the two regions + * intersect) at a time. r1_band_end and r2_band_end serve to mark the + * rectangle after the last one in the current band for their + * respective regions. + */ + critical_if_fail (r1 != r1_end); + critical_if_fail (r2 != r2_end); + + FIND_BAND (r1, r1_band_end, r1_end, r1y1); + FIND_BAND (r2, r2_band_end, r2_end, r2y1); + + /* + * First handle the band that doesn't intersect, if any. + * + * Note that attention is restricted to one band in the + * non-intersecting region at once, so if a region has n + * bands between the current position and the next place it overlaps + * the other, this entire loop will be passed through n times. + */ + if (r1y1 < r2y1) + { + if (append_non1) + { + top = MAX (r1y1, ybot); + bot = MIN (r1->y2, r2y1); + if (top != bot) + { + cur_band = new_reg->data->numRects; + if (!pixman_region_append_non_o (new_reg, r1, r1_band_end, top, bot)) + goto bail; + COALESCE (new_reg, prev_band, cur_band); + } + } + ytop = r2y1; + } + else if (r2y1 < r1y1) + { + if (append_non2) + { + top = MAX (r2y1, ybot); + bot = MIN (r2->y2, r1y1); + + if (top != bot) + { + cur_band = new_reg->data->numRects; + + if (!pixman_region_append_non_o (new_reg, r2, r2_band_end, top, bot)) + goto bail; + + COALESCE (new_reg, prev_band, cur_band); + } + } + ytop = r1y1; + } + else + { + ytop = r1y1; + } + + /* + * Now see if we've hit an intersecting band. The two bands only + * intersect if ybot > ytop + */ + ybot = MIN (r1->y2, r2->y2); + if (ybot > ytop) + { + cur_band = new_reg->data->numRects; + + if (!(*overlap_func)(new_reg, + r1, r1_band_end, + r2, r2_band_end, + ytop, ybot)) + { + goto bail; + } + + COALESCE (new_reg, prev_band, cur_band); + } + + /* + * If we've finished with a band (y2 == ybot) we skip forward + * in the region to the next band. + */ + if (r1->y2 == ybot) + r1 = r1_band_end; + + if (r2->y2 == ybot) + r2 = r2_band_end; + + } + while (r1 != r1_end && r2 != r2_end); + + /* + * Deal with whichever region (if any) still has rectangles left. + * + * We only need to worry about banding and coalescing for the very first + * band left. After that, we can just group all remaining boxes, + * regardless of how many bands, into one final append to the list. + */ + + if ((r1 != r1_end) && append_non1) + { + /* Do first non_overlap1Func call, which may be able to coalesce */ + FIND_BAND (r1, r1_band_end, r1_end, r1y1); + + cur_band = new_reg->data->numRects; + + if (!pixman_region_append_non_o (new_reg, + r1, r1_band_end, + MAX (r1y1, ybot), r1->y2)) + { + goto bail; + } + + COALESCE (new_reg, prev_band, cur_band); + + /* Just append the rest of the boxes */ + APPEND_REGIONS (new_reg, r1_band_end, r1_end); + } + else if ((r2 != r2_end) && append_non2) + { + /* Do first non_overlap2Func call, which may be able to coalesce */ + FIND_BAND (r2, r2_band_end, r2_end, r2y1); + + cur_band = new_reg->data->numRects; + + if (!pixman_region_append_non_o (new_reg, + r2, r2_band_end, + MAX (r2y1, ybot), r2->y2)) + { + goto bail; + } + + COALESCE (new_reg, prev_band, cur_band); + + /* Append rest of boxes */ + APPEND_REGIONS (new_reg, r2_band_end, r2_end); + } + + free (old_data); + + if (!(numRects = new_reg->data->numRects)) + { + FREE_DATA (new_reg); + new_reg->data = pixman_region_empty_data; + } + else if (numRects == 1) + { + new_reg->extents = *PIXREGION_BOXPTR (new_reg); + FREE_DATA (new_reg); + new_reg->data = (region_data_type_t *)NULL; + } + else + { + DOWNSIZE (new_reg, numRects); + } + + return TRUE; + +bail: + free (old_data); + + return pixman_break (new_reg); +} + +/*- + *----------------------------------------------------------------------- + * pixman_set_extents -- + * Reset the extents of a region to what they should be. Called by + * pixman_region_subtract and pixman_region_intersect as they can't + * figure it out along the way or do so easily, as pixman_region_union can. + * + * Results: + * None. + * + * Side Effects: + * The region's 'extents' structure is overwritten. + * + *----------------------------------------------------------------------- + */ +static void +pixman_set_extents (region_type_t *region) +{ + box_type_t *box, *box_end; + + if (!region->data) + return; + + if (!region->data->size) + { + region->extents.x2 = region->extents.x1; + region->extents.y2 = region->extents.y1; + return; + } + + box = PIXREGION_BOXPTR (region); + box_end = PIXREGION_END (region); + + /* + * Since box is the first rectangle in the region, it must have the + * smallest y1 and since box_end is the last rectangle in the region, + * it must have the largest y2, because of banding. Initialize x1 and + * x2 from box and box_end, resp., as good things to initialize them + * to... + */ + region->extents.x1 = box->x1; + region->extents.y1 = box->y1; + region->extents.x2 = box_end->x2; + region->extents.y2 = box_end->y2; + + critical_if_fail (region->extents.y1 < region->extents.y2); + + while (box <= box_end) + { + if (box->x1 < region->extents.x1) + region->extents.x1 = box->x1; + if (box->x2 > region->extents.x2) + region->extents.x2 = box->x2; + box++; + } + + critical_if_fail (region->extents.x1 < region->extents.x2); +} + +/*====================================================================== + * Region Intersection + *====================================================================*/ +/*- + *----------------------------------------------------------------------- + * pixman_region_intersect_o -- + * Handle an overlapping band for pixman_region_intersect. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * Rectangles may be added to the region. + * + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static pixman_bool_t +pixman_region_intersect_o (region_type_t *region, + box_type_t * r1, + box_type_t * r1_end, + box_type_t * r2, + box_type_t * r2_end, + int y1, + int y2) +{ + int x1; + int x2; + box_type_t * next_rect; + + next_rect = PIXREGION_TOP (region); + + critical_if_fail (y1 < y2); + critical_if_fail (r1 != r1_end && r2 != r2_end); + + do + { + x1 = MAX (r1->x1, r2->x1); + x2 = MIN (r1->x2, r2->x2); + + /* + * If there's any overlap between the two rectangles, add that + * overlap to the new region. + */ + if (x1 < x2) + NEWRECT (region, next_rect, x1, y1, x2, y2); + + /* + * Advance the pointer(s) with the leftmost right side, since the next + * rectangle on that list may still overlap the other region's + * current rectangle. + */ + if (r1->x2 == x2) + { + r1++; + } + if (r2->x2 == x2) + { + r2++; + } + } + while ((r1 != r1_end) && (r2 != r2_end)); + + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +PREFIX (_intersect) (region_type_t * new_reg, + const region_type_t * reg1, + const region_type_t * reg2) +{ + GOOD (reg1); + GOOD (reg2); + GOOD (new_reg); + + /* check for trivial reject */ + if (PIXREGION_NIL (reg1) || PIXREGION_NIL (reg2) || + !EXTENTCHECK (®1->extents, ®2->extents)) + { + /* Covers about 20% of all cases */ + FREE_DATA (new_reg); + new_reg->extents.x2 = new_reg->extents.x1; + new_reg->extents.y2 = new_reg->extents.y1; + if (PIXREGION_NAR (reg1) || PIXREGION_NAR (reg2)) + { + new_reg->data = pixman_broken_data; + return FALSE; + } + else + { + new_reg->data = pixman_region_empty_data; + } + } + else if (!reg1->data && !reg2->data) + { + /* Covers about 80% of cases that aren't trivially rejected */ + new_reg->extents.x1 = MAX (reg1->extents.x1, reg2->extents.x1); + new_reg->extents.y1 = MAX (reg1->extents.y1, reg2->extents.y1); + new_reg->extents.x2 = MIN (reg1->extents.x2, reg2->extents.x2); + new_reg->extents.y2 = MIN (reg1->extents.y2, reg2->extents.y2); + + FREE_DATA (new_reg); + + new_reg->data = (region_data_type_t *)NULL; + } + else if (!reg2->data && SUBSUMES (®2->extents, ®1->extents)) + { + return PREFIX (_copy) (new_reg, reg1); + } + else if (!reg1->data && SUBSUMES (®1->extents, ®2->extents)) + { + return PREFIX (_copy) (new_reg, reg2); + } + else if (reg1 == reg2) + { + return PREFIX (_copy) (new_reg, reg1); + } + else + { + /* General purpose intersection */ + + if (!pixman_op (new_reg, reg1, reg2, pixman_region_intersect_o, FALSE, FALSE)) + return FALSE; + + pixman_set_extents (new_reg); + } + + GOOD (new_reg); + return(TRUE); +} + +#define MERGERECT(r) \ + do \ + { \ + if (r->x1 <= x2) \ + { \ + /* Merge with current rectangle */ \ + if (x2 < r->x2) \ + x2 = r->x2; \ + } \ + else \ + { \ + /* Add current rectangle, start new one */ \ + NEWRECT (region, next_rect, x1, y1, x2, y2); \ + x1 = r->x1; \ + x2 = r->x2; \ + } \ + r++; \ + } while (0) + +/*====================================================================== + * Region Union + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * pixman_region_union_o -- + * Handle an overlapping band for the union operation. Picks the + * left-most rectangle each time and merges it into the region. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * region is overwritten. + * overlap is set to TRUE if any boxes overlap. + * + *----------------------------------------------------------------------- + */ +static pixman_bool_t +pixman_region_union_o (region_type_t *region, + box_type_t * r1, + box_type_t * r1_end, + box_type_t * r2, + box_type_t * r2_end, + int y1, + int y2) +{ + box_type_t *next_rect; + int x1; /* left and right side of current union */ + int x2; + + critical_if_fail (y1 < y2); + critical_if_fail (r1 != r1_end && r2 != r2_end); + + next_rect = PIXREGION_TOP (region); + + /* Start off current rectangle */ + if (r1->x1 < r2->x1) + { + x1 = r1->x1; + x2 = r1->x2; + r1++; + } + else + { + x1 = r2->x1; + x2 = r2->x2; + r2++; + } + while (r1 != r1_end && r2 != r2_end) + { + if (r1->x1 < r2->x1) + MERGERECT (r1); + else + MERGERECT (r2); + } + + /* Finish off whoever (if any) is left */ + if (r1 != r1_end) + { + do + { + MERGERECT (r1); + } + while (r1 != r1_end); + } + else if (r2 != r2_end) + { + do + { + MERGERECT (r2); + } + while (r2 != r2_end); + } + + /* Add current rectangle */ + NEWRECT (region, next_rect, x1, y1, x2, y2); + + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +PREFIX(_intersect_rect) (region_type_t *dest, + const region_type_t *source, + int x, int y, + unsigned int width, + unsigned int height) +{ + region_type_t region; + + region.data = NULL; + region.extents.x1 = x; + region.extents.y1 = y; + region.extents.x2 = x + width; + region.extents.y2 = y + height; + + return PREFIX(_intersect) (dest, source, ®ion); +} + +/* Convenience function for performing union of region with a + * single rectangle + */ +PIXMAN_EXPORT pixman_bool_t +PREFIX (_union_rect) (region_type_t *dest, + const region_type_t *source, + int x, + int y, + unsigned int width, + unsigned int height) +{ + region_type_t region; + + region.extents.x1 = x; + region.extents.y1 = y; + region.extents.x2 = x + width; + region.extents.y2 = y + height; + + if (!GOOD_RECT (®ion.extents)) + { + if (BAD_RECT (®ion.extents)) + _pixman_log_error (FUNC, "Invalid rectangle passed"); + return PREFIX (_copy) (dest, source); + } + + region.data = NULL; + + return PREFIX (_union) (dest, source, ®ion); +} + +PIXMAN_EXPORT pixman_bool_t +PREFIX (_union) (region_type_t * new_reg, + const region_type_t *reg1, + const region_type_t *reg2) +{ + /* Return TRUE if some overlap + * between reg1, reg2 + */ + GOOD (reg1); + GOOD (reg2); + GOOD (new_reg); + + /* checks all the simple cases */ + + /* + * Region 1 and 2 are the same + */ + if (reg1 == reg2) + return PREFIX (_copy) (new_reg, reg1); + + /* + * Region 1 is empty + */ + if (PIXREGION_NIL (reg1)) + { + if (PIXREGION_NAR (reg1)) + return pixman_break (new_reg); + + if (new_reg != reg2) + return PREFIX (_copy) (new_reg, reg2); + + return TRUE; + } + + /* + * Region 2 is empty + */ + if (PIXREGION_NIL (reg2)) + { + if (PIXREGION_NAR (reg2)) + return pixman_break (new_reg); + + if (new_reg != reg1) + return PREFIX (_copy) (new_reg, reg1); + + return TRUE; + } + + /* + * Region 1 completely subsumes region 2 + */ + if (!reg1->data && SUBSUMES (®1->extents, ®2->extents)) + { + if (new_reg != reg1) + return PREFIX (_copy) (new_reg, reg1); + + return TRUE; + } + + /* + * Region 2 completely subsumes region 1 + */ + if (!reg2->data && SUBSUMES (®2->extents, ®1->extents)) + { + if (new_reg != reg2) + return PREFIX (_copy) (new_reg, reg2); + + return TRUE; + } + + if (!pixman_op (new_reg, reg1, reg2, pixman_region_union_o, TRUE, TRUE)) + return FALSE; + + new_reg->extents.x1 = MIN (reg1->extents.x1, reg2->extents.x1); + new_reg->extents.y1 = MIN (reg1->extents.y1, reg2->extents.y1); + new_reg->extents.x2 = MAX (reg1->extents.x2, reg2->extents.x2); + new_reg->extents.y2 = MAX (reg1->extents.y2, reg2->extents.y2); + + GOOD (new_reg); + + return TRUE; +} + +/*====================================================================== + * Batch Rectangle Union + *====================================================================*/ + +#define EXCHANGE_RECTS(a, b) \ + { \ + box_type_t t; \ + t = rects[a]; \ + rects[a] = rects[b]; \ + rects[b] = t; \ + } + +static void +quick_sort_rects ( + box_type_t rects[], + int numRects) +{ + int y1; + int x1; + int i, j; + box_type_t *r; + + /* Always called with numRects > 1 */ + + do + { + if (numRects == 2) + { + if (rects[0].y1 > rects[1].y1 || + (rects[0].y1 == rects[1].y1 && rects[0].x1 > rects[1].x1)) + { + EXCHANGE_RECTS (0, 1); + } + + return; + } + + /* Choose partition element, stick in location 0 */ + EXCHANGE_RECTS (0, numRects >> 1); + y1 = rects[0].y1; + x1 = rects[0].x1; + + /* Partition array */ + i = 0; + j = numRects; + + do + { + r = &(rects[i]); + do + { + r++; + i++; + } + while (i != numRects && (r->y1 < y1 || (r->y1 == y1 && r->x1 < x1))); + + r = &(rects[j]); + do + { + r--; + j--; + } + while (y1 < r->y1 || (y1 == r->y1 && x1 < r->x1)); + + if (i < j) + EXCHANGE_RECTS (i, j); + } + while (i < j); + + /* Move partition element back to middle */ + EXCHANGE_RECTS (0, j); + + /* Recurse */ + if (numRects - j - 1 > 1) + quick_sort_rects (&rects[j + 1], numRects - j - 1); + + numRects = j; + } + while (numRects > 1); +} + +/*- + *----------------------------------------------------------------------- + * pixman_region_validate -- + * + * Take a ``region'' which is a non-y-x-banded random collection of + * rectangles, and compute a nice region which is the union of all the + * rectangles. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * The passed-in ``region'' may be modified. + * overlap set to TRUE if any retangles overlapped, + * else FALSE; + * + * Strategy: + * Step 1. Sort the rectangles into ascending order with primary key y1 + * and secondary key x1. + * + * Step 2. Split the rectangles into the minimum number of proper y-x + * banded regions. This may require horizontally merging + * rectangles, and vertically coalescing bands. With any luck, + * this step in an identity transformation (ala the Box widget), + * or a coalescing into 1 box (ala Menus). + * + * Step 3. Merge the separate regions down to a single region by calling + * pixman_region_union. Maximize the work each pixman_region_union call does by using + * a binary merge. + * + *----------------------------------------------------------------------- + */ + +static pixman_bool_t +validate (region_type_t * badreg) +{ + /* Descriptor for regions under construction in Step 2. */ + typedef struct + { + region_type_t reg; + int prev_band; + int cur_band; + } region_info_t; + + region_info_t stack_regions[64]; + + int numRects; /* Original numRects for badreg */ + region_info_t *ri; /* Array of current regions */ + int num_ri; /* Number of entries used in ri */ + int size_ri; /* Number of entries available in ri */ + int i; /* Index into rects */ + int j; /* Index into ri */ + region_info_t *rit; /* &ri[j] */ + region_type_t *reg; /* ri[j].reg */ + box_type_t *box; /* Current box in rects */ + box_type_t *ri_box; /* Last box in ri[j].reg */ + region_type_t *hreg; /* ri[j_half].reg */ + pixman_bool_t ret = TRUE; + + if (!badreg->data) + { + GOOD (badreg); + return TRUE; + } + + numRects = badreg->data->numRects; + if (!numRects) + { + if (PIXREGION_NAR (badreg)) + return FALSE; + GOOD (badreg); + return TRUE; + } + + if (badreg->extents.x1 < badreg->extents.x2) + { + if ((numRects) == 1) + { + FREE_DATA (badreg); + badreg->data = (region_data_type_t *) NULL; + } + else + { + DOWNSIZE (badreg, numRects); + } + + GOOD (badreg); + + return TRUE; + } + + /* Step 1: Sort the rects array into ascending (y1, x1) order */ + quick_sort_rects (PIXREGION_BOXPTR (badreg), numRects); + + /* Step 2: Scatter the sorted array into the minimum number of regions */ + + /* Set up the first region to be the first rectangle in badreg */ + /* Note that step 2 code will never overflow the ri[0].reg rects array */ + ri = stack_regions; + size_ri = sizeof (stack_regions) / sizeof (stack_regions[0]); + num_ri = 1; + ri[0].prev_band = 0; + ri[0].cur_band = 0; + ri[0].reg = *badreg; + box = PIXREGION_BOXPTR (&ri[0].reg); + ri[0].reg.extents = *box; + ri[0].reg.data->numRects = 1; + badreg->extents = *pixman_region_empty_box; + badreg->data = pixman_region_empty_data; + + /* Now scatter rectangles into the minimum set of valid regions. If the + * next rectangle to be added to a region would force an existing rectangle + * in the region to be split up in order to maintain y-x banding, just + * forget it. Try the next region. If it doesn't fit cleanly into any + * region, make a new one. + */ + + for (i = numRects; --i > 0;) + { + box++; + /* Look for a region to append box to */ + for (j = num_ri, rit = ri; --j >= 0; rit++) + { + reg = &rit->reg; + ri_box = PIXREGION_END (reg); + + if (box->y1 == ri_box->y1 && box->y2 == ri_box->y2) + { + /* box is in same band as ri_box. Merge or append it */ + if (box->x1 <= ri_box->x2) + { + /* Merge it with ri_box */ + if (box->x2 > ri_box->x2) + ri_box->x2 = box->x2; + } + else + { + RECTALLOC_BAIL (reg, 1, bail); + *PIXREGION_TOP (reg) = *box; + reg->data->numRects++; + } + + goto next_rect; /* So sue me */ + } + else if (box->y1 >= ri_box->y2) + { + /* Put box into new band */ + if (reg->extents.x2 < ri_box->x2) + reg->extents.x2 = ri_box->x2; + + if (reg->extents.x1 > box->x1) + reg->extents.x1 = box->x1; + + COALESCE (reg, rit->prev_band, rit->cur_band); + rit->cur_band = reg->data->numRects; + RECTALLOC_BAIL (reg, 1, bail); + *PIXREGION_TOP (reg) = *box; + reg->data->numRects++; + + goto next_rect; + } + /* Well, this region was inappropriate. Try the next one. */ + } /* for j */ + + /* Uh-oh. No regions were appropriate. Create a new one. */ + if (size_ri == num_ri) + { + size_t data_size; + + /* Oops, allocate space for new region information */ + size_ri <<= 1; + + data_size = size_ri * sizeof(region_info_t); + if (data_size / size_ri != sizeof(region_info_t)) + goto bail; + + if (ri == stack_regions) + { + rit = malloc (data_size); + if (!rit) + goto bail; + memcpy (rit, ri, num_ri * sizeof (region_info_t)); + } + else + { + rit = (region_info_t *) realloc (ri, data_size); + if (!rit) + goto bail; + } + ri = rit; + rit = &ri[num_ri]; + } + num_ri++; + rit->prev_band = 0; + rit->cur_band = 0; + rit->reg.extents = *box; + rit->reg.data = (region_data_type_t *)NULL; + + /* MUST force allocation */ + if (!pixman_rect_alloc (&rit->reg, (i + num_ri) / num_ri)) + goto bail; + + next_rect: ; + } /* for i */ + + /* Make a final pass over each region in order to COALESCE and set + * extents.x2 and extents.y2 + */ + for (j = num_ri, rit = ri; --j >= 0; rit++) + { + reg = &rit->reg; + ri_box = PIXREGION_END (reg); + reg->extents.y2 = ri_box->y2; + + if (reg->extents.x2 < ri_box->x2) + reg->extents.x2 = ri_box->x2; + + COALESCE (reg, rit->prev_band, rit->cur_band); + + if (reg->data->numRects == 1) /* keep unions happy below */ + { + FREE_DATA (reg); + reg->data = (region_data_type_t *)NULL; + } + } + + /* Step 3: Union all regions into a single region */ + while (num_ri > 1) + { + int half = num_ri / 2; + for (j = num_ri & 1; j < (half + (num_ri & 1)); j++) + { + reg = &ri[j].reg; + hreg = &ri[j + half].reg; + + if (!pixman_op (reg, reg, hreg, pixman_region_union_o, TRUE, TRUE)) + ret = FALSE; + + if (hreg->extents.x1 < reg->extents.x1) + reg->extents.x1 = hreg->extents.x1; + + if (hreg->extents.y1 < reg->extents.y1) + reg->extents.y1 = hreg->extents.y1; + + if (hreg->extents.x2 > reg->extents.x2) + reg->extents.x2 = hreg->extents.x2; + + if (hreg->extents.y2 > reg->extents.y2) + reg->extents.y2 = hreg->extents.y2; + + FREE_DATA (hreg); + } + + num_ri -= half; + + if (!ret) + goto bail; + } + + *badreg = ri[0].reg; + + if (ri != stack_regions) + free (ri); + + GOOD (badreg); + return ret; + +bail: + for (i = 0; i < num_ri; i++) + FREE_DATA (&ri[i].reg); + + if (ri != stack_regions) + free (ri); + + return pixman_break (badreg); +} + +/*====================================================================== + * Region Subtraction + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * pixman_region_subtract_o -- + * Overlapping band subtraction. x1 is the left-most point not yet + * checked. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * region may have rectangles added to it. + * + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static pixman_bool_t +pixman_region_subtract_o (region_type_t * region, + box_type_t * r1, + box_type_t * r1_end, + box_type_t * r2, + box_type_t * r2_end, + int y1, + int y2) +{ + box_type_t * next_rect; + int x1; + + x1 = r1->x1; + + critical_if_fail (y1 < y2); + critical_if_fail (r1 != r1_end && r2 != r2_end); + + next_rect = PIXREGION_TOP (region); + + do + { + if (r2->x2 <= x1) + { + /* + * Subtrahend entirely to left of minuend: go to next subtrahend. + */ + r2++; + } + else if (r2->x1 <= x1) + { + /* + * Subtrahend precedes minuend: nuke left edge of minuend. + */ + x1 = r2->x2; + if (x1 >= r1->x2) + { + /* + * Minuend completely covered: advance to next minuend and + * reset left fence to edge of new minuend. + */ + r1++; + if (r1 != r1_end) + x1 = r1->x1; + } + else + { + /* + * Subtrahend now used up since it doesn't extend beyond + * minuend + */ + r2++; + } + } + else if (r2->x1 < r1->x2) + { + /* + * Left part of subtrahend covers part of minuend: add uncovered + * part of minuend to region and skip to next subtrahend. + */ + critical_if_fail (x1 < r2->x1); + NEWRECT (region, next_rect, x1, y1, r2->x1, y2); + + x1 = r2->x2; + if (x1 >= r1->x2) + { + /* + * Minuend used up: advance to new... + */ + r1++; + if (r1 != r1_end) + x1 = r1->x1; + } + else + { + /* + * Subtrahend used up + */ + r2++; + } + } + else + { + /* + * Minuend used up: add any remaining piece before advancing. + */ + if (r1->x2 > x1) + NEWRECT (region, next_rect, x1, y1, r1->x2, y2); + + r1++; + + if (r1 != r1_end) + x1 = r1->x1; + } + } + while ((r1 != r1_end) && (r2 != r2_end)); + + /* + * Add remaining minuend rectangles to region. + */ + while (r1 != r1_end) + { + critical_if_fail (x1 < r1->x2); + + NEWRECT (region, next_rect, x1, y1, r1->x2, y2); + + r1++; + if (r1 != r1_end) + x1 = r1->x1; + } + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * pixman_region_subtract -- + * Subtract reg_s from reg_m and leave the result in reg_d. + * S stands for subtrahend, M for minuend and D for difference. + * + * Results: + * TRUE if successful. + * + * Side Effects: + * reg_d is overwritten. + * + *----------------------------------------------------------------------- + */ +PIXMAN_EXPORT pixman_bool_t +PREFIX (_subtract) (region_type_t * reg_d, + const region_type_t *reg_m, + const region_type_t *reg_s) +{ + GOOD (reg_m); + GOOD (reg_s); + GOOD (reg_d); + + /* check for trivial rejects */ + if (PIXREGION_NIL (reg_m) || PIXREGION_NIL (reg_s) || + !EXTENTCHECK (®_m->extents, ®_s->extents)) + { + if (PIXREGION_NAR (reg_s)) + return pixman_break (reg_d); + + return PREFIX (_copy) (reg_d, reg_m); + } + else if (reg_m == reg_s) + { + FREE_DATA (reg_d); + reg_d->extents.x2 = reg_d->extents.x1; + reg_d->extents.y2 = reg_d->extents.y1; + reg_d->data = pixman_region_empty_data; + + return TRUE; + } + + /* Add those rectangles in region 1 that aren't in region 2, + do yucky subtraction for overlaps, and + just throw away rectangles in region 2 that aren't in region 1 */ + if (!pixman_op (reg_d, reg_m, reg_s, pixman_region_subtract_o, TRUE, FALSE)) + return FALSE; + + /* + * Can't alter reg_d's extents before we call pixman_op because + * it might be one of the source regions and pixman_op depends + * on the extents of those regions being unaltered. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + pixman_set_extents (reg_d); + GOOD (reg_d); + return TRUE; +} + +/*====================================================================== + * Region Inversion + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * pixman_region_inverse -- + * Take a region and a box and return a region that is everything + * in the box but not in the region. The careful reader will note + * that this is the same as subtracting the region from the box... + * + * Results: + * TRUE. + * + * Side Effects: + * new_reg is overwritten. + * + *----------------------------------------------------------------------- + */ +PIXMAN_EXPORT pixman_bool_t +PREFIX (_inverse) (region_type_t * new_reg, /* Destination region */ + const region_type_t *reg1, /* Region to invert */ + const box_type_t * inv_rect) /* Bounding box for inversion */ +{ + region_type_t inv_reg; /* Quick and dirty region made from the + * bounding box */ + GOOD (reg1); + GOOD (new_reg); + + /* check for trivial rejects */ + if (PIXREGION_NIL (reg1) || !EXTENTCHECK (inv_rect, ®1->extents)) + { + if (PIXREGION_NAR (reg1)) + return pixman_break (new_reg); + + new_reg->extents = *inv_rect; + FREE_DATA (new_reg); + new_reg->data = (region_data_type_t *)NULL; + + return TRUE; + } + + /* Add those rectangles in region 1 that aren't in region 2, + * do yucky subtraction for overlaps, and + * just throw away rectangles in region 2 that aren't in region 1 + */ + inv_reg.extents = *inv_rect; + inv_reg.data = (region_data_type_t *)NULL; + if (!pixman_op (new_reg, &inv_reg, reg1, pixman_region_subtract_o, TRUE, FALSE)) + return FALSE; + + /* + * Can't alter new_reg's extents before we call pixman_op because + * it might be one of the source regions and pixman_op depends + * on the extents of those regions being unaltered. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + pixman_set_extents (new_reg); + GOOD (new_reg); + return TRUE; +} + +/* In time O(log n), locate the first box whose y2 is greater than y. + * Return @end if no such box exists. + */ +static box_type_t * +find_box_for_y (box_type_t *begin, box_type_t *end, int y) +{ + box_type_t *mid; + + if (end == begin) + return end; + + if (end - begin == 1) + { + if (begin->y2 > y) + return begin; + else + return end; + } + + mid = begin + (end - begin) / 2; + if (mid->y2 > y) + { + /* If no box is found in [begin, mid], the function + * will return @mid, which is then known to be the + * correct answer. + */ + return find_box_for_y (begin, mid, y); + } + else + { + return find_box_for_y (mid, end, y); + } +} + +/* + * rect_in(region, rect) + * This routine takes a pointer to a region and a pointer to a box + * and determines if the box is outside/inside/partly inside the region. + * + * The idea is to travel through the list of rectangles trying to cover the + * passed box with them. Anytime a piece of the rectangle isn't covered + * by a band of rectangles, part_out is set TRUE. Any time a rectangle in + * the region covers part of the box, part_in is set TRUE. The process ends + * when either the box has been completely covered (we reached a band that + * doesn't overlap the box, part_in is TRUE and part_out is false), the + * box has been partially covered (part_in == part_out == TRUE -- because of + * the banding, the first time this is true we know the box is only + * partially in the region) or is outside the region (we reached a band + * that doesn't overlap the box at all and part_in is false) + */ +PIXMAN_EXPORT pixman_region_overlap_t +PREFIX (_contains_rectangle) (const region_type_t * region, + const box_type_t * prect) +{ + box_type_t * pbox; + box_type_t * pbox_end; + int part_in, part_out; + int numRects; + int x, y; + + GOOD (region); + + numRects = PIXREGION_NUMRECTS (region); + + /* useful optimization */ + if (!numRects || !EXTENTCHECK (®ion->extents, prect)) + return(PIXMAN_REGION_OUT); + + if (numRects == 1) + { + /* We know that it must be PIXMAN_REGION_IN or PIXMAN_REGION_PART */ + if (SUBSUMES (®ion->extents, prect)) + return(PIXMAN_REGION_IN); + else + return(PIXMAN_REGION_PART); + } + + part_out = FALSE; + part_in = FALSE; + + /* (x,y) starts at upper left of rect, moving to the right and down */ + x = prect->x1; + y = prect->y1; + + /* can stop when both part_out and part_in are TRUE, or we reach prect->y2 */ + for (pbox = PIXREGION_BOXPTR (region), pbox_end = pbox + numRects; + pbox != pbox_end; + pbox++) + { + /* getting up to speed or skipping remainder of band */ + if (pbox->y2 <= y) + { + if ((pbox = find_box_for_y (pbox, pbox_end, y)) == pbox_end) + break; + } + + if (pbox->y1 > y) + { + part_out = TRUE; /* missed part of rectangle above */ + if (part_in || (pbox->y1 >= prect->y2)) + break; + y = pbox->y1; /* x guaranteed to be == prect->x1 */ + } + + if (pbox->x2 <= x) + continue; /* not far enough over yet */ + + if (pbox->x1 > x) + { + part_out = TRUE; /* missed part of rectangle to left */ + if (part_in) + break; + } + + if (pbox->x1 < prect->x2) + { + part_in = TRUE; /* definitely overlap */ + if (part_out) + break; + } + + if (pbox->x2 >= prect->x2) + { + y = pbox->y2; /* finished with this band */ + if (y >= prect->y2) + break; + x = prect->x1; /* reset x out to left again */ + } + else + { + /* + * Because boxes in a band are maximal width, if the first box + * to overlap the rectangle doesn't completely cover it in that + * band, the rectangle must be partially out, since some of it + * will be uncovered in that band. part_in will have been set true + * by now... + */ + part_out = TRUE; + break; + } + } + + if (part_in) + { + if (y < prect->y2) + return PIXMAN_REGION_PART; + else + return PIXMAN_REGION_IN; + } + else + { + return PIXMAN_REGION_OUT; + } +} + +/* PREFIX(_translate) (region, x, y) + * translates in place + */ + +PIXMAN_EXPORT void +PREFIX (_translate) (region_type_t *region, int x, int y) +{ + overflow_int_t x1, x2, y1, y2; + int nbox; + box_type_t * pbox; + + GOOD (region); + region->extents.x1 = x1 = region->extents.x1 + x; + region->extents.y1 = y1 = region->extents.y1 + y; + region->extents.x2 = x2 = region->extents.x2 + x; + region->extents.y2 = y2 = region->extents.y2 + y; + + if (((x1 - PIXMAN_REGION_MIN) | (y1 - PIXMAN_REGION_MIN) | (PIXMAN_REGION_MAX - x2) | (PIXMAN_REGION_MAX - y2)) >= 0) + { + if (region->data && (nbox = region->data->numRects)) + { + for (pbox = PIXREGION_BOXPTR (region); nbox--; pbox++) + { + pbox->x1 += x; + pbox->y1 += y; + pbox->x2 += x; + pbox->y2 += y; + } + } + return; + } + + if (((x2 - PIXMAN_REGION_MIN) | (y2 - PIXMAN_REGION_MIN) | (PIXMAN_REGION_MAX - x1) | (PIXMAN_REGION_MAX - y1)) <= 0) + { + region->extents.x2 = region->extents.x1; + region->extents.y2 = region->extents.y1; + FREE_DATA (region); + region->data = pixman_region_empty_data; + return; + } + + if (x1 < PIXMAN_REGION_MIN) + region->extents.x1 = PIXMAN_REGION_MIN; + else if (x2 > PIXMAN_REGION_MAX) + region->extents.x2 = PIXMAN_REGION_MAX; + + if (y1 < PIXMAN_REGION_MIN) + region->extents.y1 = PIXMAN_REGION_MIN; + else if (y2 > PIXMAN_REGION_MAX) + region->extents.y2 = PIXMAN_REGION_MAX; + + if (region->data && (nbox = region->data->numRects)) + { + box_type_t * pbox_out; + + for (pbox_out = pbox = PIXREGION_BOXPTR (region); nbox--; pbox++) + { + pbox_out->x1 = x1 = pbox->x1 + x; + pbox_out->y1 = y1 = pbox->y1 + y; + pbox_out->x2 = x2 = pbox->x2 + x; + pbox_out->y2 = y2 = pbox->y2 + y; + + if (((x2 - PIXMAN_REGION_MIN) | (y2 - PIXMAN_REGION_MIN) | + (PIXMAN_REGION_MAX - x1) | (PIXMAN_REGION_MAX - y1)) <= 0) + { + region->data->numRects--; + continue; + } + + if (x1 < PIXMAN_REGION_MIN) + pbox_out->x1 = PIXMAN_REGION_MIN; + else if (x2 > PIXMAN_REGION_MAX) + pbox_out->x2 = PIXMAN_REGION_MAX; + + if (y1 < PIXMAN_REGION_MIN) + pbox_out->y1 = PIXMAN_REGION_MIN; + else if (y2 > PIXMAN_REGION_MAX) + pbox_out->y2 = PIXMAN_REGION_MAX; + + pbox_out++; + } + + if (pbox_out != pbox) + { + if (region->data->numRects == 1) + { + region->extents = *PIXREGION_BOXPTR (region); + FREE_DATA (region); + region->data = (region_data_type_t *)NULL; + } + else + { + pixman_set_extents (region); + } + } + } + + GOOD (region); +} + +PIXMAN_EXPORT void +PREFIX (_reset) (region_type_t *region, const box_type_t *box) +{ + GOOD (region); + + critical_if_fail (GOOD_RECT (box)); + + region->extents = *box; + + FREE_DATA (region); + + region->data = NULL; +} + +PIXMAN_EXPORT void +PREFIX (_clear) (region_type_t *region) +{ + GOOD (region); + FREE_DATA (region); + + region->extents = *pixman_region_empty_box; + region->data = pixman_region_empty_data; +} + +/* box is "return" value */ +PIXMAN_EXPORT int +PREFIX (_contains_point) (const region_type_t * region, + int x, int y, + box_type_t * box) +{ + box_type_t *pbox, *pbox_end; + int numRects; + + GOOD (region); + numRects = PIXREGION_NUMRECTS (region); + + if (!numRects || !INBOX (®ion->extents, x, y)) + return(FALSE); + + if (numRects == 1) + { + if (box) + *box = region->extents; + + return(TRUE); + } + + pbox = PIXREGION_BOXPTR (region); + pbox_end = pbox + numRects; + + pbox = find_box_for_y (pbox, pbox_end, y); + + for (;pbox != pbox_end; pbox++) + { + if ((y < pbox->y1) || (x < pbox->x1)) + break; /* missed it */ + + if (x >= pbox->x2) + continue; /* not there yet */ + + if (box) + *box = *pbox; + + return(TRUE); + } + + return(FALSE); +} + +PIXMAN_EXPORT int +PREFIX (_not_empty) (const region_type_t * region) +{ + GOOD (region); + + return(!PIXREGION_NIL (region)); +} + +PIXMAN_EXPORT box_type_t * +PREFIX (_extents) (const region_type_t * region) +{ + GOOD (region); + + return(box_type_t *)(®ion->extents); +} + +/* + * Clip a list of scanlines to a region. The caller has allocated the + * space. FSorted is non-zero if the scanline origins are in ascending order. + * + * returns the number of new, clipped scanlines. + */ + +PIXMAN_EXPORT pixman_bool_t +PREFIX (_selfcheck) (region_type_t *reg) +{ + int i, numRects; + + if ((reg->extents.x1 > reg->extents.x2) || + (reg->extents.y1 > reg->extents.y2)) + { + return FALSE; + } + + numRects = PIXREGION_NUMRECTS (reg); + if (!numRects) + { + return ((reg->extents.x1 == reg->extents.x2) && + (reg->extents.y1 == reg->extents.y2) && + (reg->data->size || (reg->data == pixman_region_empty_data))); + } + else if (numRects == 1) + { + return (!reg->data); + } + else + { + box_type_t * pbox_p, * pbox_n; + box_type_t box; + + pbox_p = PIXREGION_RECTS (reg); + box = *pbox_p; + box.y2 = pbox_p[numRects - 1].y2; + pbox_n = pbox_p + 1; + + for (i = numRects; --i > 0; pbox_p++, pbox_n++) + { + if ((pbox_n->x1 >= pbox_n->x2) || + (pbox_n->y1 >= pbox_n->y2)) + { + return FALSE; + } + + if (pbox_n->x1 < box.x1) + box.x1 = pbox_n->x1; + + if (pbox_n->x2 > box.x2) + box.x2 = pbox_n->x2; + + if ((pbox_n->y1 < pbox_p->y1) || + ((pbox_n->y1 == pbox_p->y1) && + ((pbox_n->x1 < pbox_p->x2) || (pbox_n->y2 != pbox_p->y2)))) + { + return FALSE; + } + } + + return ((box.x1 == reg->extents.x1) && + (box.x2 == reg->extents.x2) && + (box.y1 == reg->extents.y1) && + (box.y2 == reg->extents.y2)); + } +} + +PIXMAN_EXPORT pixman_bool_t +PREFIX (_init_rects) (region_type_t *region, + const box_type_t *boxes, int count) +{ + box_type_t *rects; + int displacement; + int i; + + /* if it's 1, then we just want to set the extents, so call + * the existing method. */ + if (count == 1) + { + PREFIX (_init_rect) (region, + boxes[0].x1, + boxes[0].y1, + boxes[0].x2 - boxes[0].x1, + boxes[0].y2 - boxes[0].y1); + return TRUE; + } + + PREFIX (_init) (region); + + /* if it's 0, don't call pixman_rect_alloc -- 0 rectangles is + * a special case, and causing pixman_rect_alloc would cause + * us to leak memory (because the 0-rect case should be the + * static pixman_region_empty_data data). + */ + if (count == 0) + return TRUE; + + if (!pixman_rect_alloc (region, count)) + return FALSE; + + rects = PIXREGION_RECTS (region); + + /* Copy in the rects */ + memcpy (rects, boxes, sizeof(box_type_t) * count); + region->data->numRects = count; + + /* Eliminate empty and malformed rectangles */ + displacement = 0; + + for (i = 0; i < count; ++i) + { + box_type_t *box = &rects[i]; + + if (box->x1 >= box->x2 || box->y1 >= box->y2) + displacement++; + else if (displacement) + rects[i - displacement] = rects[i]; + } + + region->data->numRects -= displacement; + + /* If eliminating empty rectangles caused there + * to be only 0 or 1 rectangles, deal with that. + */ + if (region->data->numRects == 0) + { + FREE_DATA (region); + PREFIX (_init) (region); + + return TRUE; + } + + if (region->data->numRects == 1) + { + region->extents = rects[0]; + + FREE_DATA (region); + region->data = NULL; + + GOOD (region); + + return TRUE; + } + + /* Validate */ + region->extents.x1 = region->extents.x2 = 0; + + return validate (region); +} + +#define READ(_ptr) (*(_ptr)) + +static inline box_type_t * +bitmap_addrect (region_type_t *reg, + box_type_t *r, + box_type_t **first_rect, + int rx1, int ry1, + int rx2, int ry2) +{ + if ((rx1 < rx2) && (ry1 < ry2) && + (!(reg->data->numRects && + ((r-1)->y1 == ry1) && ((r-1)->y2 == ry2) && + ((r-1)->x1 <= rx1) && ((r-1)->x2 >= rx2)))) + { + if (reg->data->numRects == reg->data->size) + { + if (!pixman_rect_alloc (reg, 1)) + return NULL; + *first_rect = PIXREGION_BOXPTR(reg); + r = *first_rect + reg->data->numRects; + } + r->x1 = rx1; + r->y1 = ry1; + r->x2 = rx2; + r->y2 = ry2; + reg->data->numRects++; + if (r->x1 < reg->extents.x1) + reg->extents.x1 = r->x1; + if (r->x2 > reg->extents.x2) + reg->extents.x2 = r->x2; + r++; + } + return r; +} + +/* Convert bitmap clip mask into clipping region. + * First, goes through each line and makes boxes by noting the transitions + * from 0 to 1 and 1 to 0. + * Then it coalesces the current line with the previous if they have boxes + * at the same X coordinates. + * Stride is in number of uint32_t per line. + */ +PIXMAN_EXPORT void +PREFIX (_init_from_image) (region_type_t *region, + pixman_image_t *image) +{ + uint32_t mask0 = 0xffffffff & ~SCREEN_SHIFT_RIGHT(0xffffffff, 1); + box_type_t *first_rect, *rects, *prect_line_start; + box_type_t *old_rect, *new_rect; + uint32_t *pw, w, *pw_line, *pw_line_end; + int irect_prev_start, irect_line_start; + int h, base, rx1 = 0, crects; + int ib; + pixman_bool_t in_box, same; + int width, height, stride; + + PREFIX(_init) (region); + + critical_if_fail (region->data); + + return_if_fail (image->type == BITS); + return_if_fail (image->bits.format == PIXMAN_a1); + + pw_line = pixman_image_get_data (image); + width = pixman_image_get_width (image); + height = pixman_image_get_height (image); + stride = pixman_image_get_stride (image) / 4; + + first_rect = PIXREGION_BOXPTR(region); + rects = first_rect; + + region->extents.x1 = width - 1; + region->extents.x2 = 0; + irect_prev_start = -1; + for (h = 0; h < height; h++) + { + pw = pw_line; + pw_line += stride; + irect_line_start = rects - first_rect; + + /* If the Screen left most bit of the word is set, we're starting in + * a box */ + if (READ(pw) & mask0) + { + in_box = TRUE; + rx1 = 0; + } + else + { + in_box = FALSE; + } + + /* Process all words which are fully in the pixmap */ + pw_line_end = pw + (width >> 5); + for (base = 0; pw < pw_line_end; base += 32) + { + w = READ(pw++); + if (in_box) + { + if (!~w) + continue; + } + else + { + if (!w) + continue; + } + for (ib = 0; ib < 32; ib++) + { + /* If the Screen left most bit of the word is set, we're + * starting a box */ + if (w & mask0) + { + if (!in_box) + { + rx1 = base + ib; + /* start new box */ + in_box = TRUE; + } + } + else + { + if (in_box) + { + /* end box */ + rects = bitmap_addrect (region, rects, &first_rect, + rx1, h, base + ib, h + 1); + if (rects == NULL) + goto error; + in_box = FALSE; + } + } + /* Shift the word VISUALLY left one. */ + w = SCREEN_SHIFT_LEFT(w, 1); + } + } + + if (width & 31) + { + /* Process final partial word on line */ + w = READ(pw++); + for (ib = 0; ib < (width & 31); ib++) + { + /* If the Screen left most bit of the word is set, we're + * starting a box */ + if (w & mask0) + { + if (!in_box) + { + rx1 = base + ib; + /* start new box */ + in_box = TRUE; + } + } + else + { + if (in_box) + { + /* end box */ + rects = bitmap_addrect(region, rects, &first_rect, + rx1, h, base + ib, h + 1); + if (rects == NULL) + goto error; + in_box = FALSE; + } + } + /* Shift the word VISUALLY left one. */ + w = SCREEN_SHIFT_LEFT(w, 1); + } + } + /* If scanline ended with last bit set, end the box */ + if (in_box) + { + rects = bitmap_addrect(region, rects, &first_rect, + rx1, h, base + (width & 31), h + 1); + if (rects == NULL) + goto error; + } + /* if all rectangles on this line have the same x-coords as + * those on the previous line, then add 1 to all the previous y2s and + * throw away all the rectangles from this line + */ + same = FALSE; + if (irect_prev_start != -1) + { + crects = irect_line_start - irect_prev_start; + if (crects != 0 && + crects == ((rects - first_rect) - irect_line_start)) + { + old_rect = first_rect + irect_prev_start; + new_rect = prect_line_start = first_rect + irect_line_start; + same = TRUE; + while (old_rect < prect_line_start) + { + if ((old_rect->x1 != new_rect->x1) || + (old_rect->x2 != new_rect->x2)) + { + same = FALSE; + break; + } + old_rect++; + new_rect++; + } + if (same) + { + old_rect = first_rect + irect_prev_start; + while (old_rect < prect_line_start) + { + old_rect->y2 += 1; + old_rect++; + } + rects -= crects; + region->data->numRects -= crects; + } + } + } + if(!same) + irect_prev_start = irect_line_start; + } + if (!region->data->numRects) + { + region->extents.x1 = region->extents.x2 = 0; + } + else + { + region->extents.y1 = PIXREGION_BOXPTR(region)->y1; + region->extents.y2 = PIXREGION_END(region)->y2; + if (region->data->numRects == 1) + { + free (region->data); + region->data = NULL; + } + } + + error: + return; +} diff --git a/gfx/cairo/libpixman/src/pixman-region16.c b/gfx/cairo/libpixman/src/pixman-region16.c new file mode 100644 index 0000000000..d88d3380f8 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-region16.c @@ -0,0 +1,67 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Soren Sandmann + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#undef PIXMAN_DISABLE_DEPRECATED + +#include "pixman-private.h" + +#include + +typedef pixman_box16_t box_type_t; +typedef pixman_region16_data_t region_data_type_t; +typedef pixman_region16_t region_type_t; +typedef int32_t overflow_int_t; + +typedef struct { + int x, y; +} point_type_t; + +#define PREFIX(x) pixman_region##x + +#define PIXMAN_REGION_MAX INT16_MAX +#define PIXMAN_REGION_MIN INT16_MIN + +#include "pixman-region.c" + +/* This function exists only to make it possible to preserve the X ABI - + * it should go away at first opportunity. + * + * The problem is that the X ABI exports the three structs and has used + * them through macros. So the X server calls this function with + * the addresses of those structs which makes the existing code continue to + * work. + */ +PIXMAN_EXPORT void +pixman_region_set_static_pointers (pixman_box16_t *empty_box, + pixman_region16_data_t *empty_data, + pixman_region16_data_t *broken_data) +{ + pixman_region_empty_box = empty_box; + pixman_region_empty_data = empty_data; + pixman_broken_data = broken_data; +} diff --git a/gfx/cairo/libpixman/src/pixman-region32.c b/gfx/cairo/libpixman/src/pixman-region32.c new file mode 100644 index 0000000000..abd6b1a937 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-region32.c @@ -0,0 +1,47 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Red Hat, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Red Hat, Inc. makes no representations about the + * suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Soren Sandmann + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pixman-private.h" + +#include + +typedef pixman_box32_t box_type_t; +typedef pixman_region32_data_t region_data_type_t; +typedef pixman_region32_t region_type_t; +typedef int64_t overflow_int_t; + +typedef struct { + int x, y; +} point_type_t; + +#define PREFIX(x) pixman_region32##x + +#define PIXMAN_REGION_MAX INT32_MAX +#define PIXMAN_REGION_MIN INT32_MIN + +#include "pixman-region.c" diff --git a/gfx/cairo/libpixman/src/pixman-solid-fill.c b/gfx/cairo/libpixman/src/pixman-solid-fill.c new file mode 100644 index 0000000000..4694ebc700 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-solid-fill.c @@ -0,0 +1,67 @@ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007, 2009 Red Hat, Inc. + * Copyright © 2009 Soren Sandmann + * + * 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 SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include "pixman-private.h" + +static uint32_t +color_to_uint32 (const pixman_color_t *color) +{ + return + ((unsigned int) color->alpha >> 8 << 24) | + ((unsigned int) color->red >> 8 << 16) | + ((unsigned int) color->green & 0xff00) | + ((unsigned int) color->blue >> 8); +} + +static argb_t +color_to_float (const pixman_color_t *color) +{ + argb_t result; + + result.a = pixman_unorm_to_float (color->alpha, 16); + result.r = pixman_unorm_to_float (color->red, 16); + result.g = pixman_unorm_to_float (color->green, 16); + result.b = pixman_unorm_to_float (color->blue, 16); + + return result; +} + +PIXMAN_EXPORT pixman_image_t * +pixman_image_create_solid_fill (const pixman_color_t *color) +{ + pixman_image_t *img = _pixman_image_allocate (); + + if (!img) + return NULL; + + img->type = SOLID; + img->solid.color = *color; + img->solid.color_32 = color_to_uint32 (color); + img->solid.color_float = color_to_float (color); + + return img; +} + diff --git a/gfx/cairo/libpixman/src/pixman-sse2.c b/gfx/cairo/libpixman/src/pixman-sse2.c new file mode 100644 index 0000000000..ce4e75f247 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-sse2.c @@ -0,0 +1,6528 @@ +/* + * Copyright © 2008 Rodrigo Kumpera + * Copyright © 2008 André Tupinambá + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes 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. + * + * Author: Rodrigo Kumpera (kumpera@gmail.com) + * André Tupinambá (andrelrt@gmail.com) + * + * Based on work by Owen Taylor and Søren Sandmann + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +/* PSHUFD is slow on a lot of old processors, and new processors have SSSE3 */ +#define PSHUFD_IS_FAST 0 + +#include /* for _mm_shuffle_pi16 and _MM_SHUFFLE */ +#include /* for SSE2 intrinsics */ +#include "pixman-private.h" +#include "pixman-combine32.h" +#include "pixman-inlines.h" + +static __m128i mask_0080; +static __m128i mask_00ff; +static __m128i mask_0101; +static __m128i mask_ffff; +static __m128i mask_ff000000; +static __m128i mask_alpha; + +static __m128i mask_565_r; +static __m128i mask_565_g1, mask_565_g2; +static __m128i mask_565_b; +static __m128i mask_red; +static __m128i mask_green; +static __m128i mask_blue; + +static __m128i mask_565_fix_rb; +static __m128i mask_565_fix_g; + +static __m128i mask_565_rb; +static __m128i mask_565_pack_multiplier; + +static force_inline __m128i +unpack_32_1x128 (uint32_t data) +{ + return _mm_unpacklo_epi8 (_mm_cvtsi32_si128 (data), _mm_setzero_si128 ()); +} + +static force_inline void +unpack_128_2x128 (__m128i data, __m128i* data_lo, __m128i* data_hi) +{ + *data_lo = _mm_unpacklo_epi8 (data, _mm_setzero_si128 ()); + *data_hi = _mm_unpackhi_epi8 (data, _mm_setzero_si128 ()); +} + +static force_inline __m128i +unpack_565_to_8888 (__m128i lo) +{ + __m128i r, g, b, rb, t; + + r = _mm_and_si128 (_mm_slli_epi32 (lo, 8), mask_red); + g = _mm_and_si128 (_mm_slli_epi32 (lo, 5), mask_green); + b = _mm_and_si128 (_mm_slli_epi32 (lo, 3), mask_blue); + + rb = _mm_or_si128 (r, b); + t = _mm_and_si128 (rb, mask_565_fix_rb); + t = _mm_srli_epi32 (t, 5); + rb = _mm_or_si128 (rb, t); + + t = _mm_and_si128 (g, mask_565_fix_g); + t = _mm_srli_epi32 (t, 6); + g = _mm_or_si128 (g, t); + + return _mm_or_si128 (rb, g); +} + +static force_inline void +unpack_565_128_4x128 (__m128i data, + __m128i* data0, + __m128i* data1, + __m128i* data2, + __m128i* data3) +{ + __m128i lo, hi; + + lo = _mm_unpacklo_epi16 (data, _mm_setzero_si128 ()); + hi = _mm_unpackhi_epi16 (data, _mm_setzero_si128 ()); + + lo = unpack_565_to_8888 (lo); + hi = unpack_565_to_8888 (hi); + + unpack_128_2x128 (lo, data0, data1); + unpack_128_2x128 (hi, data2, data3); +} + +static force_inline uint16_t +pack_565_32_16 (uint32_t pixel) +{ + return (uint16_t) (((pixel >> 8) & 0xf800) | + ((pixel >> 5) & 0x07e0) | + ((pixel >> 3) & 0x001f)); +} + +static force_inline __m128i +pack_2x128_128 (__m128i lo, __m128i hi) +{ + return _mm_packus_epi16 (lo, hi); +} + +static force_inline __m128i +pack_565_2packedx128_128 (__m128i lo, __m128i hi) +{ + __m128i rb0 = _mm_and_si128 (lo, mask_565_rb); + __m128i rb1 = _mm_and_si128 (hi, mask_565_rb); + + __m128i t0 = _mm_madd_epi16 (rb0, mask_565_pack_multiplier); + __m128i t1 = _mm_madd_epi16 (rb1, mask_565_pack_multiplier); + + __m128i g0 = _mm_and_si128 (lo, mask_green); + __m128i g1 = _mm_and_si128 (hi, mask_green); + + t0 = _mm_or_si128 (t0, g0); + t1 = _mm_or_si128 (t1, g1); + + /* Simulates _mm_packus_epi32 */ + t0 = _mm_slli_epi32 (t0, 16 - 5); + t1 = _mm_slli_epi32 (t1, 16 - 5); + t0 = _mm_srai_epi32 (t0, 16); + t1 = _mm_srai_epi32 (t1, 16); + return _mm_packs_epi32 (t0, t1); +} + +static force_inline __m128i +pack_565_2x128_128 (__m128i lo, __m128i hi) +{ + __m128i data; + __m128i r, g1, g2, b; + + data = pack_2x128_128 (lo, hi); + + r = _mm_and_si128 (data, mask_565_r); + g1 = _mm_and_si128 (_mm_slli_epi32 (data, 3), mask_565_g1); + g2 = _mm_and_si128 (_mm_srli_epi32 (data, 5), mask_565_g2); + b = _mm_and_si128 (_mm_srli_epi32 (data, 3), mask_565_b); + + return _mm_or_si128 (_mm_or_si128 (_mm_or_si128 (r, g1), g2), b); +} + +static force_inline __m128i +pack_565_4x128_128 (__m128i* xmm0, __m128i* xmm1, __m128i* xmm2, __m128i* xmm3) +{ + return _mm_packus_epi16 (pack_565_2x128_128 (*xmm0, *xmm1), + pack_565_2x128_128 (*xmm2, *xmm3)); +} + +static force_inline int +is_opaque (__m128i x) +{ + __m128i ffs = _mm_cmpeq_epi8 (x, x); + + return (_mm_movemask_epi8 (_mm_cmpeq_epi8 (x, ffs)) & 0x8888) == 0x8888; +} + +static force_inline int +is_zero (__m128i x) +{ + return _mm_movemask_epi8 ( + _mm_cmpeq_epi8 (x, _mm_setzero_si128 ())) == 0xffff; +} + +static force_inline int +is_transparent (__m128i x) +{ + return (_mm_movemask_epi8 ( + _mm_cmpeq_epi8 (x, _mm_setzero_si128 ())) & 0x8888) == 0x8888; +} + +static force_inline __m128i +expand_pixel_32_1x128 (uint32_t data) +{ + return _mm_shuffle_epi32 (unpack_32_1x128 (data), _MM_SHUFFLE (1, 0, 1, 0)); +} + +static force_inline __m128i +expand_alpha_1x128 (__m128i data) +{ + return _mm_shufflehi_epi16 (_mm_shufflelo_epi16 (data, + _MM_SHUFFLE (3, 3, 3, 3)), + _MM_SHUFFLE (3, 3, 3, 3)); +} + +static force_inline void +expand_alpha_2x128 (__m128i data_lo, + __m128i data_hi, + __m128i* alpha_lo, + __m128i* alpha_hi) +{ + __m128i lo, hi; + + lo = _mm_shufflelo_epi16 (data_lo, _MM_SHUFFLE (3, 3, 3, 3)); + hi = _mm_shufflelo_epi16 (data_hi, _MM_SHUFFLE (3, 3, 3, 3)); + + *alpha_lo = _mm_shufflehi_epi16 (lo, _MM_SHUFFLE (3, 3, 3, 3)); + *alpha_hi = _mm_shufflehi_epi16 (hi, _MM_SHUFFLE (3, 3, 3, 3)); +} + +static force_inline void +expand_alpha_rev_2x128 (__m128i data_lo, + __m128i data_hi, + __m128i* alpha_lo, + __m128i* alpha_hi) +{ + __m128i lo, hi; + + lo = _mm_shufflelo_epi16 (data_lo, _MM_SHUFFLE (0, 0, 0, 0)); + hi = _mm_shufflelo_epi16 (data_hi, _MM_SHUFFLE (0, 0, 0, 0)); + *alpha_lo = _mm_shufflehi_epi16 (lo, _MM_SHUFFLE (0, 0, 0, 0)); + *alpha_hi = _mm_shufflehi_epi16 (hi, _MM_SHUFFLE (0, 0, 0, 0)); +} + +static force_inline void +pix_multiply_2x128 (__m128i* data_lo, + __m128i* data_hi, + __m128i* alpha_lo, + __m128i* alpha_hi, + __m128i* ret_lo, + __m128i* ret_hi) +{ + __m128i lo, hi; + + lo = _mm_mullo_epi16 (*data_lo, *alpha_lo); + hi = _mm_mullo_epi16 (*data_hi, *alpha_hi); + lo = _mm_adds_epu16 (lo, mask_0080); + hi = _mm_adds_epu16 (hi, mask_0080); + *ret_lo = _mm_mulhi_epu16 (lo, mask_0101); + *ret_hi = _mm_mulhi_epu16 (hi, mask_0101); +} + +static force_inline void +pix_add_multiply_2x128 (__m128i* src_lo, + __m128i* src_hi, + __m128i* alpha_dst_lo, + __m128i* alpha_dst_hi, + __m128i* dst_lo, + __m128i* dst_hi, + __m128i* alpha_src_lo, + __m128i* alpha_src_hi, + __m128i* ret_lo, + __m128i* ret_hi) +{ + __m128i t1_lo, t1_hi; + __m128i t2_lo, t2_hi; + + pix_multiply_2x128 (src_lo, src_hi, alpha_dst_lo, alpha_dst_hi, &t1_lo, &t1_hi); + pix_multiply_2x128 (dst_lo, dst_hi, alpha_src_lo, alpha_src_hi, &t2_lo, &t2_hi); + + *ret_lo = _mm_adds_epu8 (t1_lo, t2_lo); + *ret_hi = _mm_adds_epu8 (t1_hi, t2_hi); +} + +static force_inline void +negate_2x128 (__m128i data_lo, + __m128i data_hi, + __m128i* neg_lo, + __m128i* neg_hi) +{ + *neg_lo = _mm_xor_si128 (data_lo, mask_00ff); + *neg_hi = _mm_xor_si128 (data_hi, mask_00ff); +} + +static force_inline void +invert_colors_2x128 (__m128i data_lo, + __m128i data_hi, + __m128i* inv_lo, + __m128i* inv_hi) +{ + __m128i lo, hi; + + lo = _mm_shufflelo_epi16 (data_lo, _MM_SHUFFLE (3, 0, 1, 2)); + hi = _mm_shufflelo_epi16 (data_hi, _MM_SHUFFLE (3, 0, 1, 2)); + *inv_lo = _mm_shufflehi_epi16 (lo, _MM_SHUFFLE (3, 0, 1, 2)); + *inv_hi = _mm_shufflehi_epi16 (hi, _MM_SHUFFLE (3, 0, 1, 2)); +} + +static force_inline void +over_2x128 (__m128i* src_lo, + __m128i* src_hi, + __m128i* alpha_lo, + __m128i* alpha_hi, + __m128i* dst_lo, + __m128i* dst_hi) +{ + __m128i t1, t2; + + negate_2x128 (*alpha_lo, *alpha_hi, &t1, &t2); + + pix_multiply_2x128 (dst_lo, dst_hi, &t1, &t2, dst_lo, dst_hi); + + *dst_lo = _mm_adds_epu8 (*src_lo, *dst_lo); + *dst_hi = _mm_adds_epu8 (*src_hi, *dst_hi); +} + +static force_inline void +over_rev_non_pre_2x128 (__m128i src_lo, + __m128i src_hi, + __m128i* dst_lo, + __m128i* dst_hi) +{ + __m128i lo, hi; + __m128i alpha_lo, alpha_hi; + + expand_alpha_2x128 (src_lo, src_hi, &alpha_lo, &alpha_hi); + + lo = _mm_or_si128 (alpha_lo, mask_alpha); + hi = _mm_or_si128 (alpha_hi, mask_alpha); + + invert_colors_2x128 (src_lo, src_hi, &src_lo, &src_hi); + + pix_multiply_2x128 (&src_lo, &src_hi, &lo, &hi, &lo, &hi); + + over_2x128 (&lo, &hi, &alpha_lo, &alpha_hi, dst_lo, dst_hi); +} + +static force_inline void +in_over_2x128 (__m128i* src_lo, + __m128i* src_hi, + __m128i* alpha_lo, + __m128i* alpha_hi, + __m128i* mask_lo, + __m128i* mask_hi, + __m128i* dst_lo, + __m128i* dst_hi) +{ + __m128i s_lo, s_hi; + __m128i a_lo, a_hi; + + pix_multiply_2x128 (src_lo, src_hi, mask_lo, mask_hi, &s_lo, &s_hi); + pix_multiply_2x128 (alpha_lo, alpha_hi, mask_lo, mask_hi, &a_lo, &a_hi); + + over_2x128 (&s_lo, &s_hi, &a_lo, &a_hi, dst_lo, dst_hi); +} + +/* load 4 pixels from a 16-byte boundary aligned address */ +static force_inline __m128i +load_128_aligned (__m128i* src) +{ + return _mm_load_si128 (src); +} + +/* load 4 pixels from a unaligned address */ +static force_inline __m128i +load_128_unaligned (const __m128i* src) +{ + return _mm_loadu_si128 (src); +} + +/* save 4 pixels using Write Combining memory on a 16-byte + * boundary aligned address + */ +static force_inline void +save_128_write_combining (__m128i* dst, + __m128i data) +{ + _mm_stream_si128 (dst, data); +} + +/* save 4 pixels on a 16-byte boundary aligned address */ +static force_inline void +save_128_aligned (__m128i* dst, + __m128i data) +{ + _mm_store_si128 (dst, data); +} + +/* save 4 pixels on a unaligned address */ +static force_inline void +save_128_unaligned (__m128i* dst, + __m128i data) +{ + _mm_storeu_si128 (dst, data); +} + +static force_inline __m128i +load_32_1x128 (uint32_t data) +{ + return _mm_cvtsi32_si128 (data); +} + +static force_inline __m128i +expand_alpha_rev_1x128 (__m128i data) +{ + return _mm_shufflelo_epi16 (data, _MM_SHUFFLE (0, 0, 0, 0)); +} + +static force_inline __m128i +expand_pixel_8_1x128 (uint8_t data) +{ + return _mm_shufflelo_epi16 ( + unpack_32_1x128 ((uint32_t)data), _MM_SHUFFLE (0, 0, 0, 0)); +} + +static force_inline __m128i +pix_multiply_1x128 (__m128i data, + __m128i alpha) +{ + return _mm_mulhi_epu16 (_mm_adds_epu16 (_mm_mullo_epi16 (data, alpha), + mask_0080), + mask_0101); +} + +static force_inline __m128i +pix_add_multiply_1x128 (__m128i* src, + __m128i* alpha_dst, + __m128i* dst, + __m128i* alpha_src) +{ + __m128i t1 = pix_multiply_1x128 (*src, *alpha_dst); + __m128i t2 = pix_multiply_1x128 (*dst, *alpha_src); + + return _mm_adds_epu8 (t1, t2); +} + +static force_inline __m128i +negate_1x128 (__m128i data) +{ + return _mm_xor_si128 (data, mask_00ff); +} + +static force_inline __m128i +invert_colors_1x128 (__m128i data) +{ + return _mm_shufflelo_epi16 (data, _MM_SHUFFLE (3, 0, 1, 2)); +} + +static force_inline __m128i +over_1x128 (__m128i src, __m128i alpha, __m128i dst) +{ + return _mm_adds_epu8 (src, pix_multiply_1x128 (dst, negate_1x128 (alpha))); +} + +static force_inline __m128i +in_over_1x128 (__m128i* src, __m128i* alpha, __m128i* mask, __m128i* dst) +{ + return over_1x128 (pix_multiply_1x128 (*src, *mask), + pix_multiply_1x128 (*alpha, *mask), + *dst); +} + +static force_inline __m128i +over_rev_non_pre_1x128 (__m128i src, __m128i dst) +{ + __m128i alpha = expand_alpha_1x128 (src); + + return over_1x128 (pix_multiply_1x128 (invert_colors_1x128 (src), + _mm_or_si128 (alpha, mask_alpha)), + alpha, + dst); +} + +static force_inline uint32_t +pack_1x128_32 (__m128i data) +{ + return _mm_cvtsi128_si32 (_mm_packus_epi16 (data, _mm_setzero_si128 ())); +} + +static force_inline __m128i +expand565_16_1x128 (uint16_t pixel) +{ + __m128i m = _mm_cvtsi32_si128 (pixel); + + m = unpack_565_to_8888 (m); + + return _mm_unpacklo_epi8 (m, _mm_setzero_si128 ()); +} + +static force_inline uint32_t +core_combine_over_u_pixel_sse2 (uint32_t src, uint32_t dst) +{ + uint8_t a; + __m128i xmms; + + a = src >> 24; + + if (a == 0xff) + { + return src; + } + else if (src) + { + xmms = unpack_32_1x128 (src); + return pack_1x128_32 ( + over_1x128 (xmms, expand_alpha_1x128 (xmms), + unpack_32_1x128 (dst))); + } + + return dst; +} + +static force_inline uint32_t +combine1 (const uint32_t *ps, const uint32_t *pm) +{ + uint32_t s; + memcpy(&s, ps, sizeof(uint32_t)); + + if (pm) + { + __m128i ms, mm; + + mm = unpack_32_1x128 (*pm); + mm = expand_alpha_1x128 (mm); + + ms = unpack_32_1x128 (s); + ms = pix_multiply_1x128 (ms, mm); + + s = pack_1x128_32 (ms); + } + + return s; +} + +static force_inline __m128i +combine4 (const __m128i *ps, const __m128i *pm) +{ + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_msk_lo, xmm_msk_hi; + __m128i s; + + if (pm) + { + xmm_msk_lo = load_128_unaligned (pm); + + if (is_transparent (xmm_msk_lo)) + return _mm_setzero_si128 (); + } + + s = load_128_unaligned (ps); + + if (pm) + { + unpack_128_2x128 (s, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_msk_lo, &xmm_msk_lo, &xmm_msk_hi); + + expand_alpha_2x128 (xmm_msk_lo, xmm_msk_hi, &xmm_msk_lo, &xmm_msk_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_msk_lo, &xmm_msk_hi, + &xmm_src_lo, &xmm_src_hi); + + s = pack_2x128_128 (xmm_src_lo, xmm_src_hi); + } + + return s; +} + +static force_inline void +core_combine_over_u_sse2_mask (uint32_t * pd, + const uint32_t* ps, + const uint32_t* pm, + int w) +{ + uint32_t s, d; + + /* Align dst on a 16-byte boundary */ + while (w && ((uintptr_t)pd & 15)) + { + d = *pd; + s = combine1 (ps, pm); + + if (s) + *pd = core_combine_over_u_pixel_sse2 (s, d); + pd++; + ps++; + pm++; + w--; + } + + while (w >= 4) + { + __m128i mask = load_128_unaligned ((__m128i *)pm); + + if (!is_zero (mask)) + { + __m128i src; + __m128i src_hi, src_lo; + __m128i mask_hi, mask_lo; + __m128i alpha_hi, alpha_lo; + + src = load_128_unaligned ((__m128i *)ps); + + if (is_opaque (_mm_and_si128 (src, mask))) + { + save_128_aligned ((__m128i *)pd, src); + } + else + { + __m128i dst = load_128_aligned ((__m128i *)pd); + __m128i dst_hi, dst_lo; + + unpack_128_2x128 (mask, &mask_lo, &mask_hi); + unpack_128_2x128 (src, &src_lo, &src_hi); + + expand_alpha_2x128 (mask_lo, mask_hi, &mask_lo, &mask_hi); + pix_multiply_2x128 (&src_lo, &src_hi, + &mask_lo, &mask_hi, + &src_lo, &src_hi); + + unpack_128_2x128 (dst, &dst_lo, &dst_hi); + + expand_alpha_2x128 (src_lo, src_hi, + &alpha_lo, &alpha_hi); + + over_2x128 (&src_lo, &src_hi, &alpha_lo, &alpha_hi, + &dst_lo, &dst_hi); + + save_128_aligned ( + (__m128i *)pd, + pack_2x128_128 (dst_lo, dst_hi)); + } + } + + pm += 4; + ps += 4; + pd += 4; + w -= 4; + } + while (w) + { + d = *pd; + s = combine1 (ps, pm); + + if (s) + *pd = core_combine_over_u_pixel_sse2 (s, d); + pd++; + ps++; + pm++; + + w--; + } +} + +static force_inline void +core_combine_over_u_sse2_no_mask (uint32_t * pd, + const uint32_t* ps, + int w) +{ + uint32_t s, d; + + /* Align dst on a 16-byte boundary */ + while (w && ((uintptr_t)pd & 15)) + { + d = *pd; + s = *ps; + + if (s) + *pd = core_combine_over_u_pixel_sse2 (s, d); + pd++; + ps++; + w--; + } + + while (w >= 4) + { + __m128i src; + __m128i src_hi, src_lo, dst_hi, dst_lo; + __m128i alpha_hi, alpha_lo; + + src = load_128_unaligned ((__m128i *)ps); + + if (!is_zero (src)) + { + if (is_opaque (src)) + { + save_128_aligned ((__m128i *)pd, src); + } + else + { + __m128i dst = load_128_aligned ((__m128i *)pd); + + unpack_128_2x128 (src, &src_lo, &src_hi); + unpack_128_2x128 (dst, &dst_lo, &dst_hi); + + expand_alpha_2x128 (src_lo, src_hi, + &alpha_lo, &alpha_hi); + over_2x128 (&src_lo, &src_hi, &alpha_lo, &alpha_hi, + &dst_lo, &dst_hi); + + save_128_aligned ( + (__m128i *)pd, + pack_2x128_128 (dst_lo, dst_hi)); + } + } + + ps += 4; + pd += 4; + w -= 4; + } + while (w) + { + d = *pd; + s = *ps; + + if (s) + *pd = core_combine_over_u_pixel_sse2 (s, d); + pd++; + ps++; + + w--; + } +} + +static force_inline void +sse2_combine_over_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + if (pm) + core_combine_over_u_sse2_mask (pd, ps, pm, w); + else + core_combine_over_u_sse2_no_mask (pd, ps, w); +} + +static void +sse2_combine_over_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, d; + + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_alpha_lo, xmm_alpha_hi; + + /* Align dst on a 16-byte boundary */ + while (w && + ((uintptr_t)pd & 15)) + { + d = *pd; + s = combine1 (ps, pm); + + *pd++ = core_combine_over_u_pixel_sse2 (d, s); + w--; + ps++; + if (pm) + pm++; + } + + while (w >= 4) + { + /* I'm loading unaligned because I'm not sure + * about the address alignment. + */ + xmm_src_hi = combine4 ((__m128i*)ps, (__m128i*)pm); + xmm_dst_hi = load_128_aligned ((__m128i*) pd); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + over_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_src_lo, &xmm_src_hi); + + /* rebuid the 4 pixel data and save*/ + save_128_aligned ((__m128i*)pd, + pack_2x128_128 (xmm_src_lo, xmm_src_hi)); + + w -= 4; + ps += 4; + pd += 4; + + if (pm) + pm += 4; + } + + while (w) + { + d = *pd; + s = combine1 (ps, pm); + + *pd++ = core_combine_over_u_pixel_sse2 (d, s); + ps++; + w--; + if (pm) + pm++; + } +} + +static force_inline uint32_t +core_combine_in_u_pixel_sse2 (uint32_t src, uint32_t dst) +{ + uint32_t maska = src >> 24; + + if (maska == 0) + { + return 0; + } + else if (maska != 0xff) + { + return pack_1x128_32 ( + pix_multiply_1x128 (unpack_32_1x128 (dst), + expand_alpha_1x128 (unpack_32_1x128 (src)))); + } + + return dst; +} + +static void +sse2_combine_in_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, d; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + + while (w && ((uintptr_t)pd & 15)) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_in_u_pixel_sse2 (d, s); + w--; + ps++; + if (pm) + pm++; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*) pd); + xmm_src_hi = combine4 ((__m128i*) ps, (__m128i*) pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_dst_lo, &xmm_dst_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ((__m128i*)pd, + pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + w -= 4; + if (pm) + pm += 4; + } + + while (w) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_in_u_pixel_sse2 (d, s); + w--; + ps++; + if (pm) + pm++; + } +} + +static void +sse2_combine_in_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, d; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + + while (w && ((uintptr_t)pd & 15)) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_in_u_pixel_sse2 (s, d); + ps++; + w--; + if (pm) + pm++; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*) pd); + xmm_src_hi = combine4 ((__m128i*) ps, (__m128i*)pm); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_src_lo, &xmm_src_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + w -= 4; + if (pm) + pm += 4; + } + + while (w) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_in_u_pixel_sse2 (s, d); + w--; + ps++; + if (pm) + pm++; + } +} + +static void +sse2_combine_out_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + while (w && ((uintptr_t)pd & 15)) + { + uint32_t s = combine1 (ps, pm); + uint32_t d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (d), negate_1x128 ( + expand_alpha_1x128 (unpack_32_1x128 (s))))); + + if (pm) + pm++; + ps++; + w--; + } + + while (w >= 4) + { + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + + xmm_src_hi = combine4 ((__m128i*)ps, (__m128i*)pm); + xmm_dst_hi = load_128_aligned ((__m128i*) pd); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + negate_2x128 (xmm_src_lo, xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + + pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_src_lo, &xmm_src_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + if (pm) + pm += 4; + + w -= 4; + } + + while (w) + { + uint32_t s = combine1 (ps, pm); + uint32_t d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (d), negate_1x128 ( + expand_alpha_1x128 (unpack_32_1x128 (s))))); + ps++; + if (pm) + pm++; + w--; + } +} + +static void +sse2_combine_out_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + while (w && ((uintptr_t)pd & 15)) + { + uint32_t s = combine1 (ps, pm); + uint32_t d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (s), negate_1x128 ( + expand_alpha_1x128 (unpack_32_1x128 (d))))); + w--; + ps++; + if (pm) + pm++; + } + + while (w >= 4) + { + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + + xmm_src_hi = combine4 ((__m128i*) ps, (__m128i*)pm); + xmm_dst_hi = load_128_aligned ((__m128i*) pd); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + negate_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_dst_lo, &xmm_dst_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + w -= 4; + if (pm) + pm += 4; + } + + while (w) + { + uint32_t s = combine1 (ps, pm); + uint32_t d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (s), negate_1x128 ( + expand_alpha_1x128 (unpack_32_1x128 (d))))); + w--; + ps++; + if (pm) + pm++; + } +} + +static force_inline uint32_t +core_combine_atop_u_pixel_sse2 (uint32_t src, + uint32_t dst) +{ + __m128i s = unpack_32_1x128 (src); + __m128i d = unpack_32_1x128 (dst); + + __m128i sa = negate_1x128 (expand_alpha_1x128 (s)); + __m128i da = expand_alpha_1x128 (d); + + return pack_1x128_32 (pix_add_multiply_1x128 (&s, &da, &d, &sa)); +} + +static void +sse2_combine_atop_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, d; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; + __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; + + while (w && ((uintptr_t)pd & 15)) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_atop_u_pixel_sse2 (s, d); + w--; + ps++; + if (pm) + pm++; + } + + while (w >= 4) + { + xmm_src_hi = combine4 ((__m128i*)ps, (__m128i*)pm); + xmm_dst_hi = load_128_aligned ((__m128i*) pd); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi); + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + negate_2x128 (xmm_alpha_src_lo, xmm_alpha_src_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi); + + pix_add_multiply_2x128 ( + &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, + &xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + w -= 4; + if (pm) + pm += 4; + } + + while (w) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_atop_u_pixel_sse2 (s, d); + w--; + ps++; + if (pm) + pm++; + } +} + +static force_inline uint32_t +core_combine_reverse_atop_u_pixel_sse2 (uint32_t src, + uint32_t dst) +{ + __m128i s = unpack_32_1x128 (src); + __m128i d = unpack_32_1x128 (dst); + + __m128i sa = expand_alpha_1x128 (s); + __m128i da = negate_1x128 (expand_alpha_1x128 (d)); + + return pack_1x128_32 (pix_add_multiply_1x128 (&s, &da, &d, &sa)); +} + +static void +sse2_combine_atop_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, d; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; + __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; + + while (w && ((uintptr_t)pd & 15)) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_reverse_atop_u_pixel_sse2 (s, d); + ps++; + w--; + if (pm) + pm++; + } + + while (w >= 4) + { + xmm_src_hi = combine4 ((__m128i*)ps, (__m128i*)pm); + xmm_dst_hi = load_128_aligned ((__m128i*) pd); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi); + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + negate_2x128 (xmm_alpha_dst_lo, xmm_alpha_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + pix_add_multiply_2x128 ( + &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, + &xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + w -= 4; + if (pm) + pm += 4; + } + + while (w) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_reverse_atop_u_pixel_sse2 (s, d); + ps++; + w--; + if (pm) + pm++; + } +} + +static force_inline uint32_t +core_combine_xor_u_pixel_sse2 (uint32_t src, + uint32_t dst) +{ + __m128i s = unpack_32_1x128 (src); + __m128i d = unpack_32_1x128 (dst); + + __m128i neg_d = negate_1x128 (expand_alpha_1x128 (d)); + __m128i neg_s = negate_1x128 (expand_alpha_1x128 (s)); + + return pack_1x128_32 (pix_add_multiply_1x128 (&s, &neg_d, &d, &neg_s)); +} + +static void +sse2_combine_xor_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dst, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int w = width; + uint32_t s, d; + uint32_t* pd = dst; + const uint32_t* ps = src; + const uint32_t* pm = mask; + + __m128i xmm_src, xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; + __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; + + while (w && ((uintptr_t)pd & 15)) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_xor_u_pixel_sse2 (s, d); + w--; + ps++; + if (pm) + pm++; + } + + while (w >= 4) + { + xmm_src = combine4 ((__m128i*) ps, (__m128i*) pm); + xmm_dst = load_128_aligned ((__m128i*) pd); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi); + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + negate_2x128 (xmm_alpha_src_lo, xmm_alpha_src_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi); + negate_2x128 (xmm_alpha_dst_lo, xmm_alpha_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + pix_add_multiply_2x128 ( + &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, + &xmm_dst_lo, &xmm_dst_hi, &xmm_alpha_src_lo, &xmm_alpha_src_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + w -= 4; + if (pm) + pm += 4; + } + + while (w) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_xor_u_pixel_sse2 (s, d); + w--; + ps++; + if (pm) + pm++; + } +} + +static force_inline void +sse2_combine_add_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dst, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int w = width; + uint32_t s, d; + uint32_t* pd = dst; + const uint32_t* ps = src; + const uint32_t* pm = mask; + + while (w && (uintptr_t)pd & 15) + { + s = combine1 (ps, pm); + d = *pd; + + ps++; + if (pm) + pm++; + *pd++ = _mm_cvtsi128_si32 ( + _mm_adds_epu8 (_mm_cvtsi32_si128 (s), _mm_cvtsi32_si128 (d))); + w--; + } + + while (w >= 4) + { + __m128i s; + + s = combine4 ((__m128i*)ps, (__m128i*)pm); + + save_128_aligned ( + (__m128i*)pd, _mm_adds_epu8 (s, load_128_aligned ((__m128i*)pd))); + + pd += 4; + ps += 4; + if (pm) + pm += 4; + w -= 4; + } + + while (w--) + { + s = combine1 (ps, pm); + d = *pd; + + ps++; + *pd++ = _mm_cvtsi128_si32 ( + _mm_adds_epu8 (_mm_cvtsi32_si128 (s), _mm_cvtsi32_si128 (d))); + if (pm) + pm++; + } +} + +static force_inline uint32_t +core_combine_saturate_u_pixel_sse2 (uint32_t src, + uint32_t dst) +{ + __m128i ms = unpack_32_1x128 (src); + __m128i md = unpack_32_1x128 (dst); + uint32_t sa = src >> 24; + uint32_t da = ~dst >> 24; + + if (sa > da) + { + ms = pix_multiply_1x128 ( + ms, expand_alpha_1x128 (unpack_32_1x128 (DIV_UN8 (da, sa) << 24))); + } + + return pack_1x128_32 (_mm_adds_epu16 (md, ms)); +} + +static void +sse2_combine_saturate_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, d; + + uint32_t pack_cmp; + __m128i xmm_src, xmm_dst; + + while (w && (uintptr_t)pd & 15) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); + w--; + ps++; + if (pm) + pm++; + } + + while (w >= 4) + { + xmm_dst = load_128_aligned ((__m128i*)pd); + xmm_src = combine4 ((__m128i*)ps, (__m128i*)pm); + + pack_cmp = _mm_movemask_epi8 ( + _mm_cmpgt_epi32 ( + _mm_srli_epi32 (xmm_src, 24), + _mm_srli_epi32 (_mm_xor_si128 (xmm_dst, mask_ff000000), 24))); + + /* if some alpha src is grater than respective ~alpha dst */ + if (pack_cmp) + { + s = combine1 (ps++, pm); + d = *pd; + *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); + if (pm) + pm++; + + s = combine1 (ps++, pm); + d = *pd; + *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); + if (pm) + pm++; + + s = combine1 (ps++, pm); + d = *pd; + *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); + if (pm) + pm++; + + s = combine1 (ps++, pm); + d = *pd; + *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); + if (pm) + pm++; + } + else + { + save_128_aligned ((__m128i*)pd, _mm_adds_epu8 (xmm_dst, xmm_src)); + + pd += 4; + ps += 4; + if (pm) + pm += 4; + } + + w -= 4; + } + + while (w--) + { + s = combine1 (ps, pm); + d = *pd; + + *pd++ = core_combine_saturate_u_pixel_sse2 (s, d); + ps++; + if (pm) + pm++; + } +} + +static void +sse2_combine_src_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (m))); + w--; + } + + while (w >= 4) + { + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (m))); + w--; + } +} + +static force_inline uint32_t +core_combine_over_ca_pixel_sse2 (uint32_t src, + uint32_t mask, + uint32_t dst) +{ + __m128i s = unpack_32_1x128 (src); + __m128i expAlpha = expand_alpha_1x128 (s); + __m128i unpk_mask = unpack_32_1x128 (mask); + __m128i unpk_dst = unpack_32_1x128 (dst); + + return pack_1x128_32 (in_over_1x128 (&s, &expAlpha, &unpk_mask, &unpk_dst)); +} + +static void +sse2_combine_over_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_alpha_lo, xmm_alpha_hi; + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_over_ca_pixel_sse2 (s, m, d); + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_over_ca_pixel_sse2 (s, m, d); + w--; + } +} + +static force_inline uint32_t +core_combine_over_reverse_ca_pixel_sse2 (uint32_t src, + uint32_t mask, + uint32_t dst) +{ + __m128i d = unpack_32_1x128 (dst); + + return pack_1x128_32 ( + over_1x128 (d, expand_alpha_1x128 (d), + pix_multiply_1x128 (unpack_32_1x128 (src), + unpack_32_1x128 (mask)))); +} + +static void +sse2_combine_over_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_alpha_lo, xmm_alpha_hi; + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_over_reverse_ca_pixel_sse2 (s, m, d); + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + over_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_mask_lo, &xmm_mask_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_mask_lo, xmm_mask_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_over_reverse_ca_pixel_sse2 (s, m, d); + w--; + } +} + +static void +sse2_combine_in_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_alpha_lo, xmm_alpha_hi; + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (m)), + expand_alpha_1x128 (unpack_32_1x128 (d)))); + + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi); + + pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + pix_multiply_1x128 ( + unpack_32_1x128 (s), unpack_32_1x128 (m)), + expand_alpha_1x128 (unpack_32_1x128 (d)))); + + w--; + } +} + +static void +sse2_combine_in_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_alpha_lo, xmm_alpha_hi; + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (d), + pix_multiply_1x128 (unpack_32_1x128 (m), + expand_alpha_1x128 (unpack_32_1x128 (s))))); + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (d), + pix_multiply_1x128 (unpack_32_1x128 (m), + expand_alpha_1x128 (unpack_32_1x128 (s))))); + w--; + } +} + +static void +sse2_combine_out_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_alpha_lo, xmm_alpha_hi; + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + pix_multiply_1x128 ( + unpack_32_1x128 (s), unpack_32_1x128 (m)), + negate_1x128 (expand_alpha_1x128 (unpack_32_1x128 (d))))); + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + negate_2x128 (xmm_alpha_lo, xmm_alpha_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi); + pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + pix_multiply_1x128 ( + unpack_32_1x128 (s), unpack_32_1x128 (m)), + negate_1x128 (expand_alpha_1x128 (unpack_32_1x128 (d))))); + + w--; + } +} + +static void +sse2_combine_out_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_alpha_lo, xmm_alpha_hi; + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (d), + negate_1x128 (pix_multiply_1x128 ( + unpack_32_1x128 (m), + expand_alpha_1x128 (unpack_32_1x128 (s)))))); + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_mask_lo, &xmm_mask_hi); + + negate_2x128 (xmm_mask_lo, xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + pix_multiply_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (d), + negate_1x128 (pix_multiply_1x128 ( + unpack_32_1x128 (m), + expand_alpha_1x128 (unpack_32_1x128 (s)))))); + w--; + } +} + +static force_inline uint32_t +core_combine_atop_ca_pixel_sse2 (uint32_t src, + uint32_t mask, + uint32_t dst) +{ + __m128i m = unpack_32_1x128 (mask); + __m128i s = unpack_32_1x128 (src); + __m128i d = unpack_32_1x128 (dst); + __m128i sa = expand_alpha_1x128 (s); + __m128i da = expand_alpha_1x128 (d); + + s = pix_multiply_1x128 (s, m); + m = negate_1x128 (pix_multiply_1x128 (m, sa)); + + return pack_1x128_32 (pix_add_multiply_1x128 (&d, &m, &s, &da)); +} + +static void +sse2_combine_atop_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; + __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_atop_ca_pixel_sse2 (s, m, d); + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi); + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_src_lo, &xmm_src_hi); + pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi, + &xmm_mask_lo, &xmm_mask_hi); + + negate_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + pix_add_multiply_2x128 ( + &xmm_dst_lo, &xmm_dst_hi, &xmm_mask_lo, &xmm_mask_hi, + &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_atop_ca_pixel_sse2 (s, m, d); + w--; + } +} + +static force_inline uint32_t +core_combine_reverse_atop_ca_pixel_sse2 (uint32_t src, + uint32_t mask, + uint32_t dst) +{ + __m128i m = unpack_32_1x128 (mask); + __m128i s = unpack_32_1x128 (src); + __m128i d = unpack_32_1x128 (dst); + + __m128i da = negate_1x128 (expand_alpha_1x128 (d)); + __m128i sa = expand_alpha_1x128 (s); + + s = pix_multiply_1x128 (s, m); + m = pix_multiply_1x128 (m, sa); + + return pack_1x128_32 (pix_add_multiply_1x128 (&d, &m, &s, &da)); +} + +static void +sse2_combine_atop_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; + __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_reverse_atop_ca_pixel_sse2 (s, m, d); + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi); + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_src_lo, &xmm_src_hi); + pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi, + &xmm_mask_lo, &xmm_mask_hi); + + negate_2x128 (xmm_alpha_dst_lo, xmm_alpha_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + pix_add_multiply_2x128 ( + &xmm_dst_lo, &xmm_dst_hi, &xmm_mask_lo, &xmm_mask_hi, + &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_reverse_atop_ca_pixel_sse2 (s, m, d); + w--; + } +} + +static force_inline uint32_t +core_combine_xor_ca_pixel_sse2 (uint32_t src, + uint32_t mask, + uint32_t dst) +{ + __m128i a = unpack_32_1x128 (mask); + __m128i s = unpack_32_1x128 (src); + __m128i d = unpack_32_1x128 (dst); + + __m128i alpha_dst = negate_1x128 (pix_multiply_1x128 ( + a, expand_alpha_1x128 (s))); + __m128i dest = pix_multiply_1x128 (s, a); + __m128i alpha_src = negate_1x128 (expand_alpha_1x128 (d)); + + return pack_1x128_32 (pix_add_multiply_1x128 (&d, + &alpha_dst, + &dest, + &alpha_src)); +} + +static void +sse2_combine_xor_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_src_lo, xmm_alpha_src_hi; + __m128i xmm_alpha_dst_lo, xmm_alpha_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_xor_ca_pixel_sse2 (s, m, d); + w--; + } + + while (w >= 4) + { + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi); + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_src_lo, &xmm_src_hi); + pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, + &xmm_alpha_src_lo, &xmm_alpha_src_hi, + &xmm_mask_lo, &xmm_mask_hi); + + negate_2x128 (xmm_alpha_dst_lo, xmm_alpha_dst_hi, + &xmm_alpha_dst_lo, &xmm_alpha_dst_hi); + negate_2x128 (xmm_mask_lo, xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + pix_add_multiply_2x128 ( + &xmm_dst_lo, &xmm_dst_hi, &xmm_mask_lo, &xmm_mask_hi, + &xmm_src_lo, &xmm_src_hi, &xmm_alpha_dst_lo, &xmm_alpha_dst_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = core_combine_xor_ca_pixel_sse2 (s, m, d); + w--; + } +} + +static void +sse2_combine_add_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * pd, + const uint32_t * ps, + const uint32_t * pm, + int w) +{ + uint32_t s, m, d; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask_lo, xmm_mask_hi; + + while (w && (uintptr_t)pd & 15) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + _mm_adds_epu8 (pix_multiply_1x128 (unpack_32_1x128 (s), + unpack_32_1x128 (m)), + unpack_32_1x128 (d))); + w--; + } + + while (w >= 4) + { + xmm_src_hi = load_128_unaligned ((__m128i*)ps); + xmm_mask_hi = load_128_unaligned ((__m128i*)pm); + xmm_dst_hi = load_128_aligned ((__m128i*)pd); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_src_lo, &xmm_src_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 ( + _mm_adds_epu8 (xmm_src_lo, xmm_dst_lo), + _mm_adds_epu8 (xmm_src_hi, xmm_dst_hi))); + + ps += 4; + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = *ps++; + m = *pm++; + d = *pd; + + *pd++ = pack_1x128_32 ( + _mm_adds_epu8 (pix_multiply_1x128 (unpack_32_1x128 (s), + unpack_32_1x128 (m)), + unpack_32_1x128 (d))); + w--; + } +} + +static force_inline __m128i +create_mask_16_128 (uint16_t mask) +{ + return _mm_set1_epi16 (mask); +} + +/* Work around a code generation bug in Sun Studio 12. */ +#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +# define create_mask_2x32_128(mask0, mask1) \ + (_mm_set_epi32 ((mask0), (mask1), (mask0), (mask1))) +#else +static force_inline __m128i +create_mask_2x32_128 (uint32_t mask0, + uint32_t mask1) +{ + return _mm_set_epi32 (mask0, mask1, mask0, mask1); +} +#endif + +static void +sse2_composite_over_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint32_t *dst_line, *dst, d; + int32_t w; + int dst_stride; + __m128i xmm_src, xmm_alpha; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + xmm_src = expand_pixel_32_1x128 (src); + xmm_alpha = expand_alpha_1x128 (xmm_src); + + while (height--) + { + dst = dst_line; + + dst_line += dst_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + d = *dst; + *dst++ = pack_1x128_32 (over_1x128 (xmm_src, + xmm_alpha, + unpack_32_1x128 (d))); + w--; + } + + while (w >= 4) + { + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_dst_lo, &xmm_dst_hi); + + /* rebuid the 4 pixel data and save*/ + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + w -= 4; + dst += 4; + } + + while (w) + { + d = *dst; + *dst++ = pack_1x128_32 (over_1x128 (xmm_src, + xmm_alpha, + unpack_32_1x128 (d))); + w--; + } + + } +} + +static void +sse2_composite_over_n_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint16_t *dst_line, *dst, d; + int32_t w; + int dst_stride; + __m128i xmm_src, xmm_alpha; + __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + + xmm_src = expand_pixel_32_1x128 (src); + xmm_alpha = expand_alpha_1x128 (xmm_src); + + while (height--) + { + dst = dst_line; + + dst_line += dst_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + d = *dst; + + *dst++ = pack_565_32_16 ( + pack_1x128_32 (over_1x128 (xmm_src, + xmm_alpha, + expand565_16_1x128 (d)))); + w--; + } + + while (w >= 8) + { + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_565_128_4x128 (xmm_dst, + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); + + over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_dst0, &xmm_dst1); + over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_dst2, &xmm_dst3); + + xmm_dst = pack_565_4x128_128 ( + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); + + save_128_aligned ((__m128i*)dst, xmm_dst); + + dst += 8; + w -= 8; + } + + while (w--) + { + d = *dst; + *dst++ = pack_565_32_16 ( + pack_1x128_32 (over_1x128 (xmm_src, xmm_alpha, + expand565_16_1x128 (d)))); + } + } + +} + +static void +sse2_composite_add_n_8888_8888_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint32_t *dst_line, d; + uint32_t *mask_line, m; + uint32_t pack_cmp; + int dst_stride, mask_stride; + + __m128i xmm_src; + __m128i xmm_dst; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + + __m128i mmx_src, mmx_mask, mmx_dest; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + xmm_src = _mm_unpacklo_epi8 ( + create_mask_2x32_128 (src, src), _mm_setzero_si128 ()); + mmx_src = xmm_src; + + while (height--) + { + int w = width; + const uint32_t *pm = (uint32_t *)mask_line; + uint32_t *pd = (uint32_t *)dst_line; + + dst_line += dst_stride; + mask_line += mask_stride; + + while (w && (uintptr_t)pd & 15) + { + m = *pm++; + + if (m) + { + d = *pd; + + mmx_mask = unpack_32_1x128 (m); + mmx_dest = unpack_32_1x128 (d); + + *pd = pack_1x128_32 ( + _mm_adds_epu8 (pix_multiply_1x128 (mmx_mask, mmx_src), + mmx_dest)); + } + + pd++; + w--; + } + + while (w >= 4) + { + xmm_mask = load_128_unaligned ((__m128i*)pm); + + pack_cmp = + _mm_movemask_epi8 ( + _mm_cmpeq_epi32 (xmm_mask, _mm_setzero_si128 ())); + + /* if all bits in mask are zero, pack_cmp are equal to 0xffff */ + if (pack_cmp != 0xffff) + { + xmm_dst = load_128_aligned ((__m128i*)pd); + + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + + pix_multiply_2x128 (&xmm_src, &xmm_src, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + xmm_mask_hi = pack_2x128_128 (xmm_mask_lo, xmm_mask_hi); + + save_128_aligned ( + (__m128i*)pd, _mm_adds_epu8 (xmm_mask_hi, xmm_dst)); + } + + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + m = *pm++; + + if (m) + { + d = *pd; + + mmx_mask = unpack_32_1x128 (m); + mmx_dest = unpack_32_1x128 (d); + + *pd = pack_1x128_32 ( + _mm_adds_epu8 (pix_multiply_1x128 (mmx_mask, mmx_src), + mmx_dest)); + } + + pd++; + w--; + } + } + +} + +static void +sse2_composite_over_n_8888_8888_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint32_t *dst_line, d; + uint32_t *mask_line, m; + uint32_t pack_cmp; + int dst_stride, mask_stride; + + __m128i xmm_src, xmm_alpha; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + + __m128i mmx_src, mmx_alpha, mmx_mask, mmx_dest; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + xmm_src = _mm_unpacklo_epi8 ( + create_mask_2x32_128 (src, src), _mm_setzero_si128 ()); + xmm_alpha = expand_alpha_1x128 (xmm_src); + mmx_src = xmm_src; + mmx_alpha = xmm_alpha; + + while (height--) + { + int w = width; + const uint32_t *pm = (uint32_t *)mask_line; + uint32_t *pd = (uint32_t *)dst_line; + + dst_line += dst_stride; + mask_line += mask_stride; + + while (w && (uintptr_t)pd & 15) + { + m = *pm++; + + if (m) + { + d = *pd; + mmx_mask = unpack_32_1x128 (m); + mmx_dest = unpack_32_1x128 (d); + + *pd = pack_1x128_32 (in_over_1x128 (&mmx_src, + &mmx_alpha, + &mmx_mask, + &mmx_dest)); + } + + pd++; + w--; + } + + while (w >= 4) + { + xmm_mask = load_128_unaligned ((__m128i*)pm); + + pack_cmp = + _mm_movemask_epi8 ( + _mm_cmpeq_epi32 (xmm_mask, _mm_setzero_si128 ())); + + /* if all bits in mask are zero, pack_cmp are equal to 0xffff */ + if (pack_cmp != 0xffff) + { + xmm_dst = load_128_aligned ((__m128i*)pd); + + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + in_over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)pd, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + m = *pm++; + + if (m) + { + d = *pd; + mmx_mask = unpack_32_1x128 (m); + mmx_dest = unpack_32_1x128 (d); + + *pd = pack_1x128_32 ( + in_over_1x128 (&mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest)); + } + + pd++; + w--; + } + } + +} + +static void +sse2_composite_over_8888_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + uint32_t mask; + int32_t w; + int dst_stride, src_stride; + + __m128i xmm_mask; + __m128i xmm_src, xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_lo, xmm_alpha_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + mask = _pixman_image_get_solid (imp, mask_image, PIXMAN_a8r8g8b8); + + xmm_mask = create_mask_16_128 (mask >> 24); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + uint32_t s = *src++; + + if (s) + { + uint32_t d = *dst; + + __m128i ms = unpack_32_1x128 (s); + __m128i alpha = expand_alpha_1x128 (ms); + __m128i dest = xmm_mask; + __m128i alpha_dst = unpack_32_1x128 (d); + + *dst = pack_1x128_32 ( + in_over_1x128 (&ms, &alpha, &dest, &alpha_dst)); + } + dst++; + w--; + } + + while (w >= 4) + { + xmm_src = load_128_unaligned ((__m128i*)src); + + if (!is_zero (xmm_src)) + { + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_mask, &xmm_mask, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + dst += 4; + src += 4; + w -= 4; + } + + while (w) + { + uint32_t s = *src++; + + if (s) + { + uint32_t d = *dst; + + __m128i ms = unpack_32_1x128 (s); + __m128i alpha = expand_alpha_1x128 (ms); + __m128i mask = xmm_mask; + __m128i dest = unpack_32_1x128 (d); + + *dst = pack_1x128_32 ( + in_over_1x128 (&ms, &alpha, &mask, &dest)); + } + + dst++; + w--; + } + } + +} + +static void +sse2_composite_src_x888_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst; + uint32_t *src_line, *src, s; + int dst_stride, src_stride; + int32_t w; + + PIXMAN_IMAGE_GET_LINE (src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + s = *src++; + *dst = convert_8888_to_0565 (s); + dst++; + w--; + } + + while (w >= 8) + { + __m128i xmm_src0 = load_128_unaligned ((__m128i *)src + 0); + __m128i xmm_src1 = load_128_unaligned ((__m128i *)src + 1); + + save_128_aligned ((__m128i*)dst, pack_565_2packedx128_128 (xmm_src0, xmm_src1)); + + w -= 8; + src += 8; + dst += 8; + } + + while (w) + { + s = *src++; + *dst = convert_8888_to_0565 (s); + dst++; + w--; + } + } +} + +static void +sse2_composite_src_x888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int32_t w; + int dst_stride, src_stride; + + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + *dst++ = *src++ | 0xff000000; + w--; + } + + while (w >= 16) + { + __m128i xmm_src1, xmm_src2, xmm_src3, xmm_src4; + + xmm_src1 = load_128_unaligned ((__m128i*)src + 0); + xmm_src2 = load_128_unaligned ((__m128i*)src + 1); + xmm_src3 = load_128_unaligned ((__m128i*)src + 2); + xmm_src4 = load_128_unaligned ((__m128i*)src + 3); + + save_128_aligned ((__m128i*)dst + 0, _mm_or_si128 (xmm_src1, mask_ff000000)); + save_128_aligned ((__m128i*)dst + 1, _mm_or_si128 (xmm_src2, mask_ff000000)); + save_128_aligned ((__m128i*)dst + 2, _mm_or_si128 (xmm_src3, mask_ff000000)); + save_128_aligned ((__m128i*)dst + 3, _mm_or_si128 (xmm_src4, mask_ff000000)); + + dst += 16; + src += 16; + w -= 16; + } + + while (w) + { + *dst++ = *src++ | 0xff000000; + w--; + } + } + +} + +static void +sse2_composite_over_x888_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + uint32_t mask; + int dst_stride, src_stride; + int32_t w; + + __m128i xmm_mask, xmm_alpha; + __m128i xmm_src, xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + mask = _pixman_image_get_solid (imp, mask_image, PIXMAN_a8r8g8b8); + + xmm_mask = create_mask_16_128 (mask >> 24); + xmm_alpha = mask_00ff; + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + uint32_t s = (*src++) | 0xff000000; + uint32_t d = *dst; + + __m128i src = unpack_32_1x128 (s); + __m128i alpha = xmm_alpha; + __m128i mask = xmm_mask; + __m128i dest = unpack_32_1x128 (d); + + *dst++ = pack_1x128_32 ( + in_over_1x128 (&src, &alpha, &mask, &dest)); + + w--; + } + + while (w >= 4) + { + xmm_src = _mm_or_si128 ( + load_128_unaligned ((__m128i*)src), mask_ff000000); + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_alpha, &xmm_alpha, + &xmm_mask, &xmm_mask, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + dst += 4; + src += 4; + w -= 4; + + } + + while (w) + { + uint32_t s = (*src++) | 0xff000000; + uint32_t d = *dst; + + __m128i src = unpack_32_1x128 (s); + __m128i alpha = xmm_alpha; + __m128i mask = xmm_mask; + __m128i dest = unpack_32_1x128 (d); + + *dst++ = pack_1x128_32 ( + in_over_1x128 (&src, &alpha, &mask, &dest)); + + w--; + } + } + +} + +static void +sse2_composite_over_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + int dst_stride, src_stride; + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + dst = dst_line; + src = src_line; + + while (height--) + { + sse2_combine_over_u (imp, op, dst, src, NULL, width); + + dst += dst_stride; + src += src_stride; + } +} + +static force_inline uint16_t +composite_over_8888_0565pixel (uint32_t src, uint16_t dst) +{ + __m128i ms; + + ms = unpack_32_1x128 (src); + return pack_565_32_16 ( + pack_1x128_32 ( + over_1x128 ( + ms, expand_alpha_1x128 (ms), expand565_16_1x128 (dst)))); +} + +static void +sse2_composite_over_8888_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst, d; + uint32_t *src_line, *src, s; + int dst_stride, src_stride; + int32_t w; + + __m128i xmm_alpha_lo, xmm_alpha_hi; + __m128i xmm_src, xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + src = src_line; + + dst_line += dst_stride; + src_line += src_stride; + w = width; + + /* Align dst on a 16-byte boundary */ + while (w && + ((uintptr_t)dst & 15)) + { + s = *src++; + d = *dst; + + *dst++ = composite_over_8888_0565pixel (s, d); + w--; + } + + /* It's a 8 pixel loop */ + while (w >= 8) + { + /* I'm loading unaligned because I'm not sure + * about the address alignment. + */ + xmm_src = load_128_unaligned ((__m128i*) src); + xmm_dst = load_128_aligned ((__m128i*) dst); + + /* Unpacking */ + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_565_128_4x128 (xmm_dst, + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + /* I'm loading next 4 pixels from memory + * before to optimze the memory read. + */ + xmm_src = load_128_unaligned ((__m128i*) (src + 4)); + + over_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_dst0, &xmm_dst1); + + /* Unpacking */ + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + over_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_dst2, &xmm_dst3); + + save_128_aligned ( + (__m128i*)dst, pack_565_4x128_128 ( + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3)); + + w -= 8; + dst += 8; + src += 8; + } + + while (w--) + { + s = *src++; + d = *dst; + + *dst++ = composite_over_8888_0565pixel (s, d); + } + } + +} + +static void +sse2_composite_over_n_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint32_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + uint32_t d; + + __m128i xmm_src, xmm_alpha, xmm_def; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + + __m128i mmx_src, mmx_alpha, mmx_mask, mmx_dest; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + xmm_def = create_mask_2x32_128 (src, src); + xmm_src = expand_pixel_32_1x128 (src); + xmm_alpha = expand_alpha_1x128 (xmm_src); + mmx_src = xmm_src; + mmx_alpha = xmm_alpha; + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + uint8_t m = *mask++; + + if (m) + { + d = *dst; + mmx_mask = expand_pixel_8_1x128 (m); + mmx_dest = unpack_32_1x128 (d); + + *dst = pack_1x128_32 (in_over_1x128 (&mmx_src, + &mmx_alpha, + &mmx_mask, + &mmx_dest)); + } + + w--; + dst++; + } + + while (w >= 4) + { + uint32_t m; + memcpy(&m, mask, sizeof(uint32_t)); + + if (srca == 0xff && m == 0xffffffff) + { + save_128_aligned ((__m128i*)dst, xmm_def); + } + else if (m) + { + xmm_dst = load_128_aligned ((__m128i*) dst); + xmm_mask = unpack_32_1x128 (m); + xmm_mask = _mm_unpacklo_epi8 (xmm_mask, _mm_setzero_si128 ()); + + /* Unpacking */ + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + in_over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + w -= 4; + dst += 4; + mask += 4; + } + + while (w) + { + uint8_t m = *mask++; + + if (m) + { + d = *dst; + mmx_mask = expand_pixel_8_1x128 (m); + mmx_dest = unpack_32_1x128 (d); + + *dst = pack_1x128_32 (in_over_1x128 (&mmx_src, + &mmx_alpha, + &mmx_mask, + &mmx_dest)); + } + + w--; + dst++; + } + } + +} + +#if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) +__attribute__((__force_align_arg_pointer__)) +#endif +static pixman_bool_t +sse2_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + uint32_t byte_width; + uint8_t *byte_line; + + __m128i xmm_def; + + if (bpp == 8) + { + uint32_t b; + uint32_t w; + + stride = stride * (int) sizeof (uint32_t) / 1; + byte_line = (uint8_t *)(((uint8_t *)bits) + stride * y + x); + byte_width = width; + stride *= 1; + + b = filler & 0xff; + w = (b << 8) | b; + filler = (w << 16) | w; + } + else if (bpp == 16) + { + stride = stride * (int) sizeof (uint32_t) / 2; + byte_line = (uint8_t *)(((uint16_t *)bits) + stride * y + x); + byte_width = 2 * width; + stride *= 2; + + filler = (filler & 0xffff) * 0x00010001; + } + else if (bpp == 32) + { + stride = stride * (int) sizeof (uint32_t) / 4; + byte_line = (uint8_t *)(((uint32_t *)bits) + stride * y + x); + byte_width = 4 * width; + stride *= 4; + } + else + { + return FALSE; + } + + xmm_def = create_mask_2x32_128 (filler, filler); + + while (height--) + { + int w; + uint8_t *d = byte_line; + byte_line += stride; + w = byte_width; + + if (w >= 1 && ((uintptr_t)d & 1)) + { + *(uint8_t *)d = filler; + w -= 1; + d += 1; + } + + while (w >= 2 && ((uintptr_t)d & 3)) + { + *(uint16_t *)d = filler; + w -= 2; + d += 2; + } + + while (w >= 4 && ((uintptr_t)d & 15)) + { + *(uint32_t *)d = filler; + + w -= 4; + d += 4; + } + + while (w >= 128) + { + save_128_aligned ((__m128i*)(d), xmm_def); + save_128_aligned ((__m128i*)(d + 16), xmm_def); + save_128_aligned ((__m128i*)(d + 32), xmm_def); + save_128_aligned ((__m128i*)(d + 48), xmm_def); + save_128_aligned ((__m128i*)(d + 64), xmm_def); + save_128_aligned ((__m128i*)(d + 80), xmm_def); + save_128_aligned ((__m128i*)(d + 96), xmm_def); + save_128_aligned ((__m128i*)(d + 112), xmm_def); + + d += 128; + w -= 128; + } + + if (w >= 64) + { + save_128_aligned ((__m128i*)(d), xmm_def); + save_128_aligned ((__m128i*)(d + 16), xmm_def); + save_128_aligned ((__m128i*)(d + 32), xmm_def); + save_128_aligned ((__m128i*)(d + 48), xmm_def); + + d += 64; + w -= 64; + } + + if (w >= 32) + { + save_128_aligned ((__m128i*)(d), xmm_def); + save_128_aligned ((__m128i*)(d + 16), xmm_def); + + d += 32; + w -= 32; + } + + if (w >= 16) + { + save_128_aligned ((__m128i*)(d), xmm_def); + + d += 16; + w -= 16; + } + + while (w >= 4) + { + *(uint32_t *)d = filler; + + w -= 4; + d += 4; + } + + if (w >= 2) + { + *(uint16_t *)d = filler; + w -= 2; + d += 2; + } + + if (w >= 1) + { + *(uint8_t *)d = filler; + w -= 1; + d += 1; + } + } + + return TRUE; +} + +static void +sse2_composite_src_n_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint32_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + + __m128i xmm_src, xmm_def; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = src >> 24; + if (src == 0) + { + sse2_fill (imp, dest_image->bits.bits, dest_image->bits.rowstride, + PIXMAN_FORMAT_BPP (dest_image->bits.format), + dest_x, dest_y, width, height, 0); + return; + } + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + xmm_def = create_mask_2x32_128 (src, src); + xmm_src = expand_pixel_32_1x128 (src); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + uint8_t m = *mask++; + + if (m) + { + *dst = pack_1x128_32 ( + pix_multiply_1x128 (xmm_src, expand_pixel_8_1x128 (m))); + } + else + { + *dst = 0; + } + + w--; + dst++; + } + + while (w >= 4) + { + uint32_t m; + memcpy(&m, mask, sizeof(uint32_t)); + + if (srca == 0xff && m == 0xffffffff) + { + save_128_aligned ((__m128i*)dst, xmm_def); + } + else if (m) + { + xmm_mask = unpack_32_1x128 (m); + xmm_mask = _mm_unpacklo_epi8 (xmm_mask, _mm_setzero_si128 ()); + + /* Unpacking */ + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + pix_multiply_2x128 (&xmm_src, &xmm_src, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_mask_lo, xmm_mask_hi)); + } + else + { + save_128_aligned ((__m128i*)dst, _mm_setzero_si128 ()); + } + + w -= 4; + dst += 4; + mask += 4; + } + + while (w) + { + uint8_t m = *mask++; + + if (m) + { + *dst = pack_1x128_32 ( + pix_multiply_1x128 ( + xmm_src, expand_pixel_8_1x128 (m))); + } + else + { + *dst = 0; + } + + w--; + dst++; + } + } + +} + +static void +sse2_composite_over_n_8_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint16_t *dst_line, *dst, d; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + __m128i mmx_src, mmx_alpha, mmx_mask, mmx_dest; + + __m128i xmm_src, xmm_alpha; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + xmm_src = expand_pixel_32_1x128 (src); + xmm_alpha = expand_alpha_1x128 (xmm_src); + mmx_src = xmm_src; + mmx_alpha = xmm_alpha; + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + uint8_t m = *mask++; + + if (m) + { + d = *dst; + mmx_mask = expand_alpha_rev_1x128 (unpack_32_1x128 (m)); + mmx_dest = expand565_16_1x128 (d); + + *dst = pack_565_32_16 ( + pack_1x128_32 ( + in_over_1x128 ( + &mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest))); + } + + w--; + dst++; + } + + while (w >= 8) + { + uint32_t m; + + xmm_dst = load_128_aligned ((__m128i*) dst); + unpack_565_128_4x128 (xmm_dst, + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); + + memcpy(&m, mask, sizeof(uint32_t)); + mask += 4; + + if (m) + { + xmm_mask = unpack_32_1x128 (m); + xmm_mask = _mm_unpacklo_epi8 (xmm_mask, _mm_setzero_si128 ()); + + /* Unpacking */ + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + in_over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst0, &xmm_dst1); + } + + memcpy(&m, mask, sizeof(uint32_t)); + mask += 4; + + if (m) + { + xmm_mask = unpack_32_1x128 (m); + xmm_mask = _mm_unpacklo_epi8 (xmm_mask, _mm_setzero_si128 ()); + + /* Unpacking */ + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + + expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + in_over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst2, &xmm_dst3); + } + + save_128_aligned ( + (__m128i*)dst, pack_565_4x128_128 ( + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3)); + + w -= 8; + dst += 8; + } + + while (w) + { + uint8_t m = *mask++; + + if (m) + { + d = *dst; + mmx_mask = expand_alpha_rev_1x128 (unpack_32_1x128 (m)); + mmx_dest = expand565_16_1x128 (d); + + *dst = pack_565_32_16 ( + pack_1x128_32 ( + in_over_1x128 ( + &mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest))); + } + + w--; + dst++; + } + } + +} + +static void +sse2_composite_over_pixbuf_0565 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint16_t *dst_line, *dst, d; + uint32_t *src_line, *src, s; + int dst_stride, src_stride; + int32_t w; + uint32_t opaque, zero; + + __m128i ms; + __m128i xmm_src, xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + s = *src++; + d = *dst; + + ms = unpack_32_1x128 (s); + + *dst++ = pack_565_32_16 ( + pack_1x128_32 ( + over_rev_non_pre_1x128 (ms, expand565_16_1x128 (d)))); + w--; + } + + while (w >= 8) + { + /* First round */ + xmm_src = load_128_unaligned ((__m128i*)src); + xmm_dst = load_128_aligned ((__m128i*)dst); + + opaque = is_opaque (xmm_src); + zero = is_zero (xmm_src); + + unpack_565_128_4x128 (xmm_dst, + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + + /* preload next round*/ + xmm_src = load_128_unaligned ((__m128i*)(src + 4)); + + if (opaque) + { + invert_colors_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_dst0, &xmm_dst1); + } + else if (!zero) + { + over_rev_non_pre_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_dst0, &xmm_dst1); + } + + /* Second round */ + opaque = is_opaque (xmm_src); + zero = is_zero (xmm_src); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + + if (opaque) + { + invert_colors_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_dst2, &xmm_dst3); + } + else if (!zero) + { + over_rev_non_pre_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_dst2, &xmm_dst3); + } + + save_128_aligned ( + (__m128i*)dst, pack_565_4x128_128 ( + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3)); + + w -= 8; + src += 8; + dst += 8; + } + + while (w) + { + s = *src++; + d = *dst; + + ms = unpack_32_1x128 (s); + + *dst++ = pack_565_32_16 ( + pack_1x128_32 ( + over_rev_non_pre_1x128 (ms, expand565_16_1x128 (d)))); + w--; + } + } + +} + +static void +sse2_composite_over_pixbuf_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst, d; + uint32_t *src_line, *src, s; + int dst_stride, src_stride; + int32_t w; + uint32_t opaque, zero; + + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + s = *src++; + d = *dst; + + *dst++ = pack_1x128_32 ( + over_rev_non_pre_1x128 ( + unpack_32_1x128 (s), unpack_32_1x128 (d))); + + w--; + } + + while (w >= 4) + { + xmm_src_hi = load_128_unaligned ((__m128i*)src); + + opaque = is_opaque (xmm_src_hi); + zero = is_zero (xmm_src_hi); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + + if (opaque) + { + invert_colors_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + else if (!zero) + { + xmm_dst_hi = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + over_rev_non_pre_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + w -= 4; + dst += 4; + src += 4; + } + + while (w) + { + s = *src++; + d = *dst; + + *dst++ = pack_1x128_32 ( + over_rev_non_pre_1x128 ( + unpack_32_1x128 (s), unpack_32_1x128 (d))); + + w--; + } + } + +} + +static void +sse2_composite_over_n_8888_0565_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint16_t *dst_line, *dst, d; + uint32_t *mask_line, *mask, m; + int dst_stride, mask_stride; + int w; + uint32_t pack_cmp; + + __m128i xmm_src, xmm_alpha; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + __m128i xmm_dst, xmm_dst0, xmm_dst1, xmm_dst2, xmm_dst3; + + __m128i mmx_src, mmx_alpha, mmx_mask, mmx_dest; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint16_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + xmm_src = expand_pixel_32_1x128 (src); + xmm_alpha = expand_alpha_1x128 (xmm_src); + mmx_src = xmm_src; + mmx_alpha = xmm_alpha; + + while (height--) + { + w = width; + mask = mask_line; + dst = dst_line; + mask_line += mask_stride; + dst_line += dst_stride; + + while (w && ((uintptr_t)dst & 15)) + { + m = *(uint32_t *) mask; + + if (m) + { + d = *dst; + mmx_mask = unpack_32_1x128 (m); + mmx_dest = expand565_16_1x128 (d); + + *dst = pack_565_32_16 ( + pack_1x128_32 ( + in_over_1x128 ( + &mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest))); + } + + w--; + dst++; + mask++; + } + + while (w >= 8) + { + /* First round */ + xmm_mask = load_128_unaligned ((__m128i*)mask); + xmm_dst = load_128_aligned ((__m128i*)dst); + + pack_cmp = _mm_movemask_epi8 ( + _mm_cmpeq_epi32 (xmm_mask, _mm_setzero_si128 ())); + + unpack_565_128_4x128 (xmm_dst, + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3); + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + + /* preload next round */ + xmm_mask = load_128_unaligned ((__m128i*)(mask + 4)); + + /* preload next round */ + if (pack_cmp != 0xffff) + { + in_over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst0, &xmm_dst1); + } + + /* Second round */ + pack_cmp = _mm_movemask_epi8 ( + _mm_cmpeq_epi32 (xmm_mask, _mm_setzero_si128 ())); + + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + + if (pack_cmp != 0xffff) + { + in_over_2x128 (&xmm_src, &xmm_src, + &xmm_alpha, &xmm_alpha, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst2, &xmm_dst3); + } + + save_128_aligned ( + (__m128i*)dst, pack_565_4x128_128 ( + &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3)); + + w -= 8; + dst += 8; + mask += 8; + } + + while (w) + { + m = *(uint32_t *) mask; + + if (m) + { + d = *dst; + mmx_mask = unpack_32_1x128 (m); + mmx_dest = expand565_16_1x128 (d); + + *dst = pack_565_32_16 ( + pack_1x128_32 ( + in_over_1x128 ( + &mmx_src, &mmx_alpha, &mmx_mask, &mmx_dest))); + } + + w--; + dst++; + mask++; + } + } + +} + +static void +sse2_composite_in_n_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + uint32_t d; + uint32_t src; + int32_t w; + + __m128i xmm_alpha; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + xmm_alpha = expand_alpha_1x128 (expand_pixel_32_1x128 (src)); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w && ((uintptr_t)dst & 15)) + { + uint8_t m = *mask++; + d = (uint32_t) *dst; + + *dst++ = (uint8_t) pack_1x128_32 ( + pix_multiply_1x128 ( + pix_multiply_1x128 (xmm_alpha, + unpack_32_1x128 (m)), + unpack_32_1x128 (d))); + w--; + } + + while (w >= 16) + { + xmm_mask = load_128_unaligned ((__m128i*)mask); + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + pix_multiply_2x128 (&xmm_alpha, &xmm_alpha, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + pix_multiply_2x128 (&xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + mask += 16; + dst += 16; + w -= 16; + } + + while (w) + { + uint8_t m = *mask++; + d = (uint32_t) *dst; + + *dst++ = (uint8_t) pack_1x128_32 ( + pix_multiply_1x128 ( + pix_multiply_1x128 ( + xmm_alpha, unpack_32_1x128 (m)), + unpack_32_1x128 (d))); + w--; + } + } + +} + +static void +sse2_composite_in_n_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + int dst_stride; + uint32_t d; + uint32_t src; + int32_t w; + + __m128i xmm_alpha; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + xmm_alpha = expand_alpha_1x128 (expand_pixel_32_1x128 (src)); + + src = src >> 24; + + if (src == 0xff) + return; + + if (src == 0x00) + { + pixman_fill (dest_image->bits.bits, dest_image->bits.rowstride, + 8, dest_x, dest_y, width, height, src); + + return; + } + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + w = width; + + while (w && ((uintptr_t)dst & 15)) + { + d = (uint32_t) *dst; + + *dst++ = (uint8_t) pack_1x128_32 ( + pix_multiply_1x128 ( + xmm_alpha, + unpack_32_1x128 (d))); + w--; + } + + while (w >= 16) + { + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + pix_multiply_2x128 (&xmm_alpha, &xmm_alpha, + &xmm_dst_lo, &xmm_dst_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + dst += 16; + w -= 16; + } + + while (w) + { + d = (uint32_t) *dst; + + *dst++ = (uint8_t) pack_1x128_32 ( + pix_multiply_1x128 ( + xmm_alpha, + unpack_32_1x128 (d))); + w--; + } + } + +} + +static void +sse2_composite_in_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *src_line, *src; + int src_stride, dst_stride; + int32_t w; + uint32_t s, d; + + __m128i xmm_src, xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && ((uintptr_t)dst & 15)) + { + s = (uint32_t) *src++; + d = (uint32_t) *dst; + + *dst++ = (uint8_t) pack_1x128_32 ( + pix_multiply_1x128 ( + unpack_32_1x128 (s), unpack_32_1x128 (d))); + w--; + } + + while (w >= 16) + { + xmm_src = load_128_unaligned ((__m128i*)src); + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + pix_multiply_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_dst_lo, &xmm_dst_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + src += 16; + dst += 16; + w -= 16; + } + + while (w) + { + s = (uint32_t) *src++; + d = (uint32_t) *dst; + + *dst++ = (uint8_t) pack_1x128_32 ( + pix_multiply_1x128 (unpack_32_1x128 (s), unpack_32_1x128 (d))); + w--; + } + } + +} + +static void +sse2_composite_add_n_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + uint32_t src; + uint32_t d; + + __m128i xmm_alpha; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + xmm_alpha = expand_alpha_1x128 (expand_pixel_32_1x128 (src)); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w && ((uintptr_t)dst & 15)) + { + uint8_t m = *mask++; + d = (uint32_t) *dst; + + *dst++ = (uint8_t) pack_1x128_32 ( + _mm_adds_epu16 ( + pix_multiply_1x128 ( + xmm_alpha, unpack_32_1x128 (m)), + unpack_32_1x128 (d))); + w--; + } + + while (w >= 16) + { + xmm_mask = load_128_unaligned ((__m128i*)mask); + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + pix_multiply_2x128 (&xmm_alpha, &xmm_alpha, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + xmm_dst_lo = _mm_adds_epu16 (xmm_mask_lo, xmm_dst_lo); + xmm_dst_hi = _mm_adds_epu16 (xmm_mask_hi, xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + + mask += 16; + dst += 16; + w -= 16; + } + + while (w) + { + uint8_t m = (uint32_t) *mask++; + d = (uint32_t) *dst; + + *dst++ = (uint8_t) pack_1x128_32 ( + _mm_adds_epu16 ( + pix_multiply_1x128 ( + xmm_alpha, unpack_32_1x128 (m)), + unpack_32_1x128 (d))); + + w--; + } + } + +} + +static void +sse2_composite_add_n_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + int dst_stride; + int32_t w; + uint32_t src; + + __m128i xmm_src; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + src >>= 24; + + if (src == 0x00) + return; + + if (src == 0xff) + { + pixman_fill (dest_image->bits.bits, dest_image->bits.rowstride, + 8, dest_x, dest_y, width, height, 0xff); + + return; + } + + src = (src << 24) | (src << 16) | (src << 8) | src; + xmm_src = _mm_set_epi32 (src, src, src, src); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + w = width; + + while (w && ((uintptr_t)dst & 15)) + { + *dst = (uint8_t)_mm_cvtsi128_si32 ( + _mm_adds_epu8 ( + xmm_src, + _mm_cvtsi32_si128 (*dst))); + + w--; + dst++; + } + + while (w >= 16) + { + save_128_aligned ( + (__m128i*)dst, _mm_adds_epu8 (xmm_src, load_128_aligned ((__m128i*)dst))); + + dst += 16; + w -= 16; + } + + while (w) + { + *dst = (uint8_t)_mm_cvtsi128_si32 ( + _mm_adds_epu8 ( + xmm_src, + _mm_cvtsi32_si128 (*dst))); + + w--; + dst++; + } + } + +} + +static void +sse2_composite_add_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + uint16_t t; + + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + src = src_line; + + dst_line += dst_stride; + src_line += src_stride; + w = width; + + /* Small head */ + while (w && (uintptr_t)dst & 3) + { + t = (*dst) + (*src++); + *dst++ = t | (0 - (t >> 8)); + w--; + } + + sse2_combine_add_u (imp, op, + (uint32_t*)dst, (uint32_t*)src, NULL, w >> 2); + + /* Small tail */ + dst += w & 0xfffc; + src += w & 0xfffc; + + w &= 3; + + while (w) + { + t = (*dst) + (*src++); + *dst++ = t | (0 - (t >> 8)); + w--; + } + } + +} + +static void +sse2_composite_add_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + + sse2_combine_add_u (imp, op, dst, src, NULL, width); + } +} + +static void +sse2_composite_add_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst, src; + int dst_stride; + + __m128i xmm_src; + + PIXMAN_IMAGE_GET_LINE (dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + if (src == 0) + return; + + if (src == ~0) + { + pixman_fill (dest_image->bits.bits, dest_image->bits.rowstride, 32, + dest_x, dest_y, width, height, ~0); + + return; + } + + xmm_src = _mm_set_epi32 (src, src, src, src); + while (height--) + { + int w = width; + uint32_t d; + + dst = dst_line; + dst_line += dst_stride; + + while (w && (uintptr_t)dst & 15) + { + d = *dst; + *dst++ = + _mm_cvtsi128_si32 ( _mm_adds_epu8 (xmm_src, _mm_cvtsi32_si128 (d))); + w--; + } + + while (w >= 4) + { + save_128_aligned + ((__m128i*)dst, + _mm_adds_epu8 (xmm_src, load_128_aligned ((__m128i*)dst))); + + dst += 4; + w -= 4; + } + + while (w--) + { + d = *dst; + *dst++ = + _mm_cvtsi128_si32 (_mm_adds_epu8 (xmm_src, + _mm_cvtsi32_si128 (d))); + } + } +} + +static void +sse2_composite_add_n_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint8_t *mask_line, *mask; + int dst_stride, mask_stride; + int32_t w; + uint32_t src; + + __m128i xmm_src; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + if (src == 0) + return; + xmm_src = expand_pixel_32_1x128 (src); + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + w = width; + + while (w && ((uintptr_t)dst & 15)) + { + uint8_t m = *mask++; + if (m) + { + *dst = pack_1x128_32 + (_mm_adds_epu16 + (pix_multiply_1x128 (xmm_src, expand_pixel_8_1x128 (m)), + unpack_32_1x128 (*dst))); + } + dst++; + w--; + } + + while (w >= 4) + { + uint32_t m; + memcpy(&m, mask, sizeof(uint32_t)); + + if (m) + { + __m128i xmm_mask_lo, xmm_mask_hi; + __m128i xmm_dst_lo, xmm_dst_hi; + + __m128i xmm_dst = load_128_aligned ((__m128i*)dst); + __m128i xmm_mask = + _mm_unpacklo_epi8 (unpack_32_1x128(m), + _mm_setzero_si128 ()); + + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + pix_multiply_2x128 (&xmm_src, &xmm_src, + &xmm_mask_lo, &xmm_mask_hi, + &xmm_mask_lo, &xmm_mask_hi); + + xmm_dst_lo = _mm_adds_epu16 (xmm_mask_lo, xmm_dst_lo); + xmm_dst_hi = _mm_adds_epu16 (xmm_mask_hi, xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + w -= 4; + dst += 4; + mask += 4; + } + + while (w) + { + uint8_t m = *mask++; + if (m) + { + *dst = pack_1x128_32 + (_mm_adds_epu16 + (pix_multiply_1x128 (xmm_src, expand_pixel_8_1x128 (m)), + unpack_32_1x128 (*dst))); + } + dst++; + w--; + } + } +} + +static pixman_bool_t +sse2_blt (pixman_implementation_t *imp, + uint32_t * src_bits, + uint32_t * dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height) +{ + uint8_t * src_bytes; + uint8_t * dst_bytes; + int byte_width; + + if (src_bpp != dst_bpp) + return FALSE; + + if (src_bpp == 16) + { + src_stride = src_stride * (int) sizeof (uint32_t) / 2; + dst_stride = dst_stride * (int) sizeof (uint32_t) / 2; + src_bytes =(uint8_t *)(((uint16_t *)src_bits) + src_stride * (src_y) + (src_x)); + dst_bytes = (uint8_t *)(((uint16_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); + byte_width = 2 * width; + src_stride *= 2; + dst_stride *= 2; + } + else if (src_bpp == 32) + { + src_stride = src_stride * (int) sizeof (uint32_t) / 4; + dst_stride = dst_stride * (int) sizeof (uint32_t) / 4; + src_bytes = (uint8_t *)(((uint32_t *)src_bits) + src_stride * (src_y) + (src_x)); + dst_bytes = (uint8_t *)(((uint32_t *)dst_bits) + dst_stride * (dest_y) + (dest_x)); + byte_width = 4 * width; + src_stride *= 4; + dst_stride *= 4; + } + else + { + return FALSE; + } + + while (height--) + { + int w; + uint8_t *s = src_bytes; + uint8_t *d = dst_bytes; + src_bytes += src_stride; + dst_bytes += dst_stride; + w = byte_width; + + while (w >= 2 && ((uintptr_t)d & 3)) + { + memmove(d, s, 2); + w -= 2; + s += 2; + d += 2; + } + + while (w >= 4 && ((uintptr_t)d & 15)) + { + memmove(d, s, 4); + + w -= 4; + s += 4; + d += 4; + } + + while (w >= 64) + { + __m128i xmm0, xmm1, xmm2, xmm3; + + xmm0 = load_128_unaligned ((__m128i*)(s)); + xmm1 = load_128_unaligned ((__m128i*)(s + 16)); + xmm2 = load_128_unaligned ((__m128i*)(s + 32)); + xmm3 = load_128_unaligned ((__m128i*)(s + 48)); + + save_128_aligned ((__m128i*)(d), xmm0); + save_128_aligned ((__m128i*)(d + 16), xmm1); + save_128_aligned ((__m128i*)(d + 32), xmm2); + save_128_aligned ((__m128i*)(d + 48), xmm3); + + s += 64; + d += 64; + w -= 64; + } + + while (w >= 16) + { + save_128_aligned ((__m128i*)d, load_128_unaligned ((__m128i*)s) ); + + w -= 16; + d += 16; + s += 16; + } + + while (w >= 4) + { + memmove(d, s, 4); + + w -= 4; + s += 4; + d += 4; + } + + if (w >= 2) + { + memmove(d, s, 2); + w -= 2; + s += 2; + d += 2; + } + } + + return TRUE; +} + +static void +sse2_composite_copy_area (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + sse2_blt (imp, src_image->bits.bits, + dest_image->bits.bits, + src_image->bits.rowstride, + dest_image->bits.rowstride, + PIXMAN_FORMAT_BPP (src_image->bits.format), + PIXMAN_FORMAT_BPP (dest_image->bits.format), + src_x, src_y, dest_x, dest_y, width, height); +} + +static void +sse2_composite_over_x888_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *src, *src_line, s; + uint32_t *dst, *dst_line, d; + uint8_t *mask, *mask_line; + int src_stride, mask_stride, dst_stride; + int32_t w; + __m128i ms; + + __m128i xmm_src, xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + src = src_line; + src_line += src_stride; + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + + w = width; + + while (w && (uintptr_t)dst & 15) + { + uint8_t m = *mask++; + s = 0xff000000 | *src++; + d = *dst; + ms = unpack_32_1x128 (s); + + if (m != 0xff) + { + __m128i ma = expand_alpha_rev_1x128 (unpack_32_1x128 (m)); + __m128i md = unpack_32_1x128 (d); + + ms = in_over_1x128 (&ms, &mask_00ff, &ma, &md); + } + + *dst++ = pack_1x128_32 (ms); + w--; + } + + while (w >= 4) + { + uint32_t m; + memcpy(&m, mask, sizeof(uint32_t)); + xmm_src = _mm_or_si128 ( + load_128_unaligned ((__m128i*)src), mask_ff000000); + + if (m == 0xffffffff) + { + save_128_aligned ((__m128i*)dst, xmm_src); + } + else + { + xmm_dst = load_128_aligned ((__m128i*)dst); + + xmm_mask = _mm_unpacklo_epi16 (unpack_32_1x128 (m), _mm_setzero_si128()); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_rev_2x128 ( + xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, + &mask_00ff, &mask_00ff, &xmm_mask_lo, &xmm_mask_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + src += 4; + dst += 4; + mask += 4; + w -= 4; + } + + while (w) + { + uint8_t m = *mask++; + + if (m) + { + s = 0xff000000 | *src; + + if (m == 0xff) + { + *dst = s; + } + else + { + __m128i ma, md, ms; + + d = *dst; + + ma = expand_alpha_rev_1x128 (unpack_32_1x128 (m)); + md = unpack_32_1x128 (d); + ms = unpack_32_1x128 (s); + + *dst = pack_1x128_32 (in_over_1x128 (&ms, &mask_00ff, &ma, &md)); + } + + } + + src++; + dst++; + w--; + } + } + +} + +static void +sse2_composite_over_8888_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *src, *src_line, s; + uint32_t *dst, *dst_line, d; + uint8_t *mask, *mask_line; + int src_stride, mask_stride, dst_stride; + int32_t w; + + __m128i xmm_src, xmm_src_lo, xmm_src_hi, xmm_srca_lo, xmm_srca_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + src = src_line; + src_line += src_stride; + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + + w = width; + + while (w && (uintptr_t)dst & 15) + { + uint32_t sa; + uint8_t m = *mask++; + + s = *src++; + d = *dst; + + sa = s >> 24; + + if (m) + { + if (sa == 0xff && m == 0xff) + { + *dst = s; + } + else + { + __m128i ms, md, ma, msa; + + ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); + ms = unpack_32_1x128 (s); + md = unpack_32_1x128 (d); + + msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); + + *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); + } + } + + dst++; + w--; + } + + while (w >= 4) + { + uint32_t m; + memcpy(&m, mask, sizeof(uint32_t)); + + if (m) + { + xmm_src = load_128_unaligned ((__m128i*)src); + + if (m == 0xffffffff && is_opaque (xmm_src)) + { + save_128_aligned ((__m128i *)dst, xmm_src); + } + else + { + xmm_dst = load_128_aligned ((__m128i *)dst); + + xmm_mask = _mm_unpacklo_epi16 (unpack_32_1x128 (m), _mm_setzero_si128()); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi); + expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi, + &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + } + + src += 4; + dst += 4; + mask += 4; + w -= 4; + } + + while (w) + { + uint32_t sa; + uint8_t m = *mask++; + + s = *src++; + d = *dst; + + sa = s >> 24; + + if (m) + { + if (sa == 0xff && m == 0xff) + { + *dst = s; + } + else + { + __m128i ms, md, ma, msa; + + ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); + ms = unpack_32_1x128 (s); + md = unpack_32_1x128 (d); + + msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); + + *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); + } + } + + dst++; + w--; + } + } + +} + +static void +sse2_composite_over_reverse_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src; + uint32_t *dst_line, *dst; + __m128i xmm_src; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_dsta_hi, xmm_dsta_lo; + int dst_stride; + int32_t w; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + xmm_src = expand_pixel_32_1x128 (src); + + while (height--) + { + dst = dst_line; + + dst_line += dst_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + __m128i vd; + + vd = unpack_32_1x128 (*dst); + + *dst = pack_1x128_32 (over_1x128 (vd, expand_alpha_1x128 (vd), + xmm_src)); + w--; + dst++; + } + + while (w >= 4) + { + __m128i tmp_lo, tmp_hi; + + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + expand_alpha_2x128 (xmm_dst_lo, xmm_dst_hi, &xmm_dsta_lo, &xmm_dsta_hi); + + tmp_lo = xmm_src; + tmp_hi = xmm_src; + + over_2x128 (&xmm_dst_lo, &xmm_dst_hi, + &xmm_dsta_lo, &xmm_dsta_hi, + &tmp_lo, &tmp_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (tmp_lo, tmp_hi)); + + w -= 4; + dst += 4; + } + + while (w) + { + __m128i vd; + + vd = unpack_32_1x128 (*dst); + + *dst = pack_1x128_32 (over_1x128 (vd, expand_alpha_1x128 (vd), + xmm_src)); + w--; + dst++; + } + + } + +} + +static void +sse2_composite_over_8888_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *src, *src_line, s; + uint32_t *dst, *dst_line, d; + uint32_t *mask, *mask_line; + uint32_t m; + int src_stride, mask_stride, dst_stride; + int32_t w; + + __m128i xmm_src, xmm_src_lo, xmm_src_hi, xmm_srca_lo, xmm_srca_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + src = src_line; + src_line += src_stride; + dst = dst_line; + dst_line += dst_stride; + mask = mask_line; + mask_line += mask_stride; + + w = width; + + while (w && (uintptr_t)dst & 15) + { + uint32_t sa; + + s = *src++; + m = (*mask++) >> 24; + d = *dst; + + sa = s >> 24; + + if (m) + { + if (sa == 0xff && m == 0xff) + { + *dst = s; + } + else + { + __m128i ms, md, ma, msa; + + ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); + ms = unpack_32_1x128 (s); + md = unpack_32_1x128 (d); + + msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); + + *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); + } + } + + dst++; + w--; + } + + while (w >= 4) + { + xmm_mask = load_128_unaligned ((__m128i*)mask); + + if (!is_transparent (xmm_mask)) + { + xmm_src = load_128_unaligned ((__m128i*)src); + + if (is_opaque (xmm_mask) && is_opaque (xmm_src)) + { + save_128_aligned ((__m128i *)dst, xmm_src); + } + else + { + xmm_dst = load_128_aligned ((__m128i *)dst); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi); + expand_alpha_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi, + &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + } + + src += 4; + dst += 4; + mask += 4; + w -= 4; + } + + while (w) + { + uint32_t sa; + + s = *src++; + m = (*mask++) >> 24; + d = *dst; + + sa = s >> 24; + + if (m) + { + if (sa == 0xff && m == 0xff) + { + *dst = s; + } + else + { + __m128i ms, md, ma, msa; + + ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); + ms = unpack_32_1x128 (s); + md = unpack_32_1x128 (d); + + msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); + + *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); + } + } + + dst++; + w--; + } + } + +} + +/* A variant of 'sse2_combine_over_u' with minor tweaks */ +static force_inline void +scaled_nearest_scanline_sse2_8888_8888_OVER (uint32_t* pd, + const uint32_t* ps, + int32_t w, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t src_width_fixed, + pixman_bool_t fully_transparent_src) +{ + uint32_t s, d; + const uint32_t* pm = NULL; + + __m128i xmm_dst_lo, xmm_dst_hi; + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_alpha_lo, xmm_alpha_hi; + + if (fully_transparent_src) + return; + + /* Align dst on a 16-byte boundary */ + while (w && ((uintptr_t)pd & 15)) + { + d = *pd; + s = combine1 (ps + pixman_fixed_to_int (vx), pm); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + *pd++ = core_combine_over_u_pixel_sse2 (s, d); + if (pm) + pm++; + w--; + } + + while (w >= 4) + { + __m128i tmp; + uint32_t tmp1, tmp2, tmp3, tmp4; + + tmp1 = *(ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp2 = *(ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp3 = *(ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp4 = *(ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + tmp = _mm_set_epi32 (tmp4, tmp3, tmp2, tmp1); + + xmm_src_hi = combine4 ((__m128i*)&tmp, (__m128i*)pm); + + if (is_opaque (xmm_src_hi)) + { + save_128_aligned ((__m128i*)pd, xmm_src_hi); + } + else if (!is_zero (xmm_src_hi)) + { + xmm_dst_hi = load_128_aligned ((__m128i*) pd); + + unpack_128_2x128 (xmm_src_hi, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst_hi, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 ( + xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); + + over_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_dst_lo, &xmm_dst_hi); + + /* rebuid the 4 pixel data and save*/ + save_128_aligned ((__m128i*)pd, + pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + w -= 4; + pd += 4; + if (pm) + pm += 4; + } + + while (w) + { + d = *pd; + s = combine1 (ps + pixman_fixed_to_int (vx), pm); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + *pd++ = core_combine_over_u_pixel_sse2 (s, d); + if (pm) + pm++; + + w--; + } +} + +FAST_NEAREST_MAINLOOP (sse2_8888_8888_cover_OVER, + scaled_nearest_scanline_sse2_8888_8888_OVER, + uint32_t, uint32_t, COVER) +FAST_NEAREST_MAINLOOP (sse2_8888_8888_none_OVER, + scaled_nearest_scanline_sse2_8888_8888_OVER, + uint32_t, uint32_t, NONE) +FAST_NEAREST_MAINLOOP (sse2_8888_8888_pad_OVER, + scaled_nearest_scanline_sse2_8888_8888_OVER, + uint32_t, uint32_t, PAD) +FAST_NEAREST_MAINLOOP (sse2_8888_8888_normal_OVER, + scaled_nearest_scanline_sse2_8888_8888_OVER, + uint32_t, uint32_t, NORMAL) + +static force_inline void +scaled_nearest_scanline_sse2_8888_n_8888_OVER (const uint32_t * mask, + uint32_t * dst, + const uint32_t * src, + int32_t w, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t src_width_fixed, + pixman_bool_t zero_src) +{ + __m128i xmm_mask; + __m128i xmm_src, xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_lo, xmm_alpha_hi; + + if (zero_src || (*mask >> 24) == 0) + return; + + xmm_mask = create_mask_16_128 (*mask >> 24); + + while (w && (uintptr_t)dst & 15) + { + uint32_t s = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + if (s) + { + uint32_t d = *dst; + + __m128i ms = unpack_32_1x128 (s); + __m128i alpha = expand_alpha_1x128 (ms); + __m128i dest = xmm_mask; + __m128i alpha_dst = unpack_32_1x128 (d); + + *dst = pack_1x128_32 ( + in_over_1x128 (&ms, &alpha, &dest, &alpha_dst)); + } + dst++; + w--; + } + + while (w >= 4) + { + uint32_t tmp1, tmp2, tmp3, tmp4; + + tmp1 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp2 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp3 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp4 = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + xmm_src = _mm_set_epi32 (tmp4, tmp3, tmp2, tmp1); + + if (!is_zero (xmm_src)) + { + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_mask, &xmm_mask, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ( + (__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + dst += 4; + w -= 4; + } + + while (w) + { + uint32_t s = *(src + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + if (s) + { + uint32_t d = *dst; + + __m128i ms = unpack_32_1x128 (s); + __m128i alpha = expand_alpha_1x128 (ms); + __m128i mask = xmm_mask; + __m128i dest = unpack_32_1x128 (d); + + *dst = pack_1x128_32 ( + in_over_1x128 (&ms, &alpha, &mask, &dest)); + } + + dst++; + w--; + } + +} + +FAST_NEAREST_MAINLOOP_COMMON (sse2_8888_n_8888_cover_OVER, + scaled_nearest_scanline_sse2_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, COVER, TRUE, TRUE) +FAST_NEAREST_MAINLOOP_COMMON (sse2_8888_n_8888_pad_OVER, + scaled_nearest_scanline_sse2_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, PAD, TRUE, TRUE) +FAST_NEAREST_MAINLOOP_COMMON (sse2_8888_n_8888_none_OVER, + scaled_nearest_scanline_sse2_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, NONE, TRUE, TRUE) +FAST_NEAREST_MAINLOOP_COMMON (sse2_8888_n_8888_normal_OVER, + scaled_nearest_scanline_sse2_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, NORMAL, TRUE, TRUE) + +#if PSHUFD_IS_FAST + +/***********************************************************************************/ + +# define BILINEAR_DECLARE_VARIABLES \ + const __m128i xmm_wt = _mm_set_epi16 (wt, wt, wt, wt, wt, wt, wt, wt); \ + const __m128i xmm_wb = _mm_set_epi16 (wb, wb, wb, wb, wb, wb, wb, wb); \ + const __m128i xmm_addc = _mm_set_epi16 (0, 1, 0, 1, 0, 1, 0, 1); \ + const __m128i xmm_ux1 = _mm_set_epi16 (unit_x, -unit_x, unit_x, -unit_x, \ + unit_x, -unit_x, unit_x, -unit_x); \ + const __m128i xmm_ux4 = _mm_set_epi16 (unit_x * 4, -unit_x * 4, \ + unit_x * 4, -unit_x * 4, \ + unit_x * 4, -unit_x * 4, \ + unit_x * 4, -unit_x * 4); \ + const __m128i xmm_zero = _mm_setzero_si128 (); \ + __m128i xmm_x = _mm_set_epi16 (vx + unit_x * 3, -(vx + 1) - unit_x * 3, \ + vx + unit_x * 2, -(vx + 1) - unit_x * 2, \ + vx + unit_x * 1, -(vx + 1) - unit_x * 1, \ + vx + unit_x * 0, -(vx + 1) - unit_x * 0); \ + __m128i xmm_wh_state; + +#define BILINEAR_INTERPOLATE_ONE_PIXEL_HELPER(pix, phase_) \ +do { \ + int phase = phase_; \ + __m128i xmm_wh, xmm_a, xmm_b; \ + /* fetch 2x2 pixel block into sse2 registers */ \ + __m128i tltr = _mm_loadl_epi64 ((__m128i *)&src_top[vx >> 16]); \ + __m128i blbr = _mm_loadl_epi64 ((__m128i *)&src_bottom[vx >> 16]); \ + vx += unit_x; \ + /* vertical interpolation */ \ + xmm_a = _mm_mullo_epi16 (_mm_unpacklo_epi8 (tltr, xmm_zero), xmm_wt); \ + xmm_b = _mm_mullo_epi16 (_mm_unpacklo_epi8 (blbr, xmm_zero), xmm_wb); \ + xmm_a = _mm_add_epi16 (xmm_a, xmm_b); \ + /* calculate horizontal weights */ \ + if (phase <= 0) \ + { \ + xmm_wh_state = _mm_add_epi16 (xmm_addc, _mm_srli_epi16 (xmm_x, \ + 16 - BILINEAR_INTERPOLATION_BITS)); \ + xmm_x = _mm_add_epi16 (xmm_x, (phase < 0) ? xmm_ux1 : xmm_ux4); \ + phase = 0; \ + } \ + xmm_wh = _mm_shuffle_epi32 (xmm_wh_state, _MM_SHUFFLE (phase, phase, \ + phase, phase)); \ + /* horizontal interpolation */ \ + xmm_a = _mm_madd_epi16 (_mm_unpackhi_epi16 (_mm_shuffle_epi32 ( \ + xmm_a, _MM_SHUFFLE (1, 0, 3, 2)), xmm_a), xmm_wh); \ + /* shift the result */ \ + pix = _mm_srli_epi32 (xmm_a, BILINEAR_INTERPOLATION_BITS * 2); \ +} while (0) + +#else /************************************************************************/ + +# define BILINEAR_DECLARE_VARIABLES \ + const __m128i xmm_wt = _mm_set_epi16 (wt, wt, wt, wt, wt, wt, wt, wt); \ + const __m128i xmm_wb = _mm_set_epi16 (wb, wb, wb, wb, wb, wb, wb, wb); \ + const __m128i xmm_addc = _mm_set_epi16 (0, 1, 0, 1, 0, 1, 0, 1); \ + const __m128i xmm_ux1 = _mm_set_epi16 (unit_x, -unit_x, unit_x, -unit_x, \ + unit_x, -unit_x, unit_x, -unit_x); \ + const __m128i xmm_ux4 = _mm_set_epi16 (unit_x * 4, -unit_x * 4, \ + unit_x * 4, -unit_x * 4, \ + unit_x * 4, -unit_x * 4, \ + unit_x * 4, -unit_x * 4); \ + const __m128i xmm_zero = _mm_setzero_si128 (); \ + __m128i xmm_x = _mm_set_epi16 (vx, -(vx + 1), vx, -(vx + 1), \ + vx, -(vx + 1), vx, -(vx + 1)) + +#define BILINEAR_INTERPOLATE_ONE_PIXEL_HELPER(pix, phase) \ +do { \ + __m128i xmm_wh, xmm_a, xmm_b; \ + /* fetch 2x2 pixel block into sse2 registers */ \ + __m128i tltr = _mm_loadl_epi64 ((__m128i *)&src_top[vx >> 16]); \ + __m128i blbr = _mm_loadl_epi64 ((__m128i *)&src_bottom[vx >> 16]); \ + (void)xmm_ux4; /* suppress warning: unused variable 'xmm_ux4' */ \ + vx += unit_x; \ + /* vertical interpolation */ \ + xmm_a = _mm_mullo_epi16 (_mm_unpacklo_epi8 (tltr, xmm_zero), xmm_wt); \ + xmm_b = _mm_mullo_epi16 (_mm_unpacklo_epi8 (blbr, xmm_zero), xmm_wb); \ + xmm_a = _mm_add_epi16 (xmm_a, xmm_b); \ + /* calculate horizontal weights */ \ + xmm_wh = _mm_add_epi16 (xmm_addc, _mm_srli_epi16 (xmm_x, \ + 16 - BILINEAR_INTERPOLATION_BITS)); \ + xmm_x = _mm_add_epi16 (xmm_x, xmm_ux1); \ + /* horizontal interpolation */ \ + xmm_b = _mm_unpacklo_epi64 (/* any value is fine here */ xmm_b, xmm_a); \ + xmm_a = _mm_madd_epi16 (_mm_unpackhi_epi16 (xmm_b, xmm_a), xmm_wh); \ + /* shift the result */ \ + pix = _mm_srli_epi32 (xmm_a, BILINEAR_INTERPOLATION_BITS * 2); \ +} while (0) + +/***********************************************************************************/ + +#endif + +#define BILINEAR_INTERPOLATE_ONE_PIXEL(pix); \ +do { \ + __m128i xmm_pix; \ + BILINEAR_INTERPOLATE_ONE_PIXEL_HELPER (xmm_pix, -1); \ + xmm_pix = _mm_packs_epi32 (xmm_pix, xmm_pix); \ + xmm_pix = _mm_packus_epi16 (xmm_pix, xmm_pix); \ + pix = _mm_cvtsi128_si32 (xmm_pix); \ +} while(0) + +#define BILINEAR_INTERPOLATE_FOUR_PIXELS(pix); \ +do { \ + __m128i xmm_pix1, xmm_pix2, xmm_pix3, xmm_pix4; \ + BILINEAR_INTERPOLATE_ONE_PIXEL_HELPER (xmm_pix1, 0); \ + BILINEAR_INTERPOLATE_ONE_PIXEL_HELPER (xmm_pix2, 1); \ + BILINEAR_INTERPOLATE_ONE_PIXEL_HELPER (xmm_pix3, 2); \ + BILINEAR_INTERPOLATE_ONE_PIXEL_HELPER (xmm_pix4, 3); \ + xmm_pix1 = _mm_packs_epi32 (xmm_pix1, xmm_pix2); \ + xmm_pix3 = _mm_packs_epi32 (xmm_pix3, xmm_pix4); \ + pix = _mm_packus_epi16 (xmm_pix1, xmm_pix3); \ +} while(0) + +#define BILINEAR_SKIP_ONE_PIXEL() \ +do { \ + vx += unit_x; \ + xmm_x = _mm_add_epi16 (xmm_x, xmm_ux1); \ +} while(0) + +#define BILINEAR_SKIP_FOUR_PIXELS() \ +do { \ + vx += unit_x * 4; \ + xmm_x = _mm_add_epi16 (xmm_x, xmm_ux4); \ +} while(0) + +/***********************************************************************************/ + +static force_inline void +scaled_bilinear_scanline_sse2_8888_8888_SRC (uint32_t * dst, + const uint32_t * mask, + const uint32_t * src_top, + const uint32_t * src_bottom, + int32_t w, + int wt, + int wb, + pixman_fixed_t vx_, + pixman_fixed_t unit_x_, + pixman_fixed_t max_vx, + pixman_bool_t zero_src) +{ + intptr_t vx = vx_; + intptr_t unit_x = unit_x_; + BILINEAR_DECLARE_VARIABLES; + uint32_t pix1, pix2; + + while (w && ((uintptr_t)dst & 15)) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + *dst++ = pix1; + w--; + } + + while ((w -= 4) >= 0) { + __m128i xmm_src; + BILINEAR_INTERPOLATE_FOUR_PIXELS (xmm_src); + _mm_store_si128 ((__m128i *)dst, xmm_src); + dst += 4; + } + + if (w & 2) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + BILINEAR_INTERPOLATE_ONE_PIXEL (pix2); + *dst++ = pix1; + *dst++ = pix2; + } + + if (w & 1) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + *dst = pix1; + } + +} + +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_cover_SRC, + scaled_bilinear_scanline_sse2_8888_8888_SRC, + uint32_t, uint32_t, uint32_t, + COVER, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_pad_SRC, + scaled_bilinear_scanline_sse2_8888_8888_SRC, + uint32_t, uint32_t, uint32_t, + PAD, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_none_SRC, + scaled_bilinear_scanline_sse2_8888_8888_SRC, + uint32_t, uint32_t, uint32_t, + NONE, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_normal_SRC, + scaled_bilinear_scanline_sse2_8888_8888_SRC, + uint32_t, uint32_t, uint32_t, + NORMAL, FLAG_NONE) + +static force_inline void +scaled_bilinear_scanline_sse2_x888_8888_SRC (uint32_t * dst, + const uint32_t * mask, + const uint32_t * src_top, + const uint32_t * src_bottom, + int32_t w, + int wt, + int wb, + pixman_fixed_t vx_, + pixman_fixed_t unit_x_, + pixman_fixed_t max_vx, + pixman_bool_t zero_src) +{ + intptr_t vx = vx_; + intptr_t unit_x = unit_x_; + BILINEAR_DECLARE_VARIABLES; + uint32_t pix1, pix2; + + while (w && ((uintptr_t)dst & 15)) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + *dst++ = pix1 | 0xFF000000; + w--; + } + + while ((w -= 4) >= 0) { + __m128i xmm_src; + BILINEAR_INTERPOLATE_FOUR_PIXELS (xmm_src); + _mm_store_si128 ((__m128i *)dst, _mm_or_si128 (xmm_src, mask_ff000000)); + dst += 4; + } + + if (w & 2) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + BILINEAR_INTERPOLATE_ONE_PIXEL (pix2); + *dst++ = pix1 | 0xFF000000; + *dst++ = pix2 | 0xFF000000; + } + + if (w & 1) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + *dst = pix1 | 0xFF000000; + } +} + +FAST_BILINEAR_MAINLOOP_COMMON (sse2_x888_8888_cover_SRC, + scaled_bilinear_scanline_sse2_x888_8888_SRC, + uint32_t, uint32_t, uint32_t, + COVER, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_x888_8888_pad_SRC, + scaled_bilinear_scanline_sse2_x888_8888_SRC, + uint32_t, uint32_t, uint32_t, + PAD, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_x888_8888_normal_SRC, + scaled_bilinear_scanline_sse2_x888_8888_SRC, + uint32_t, uint32_t, uint32_t, + NORMAL, FLAG_NONE) + +static force_inline void +scaled_bilinear_scanline_sse2_8888_8888_OVER (uint32_t * dst, + const uint32_t * mask, + const uint32_t * src_top, + const uint32_t * src_bottom, + int32_t w, + int wt, + int wb, + pixman_fixed_t vx_, + pixman_fixed_t unit_x_, + pixman_fixed_t max_vx, + pixman_bool_t zero_src) +{ + intptr_t vx = vx_; + intptr_t unit_x = unit_x_; + BILINEAR_DECLARE_VARIABLES; + uint32_t pix1, pix2; + + while (w && ((uintptr_t)dst & 15)) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + + if (pix1) + { + pix2 = *dst; + *dst = core_combine_over_u_pixel_sse2 (pix1, pix2); + } + + w--; + dst++; + } + + while (w >= 4) + { + __m128i xmm_src; + __m128i xmm_src_hi, xmm_src_lo, xmm_dst_hi, xmm_dst_lo; + __m128i xmm_alpha_hi, xmm_alpha_lo; + + BILINEAR_INTERPOLATE_FOUR_PIXELS (xmm_src); + + if (!is_zero (xmm_src)) + { + if (is_opaque (xmm_src)) + { + save_128_aligned ((__m128i *)dst, xmm_src); + } + else + { + __m128i xmm_dst = load_128_aligned ((__m128i *)dst); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi); + over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ((__m128i *)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + } + + w -= 4; + dst += 4; + } + + while (w) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + + if (pix1) + { + pix2 = *dst; + *dst = core_combine_over_u_pixel_sse2 (pix1, pix2); + } + + w--; + dst++; + } +} + +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_cover_OVER, + scaled_bilinear_scanline_sse2_8888_8888_OVER, + uint32_t, uint32_t, uint32_t, + COVER, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_pad_OVER, + scaled_bilinear_scanline_sse2_8888_8888_OVER, + uint32_t, uint32_t, uint32_t, + PAD, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_none_OVER, + scaled_bilinear_scanline_sse2_8888_8888_OVER, + uint32_t, uint32_t, uint32_t, + NONE, FLAG_NONE) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_normal_OVER, + scaled_bilinear_scanline_sse2_8888_8888_OVER, + uint32_t, uint32_t, uint32_t, + NORMAL, FLAG_NONE) + +static force_inline void +scaled_bilinear_scanline_sse2_8888_8_8888_OVER (uint32_t * dst, + const uint8_t * mask, + const uint32_t * src_top, + const uint32_t * src_bottom, + int32_t w, + int wt, + int wb, + pixman_fixed_t vx_, + pixman_fixed_t unit_x_, + pixman_fixed_t max_vx, + pixman_bool_t zero_src) +{ + intptr_t vx = vx_; + intptr_t unit_x = unit_x_; + BILINEAR_DECLARE_VARIABLES; + uint32_t pix1, pix2; + + while (w && ((uintptr_t)dst & 15)) + { + uint32_t sa; + uint8_t m = *mask++; + + if (m) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + sa = pix1 >> 24; + + if (sa == 0xff && m == 0xff) + { + *dst = pix1; + } + else + { + __m128i ms, md, ma, msa; + + pix2 = *dst; + ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); + ms = unpack_32_1x128 (pix1); + md = unpack_32_1x128 (pix2); + + msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); + + *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); + } + } + else + { + BILINEAR_SKIP_ONE_PIXEL (); + } + + w--; + dst++; + } + + while (w >= 4) + { + uint32_t m; + + __m128i xmm_src, xmm_src_lo, xmm_src_hi, xmm_srca_lo, xmm_srca_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_mask, xmm_mask_lo, xmm_mask_hi; + + memcpy(&m, mask, sizeof(uint32_t)); + + if (m) + { + BILINEAR_INTERPOLATE_FOUR_PIXELS (xmm_src); + + if (m == 0xffffffff && is_opaque (xmm_src)) + { + save_128_aligned ((__m128i *)dst, xmm_src); + } + else + { + xmm_dst = load_128_aligned ((__m128i *)dst); + + xmm_mask = _mm_unpacklo_epi16 (unpack_32_1x128 (m), _mm_setzero_si128()); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_mask, &xmm_mask_lo, &xmm_mask_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi); + expand_alpha_rev_2x128 (xmm_mask_lo, xmm_mask_hi, &xmm_mask_lo, &xmm_mask_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, &xmm_srca_lo, &xmm_srca_hi, + &xmm_mask_lo, &xmm_mask_hi, &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + } + else + { + BILINEAR_SKIP_FOUR_PIXELS (); + } + + w -= 4; + dst += 4; + mask += 4; + } + + while (w) + { + uint32_t sa; + uint8_t m = *mask++; + + if (m) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + sa = pix1 >> 24; + + if (sa == 0xff && m == 0xff) + { + *dst = pix1; + } + else + { + __m128i ms, md, ma, msa; + + pix2 = *dst; + ma = expand_alpha_rev_1x128 (load_32_1x128 (m)); + ms = unpack_32_1x128 (pix1); + md = unpack_32_1x128 (pix2); + + msa = expand_alpha_rev_1x128 (load_32_1x128 (sa)); + + *dst = pack_1x128_32 (in_over_1x128 (&ms, &msa, &ma, &md)); + } + } + else + { + BILINEAR_SKIP_ONE_PIXEL (); + } + + w--; + dst++; + } +} + +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_cover_OVER, + scaled_bilinear_scanline_sse2_8888_8_8888_OVER, + uint32_t, uint8_t, uint32_t, + COVER, FLAG_HAVE_NON_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_pad_OVER, + scaled_bilinear_scanline_sse2_8888_8_8888_OVER, + uint32_t, uint8_t, uint32_t, + PAD, FLAG_HAVE_NON_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_none_OVER, + scaled_bilinear_scanline_sse2_8888_8_8888_OVER, + uint32_t, uint8_t, uint32_t, + NONE, FLAG_HAVE_NON_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_normal_OVER, + scaled_bilinear_scanline_sse2_8888_8_8888_OVER, + uint32_t, uint8_t, uint32_t, + NORMAL, FLAG_HAVE_NON_SOLID_MASK) + +static force_inline void +scaled_bilinear_scanline_sse2_8888_n_8888_OVER (uint32_t * dst, + const uint32_t * mask, + const uint32_t * src_top, + const uint32_t * src_bottom, + int32_t w, + int wt, + int wb, + pixman_fixed_t vx_, + pixman_fixed_t unit_x_, + pixman_fixed_t max_vx, + pixman_bool_t zero_src) +{ + intptr_t vx = vx_; + intptr_t unit_x = unit_x_; + BILINEAR_DECLARE_VARIABLES; + uint32_t pix1; + __m128i xmm_mask; + + if (zero_src || (*mask >> 24) == 0) + return; + + xmm_mask = create_mask_16_128 (*mask >> 24); + + while (w && ((uintptr_t)dst & 15)) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + if (pix1) + { + uint32_t d = *dst; + + __m128i ms = unpack_32_1x128 (pix1); + __m128i alpha = expand_alpha_1x128 (ms); + __m128i dest = xmm_mask; + __m128i alpha_dst = unpack_32_1x128 (d); + + *dst = pack_1x128_32 + (in_over_1x128 (&ms, &alpha, &dest, &alpha_dst)); + } + + dst++; + w--; + } + + while (w >= 4) + { + __m128i xmm_src; + BILINEAR_INTERPOLATE_FOUR_PIXELS (xmm_src); + + if (!is_zero (xmm_src)) + { + __m128i xmm_src_lo, xmm_src_hi; + __m128i xmm_dst, xmm_dst_lo, xmm_dst_hi; + __m128i xmm_alpha_lo, xmm_alpha_hi; + + xmm_dst = load_128_aligned ((__m128i*)dst); + + unpack_128_2x128 (xmm_src, &xmm_src_lo, &xmm_src_hi); + unpack_128_2x128 (xmm_dst, &xmm_dst_lo, &xmm_dst_hi); + expand_alpha_2x128 (xmm_src_lo, xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi); + + in_over_2x128 (&xmm_src_lo, &xmm_src_hi, + &xmm_alpha_lo, &xmm_alpha_hi, + &xmm_mask, &xmm_mask, + &xmm_dst_lo, &xmm_dst_hi); + + save_128_aligned + ((__m128i*)dst, pack_2x128_128 (xmm_dst_lo, xmm_dst_hi)); + } + + dst += 4; + w -= 4; + } + + while (w) + { + BILINEAR_INTERPOLATE_ONE_PIXEL (pix1); + if (pix1) + { + uint32_t d = *dst; + + __m128i ms = unpack_32_1x128 (pix1); + __m128i alpha = expand_alpha_1x128 (ms); + __m128i dest = xmm_mask; + __m128i alpha_dst = unpack_32_1x128 (d); + + *dst = pack_1x128_32 + (in_over_1x128 (&ms, &alpha, &dest, &alpha_dst)); + } + + dst++; + w--; + } +} + +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_n_8888_cover_OVER, + scaled_bilinear_scanline_sse2_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, + COVER, FLAG_HAVE_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_n_8888_pad_OVER, + scaled_bilinear_scanline_sse2_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, + PAD, FLAG_HAVE_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_n_8888_none_OVER, + scaled_bilinear_scanline_sse2_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, + NONE, FLAG_HAVE_SOLID_MASK) +FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_n_8888_normal_OVER, + scaled_bilinear_scanline_sse2_8888_n_8888_OVER, + uint32_t, uint32_t, uint32_t, + NORMAL, FLAG_HAVE_SOLID_MASK) + +static const pixman_fast_path_t sse2_fast_paths[] = +{ + /* PIXMAN_OP_OVER */ + PIXMAN_STD_FAST_PATH (OVER, solid, a8, r5g6b5, sse2_composite_over_n_8_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, b5g6r5, sse2_composite_over_n_8_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, sse2_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, sse2_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, r5g6b5, sse2_composite_over_n_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, null, b5g6r5, sse2_composite_over_n_0565), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, sse2_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, sse2_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, sse2_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, sse2_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, r5g6b5, sse2_composite_over_8888_0565), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, b5g6r5, sse2_composite_over_8888_0565), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, sse2_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, sse2_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, sse2_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, sse2_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, a8r8g8b8, sse2_composite_over_8888_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, x8r8g8b8, sse2_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, a8, a8r8g8b8, sse2_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, x8b8g8r8, sse2_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, a8, a8b8g8r8, sse2_composite_over_8888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, x8r8g8b8, sse2_composite_over_x888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, a8, a8r8g8b8, sse2_composite_over_x888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, x8b8g8r8, sse2_composite_over_x888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, a8, a8b8g8r8, sse2_composite_over_x888_8_8888), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, solid, a8r8g8b8, sse2_composite_over_x888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, solid, x8r8g8b8, sse2_composite_over_x888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, solid, a8b8g8r8, sse2_composite_over_x888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, solid, x8b8g8r8, sse2_composite_over_x888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, a8r8g8b8, sse2_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, solid, x8r8g8b8, sse2_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, a8b8g8r8, sse2_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, solid, x8b8g8r8, sse2_composite_over_8888_n_8888), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, sse2_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, sse2_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, sse2_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, sse2_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, r5g6b5, sse2_composite_over_n_8888_0565_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, b5g6r5, sse2_composite_over_n_8888_0565_ca), + PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, a8r8g8b8, sse2_composite_over_pixbuf_8888), + PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, x8r8g8b8, sse2_composite_over_pixbuf_8888), + PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, a8b8g8r8, sse2_composite_over_pixbuf_8888), + PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, x8b8g8r8, sse2_composite_over_pixbuf_8888), + PIXMAN_STD_FAST_PATH (OVER, pixbuf, pixbuf, r5g6b5, sse2_composite_over_pixbuf_0565), + PIXMAN_STD_FAST_PATH (OVER, rpixbuf, rpixbuf, b5g6r5, sse2_composite_over_pixbuf_0565), + PIXMAN_STD_FAST_PATH (OVER, x8r8g8b8, null, x8r8g8b8, sse2_composite_copy_area), + PIXMAN_STD_FAST_PATH (OVER, x8b8g8r8, null, x8b8g8r8, sse2_composite_copy_area), + + /* PIXMAN_OP_OVER_REVERSE */ + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8r8g8b8, sse2_composite_over_reverse_n_8888), + PIXMAN_STD_FAST_PATH (OVER_REVERSE, solid, null, a8b8g8r8, sse2_composite_over_reverse_n_8888), + + /* PIXMAN_OP_ADD */ + PIXMAN_STD_FAST_PATH_CA (ADD, solid, a8r8g8b8, a8r8g8b8, sse2_composite_add_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, sse2_composite_add_8_8), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, sse2_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, sse2_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8, sse2_composite_add_n_8_8), + PIXMAN_STD_FAST_PATH (ADD, solid, null, a8, sse2_composite_add_n_8), + PIXMAN_STD_FAST_PATH (ADD, solid, null, x8r8g8b8, sse2_composite_add_n_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, null, a8r8g8b8, sse2_composite_add_n_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, null, x8b8g8r8, sse2_composite_add_n_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, null, a8b8g8r8, sse2_composite_add_n_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, x8r8g8b8, sse2_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8r8g8b8, sse2_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, x8b8g8r8, sse2_composite_add_n_8_8888), + PIXMAN_STD_FAST_PATH (ADD, solid, a8, a8b8g8r8, sse2_composite_add_n_8_8888), + + /* PIXMAN_OP_SRC */ + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8r8g8b8, sse2_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8r8g8b8, sse2_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, a8b8g8r8, sse2_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, solid, a8, x8b8g8r8, sse2_composite_src_n_8_8888), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, r5g6b5, sse2_composite_src_x888_0565), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, b5g6r5, sse2_composite_src_x888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, r5g6b5, sse2_composite_src_x888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, b5g6r5, sse2_composite_src_x888_0565), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, sse2_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, sse2_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, a8r8g8b8, sse2_composite_copy_area), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, a8b8g8r8, sse2_composite_copy_area), + PIXMAN_STD_FAST_PATH (SRC, a8r8g8b8, null, x8r8g8b8, sse2_composite_copy_area), + PIXMAN_STD_FAST_PATH (SRC, a8b8g8r8, null, x8b8g8r8, sse2_composite_copy_area), + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, x8r8g8b8, sse2_composite_copy_area), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, x8b8g8r8, sse2_composite_copy_area), + PIXMAN_STD_FAST_PATH (SRC, r5g6b5, null, r5g6b5, sse2_composite_copy_area), + PIXMAN_STD_FAST_PATH (SRC, b5g6r5, null, b5g6r5, sse2_composite_copy_area), + + /* PIXMAN_OP_IN */ + PIXMAN_STD_FAST_PATH (IN, a8, null, a8, sse2_composite_in_8_8), + PIXMAN_STD_FAST_PATH (IN, solid, a8, a8, sse2_composite_in_n_8_8), + PIXMAN_STD_FAST_PATH (IN, solid, null, a8, sse2_composite_in_n_8), + + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888), + + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_n_8888), + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_n_8888), + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_n_8888), + SIMPLE_NEAREST_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_n_8888), + + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, sse2_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, sse2_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, sse2_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, sse2_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, sse2_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (SRC, x8b8g8r8, x8b8g8r8, sse2_8888_8888), + + SIMPLE_BILINEAR_FAST_PATH_COVER (SRC, x8r8g8b8, a8r8g8b8, sse2_x888_8888), + SIMPLE_BILINEAR_FAST_PATH_COVER (SRC, x8b8g8r8, a8b8g8r8, sse2_x888_8888), + SIMPLE_BILINEAR_FAST_PATH_PAD (SRC, x8r8g8b8, a8r8g8b8, sse2_x888_8888), + SIMPLE_BILINEAR_FAST_PATH_PAD (SRC, x8b8g8r8, a8b8g8r8, sse2_x888_8888), + SIMPLE_BILINEAR_FAST_PATH_NORMAL (SRC, x8r8g8b8, a8r8g8b8, sse2_x888_8888), + SIMPLE_BILINEAR_FAST_PATH_NORMAL (SRC, x8b8g8r8, a8b8g8r8, sse2_x888_8888), + + SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888), + SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888), + + SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_n_8888), + SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_n_8888), + SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_n_8888), + SIMPLE_BILINEAR_SOLID_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_n_8888), + + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8_8888), + SIMPLE_BILINEAR_A8_MASK_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8_8888), + + { PIXMAN_OP_NONE }, +}; + +static uint32_t * +sse2_fetch_x8r8g8b8 (pixman_iter_t *iter, const uint32_t *mask) +{ + int w = iter->width; + __m128i ff000000 = mask_ff000000; + uint32_t *dst = iter->buffer; + uint32_t *src = (uint32_t *)iter->bits; + + iter->bits += iter->stride; + + while (w && ((uintptr_t)dst) & 0x0f) + { + *dst++ = (*src++) | 0xff000000; + w--; + } + + while (w >= 4) + { + save_128_aligned ( + (__m128i *)dst, _mm_or_si128 ( + load_128_unaligned ((__m128i *)src), ff000000)); + + dst += 4; + src += 4; + w -= 4; + } + + while (w) + { + *dst++ = (*src++) | 0xff000000; + w--; + } + + return iter->buffer; +} + +static uint32_t * +sse2_fetch_r5g6b5 (pixman_iter_t *iter, const uint32_t *mask) +{ + int w = iter->width; + uint32_t *dst = iter->buffer; + uint16_t *src = (uint16_t *)iter->bits; + __m128i ff000000 = mask_ff000000; + + iter->bits += iter->stride; + + while (w && ((uintptr_t)dst) & 0x0f) + { + uint16_t s = *src++; + + *dst++ = convert_0565_to_8888 (s); + w--; + } + + while (w >= 8) + { + __m128i lo, hi, s; + + s = _mm_loadu_si128 ((__m128i *)src); + + lo = unpack_565_to_8888 (_mm_unpacklo_epi16 (s, _mm_setzero_si128 ())); + hi = unpack_565_to_8888 (_mm_unpackhi_epi16 (s, _mm_setzero_si128 ())); + + save_128_aligned ((__m128i *)(dst + 0), _mm_or_si128 (lo, ff000000)); + save_128_aligned ((__m128i *)(dst + 4), _mm_or_si128 (hi, ff000000)); + + dst += 8; + src += 8; + w -= 8; + } + + while (w) + { + uint16_t s = *src++; + + *dst++ = convert_0565_to_8888 (s); + w--; + } + + return iter->buffer; +} + +static uint32_t * +sse2_fetch_a8 (pixman_iter_t *iter, const uint32_t *mask) +{ + int w = iter->width; + uint32_t *dst = iter->buffer; + uint8_t *src = iter->bits; + __m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6; + + iter->bits += iter->stride; + + while (w && (((uintptr_t)dst) & 15)) + { + *dst++ = (uint32_t)(*(src++)) << 24; + w--; + } + + while (w >= 16) + { + xmm0 = _mm_loadu_si128((__m128i *)src); + + xmm1 = _mm_unpacklo_epi8 (_mm_setzero_si128(), xmm0); + xmm2 = _mm_unpackhi_epi8 (_mm_setzero_si128(), xmm0); + xmm3 = _mm_unpacklo_epi16 (_mm_setzero_si128(), xmm1); + xmm4 = _mm_unpackhi_epi16 (_mm_setzero_si128(), xmm1); + xmm5 = _mm_unpacklo_epi16 (_mm_setzero_si128(), xmm2); + xmm6 = _mm_unpackhi_epi16 (_mm_setzero_si128(), xmm2); + + _mm_store_si128(((__m128i *)(dst + 0)), xmm3); + _mm_store_si128(((__m128i *)(dst + 4)), xmm4); + _mm_store_si128(((__m128i *)(dst + 8)), xmm5); + _mm_store_si128(((__m128i *)(dst + 12)), xmm6); + + dst += 16; + src += 16; + w -= 16; + } + + while (w) + { + *dst++ = (uint32_t)(*(src++)) << 24; + w--; + } + + return iter->buffer; +} + +#define IMAGE_FLAGS \ + (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \ + FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) + +static const pixman_iter_info_t sse2_iters[] = +{ + { PIXMAN_x8r8g8b8, IMAGE_FLAGS, ITER_NARROW, + _pixman_iter_init_bits_stride, sse2_fetch_x8r8g8b8, NULL + }, + { PIXMAN_r5g6b5, IMAGE_FLAGS, ITER_NARROW, + _pixman_iter_init_bits_stride, sse2_fetch_r5g6b5, NULL + }, + { PIXMAN_a8, IMAGE_FLAGS, ITER_NARROW, + _pixman_iter_init_bits_stride, sse2_fetch_a8, NULL + }, + { PIXMAN_null }, +}; + +#if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) +__attribute__((__force_align_arg_pointer__)) +#endif +pixman_implementation_t * +_pixman_implementation_create_sse2 (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = _pixman_implementation_create (fallback, sse2_fast_paths); + + /* SSE2 constants */ + mask_565_r = create_mask_2x32_128 (0x00f80000, 0x00f80000); + mask_565_g1 = create_mask_2x32_128 (0x00070000, 0x00070000); + mask_565_g2 = create_mask_2x32_128 (0x000000e0, 0x000000e0); + mask_565_b = create_mask_2x32_128 (0x0000001f, 0x0000001f); + mask_red = create_mask_2x32_128 (0x00f80000, 0x00f80000); + mask_green = create_mask_2x32_128 (0x0000fc00, 0x0000fc00); + mask_blue = create_mask_2x32_128 (0x000000f8, 0x000000f8); + mask_565_fix_rb = create_mask_2x32_128 (0x00e000e0, 0x00e000e0); + mask_565_fix_g = create_mask_2x32_128 (0x0000c000, 0x0000c000); + mask_0080 = create_mask_16_128 (0x0080); + mask_00ff = create_mask_16_128 (0x00ff); + mask_0101 = create_mask_16_128 (0x0101); + mask_ffff = create_mask_16_128 (0xffff); + mask_ff000000 = create_mask_2x32_128 (0xff000000, 0xff000000); + mask_alpha = create_mask_2x32_128 (0x00ff0000, 0x00000000); + mask_565_rb = create_mask_2x32_128 (0x00f800f8, 0x00f800f8); + mask_565_pack_multiplier = create_mask_2x32_128 (0x20000004, 0x20000004); + + /* Set up function pointers */ + imp->combine_32[PIXMAN_OP_OVER] = sse2_combine_over_u; + imp->combine_32[PIXMAN_OP_OVER_REVERSE] = sse2_combine_over_reverse_u; + imp->combine_32[PIXMAN_OP_IN] = sse2_combine_in_u; + imp->combine_32[PIXMAN_OP_IN_REVERSE] = sse2_combine_in_reverse_u; + imp->combine_32[PIXMAN_OP_OUT] = sse2_combine_out_u; + imp->combine_32[PIXMAN_OP_OUT_REVERSE] = sse2_combine_out_reverse_u; + imp->combine_32[PIXMAN_OP_ATOP] = sse2_combine_atop_u; + imp->combine_32[PIXMAN_OP_ATOP_REVERSE] = sse2_combine_atop_reverse_u; + imp->combine_32[PIXMAN_OP_XOR] = sse2_combine_xor_u; + imp->combine_32[PIXMAN_OP_ADD] = sse2_combine_add_u; + + imp->combine_32[PIXMAN_OP_SATURATE] = sse2_combine_saturate_u; + + imp->combine_32_ca[PIXMAN_OP_SRC] = sse2_combine_src_ca; + imp->combine_32_ca[PIXMAN_OP_OVER] = sse2_combine_over_ca; + imp->combine_32_ca[PIXMAN_OP_OVER_REVERSE] = sse2_combine_over_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_IN] = sse2_combine_in_ca; + imp->combine_32_ca[PIXMAN_OP_IN_REVERSE] = sse2_combine_in_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_OUT] = sse2_combine_out_ca; + imp->combine_32_ca[PIXMAN_OP_OUT_REVERSE] = sse2_combine_out_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_ATOP] = sse2_combine_atop_ca; + imp->combine_32_ca[PIXMAN_OP_ATOP_REVERSE] = sse2_combine_atop_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_XOR] = sse2_combine_xor_ca; + imp->combine_32_ca[PIXMAN_OP_ADD] = sse2_combine_add_ca; + + imp->blt = sse2_blt; + imp->fill = sse2_fill; + + imp->iter_info = sse2_iters; + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-ssse3.c b/gfx/cairo/libpixman/src/pixman-ssse3.c new file mode 100644 index 0000000000..680d6b95a0 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-ssse3.c @@ -0,0 +1,351 @@ +/* + * Copyright © 2013 Soren Sandmann Pedersen + * Copyright © 2013 Red Hat, Inc. + * + * 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. + * + * Author: Soren Sandmann (soren.sandmann@gmail.com) + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include "pixman-private.h" +#include "pixman-inlines.h" + +typedef struct +{ + int y; + uint64_t * buffer; +} line_t; + +typedef struct +{ + line_t lines[2]; + pixman_fixed_t y; + pixman_fixed_t x; + uint64_t data[1]; +} bilinear_info_t; + +static void +ssse3_fetch_horizontal (bits_image_t *image, line_t *line, + int y, pixman_fixed_t x, pixman_fixed_t ux, int n) +{ + uint32_t *bits = image->bits + y * image->rowstride; + __m128i vx = _mm_set_epi16 ( + - (x + 1), x, - (x + 1), x, + - (x + ux + 1), x + ux, - (x + ux + 1), x + ux); + __m128i vux = _mm_set_epi16 ( + - 2 * ux, 2 * ux, - 2 * ux, 2 * ux, + - 2 * ux, 2 * ux, - 2 * ux, 2 * ux); + __m128i vaddc = _mm_set_epi16 (1, 0, 1, 0, 1, 0, 1, 0); + __m128i *b = (__m128i *)line->buffer; + __m128i vrl0, vrl1; + + while ((n -= 2) >= 0) + { + __m128i vw, vr, s; + + vrl1 = _mm_loadl_epi64 ( + (__m128i *)(bits + pixman_fixed_to_int (x + ux))); + /* vrl1: R1, L1 */ + + final_pixel: + vrl0 = _mm_loadl_epi64 ( + (__m128i *)(bits + pixman_fixed_to_int (x))); + /* vrl0: R0, L0 */ + + /* The weights are based on vx which is a vector of + * + * - (x + 1), x, - (x + 1), x, + * - (x + ux + 1), x + ux, - (x + ux + 1), x + ux + * + * so the 16 bit weights end up like this: + * + * iw0, w0, iw0, w0, iw1, w1, iw1, w1 + * + * and after shifting and packing, we get these bytes: + * + * iw0, w0, iw0, w0, iw1, w1, iw1, w1, + * iw0, w0, iw0, w0, iw1, w1, iw1, w1, + * + * which means the first and the second input pixel + * have to be interleaved like this: + * + * la0, ra0, lr0, rr0, la1, ra1, lr1, rr1, + * lg0, rg0, lb0, rb0, lg1, rg1, lb1, rb1 + * + * before maddubsw can be used. + */ + + vw = _mm_add_epi16 ( + vaddc, _mm_srli_epi16 (vx, 16 - BILINEAR_INTERPOLATION_BITS)); + /* vw: iw0, w0, iw0, w0, iw1, w1, iw1, w1 + */ + + vw = _mm_packus_epi16 (vw, vw); + /* vw: iw0, w0, iw0, w0, iw1, w1, iw1, w1, + * iw0, w0, iw0, w0, iw1, w1, iw1, w1 + */ + vx = _mm_add_epi16 (vx, vux); + + x += 2 * ux; + + vr = _mm_unpacklo_epi16 (vrl1, vrl0); + /* vr: rar0, rar1, rgb0, rgb1, lar0, lar1, lgb0, lgb1 */ + + s = _mm_shuffle_epi32 (vr, _MM_SHUFFLE (1, 0, 3, 2)); + /* s: lar0, lar1, lgb0, lgb1, rar0, rar1, rgb0, rgb1 */ + + vr = _mm_unpackhi_epi8 (vr, s); + /* vr: la0, ra0, lr0, rr0, la1, ra1, lr1, rr1, + * lg0, rg0, lb0, rb0, lg1, rg1, lb1, rb1 + */ + + vr = _mm_maddubs_epi16 (vr, vw); + + /* When the weight is 0, the inverse weight is + * 128 which can't be represented in a signed byte. + * As a result maddubsw computes the following: + * + * r = l * -128 + r * 0 + * + * rather than the desired + * + * r = l * 128 + r * 0 + * + * We fix this by taking the absolute value of the + * result. + */ + vr = _mm_abs_epi16 (vr); + + /* vr: A0, R0, A1, R1, G0, B0, G1, B1 */ + _mm_store_si128 (b++, vr); + } + + if (n == -1) + { + vrl1 = _mm_setzero_si128(); + goto final_pixel; + } + + line->y = y; +} + +static uint32_t * +ssse3_fetch_bilinear_cover (pixman_iter_t *iter, const uint32_t *mask) +{ + pixman_fixed_t fx, ux; + bilinear_info_t *info = iter->data; + line_t *line0, *line1; + int y0, y1; + int32_t dist_y; + __m128i vw; + int i; + + fx = info->x; + ux = iter->image->common.transform->matrix[0][0]; + + y0 = pixman_fixed_to_int (info->y); + y1 = y0 + 1; + + line0 = &info->lines[y0 & 0x01]; + line1 = &info->lines[y1 & 0x01]; + + if (line0->y != y0) + { + ssse3_fetch_horizontal ( + &iter->image->bits, line0, y0, fx, ux, iter->width); + } + + if (line1->y != y1) + { + ssse3_fetch_horizontal ( + &iter->image->bits, line1, y1, fx, ux, iter->width); + } + + dist_y = pixman_fixed_to_bilinear_weight (info->y); + dist_y <<= (16 - BILINEAR_INTERPOLATION_BITS); + + vw = _mm_set_epi16 ( + dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y, dist_y); + + for (i = 0; i + 3 < iter->width; i += 4) + { + __m128i top0 = _mm_load_si128 ((__m128i *)(line0->buffer + i)); + __m128i bot0 = _mm_load_si128 ((__m128i *)(line1->buffer + i)); + __m128i top1 = _mm_load_si128 ((__m128i *)(line0->buffer + i + 2)); + __m128i bot1 = _mm_load_si128 ((__m128i *)(line1->buffer + i + 2)); + __m128i r0, r1, tmp, p; + + r0 = _mm_mulhi_epu16 ( + _mm_sub_epi16 (bot0, top0), vw); + tmp = _mm_cmplt_epi16 (bot0, top0); + tmp = _mm_and_si128 (tmp, vw); + r0 = _mm_sub_epi16 (r0, tmp); + r0 = _mm_add_epi16 (r0, top0); + r0 = _mm_srli_epi16 (r0, BILINEAR_INTERPOLATION_BITS); + /* r0: A0 R0 A1 R1 G0 B0 G1 B1 */ + r0 = _mm_shuffle_epi32 (r0, _MM_SHUFFLE (2, 0, 3, 1)); + /* r0: A1 R1 G1 B1 A0 R0 G0 B0 */ + + r1 = _mm_mulhi_epu16 ( + _mm_sub_epi16 (bot1, top1), vw); + tmp = _mm_cmplt_epi16 (bot1, top1); + tmp = _mm_and_si128 (tmp, vw); + r1 = _mm_sub_epi16 (r1, tmp); + r1 = _mm_add_epi16 (r1, top1); + r1 = _mm_srli_epi16 (r1, BILINEAR_INTERPOLATION_BITS); + r1 = _mm_shuffle_epi32 (r1, _MM_SHUFFLE (2, 0, 3, 1)); + /* r1: A3 R3 G3 B3 A2 R2 G2 B2 */ + + p = _mm_packus_epi16 (r0, r1); + + _mm_storeu_si128 ((__m128i *)(iter->buffer + i), p); + } + + while (i < iter->width) + { + __m128i top0 = _mm_load_si128 ((__m128i *)(line0->buffer + i)); + __m128i bot0 = _mm_load_si128 ((__m128i *)(line1->buffer + i)); + __m128i r0, tmp, p; + + r0 = _mm_mulhi_epu16 ( + _mm_sub_epi16 (bot0, top0), vw); + tmp = _mm_cmplt_epi16 (bot0, top0); + tmp = _mm_and_si128 (tmp, vw); + r0 = _mm_sub_epi16 (r0, tmp); + r0 = _mm_add_epi16 (r0, top0); + r0 = _mm_srli_epi16 (r0, BILINEAR_INTERPOLATION_BITS); + /* r0: A0 R0 A1 R1 G0 B0 G1 B1 */ + r0 = _mm_shuffle_epi32 (r0, _MM_SHUFFLE (2, 0, 3, 1)); + /* r0: A1 R1 G1 B1 A0 R0 G0 B0 */ + + p = _mm_packus_epi16 (r0, r0); + + if (iter->width - i == 1) + { + *(uint32_t *)(iter->buffer + i) = _mm_cvtsi128_si32 (p); + i++; + } + else + { + _mm_storel_epi64 ((__m128i *)(iter->buffer + i), p); + i += 2; + } + } + + info->y += iter->image->common.transform->matrix[1][1]; + + return iter->buffer; +} + +static void +ssse3_bilinear_cover_iter_fini (pixman_iter_t *iter) +{ + free (iter->data); +} + +static void +ssse3_bilinear_cover_iter_init (pixman_iter_t *iter, const pixman_iter_info_t *iter_info) +{ + int width = iter->width; + bilinear_info_t *info; + pixman_vector_t v; + + /* Reference point is the center of the pixel */ + v.vector[0] = pixman_int_to_fixed (iter->x) + pixman_fixed_1 / 2; + v.vector[1] = pixman_int_to_fixed (iter->y) + pixman_fixed_1 / 2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point_3d (iter->image->common.transform, &v)) + goto fail; + + info = malloc (sizeof (*info) + (2 * width - 1) * sizeof (uint64_t) + 64); + if (!info) + goto fail; + + info->x = v.vector[0] - pixman_fixed_1 / 2; + info->y = v.vector[1] - pixman_fixed_1 / 2; + +#define ALIGN(addr) \ + ((void *)((((uintptr_t)(addr)) + 15) & (~15))) + + /* It is safe to set the y coordinates to -1 initially + * because COVER_CLIP_BILINEAR ensures that we will only + * be asked to fetch lines in the [0, height) interval + */ + info->lines[0].y = -1; + info->lines[0].buffer = ALIGN (&(info->data[0])); + info->lines[1].y = -1; + info->lines[1].buffer = ALIGN (info->lines[0].buffer + width); + + iter->get_scanline = ssse3_fetch_bilinear_cover; + iter->fini = ssse3_bilinear_cover_iter_fini; + + iter->data = info; + return; + +fail: + /* Something went wrong, either a bad matrix or OOM; in such cases, + * we don't guarantee any particular rendering. + */ + _pixman_log_error ( + FUNC, "Allocation failure or bad matrix, skipping rendering\n"); + + iter->get_scanline = _pixman_iter_get_scanline_noop; + iter->fini = NULL; +} + +static const pixman_iter_info_t ssse3_iters[] = +{ + { PIXMAN_a8r8g8b8, + (FAST_PATH_STANDARD_FLAGS | + FAST_PATH_SCALE_TRANSFORM | + FAST_PATH_BILINEAR_FILTER | + FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR), + ITER_NARROW | ITER_SRC, + ssse3_bilinear_cover_iter_init, + NULL, NULL + }, + + { PIXMAN_null }, +}; + +static const pixman_fast_path_t ssse3_fast_paths[] = +{ + { PIXMAN_OP_NONE }, +}; + +pixman_implementation_t * +_pixman_implementation_create_ssse3 (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = + _pixman_implementation_create (fallback, ssse3_fast_paths); + + imp->iter_info = ssse3_iters; + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-timer.c b/gfx/cairo/libpixman/src/pixman-timer.c new file mode 100644 index 0000000000..f5ae18e89f --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-timer.c @@ -0,0 +1,66 @@ +/* + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "pixman-private.h" + +#ifdef PIXMAN_TIMERS + +static pixman_timer_t *timers; + +static void +dump_timers (void) +{ + pixman_timer_t *timer; + + for (timer = timers; timer != NULL; timer = timer->next) + { + printf ("%s: total: %llu n: %llu avg: %f\n", + timer->name, + timer->total, + timer->n_times, + timer->total / (double)timer->n_times); + } +} + +void +pixman_timer_register (pixman_timer_t *timer) +{ + static int initialized; + + int atexit (void (*function)(void)); + + if (!initialized) + { + atexit (dump_timers); + initialized = 1; + } + + timer->next = timers; + timers = timer; +} + +#endif diff --git a/gfx/cairo/libpixman/src/pixman-trap.c b/gfx/cairo/libpixman/src/pixman-trap.c new file mode 100644 index 0000000000..7560405ee2 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-trap.c @@ -0,0 +1,711 @@ +/* + * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc. + * Copyright © 2004 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "pixman-private.h" + +/* + * Compute the smallest value greater than or equal to y which is on a + * grid row. + */ + +PIXMAN_EXPORT pixman_fixed_t +pixman_sample_ceil_y (pixman_fixed_t y, int n) +{ + pixman_fixed_t f = pixman_fixed_frac (y); + pixman_fixed_t i = pixman_fixed_floor (y); + + f = DIV (f - Y_FRAC_FIRST (n) + (STEP_Y_SMALL (n) - pixman_fixed_e), STEP_Y_SMALL (n)) * STEP_Y_SMALL (n) + + Y_FRAC_FIRST (n); + + if (f > Y_FRAC_LAST (n)) + { + if (pixman_fixed_to_int (i) == 0x7fff) + { + f = 0xffff; /* saturate */ + } + else + { + f = Y_FRAC_FIRST (n); + i += pixman_fixed_1; + } + } + return (i | f); +} + +/* + * Compute the largest value strictly less than y which is on a + * grid row. + */ +PIXMAN_EXPORT pixman_fixed_t +pixman_sample_floor_y (pixman_fixed_t y, + int n) +{ + pixman_fixed_t f = pixman_fixed_frac (y); + pixman_fixed_t i = pixman_fixed_floor (y); + + f = DIV (f - pixman_fixed_e - Y_FRAC_FIRST (n), STEP_Y_SMALL (n)) * STEP_Y_SMALL (n) + + Y_FRAC_FIRST (n); + + if (f < Y_FRAC_FIRST (n)) + { + if (pixman_fixed_to_int (i) == 0xffff8000) + { + f = 0; /* saturate */ + } + else + { + f = Y_FRAC_LAST (n); + i -= pixman_fixed_1; + } + } + return (i | f); +} + +/* + * Step an edge by any amount (including negative values) + */ +PIXMAN_EXPORT void +pixman_edge_step (pixman_edge_t *e, + int n) +{ + pixman_fixed_48_16_t ne; + + e->x += n * e->stepx; + + ne = e->e + n * (pixman_fixed_48_16_t) e->dx; + + if (n >= 0) + { + if (ne > 0) + { + int nx = (ne + e->dy - 1) / e->dy; + e->e = ne - nx * (pixman_fixed_48_16_t) e->dy; + e->x += nx * e->signdx; + } + } + else + { + if (ne <= -e->dy) + { + int nx = (-ne) / e->dy; + e->e = ne + nx * (pixman_fixed_48_16_t) e->dy; + e->x -= nx * e->signdx; + } + } +} + +/* + * A private routine to initialize the multi-step + * elements of an edge structure + */ +static void +_pixman_edge_multi_init (pixman_edge_t * e, + int n, + pixman_fixed_t *stepx_p, + pixman_fixed_t *dx_p) +{ + pixman_fixed_t stepx; + pixman_fixed_48_16_t ne; + + ne = n * (pixman_fixed_48_16_t) e->dx; + stepx = n * e->stepx; + + if (ne > 0) + { + int nx = ne / e->dy; + ne -= nx * (pixman_fixed_48_16_t)e->dy; + stepx += nx * e->signdx; + } + + *dx_p = ne; + *stepx_p = stepx; +} + +/* + * Initialize one edge structure given the line endpoints and a + * starting y value + */ +PIXMAN_EXPORT void +pixman_edge_init (pixman_edge_t *e, + int n, + pixman_fixed_t y_start, + pixman_fixed_t x_top, + pixman_fixed_t y_top, + pixman_fixed_t x_bot, + pixman_fixed_t y_bot) +{ + pixman_fixed_t dx, dy; + + e->x = x_top; + e->e = 0; + dx = x_bot - x_top; + dy = y_bot - y_top; + e->dy = dy; + e->dx = 0; + + if (dy) + { + if (dx >= 0) + { + e->signdx = 1; + e->stepx = dx / dy; + e->dx = dx % dy; + e->e = -dy; + } + else + { + e->signdx = -1; + e->stepx = -(-dx / dy); + e->dx = -dx % dy; + e->e = 0; + } + + _pixman_edge_multi_init (e, STEP_Y_SMALL (n), + &e->stepx_small, &e->dx_small); + + _pixman_edge_multi_init (e, STEP_Y_BIG (n), + &e->stepx_big, &e->dx_big); + } + pixman_edge_step (e, y_start - y_top); +} + +/* + * Initialize one edge structure given a line, starting y value + * and a pixel offset for the line + */ +PIXMAN_EXPORT void +pixman_line_fixed_edge_init (pixman_edge_t * e, + int n, + pixman_fixed_t y, + const pixman_line_fixed_t *line, + int x_off, + int y_off) +{ + pixman_fixed_t x_off_fixed = pixman_int_to_fixed (x_off); + pixman_fixed_t y_off_fixed = pixman_int_to_fixed (y_off); + const pixman_point_fixed_t *top, *bot; + + if (line->p1.y <= line->p2.y) + { + top = &line->p1; + bot = &line->p2; + } + else + { + top = &line->p2; + bot = &line->p1; + } + + pixman_edge_init (e, n, y, + top->x + x_off_fixed, + top->y + y_off_fixed, + bot->x + x_off_fixed, + bot->y + y_off_fixed); +} + +PIXMAN_EXPORT void +pixman_add_traps (pixman_image_t * image, + int16_t x_off, + int16_t y_off, + int ntrap, + const pixman_trap_t *traps) +{ + int bpp; + int height; + + pixman_fixed_t x_off_fixed; + pixman_fixed_t y_off_fixed; + pixman_edge_t l, r; + pixman_fixed_t t, b; + + _pixman_image_validate (image); + + height = image->bits.height; + bpp = PIXMAN_FORMAT_BPP (image->bits.format); + + x_off_fixed = pixman_int_to_fixed (x_off); + y_off_fixed = pixman_int_to_fixed (y_off); + + while (ntrap--) + { + t = traps->top.y + y_off_fixed; + if (t < 0) + t = 0; + t = pixman_sample_ceil_y (t, bpp); + + b = traps->bot.y + y_off_fixed; + if (pixman_fixed_to_int (b) >= height) + b = pixman_int_to_fixed (height) - 1; + b = pixman_sample_floor_y (b, bpp); + + if (b >= t) + { + /* initialize edge walkers */ + pixman_edge_init (&l, bpp, t, + traps->top.l + x_off_fixed, + traps->top.y + y_off_fixed, + traps->bot.l + x_off_fixed, + traps->bot.y + y_off_fixed); + + pixman_edge_init (&r, bpp, t, + traps->top.r + x_off_fixed, + traps->top.y + y_off_fixed, + traps->bot.r + x_off_fixed, + traps->bot.y + y_off_fixed); + + pixman_rasterize_edges (image, &l, &r, t, b); + } + + traps++; + } +} + +#if 0 +static void +dump_image (pixman_image_t *image, + const char * title) +{ + int i, j; + + if (!image->type == BITS) + printf ("%s is not a regular image\n", title); + + if (!image->bits.format == PIXMAN_a8) + printf ("%s is not an alpha mask\n", title); + + printf ("\n\n\n%s: \n", title); + + for (i = 0; i < image->bits.height; ++i) + { + uint8_t *line = + (uint8_t *)&(image->bits.bits[i * image->bits.rowstride]); + + for (j = 0; j < image->bits.width; ++j) + printf ("%c", line[j] ? '#' : ' '); + + printf ("\n"); + } +} +#endif + +PIXMAN_EXPORT void +pixman_add_trapezoids (pixman_image_t * image, + int16_t x_off, + int y_off, + int ntraps, + const pixman_trapezoid_t *traps) +{ + int i; + +#if 0 + dump_image (image, "before"); +#endif + + for (i = 0; i < ntraps; ++i) + { + const pixman_trapezoid_t *trap = &(traps[i]); + + if (!pixman_trapezoid_valid (trap)) + continue; + + pixman_rasterize_trapezoid (image, trap, x_off, y_off); + } + +#if 0 + dump_image (image, "after"); +#endif +} + +PIXMAN_EXPORT void +pixman_rasterize_trapezoid (pixman_image_t * image, + const pixman_trapezoid_t *trap, + int x_off, + int y_off) +{ + int bpp; + int height; + + pixman_fixed_t y_off_fixed; + pixman_edge_t l, r; + pixman_fixed_t t, b; + + return_if_fail (image->type == BITS); + + _pixman_image_validate (image); + + if (!pixman_trapezoid_valid (trap)) + return; + + height = image->bits.height; + bpp = PIXMAN_FORMAT_BPP (image->bits.format); + + y_off_fixed = pixman_int_to_fixed (y_off); + + t = trap->top + y_off_fixed; + if (t < 0) + t = 0; + t = pixman_sample_ceil_y (t, bpp); + + b = trap->bottom + y_off_fixed; + if (pixman_fixed_to_int (b) >= height) + b = pixman_int_to_fixed (height) - 1; + b = pixman_sample_floor_y (b, bpp); + + if (b >= t) + { + /* initialize edge walkers */ + pixman_line_fixed_edge_init (&l, bpp, t, &trap->left, x_off, y_off); + pixman_line_fixed_edge_init (&r, bpp, t, &trap->right, x_off, y_off); + + pixman_rasterize_edges (image, &l, &r, t, b); + } +} + +static const pixman_bool_t zero_src_has_no_effect[PIXMAN_N_OPERATORS] = +{ + FALSE, /* Clear 0 0 */ + FALSE, /* Src 1 0 */ + TRUE, /* Dst 0 1 */ + TRUE, /* Over 1 1-Aa */ + TRUE, /* OverReverse 1-Ab 1 */ + FALSE, /* In Ab 0 */ + FALSE, /* InReverse 0 Aa */ + FALSE, /* Out 1-Ab 0 */ + TRUE, /* OutReverse 0 1-Aa */ + TRUE, /* Atop Ab 1-Aa */ + FALSE, /* AtopReverse 1-Ab Aa */ + TRUE, /* Xor 1-Ab 1-Aa */ + TRUE, /* Add 1 1 */ +}; + +static pixman_bool_t +get_trap_extents (pixman_op_t op, pixman_image_t *dest, + const pixman_trapezoid_t *traps, int n_traps, + pixman_box32_t *box) +{ + int i; + + /* When the operator is such that a zero source has an + * effect on the underlying image, we have to + * composite across the entire destination + */ + if (!zero_src_has_no_effect [op]) + { + box->x1 = 0; + box->y1 = 0; + box->x2 = dest->bits.width; + box->y2 = dest->bits.height; + return TRUE; + } + + box->x1 = INT32_MAX; + box->y1 = INT32_MAX; + box->x2 = INT32_MIN; + box->y2 = INT32_MIN; + + for (i = 0; i < n_traps; ++i) + { + const pixman_trapezoid_t *trap = &(traps[i]); + int y1, y2; + + if (!pixman_trapezoid_valid (trap)) + continue; + + y1 = pixman_fixed_to_int (trap->top); + if (y1 < box->y1) + box->y1 = y1; + + y2 = pixman_fixed_to_int (pixman_fixed_ceil (trap->bottom)); + if (y2 > box->y2) + box->y2 = y2; + +#define EXTEND_MIN(x) \ + if (pixman_fixed_to_int ((x)) < box->x1) \ + box->x1 = pixman_fixed_to_int ((x)); +#define EXTEND_MAX(x) \ + if (pixman_fixed_to_int (pixman_fixed_ceil ((x))) > box->x2) \ + box->x2 = pixman_fixed_to_int (pixman_fixed_ceil ((x))); + +#define EXTEND(x) \ + EXTEND_MIN(x); \ + EXTEND_MAX(x); + + EXTEND(trap->left.p1.x); + EXTEND(trap->left.p2.x); + EXTEND(trap->right.p1.x); + EXTEND(trap->right.p2.x); + } + + if (box->x1 >= box->x2 || box->y1 >= box->y2) + return FALSE; + + return TRUE; +} + +/* + * pixman_composite_trapezoids() + * + * All the trapezoids are conceptually rendered to an infinitely big image. + * The (0, 0) coordinates of this image are then aligned with the (x, y) + * coordinates of the source image, and then both images are aligned with + * the (x, y) coordinates of the destination. Then these three images are + * composited across the entire destination. + */ +PIXMAN_EXPORT void +pixman_composite_trapezoids (pixman_op_t op, + pixman_image_t * src, + pixman_image_t * dst, + pixman_format_code_t mask_format, + int x_src, + int y_src, + int x_dst, + int y_dst, + int n_traps, + const pixman_trapezoid_t * traps) +{ + int i; + + return_if_fail (PIXMAN_FORMAT_TYPE (mask_format) == PIXMAN_TYPE_A); + + if (n_traps <= 0) + return; + + _pixman_image_validate (src); + _pixman_image_validate (dst); + + if (op == PIXMAN_OP_ADD && + (src->common.flags & FAST_PATH_IS_OPAQUE) && + (mask_format == dst->common.extended_format_code) && + !(dst->common.have_clip_region)) + { + for (i = 0; i < n_traps; ++i) + { + const pixman_trapezoid_t *trap = &(traps[i]); + + if (!pixman_trapezoid_valid (trap)) + continue; + + pixman_rasterize_trapezoid (dst, trap, x_dst, y_dst); + } + } + else + { + pixman_image_t *tmp; + pixman_box32_t box; + int i; + + if (!get_trap_extents (op, dst, traps, n_traps, &box)) + return; + + if (!(tmp = pixman_image_create_bits ( + mask_format, box.x2 - box.x1, box.y2 - box.y1, NULL, -1))) + return; + + for (i = 0; i < n_traps; ++i) + { + const pixman_trapezoid_t *trap = &(traps[i]); + + if (!pixman_trapezoid_valid (trap)) + continue; + + pixman_rasterize_trapezoid (tmp, trap, - box.x1, - box.y1); + } + + pixman_image_composite (op, src, tmp, dst, + x_src + box.x1, y_src + box.y1, + 0, 0, + x_dst + box.x1, y_dst + box.y1, + box.x2 - box.x1, box.y2 - box.y1); + + pixman_image_unref (tmp); + } +} + +static int +greater_y (const pixman_point_fixed_t *a, const pixman_point_fixed_t *b) +{ + if (a->y == b->y) + return a->x > b->x; + return a->y > b->y; +} + +/* + * Note that the definition of this function is a bit odd because + * of the X coordinate space (y increasing downwards). + */ +static int +clockwise (const pixman_point_fixed_t *ref, + const pixman_point_fixed_t *a, + const pixman_point_fixed_t *b) +{ + pixman_point_fixed_t ad, bd; + + ad.x = a->x - ref->x; + ad.y = a->y - ref->y; + bd.x = b->x - ref->x; + bd.y = b->y - ref->y; + + return ((pixman_fixed_32_32_t) bd.y * ad.x - + (pixman_fixed_32_32_t) ad.y * bd.x) < 0; +} + +static void +triangle_to_trapezoids (const pixman_triangle_t *tri, pixman_trapezoid_t *traps) +{ + const pixman_point_fixed_t *top, *left, *right, *tmp; + + top = &tri->p1; + left = &tri->p2; + right = &tri->p3; + + if (greater_y (top, left)) + { + tmp = left; + left = top; + top = tmp; + } + + if (greater_y (top, right)) + { + tmp = right; + right = top; + top = tmp; + } + + if (clockwise (top, right, left)) + { + tmp = right; + right = left; + left = tmp; + } + + /* + * Two cases: + * + * + + + * / \ / \ + * / \ / \ + * / + + \ + * / -- -- \ + * / -- -- \ + * / --- --- \ + * +-- --+ + */ + + traps->top = top->y; + traps->left.p1 = *top; + traps->left.p2 = *left; + traps->right.p1 = *top; + traps->right.p2 = *right; + + if (right->y < left->y) + traps->bottom = right->y; + else + traps->bottom = left->y; + + traps++; + + *traps = *(traps - 1); + + if (right->y < left->y) + { + traps->top = right->y; + traps->bottom = left->y; + traps->right.p1 = *right; + traps->right.p2 = *left; + } + else + { + traps->top = left->y; + traps->bottom = right->y; + traps->left.p1 = *left; + traps->left.p2 = *right; + } +} + +static pixman_trapezoid_t * +convert_triangles (int n_tris, const pixman_triangle_t *tris) +{ + pixman_trapezoid_t *traps; + int i; + + if (n_tris <= 0) + return NULL; + + traps = pixman_malloc_ab (n_tris, 2 * sizeof (pixman_trapezoid_t)); + if (!traps) + return NULL; + + for (i = 0; i < n_tris; ++i) + triangle_to_trapezoids (&(tris[i]), traps + 2 * i); + + return traps; +} + +PIXMAN_EXPORT void +pixman_composite_triangles (pixman_op_t op, + pixman_image_t * src, + pixman_image_t * dst, + pixman_format_code_t mask_format, + int x_src, + int y_src, + int x_dst, + int y_dst, + int n_tris, + const pixman_triangle_t * tris) +{ + pixman_trapezoid_t *traps; + + if ((traps = convert_triangles (n_tris, tris))) + { + pixman_composite_trapezoids (op, src, dst, mask_format, + x_src, y_src, x_dst, y_dst, + n_tris * 2, traps); + + free (traps); + } +} + +PIXMAN_EXPORT void +pixman_add_triangles (pixman_image_t *image, + int32_t x_off, + int32_t y_off, + int n_tris, + const pixman_triangle_t *tris) +{ + pixman_trapezoid_t *traps; + + if ((traps = convert_triangles (n_tris, tris))) + { + pixman_add_trapezoids (image, x_off, y_off, + n_tris * 2, traps); + + free (traps); + } +} diff --git a/gfx/cairo/libpixman/src/pixman-utils.c b/gfx/cairo/libpixman/src/pixman-utils.c new file mode 100644 index 0000000000..2c2dddd64c --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-utils.c @@ -0,0 +1,330 @@ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 1999 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 SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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: Keith Packard, SuSE, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include + +#include "pixman-private.h" + +pixman_bool_t +_pixman_multiply_overflows_size (size_t a, size_t b) +{ + return a >= SIZE_MAX / b; +} + +pixman_bool_t +_pixman_multiply_overflows_int (unsigned int a, unsigned int b) +{ + return a >= INT32_MAX / b; +} + +pixman_bool_t +_pixman_addition_overflows_int (unsigned int a, unsigned int b) +{ + return a > INT32_MAX - b; +} + +void * +pixman_malloc_ab_plus_c (unsigned int a, unsigned int b, unsigned int c) +{ + if (!b || a >= INT32_MAX / b || (a * b) > INT32_MAX - c) + return NULL; + + return malloc (a * b + c); +} + +void * +pixman_malloc_ab (unsigned int a, + unsigned int b) +{ + if (a >= INT32_MAX / b) + return NULL; + + return malloc (a * b); +} + +void * +pixman_malloc_abc (unsigned int a, + unsigned int b, + unsigned int c) +{ + if (a >= INT32_MAX / b) + return NULL; + else if (a * b >= INT32_MAX / c) + return NULL; + else + return malloc (a * b * c); +} + +static force_inline uint16_t +float_to_unorm (float f, int n_bits) +{ + uint32_t u; + + if (f > 1.0) + f = 1.0; + if (f < 0.0) + f = 0.0; + + u = f * (1 << n_bits); + u -= (u >> n_bits); + + return u; +} + +static force_inline float +unorm_to_float (uint16_t u, int n_bits) +{ + uint32_t m = ((1 << n_bits) - 1); + + return (u & m) * (1.f / (float)m); +} + +/* + * This function expands images from a8r8g8b8 to argb_t. To preserve + * precision, it needs to know from which source format the a8r8g8b8 pixels + * originally came. + * + * For example, if the source was PIXMAN_x1r5g5b5 and the red component + * contained bits 12345, then the 8-bit value is 12345123. To correctly + * expand this to floating point, it should be 12345 / 31.0 and not + * 12345123 / 255.0. + */ +void +pixman_expand_to_float (argb_t *dst, + const uint32_t *src, + pixman_format_code_t format, + int width) +{ + static const float multipliers[16] = { + 0.0f, + 1.0f / ((1 << 1) - 1), + 1.0f / ((1 << 2) - 1), + 1.0f / ((1 << 3) - 1), + 1.0f / ((1 << 4) - 1), + 1.0f / ((1 << 5) - 1), + 1.0f / ((1 << 6) - 1), + 1.0f / ((1 << 7) - 1), + 1.0f / ((1 << 8) - 1), + 1.0f / ((1 << 9) - 1), + 1.0f / ((1 << 10) - 1), + 1.0f / ((1 << 11) - 1), + 1.0f / ((1 << 12) - 1), + 1.0f / ((1 << 13) - 1), + 1.0f / ((1 << 14) - 1), + 1.0f / ((1 << 15) - 1), + }; + int a_size, r_size, g_size, b_size; + int a_shift, r_shift, g_shift, b_shift; + float a_mul, r_mul, g_mul, b_mul; + uint32_t a_mask, r_mask, g_mask, b_mask; + int i; + + if (!PIXMAN_FORMAT_VIS (format)) + format = PIXMAN_a8r8g8b8; + + /* + * Determine the sizes of each component and the masks and shifts + * required to extract them from the source pixel. + */ + a_size = PIXMAN_FORMAT_A (format); + r_size = PIXMAN_FORMAT_R (format); + g_size = PIXMAN_FORMAT_G (format); + b_size = PIXMAN_FORMAT_B (format); + + a_shift = 32 - a_size; + r_shift = 24 - r_size; + g_shift = 16 - g_size; + b_shift = 8 - b_size; + + a_mask = ((1 << a_size) - 1); + r_mask = ((1 << r_size) - 1); + g_mask = ((1 << g_size) - 1); + b_mask = ((1 << b_size) - 1); + + a_mul = multipliers[a_size]; + r_mul = multipliers[r_size]; + g_mul = multipliers[g_size]; + b_mul = multipliers[b_size]; + + /* Start at the end so that we can do the expansion in place + * when src == dst + */ + for (i = width - 1; i >= 0; i--) + { + const uint32_t pixel = src[i]; + + dst[i].a = a_mask? ((pixel >> a_shift) & a_mask) * a_mul : 1.0f; + dst[i].r = ((pixel >> r_shift) & r_mask) * r_mul; + dst[i].g = ((pixel >> g_shift) & g_mask) * g_mul; + dst[i].b = ((pixel >> b_shift) & b_mask) * b_mul; + } +} + +uint16_t +pixman_float_to_unorm (float f, int n_bits) +{ + return float_to_unorm (f, n_bits); +} + +float +pixman_unorm_to_float (uint16_t u, int n_bits) +{ + return unorm_to_float (u, n_bits); +} + +void +pixman_contract_from_float (uint32_t *dst, + const argb_t *src, + int width) +{ + int i; + + for (i = 0; i < width; ++i) + { + uint32_t a, r, g, b; + + a = float_to_unorm (src[i].a, 8); + r = float_to_unorm (src[i].r, 8); + g = float_to_unorm (src[i].g, 8); + b = float_to_unorm (src[i].b, 8); + + dst[i] = (a << 24) | (r << 16) | (g << 8) | (b << 0); + } +} + +uint32_t * +_pixman_iter_get_scanline_noop (pixman_iter_t *iter, const uint32_t *mask) +{ + return iter->buffer; +} + +void +_pixman_iter_init_bits_stride (pixman_iter_t *iter, const pixman_iter_info_t *info) +{ + pixman_image_t *image = iter->image; + uint8_t *b = (uint8_t *)image->bits.bits; + int s = image->bits.rowstride * 4; + + iter->bits = b + s * iter->y + iter->x * PIXMAN_FORMAT_BPP (info->format) / 8; + iter->stride = s; +} + +#define N_TMP_BOXES (16) + +pixman_bool_t +pixman_region16_copy_from_region32 (pixman_region16_t *dst, + pixman_region32_t *src) +{ + int n_boxes, i; + pixman_box32_t *boxes32; + pixman_box16_t *boxes16; + pixman_bool_t retval; + + boxes32 = pixman_region32_rectangles (src, &n_boxes); + + boxes16 = pixman_malloc_ab (n_boxes, sizeof (pixman_box16_t)); + + if (!boxes16) + return FALSE; + + for (i = 0; i < n_boxes; ++i) + { + boxes16[i].x1 = boxes32[i].x1; + boxes16[i].y1 = boxes32[i].y1; + boxes16[i].x2 = boxes32[i].x2; + boxes16[i].y2 = boxes32[i].y2; + } + + pixman_region_fini (dst); + retval = pixman_region_init_rects (dst, boxes16, n_boxes); + free (boxes16); + return retval; +} + +pixman_bool_t +pixman_region32_copy_from_region16 (pixman_region32_t *dst, + pixman_region16_t *src) +{ + int n_boxes, i; + pixman_box16_t *boxes16; + pixman_box32_t *boxes32; + pixman_box32_t tmp_boxes[N_TMP_BOXES]; + pixman_bool_t retval; + + boxes16 = pixman_region_rectangles (src, &n_boxes); + + if (n_boxes > N_TMP_BOXES) + boxes32 = pixman_malloc_ab (n_boxes, sizeof (pixman_box32_t)); + else + boxes32 = tmp_boxes; + + if (!boxes32) + return FALSE; + + for (i = 0; i < n_boxes; ++i) + { + boxes32[i].x1 = boxes16[i].x1; + boxes32[i].y1 = boxes16[i].y1; + boxes32[i].x2 = boxes16[i].x2; + boxes32[i].y2 = boxes16[i].y2; + } + + pixman_region32_fini (dst); + retval = pixman_region32_init_rects (dst, boxes32, n_boxes); + + if (boxes32 != tmp_boxes) + free (boxes32); + + return retval; +} + +/* This function is exported for the sake of the test suite and not part + * of the ABI. + */ +PIXMAN_EXPORT pixman_implementation_t * +_pixman_internal_only_get_implementation (void) +{ + return get_implementation (); +} + +void +_pixman_log_error (const char *function, const char *message) +{ + static int n_messages = 0; + + if (n_messages < 10) + { + fprintf (stderr, + "*** BUG ***\n" + "In %s: %s\n" + "Set a breakpoint on '_pixman_log_error' to debug\n\n", + function, message); + + n_messages++; + } +} diff --git a/gfx/cairo/libpixman/src/pixman-version.h b/gfx/cairo/libpixman/src/pixman-version.h new file mode 100644 index 0000000000..0f39bf3400 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-version.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * + * 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. + * + * Author: Carl D. Worth + */ + +#ifndef PIXMAN_VERSION_H__ +#define PIXMAN_VERSION_H__ + +#ifndef PIXMAN_H__ +# error pixman-version.h should only be included by pixman.h +#endif + +#define PIXMAN_VERSION_MAJOR 0 +#define PIXMAN_VERSION_MINOR 42 +#define PIXMAN_VERSION_MICRO 2 + +#define PIXMAN_VERSION_STRING "0.42.2" + +#define PIXMAN_VERSION_ENCODE(major, minor, micro) ( \ + ((major) * 10000) \ + + ((minor) * 100) \ + + ((micro) * 1)) + +#define PIXMAN_VERSION PIXMAN_VERSION_ENCODE( \ + PIXMAN_VERSION_MAJOR, \ + PIXMAN_VERSION_MINOR, \ + PIXMAN_VERSION_MICRO) + +#ifndef PIXMAN_API +# define PIXMAN_API +#endif + +#endif /* PIXMAN_VERSION_H__ */ diff --git a/gfx/cairo/libpixman/src/pixman-version.h.in b/gfx/cairo/libpixman/src/pixman-version.h.in new file mode 100644 index 0000000000..64778a595c --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-version.h.in @@ -0,0 +1,54 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * + * 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. + * + * Author: Carl D. Worth + */ + +#ifndef PIXMAN_VERSION_H__ +#define PIXMAN_VERSION_H__ + +#ifndef PIXMAN_H__ +# error pixman-version.h should only be included by pixman.h +#endif + +#define PIXMAN_VERSION_MAJOR @PIXMAN_VERSION_MAJOR@ +#define PIXMAN_VERSION_MINOR @PIXMAN_VERSION_MINOR@ +#define PIXMAN_VERSION_MICRO @PIXMAN_VERSION_MICRO@ + +#define PIXMAN_VERSION_STRING "@PIXMAN_VERSION_MAJOR@.@PIXMAN_VERSION_MINOR@.@PIXMAN_VERSION_MICRO@" + +#define PIXMAN_VERSION_ENCODE(major, minor, micro) ( \ + ((major) * 10000) \ + + ((minor) * 100) \ + + ((micro) * 1)) + +#define PIXMAN_VERSION PIXMAN_VERSION_ENCODE( \ + PIXMAN_VERSION_MAJOR, \ + PIXMAN_VERSION_MINOR, \ + PIXMAN_VERSION_MICRO) + +#ifndef PIXMAN_API +# define PIXMAN_API +#endif + +#endif /* PIXMAN_VERSION_H__ */ diff --git a/gfx/cairo/libpixman/src/pixman-vmx.c b/gfx/cairo/libpixman/src/pixman-vmx.c new file mode 100644 index 0000000000..52de37e69e --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-vmx.c @@ -0,0 +1,3159 @@ +/* + * Copyright © 2007 Luca Barbato + * + * 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 Luca Barbato not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Luca Barbato makes 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. + * + * Author: Luca Barbato (lu_zero@gentoo.org) + * + * Based on fbmmx.c by Owen Taylor, Søren Sandmann and Nicholas Miell + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include "pixman-private.h" +#include "pixman-combine32.h" +#include "pixman-inlines.h" +#include + +#define AVV(x...) {x} + +static vector unsigned int mask_ff000000; +static vector unsigned int mask_red; +static vector unsigned int mask_green; +static vector unsigned int mask_blue; +static vector unsigned int mask_565_fix_rb; +static vector unsigned int mask_565_fix_g; + +static force_inline vector unsigned int +splat_alpha (vector unsigned int pix) +{ +#ifdef WORDS_BIGENDIAN + return vec_perm (pix, pix, + (vector unsigned char)AVV ( + 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, + 0x08, 0x08, 0x08, 0x08, 0x0C, 0x0C, 0x0C, 0x0C)); +#else + return vec_perm (pix, pix, + (vector unsigned char)AVV ( + 0x03, 0x03, 0x03, 0x03, 0x07, 0x07, 0x07, 0x07, + 0x0B, 0x0B, 0x0B, 0x0B, 0x0F, 0x0F, 0x0F, 0x0F)); +#endif +} + +static force_inline vector unsigned int +splat_pixel (vector unsigned int pix) +{ + return vec_perm (pix, pix, + (vector unsigned char)AVV ( + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03)); +} + +static force_inline vector unsigned int +pix_multiply (vector unsigned int p, vector unsigned int a) +{ + vector unsigned short hi, lo, mod; + + /* unpack to short */ + hi = (vector unsigned short) +#ifdef WORDS_BIGENDIAN + vec_mergeh ((vector unsigned char)AVV (0), + (vector unsigned char)p); +#else + vec_mergeh ((vector unsigned char) p, + (vector unsigned char) AVV (0)); +#endif + + mod = (vector unsigned short) +#ifdef WORDS_BIGENDIAN + vec_mergeh ((vector unsigned char)AVV (0), + (vector unsigned char)a); +#else + vec_mergeh ((vector unsigned char) a, + (vector unsigned char) AVV (0)); +#endif + + hi = vec_mladd (hi, mod, (vector unsigned short) + AVV (0x0080, 0x0080, 0x0080, 0x0080, + 0x0080, 0x0080, 0x0080, 0x0080)); + + hi = vec_adds (hi, vec_sr (hi, vec_splat_u16 (8))); + + hi = vec_sr (hi, vec_splat_u16 (8)); + + /* unpack to short */ + lo = (vector unsigned short) +#ifdef WORDS_BIGENDIAN + vec_mergel ((vector unsigned char)AVV (0), + (vector unsigned char)p); +#else + vec_mergel ((vector unsigned char) p, + (vector unsigned char) AVV (0)); +#endif + + mod = (vector unsigned short) +#ifdef WORDS_BIGENDIAN + vec_mergel ((vector unsigned char)AVV (0), + (vector unsigned char)a); +#else + vec_mergel ((vector unsigned char) a, + (vector unsigned char) AVV (0)); +#endif + + lo = vec_mladd (lo, mod, (vector unsigned short) + AVV (0x0080, 0x0080, 0x0080, 0x0080, + 0x0080, 0x0080, 0x0080, 0x0080)); + + lo = vec_adds (lo, vec_sr (lo, vec_splat_u16 (8))); + + lo = vec_sr (lo, vec_splat_u16 (8)); + + return (vector unsigned int)vec_packsu (hi, lo); +} + +static force_inline vector unsigned int +pix_add (vector unsigned int a, vector unsigned int b) +{ + return (vector unsigned int)vec_adds ((vector unsigned char)a, + (vector unsigned char)b); +} + +static force_inline vector unsigned int +pix_add_mul (vector unsigned int x, + vector unsigned int a, + vector unsigned int y, + vector unsigned int b) +{ + vector unsigned int t1, t2; + + t1 = pix_multiply (x, a); + t2 = pix_multiply (y, b); + + return pix_add (t1, t2); +} + +static force_inline vector unsigned int +negate (vector unsigned int src) +{ + return vec_nor (src, src); +} + +/* dest*~srca + src */ +static force_inline vector unsigned int +over (vector unsigned int src, + vector unsigned int srca, + vector unsigned int dest) +{ + vector unsigned char tmp = (vector unsigned char) + pix_multiply (dest, negate (srca)); + + tmp = vec_adds ((vector unsigned char)src, tmp); + return (vector unsigned int)tmp; +} + +/* in == pix_multiply */ +#define in_over(src, srca, mask, dest) \ + over (pix_multiply (src, mask), \ + pix_multiply (srca, mask), dest) + +#ifdef WORDS_BIGENDIAN + +#define COMPUTE_SHIFT_MASK(source) \ + source ## _mask = vec_lvsl (0, source); + +#define COMPUTE_SHIFT_MASKS(dest, source) \ + source ## _mask = vec_lvsl (0, source); + +#define COMPUTE_SHIFT_MASKC(dest, source, mask) \ + mask ## _mask = vec_lvsl (0, mask); \ + source ## _mask = vec_lvsl (0, source); + +#define LOAD_VECTOR(source) \ +do \ +{ \ + vector unsigned char tmp1, tmp2; \ + tmp1 = (typeof(tmp1))vec_ld (0, source); \ + tmp2 = (typeof(tmp2))vec_ld (15, source); \ + v ## source = (typeof(v ## source)) \ + vec_perm (tmp1, tmp2, source ## _mask); \ +} while (0) + +#define LOAD_VECTORS(dest, source) \ +do \ +{ \ + LOAD_VECTOR(source); \ + v ## dest = (typeof(v ## dest))vec_ld (0, dest); \ +} while (0) + +#define LOAD_VECTORSC(dest, source, mask) \ +do \ +{ \ + LOAD_VECTORS(dest, source); \ + LOAD_VECTOR(mask); \ +} while (0) + +#define DECLARE_SRC_MASK_VAR vector unsigned char src_mask +#define DECLARE_MASK_MASK_VAR vector unsigned char mask_mask + +#else + +/* Now the COMPUTE_SHIFT_{MASK, MASKS, MASKC} below are just no-op. + * They are defined that way because little endian altivec can do unaligned + * reads natively and have no need for constructing the permutation pattern + * variables. + */ +#define COMPUTE_SHIFT_MASK(source) + +#define COMPUTE_SHIFT_MASKS(dest, source) + +#define COMPUTE_SHIFT_MASKC(dest, source, mask) + +# define LOAD_VECTOR(source) \ + v ## source = (typeof(v ## source))vec_xl(0, source); + +# define LOAD_VECTORS(dest, source) \ + LOAD_VECTOR(source); \ + LOAD_VECTOR(dest); \ + +# define LOAD_VECTORSC(dest, source, mask) \ + LOAD_VECTORS(dest, source); \ + LOAD_VECTOR(mask); \ + +#define DECLARE_SRC_MASK_VAR +#define DECLARE_MASK_MASK_VAR + +#endif /* WORDS_BIGENDIAN */ + +#define LOAD_VECTORSM(dest, source, mask) \ + LOAD_VECTORSC (dest, source, mask); \ + v ## source = pix_multiply (v ## source, \ + splat_alpha (v ## mask)); + +#define STORE_VECTOR(dest) \ + vec_st ((vector unsigned int) v ## dest, 0, dest); + +/* load 4 pixels from a 16-byte boundary aligned address */ +static force_inline vector unsigned int +load_128_aligned (const uint32_t* src) +{ + return *((vector unsigned int *) src); +} + +/* load 4 pixels from a unaligned address */ +static force_inline vector unsigned int +load_128_unaligned (const uint32_t* src) +{ + vector unsigned int vsrc; + DECLARE_SRC_MASK_VAR; + + COMPUTE_SHIFT_MASK (src); + LOAD_VECTOR (src); + + return vsrc; +} + +/* save 4 pixels on a 16-byte boundary aligned address */ +static force_inline void +save_128_aligned (uint32_t* data, + vector unsigned int vdata) +{ + STORE_VECTOR(data) +} + +static force_inline vector unsigned int +create_mask_1x32_128 (const uint32_t *src) +{ + vector unsigned int vsrc; + DECLARE_SRC_MASK_VAR; + + COMPUTE_SHIFT_MASK (src); + LOAD_VECTOR (src); + return vec_splat(vsrc, 0); +} + +static force_inline vector unsigned int +create_mask_32_128 (uint32_t mask) +{ + return create_mask_1x32_128(&mask); +} + +static force_inline vector unsigned int +unpacklo_128_16x8 (vector unsigned int data1, vector unsigned int data2) +{ + vector unsigned char lo; + + /* unpack to short */ + lo = (vector unsigned char) +#ifdef WORDS_BIGENDIAN + vec_mergel ((vector unsigned char) data2, + (vector unsigned char) data1); +#else + vec_mergel ((vector unsigned char) data1, + (vector unsigned char) data2); +#endif + + return (vector unsigned int) lo; +} + +static force_inline vector unsigned int +unpackhi_128_16x8 (vector unsigned int data1, vector unsigned int data2) +{ + vector unsigned char hi; + + /* unpack to short */ + hi = (vector unsigned char) +#ifdef WORDS_BIGENDIAN + vec_mergeh ((vector unsigned char) data2, + (vector unsigned char) data1); +#else + vec_mergeh ((vector unsigned char) data1, + (vector unsigned char) data2); +#endif + + return (vector unsigned int) hi; +} + +static force_inline vector unsigned int +unpacklo_128_8x16 (vector unsigned int data1, vector unsigned int data2) +{ + vector unsigned short lo; + + /* unpack to char */ + lo = (vector unsigned short) +#ifdef WORDS_BIGENDIAN + vec_mergel ((vector unsigned short) data2, + (vector unsigned short) data1); +#else + vec_mergel ((vector unsigned short) data1, + (vector unsigned short) data2); +#endif + + return (vector unsigned int) lo; +} + +static force_inline vector unsigned int +unpackhi_128_8x16 (vector unsigned int data1, vector unsigned int data2) +{ + vector unsigned short hi; + + /* unpack to char */ + hi = (vector unsigned short) +#ifdef WORDS_BIGENDIAN + vec_mergeh ((vector unsigned short) data2, + (vector unsigned short) data1); +#else + vec_mergeh ((vector unsigned short) data1, + (vector unsigned short) data2); +#endif + + return (vector unsigned int) hi; +} + +static force_inline void +unpack_128_2x128 (vector unsigned int data1, vector unsigned int data2, + vector unsigned int* data_lo, vector unsigned int* data_hi) +{ + *data_lo = unpacklo_128_16x8(data1, data2); + *data_hi = unpackhi_128_16x8(data1, data2); +} + +static force_inline void +unpack_128_2x128_16 (vector unsigned int data1, vector unsigned int data2, + vector unsigned int* data_lo, vector unsigned int* data_hi) +{ + *data_lo = unpacklo_128_8x16(data1, data2); + *data_hi = unpackhi_128_8x16(data1, data2); +} + +static force_inline vector unsigned int +unpack_565_to_8888 (vector unsigned int lo) +{ + vector unsigned int r, g, b, rb, t; + + r = vec_and (vec_sl(lo, create_mask_32_128(8)), mask_red); + g = vec_and (vec_sl(lo, create_mask_32_128(5)), mask_green); + b = vec_and (vec_sl(lo, create_mask_32_128(3)), mask_blue); + + rb = vec_or (r, b); + t = vec_and (rb, mask_565_fix_rb); + t = vec_sr (t, create_mask_32_128(5)); + rb = vec_or (rb, t); + + t = vec_and (g, mask_565_fix_g); + t = vec_sr (t, create_mask_32_128(6)); + g = vec_or (g, t); + + return vec_or (rb, g); +} + +static force_inline int +is_opaque (vector unsigned int x) +{ + uint32_t cmp_result; + vector bool int ffs = vec_cmpeq(x, x); + + cmp_result = vec_all_eq(x, ffs); + + return (cmp_result & 0x8888) == 0x8888; +} + +static force_inline int +is_zero (vector unsigned int x) +{ + uint32_t cmp_result; + + cmp_result = vec_all_eq(x, (vector unsigned int) AVV(0)); + + return cmp_result == 0xffff; +} + +static force_inline int +is_transparent (vector unsigned int x) +{ + uint32_t cmp_result; + + cmp_result = vec_all_eq(x, (vector unsigned int) AVV(0)); + return (cmp_result & 0x8888) == 0x8888; +} + +static force_inline uint32_t +core_combine_over_u_pixel_vmx (uint32_t src, uint32_t dst) +{ + uint32_t a; + + a = ALPHA_8(src); + + if (a == 0xff) + { + return src; + } + else if (src) + { + UN8x4_MUL_UN8_ADD_UN8x4(dst, (~a & MASK), src); + } + + return dst; +} + +static force_inline uint32_t +combine1 (const uint32_t *ps, const uint32_t *pm) +{ + uint32_t s = *ps; + + if (pm) + UN8x4_MUL_UN8(s, ALPHA_8(*pm)); + + return s; +} + +static force_inline vector unsigned int +combine4 (const uint32_t* ps, const uint32_t* pm) +{ + vector unsigned int src, msk; + + if (pm) + { + msk = load_128_unaligned(pm); + + if (is_transparent(msk)) + return (vector unsigned int) AVV(0); + } + + src = load_128_unaligned(ps); + + if (pm) + src = pix_multiply(src, msk); + + return src; +} + +static void +vmx_combine_over_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + + LOAD_VECTORS (dest, src); + + vdest = over (vsrc, splat_alpha (vsrc), vdest); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); + + dest[i] = d; + } +} + +static void +vmx_combine_over_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t ia; + + UN8x4_MUL_UN8 (s, m); + + ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = over (vsrc, splat_alpha (vsrc), vdest); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t ia; + + UN8x4_MUL_UN8 (s, m); + + ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); + dest[i] = d; + } +} + +static void +vmx_combine_over_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_over_u_mask (dest, src, mask, width); + else + vmx_combine_over_u_no_mask (dest, src, width); +} + +static void +vmx_combine_over_reverse_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8_ADD_UN8x4 (s, ia, d); + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + + LOAD_VECTORS (dest, src); + + vdest = over (vdest, splat_alpha (vdest), vsrc); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t ia = ALPHA_8 (~dest[i]); + + UN8x4_MUL_UN8_ADD_UN8x4 (s, ia, d); + dest[i] = s; + } +} + +static void +vmx_combine_over_reverse_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8 (s, m); + + UN8x4_MUL_UN8_ADD_UN8x4 (s, ia, d); + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + + LOAD_VECTORSM (dest, src, mask); + + vdest = over (vdest, splat_alpha (vdest), vsrc); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t ia = ALPHA_8 (~dest[i]); + + UN8x4_MUL_UN8 (s, m); + + UN8x4_MUL_UN8_ADD_UN8x4 (s, ia, d); + dest[i] = s; + } +} + +static void +vmx_combine_over_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_over_reverse_u_mask (dest, src, mask, width); + else + vmx_combine_over_reverse_u_no_mask (dest, src, width); +} + +static void +vmx_combine_in_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t s = *src++; + uint32_t a = ALPHA_8 (*dest); + + UN8x4_MUL_UN8 (s, a); + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORS (dest, src); + + vdest = pix_multiply (vsrc, splat_alpha (vdest)); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t s = src[i]; + uint32_t a = ALPHA_8 (dest[i]); + + UN8x4_MUL_UN8 (s, a); + dest[i] = s; + } +} + +static void +vmx_combine_in_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t s = *src++; + uint32_t a = ALPHA_8 (*dest); + + UN8x4_MUL_UN8 (s, m); + UN8x4_MUL_UN8 (s, a); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = pix_multiply (vsrc, splat_alpha (vdest)); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t s = src[i]; + uint32_t a = ALPHA_8 (dest[i]); + + UN8x4_MUL_UN8 (s, m); + UN8x4_MUL_UN8 (s, a); + + dest[i] = s; + } +} + +static void +vmx_combine_in_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_in_u_mask (dest, src, mask, width); + else + vmx_combine_in_u_no_mask (dest, src, width); +} + +static void +vmx_combine_in_reverse_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t d = *dest; + uint32_t a = ALPHA_8 (*src++); + + UN8x4_MUL_UN8 (d, a); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORS (dest, src); + + vdest = pix_multiply (vdest, splat_alpha (vsrc)); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t d = dest[i]; + uint32_t a = ALPHA_8 (src[i]); + + UN8x4_MUL_UN8 (d, a); + + dest[i] = d; + } +} + +static void +vmx_combine_in_reverse_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t d = *dest; + uint32_t a = *src++; + + UN8x4_MUL_UN8 (a, m); + a = ALPHA_8 (a); + UN8x4_MUL_UN8 (d, a); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = pix_multiply (vdest, splat_alpha (vsrc)); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t d = dest[i]; + uint32_t a = src[i]; + + UN8x4_MUL_UN8 (a, m); + a = ALPHA_8 (a); + UN8x4_MUL_UN8 (d, a); + + dest[i] = d; + } +} + +static void +vmx_combine_in_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_in_reverse_u_mask (dest, src, mask, width); + else + vmx_combine_in_reverse_u_no_mask (dest, src, width); +} + +static void +vmx_combine_out_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t s = *src++; + uint32_t a = ALPHA_8 (~(*dest)); + + UN8x4_MUL_UN8 (s, a); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORS (dest, src); + + vdest = pix_multiply (vsrc, splat_alpha (negate (vdest))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t s = src[i]; + uint32_t a = ALPHA_8 (~dest[i]); + + UN8x4_MUL_UN8 (s, a); + + dest[i] = s; + } +} + +static void +vmx_combine_out_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t s = *src++; + uint32_t a = ALPHA_8 (~(*dest)); + + UN8x4_MUL_UN8 (s, m); + UN8x4_MUL_UN8 (s, a); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = pix_multiply (vsrc, splat_alpha (negate (vdest))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t s = src[i]; + uint32_t a = ALPHA_8 (~dest[i]); + + UN8x4_MUL_UN8 (s, m); + UN8x4_MUL_UN8 (s, a); + + dest[i] = s; + } +} + +static void +vmx_combine_out_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_out_u_mask (dest, src, mask, width); + else + vmx_combine_out_u_no_mask (dest, src, width); +} + +static void +vmx_combine_out_reverse_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t d = *dest; + uint32_t a = ALPHA_8 (~(*src++)); + + UN8x4_MUL_UN8 (d, a); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + + LOAD_VECTORS (dest, src); + + vdest = pix_multiply (vdest, splat_alpha (negate (vsrc))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t d = dest[i]; + uint32_t a = ALPHA_8 (~src[i]); + + UN8x4_MUL_UN8 (d, a); + + dest[i] = d; + } +} + +static void +vmx_combine_out_reverse_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t d = *dest; + uint32_t a = *src++; + + UN8x4_MUL_UN8 (a, m); + a = ALPHA_8 (~a); + UN8x4_MUL_UN8 (d, a); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = pix_multiply (vdest, splat_alpha (negate (vsrc))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t d = dest[i]; + uint32_t a = src[i]; + + UN8x4_MUL_UN8 (a, m); + a = ALPHA_8 (~a); + UN8x4_MUL_UN8 (d, a); + + dest[i] = d; + } +} + +static void +vmx_combine_out_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_out_reverse_u_mask (dest, src, mask, width); + else + vmx_combine_out_reverse_u_no_mask (dest, src, width); +} + +static void +vmx_combine_atop_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t dest_a = ALPHA_8 (d); + uint32_t src_ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_a, d, src_ia); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORS (dest, src); + + vdest = pix_add_mul (vsrc, splat_alpha (vdest), + vdest, splat_alpha (negate (vsrc))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t dest_a = ALPHA_8 (d); + uint32_t src_ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_a, d, src_ia); + + dest[i] = s; + } +} + +static void +vmx_combine_atop_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t dest_a = ALPHA_8 (d); + uint32_t src_ia; + + UN8x4_MUL_UN8 (s, m); + + src_ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_a, d, src_ia); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = pix_add_mul (vsrc, splat_alpha (vdest), + vdest, splat_alpha (negate (vsrc))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t dest_a = ALPHA_8 (d); + uint32_t src_ia; + + UN8x4_MUL_UN8 (s, m); + + src_ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_a, d, src_ia); + + dest[i] = s; + } +} + +static void +vmx_combine_atop_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_atop_u_mask (dest, src, mask, width); + else + vmx_combine_atop_u_no_mask (dest, src, width); +} + +static void +vmx_combine_atop_reverse_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t src_a = ALPHA_8 (s); + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_a); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORS (dest, src); + + vdest = pix_add_mul (vdest, splat_alpha (vsrc), + vsrc, splat_alpha (negate (vdest))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t src_a = ALPHA_8 (s); + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_a); + + dest[i] = s; + } +} + +static void +vmx_combine_atop_reverse_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t src_a; + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8 (s, m); + + src_a = ALPHA_8 (s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_a); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = pix_add_mul (vdest, splat_alpha (vsrc), + vsrc, splat_alpha (negate (vdest))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t src_a; + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8 (s, m); + + src_a = ALPHA_8 (s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_a); + + dest[i] = s; + } +} + +static void +vmx_combine_atop_reverse_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_atop_reverse_u_mask (dest, src, mask, width); + else + vmx_combine_atop_reverse_u_no_mask (dest, src, width); +} + +static void +vmx_combine_xor_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t src_ia = ALPHA_8 (~s); + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_ia); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORS (dest, src); + + vdest = pix_add_mul (vsrc, splat_alpha (negate (vdest)), + vdest, splat_alpha (negate (vsrc))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t src_ia = ALPHA_8 (~s); + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_ia); + + dest[i] = s; + } +} + +static void +vmx_combine_xor_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t src_ia; + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8 (s, m); + + src_ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_ia); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = pix_add_mul (vsrc, splat_alpha (negate (vdest)), + vdest, splat_alpha (negate (vsrc))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t src_ia; + uint32_t dest_ia = ALPHA_8 (~d); + + UN8x4_MUL_UN8 (s, m); + + src_ia = ALPHA_8 (~s); + + UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (s, dest_ia, d, src_ia); + + dest[i] = s; + } +} + +static void +vmx_combine_xor_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_xor_u_mask (dest, src, mask, width); + else + vmx_combine_xor_u_no_mask (dest, src, width); +} + +static void +vmx_combine_add_u_no_mask (uint32_t * dest, + const uint32_t *src, + int width) +{ + int i; + vector unsigned int vdest, vsrc; + DECLARE_SRC_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t s = *src++; + uint32_t d = *dest; + + UN8x4_ADD_UN8x4 (d, s); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKS (dest, src); + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORS (dest, src); + + vdest = pix_add (vsrc, vdest); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t s = src[i]; + uint32_t d = dest[i]; + + UN8x4_ADD_UN8x4 (d, s); + + dest[i] = d; + } +} + +static void +vmx_combine_add_u_mask (uint32_t * dest, + const uint32_t *src, + const uint32_t *mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t m = ALPHA_8 (*mask++); + uint32_t s = *src++; + uint32_t d = *dest; + + UN8x4_MUL_UN8 (s, m); + UN8x4_ADD_UN8x4 (d, s); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSM (dest, src, mask); + + vdest = pix_add (vsrc, vdest); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t m = ALPHA_8 (mask[i]); + uint32_t s = src[i]; + uint32_t d = dest[i]; + + UN8x4_MUL_UN8 (s, m); + UN8x4_ADD_UN8x4 (d, s); + + dest[i] = d; + } +} + +static void +vmx_combine_add_u (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + if (mask) + vmx_combine_add_u_mask (dest, src, mask, width); + else + vmx_combine_add_u_no_mask (dest, src, width); +} + +static void +vmx_combine_src_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + + UN8x4_MUL_UN8x4 (s, a); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = pix_multiply (vsrc, vmask); + + STORE_VECTOR (dest); + + mask += 4; + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + + UN8x4_MUL_UN8x4 (s, a); + + dest[i] = s; + } +} + +static void +vmx_combine_over_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t sa = ALPHA_8 (s); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4_ADD_UN8x4 (d, ~a, s); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = in_over (vsrc, splat_alpha (vsrc), vmask, vdest); + + STORE_VECTOR (dest); + + mask += 4; + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t sa = ALPHA_8 (s); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4_ADD_UN8x4 (d, ~a, s); + + dest[i] = d; + } +} + +static void +vmx_combine_over_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t ida = ALPHA_8 (~d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8_ADD_UN8x4 (s, ida, d); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = over (vdest, splat_alpha (vdest), pix_multiply (vsrc, vmask)); + + STORE_VECTOR (dest); + + mask += 4; + src += 4; + dest += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t ida = ALPHA_8 (~d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8_ADD_UN8x4 (s, ida, d); + + dest[i] = s; + } +} + +static void +vmx_combine_in_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t da = ALPHA_8 (*dest); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (s, da); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = pix_multiply (pix_multiply (vsrc, vmask), splat_alpha (vdest)); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t da = ALPHA_8 (dest[i]); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (s, da); + + dest[i] = s; + } +} + +static void +vmx_combine_in_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t d = *dest; + uint32_t sa = ALPHA_8 (*src++); + + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4 (d, a); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + + LOAD_VECTORSC (dest, src, mask); + + vdest = pix_multiply (vdest, pix_multiply (vmask, splat_alpha (vsrc))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t d = dest[i]; + uint32_t sa = ALPHA_8 (src[i]); + + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4 (d, a); + + dest[i] = d; + } +} + +static void +vmx_combine_out_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t da = ALPHA_8 (~d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (s, da); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = pix_multiply ( + pix_multiply (vsrc, vmask), splat_alpha (negate (vdest))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t da = ALPHA_8 (~d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (s, da); + + dest[i] = s; + } +} + +static void +vmx_combine_out_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t sa = ALPHA_8 (s); + + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4 (d, ~a); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = pix_multiply ( + vdest, negate (pix_multiply (vmask, splat_alpha (vsrc)))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t sa = ALPHA_8 (s); + + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4 (d, ~a); + + dest[i] = d; + } +} + +static void +vmx_combine_atop_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask, vsrca; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t sa = ALPHA_8 (s); + uint32_t da = ALPHA_8 (d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ~a, s, da); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vsrca = splat_alpha (vsrc); + + vsrc = pix_multiply (vsrc, vmask); + vmask = pix_multiply (vmask, vsrca); + + vdest = pix_add_mul (vsrc, splat_alpha (vdest), + negate (vmask), vdest); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t sa = ALPHA_8 (s); + uint32_t da = ALPHA_8 (d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ~a, s, da); + + dest[i] = d; + } +} + +static void +vmx_combine_atop_reverse_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t sa = ALPHA_8 (s); + uint32_t da = ALPHA_8 (~d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, a, s, da); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = pix_add_mul (vdest, + pix_multiply (vmask, splat_alpha (vsrc)), + pix_multiply (vsrc, vmask), + negate (splat_alpha (vdest))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t sa = ALPHA_8 (s); + uint32_t da = ALPHA_8 (~d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, a, s, da); + + dest[i] = d; + } +} + +static void +vmx_combine_xor_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t d = *dest; + uint32_t sa = ALPHA_8 (s); + uint32_t da = ALPHA_8 (~d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ~a, s, da); + + *dest++ = d; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = pix_add_mul (vdest, + negate (pix_multiply (vmask, splat_alpha (vsrc))), + pix_multiply (vsrc, vmask), + negate (splat_alpha (vdest))); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t d = dest[i]; + uint32_t sa = ALPHA_8 (s); + uint32_t da = ALPHA_8 (~d); + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_MUL_UN8 (a, sa); + UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (d, ~a, s, da); + + dest[i] = d; + } +} + +static void +vmx_combine_add_ca (pixman_implementation_t *imp, + pixman_op_t op, + uint32_t * dest, + const uint32_t * src, + const uint32_t * mask, + int width) +{ + int i; + vector unsigned int vdest, vsrc, vmask; + DECLARE_SRC_MASK_VAR; + DECLARE_MASK_MASK_VAR; + + while (width && ((uintptr_t)dest & 15)) + { + uint32_t a = *mask++; + uint32_t s = *src++; + uint32_t d = *dest; + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_ADD_UN8x4 (s, d); + + *dest++ = s; + width--; + } + + COMPUTE_SHIFT_MASKC (dest, src, mask); + + /* printf ("%s\n",__PRETTY_FUNCTION__); */ + for (i = width / 4; i > 0; i--) + { + LOAD_VECTORSC (dest, src, mask); + + vdest = pix_add (pix_multiply (vsrc, vmask), vdest); + + STORE_VECTOR (dest); + + src += 4; + dest += 4; + mask += 4; + } + + for (i = width % 4; --i >= 0;) + { + uint32_t a = mask[i]; + uint32_t s = src[i]; + uint32_t d = dest[i]; + + UN8x4_MUL_UN8x4 (s, a); + UN8x4_ADD_UN8x4 (s, d); + + dest[i] = s; + } +} + +static void +vmx_composite_over_n_8_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, srca; + uint32_t *dst_line, *dst; + uint8_t *mask_line; + int dst_stride, mask_stride; + int32_t w; + uint32_t m, d, s, ia; + + vector unsigned int vsrc, valpha, vmask, vdst; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + srca = ALPHA_8(src); + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint8_t, mask_stride, mask_line, 1); + + vsrc = (vector unsigned int) {src, src, src, src}; + valpha = splat_alpha(vsrc); + + while (height--) + { + const uint8_t *pm = mask_line; + dst = dst_line; + dst_line += dst_stride; + mask_line += mask_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + s = src; + m = *pm++; + + if (m) + { + d = *dst; + UN8x4_MUL_UN8 (s, m); + ia = ALPHA_8 (~s); + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); + *dst = d; + } + + w--; + dst++; + } + + while (w >= 4) + { + m = *((uint32_t*)pm); + + if (srca == 0xff && m == 0xffffffff) + { + save_128_aligned(dst, vsrc); + } + else if (m) + { + vmask = splat_pixel((vector unsigned int) {m, m, m, m}); + + /* dst is 16-byte aligned */ + vdst = in_over (vsrc, valpha, vmask, load_128_aligned (dst)); + + save_128_aligned(dst, vdst); + } + + w -= 4; + dst += 4; + pm += 4; + } + + while (w) + { + s = src; + m = *pm++; + + if (m) + { + d = *dst; + UN8x4_MUL_UN8 (s, m); + ia = ALPHA_8 (~s); + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, s); + *dst = d; + } + + w--; + dst++; + } + } + +} + +static pixman_bool_t +vmx_fill (pixman_implementation_t *imp, + uint32_t * bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + uint32_t byte_width; + uint8_t *byte_line; + + vector unsigned int vfiller; + + if (bpp == 8) + { + uint8_t b; + uint16_t w; + + stride = stride * (int) sizeof (uint32_t) / 1; + byte_line = (uint8_t *)(((uint8_t *)bits) + stride * y + x); + byte_width = width; + stride *= 1; + + b = filler & 0xff; + w = (b << 8) | b; + filler = (w << 16) | w; + } + else if (bpp == 16) + { + stride = stride * (int) sizeof (uint32_t) / 2; + byte_line = (uint8_t *)(((uint16_t *)bits) + stride * y + x); + byte_width = 2 * width; + stride *= 2; + + filler = (filler & 0xffff) * 0x00010001; + } + else if (bpp == 32) + { + stride = stride * (int) sizeof (uint32_t) / 4; + byte_line = (uint8_t *)(((uint32_t *)bits) + stride * y + x); + byte_width = 4 * width; + stride *= 4; + } + else + { + return FALSE; + } + + vfiller = create_mask_1x32_128(&filler); + + while (height--) + { + int w; + uint8_t *d = byte_line; + byte_line += stride; + w = byte_width; + + if (w >= 1 && ((uintptr_t)d & 1)) + { + *(uint8_t *)d = filler; + w -= 1; + d += 1; + } + + while (w >= 2 && ((uintptr_t)d & 3)) + { + *(uint16_t *)d = filler; + w -= 2; + d += 2; + } + + while (w >= 4 && ((uintptr_t)d & 15)) + { + *(uint32_t *)d = filler; + + w -= 4; + d += 4; + } + + while (w >= 128) + { + vec_st(vfiller, 0, (uint32_t *) d); + vec_st(vfiller, 0, (uint32_t *) d + 4); + vec_st(vfiller, 0, (uint32_t *) d + 8); + vec_st(vfiller, 0, (uint32_t *) d + 12); + vec_st(vfiller, 0, (uint32_t *) d + 16); + vec_st(vfiller, 0, (uint32_t *) d + 20); + vec_st(vfiller, 0, (uint32_t *) d + 24); + vec_st(vfiller, 0, (uint32_t *) d + 28); + + d += 128; + w -= 128; + } + + if (w >= 64) + { + vec_st(vfiller, 0, (uint32_t *) d); + vec_st(vfiller, 0, (uint32_t *) d + 4); + vec_st(vfiller, 0, (uint32_t *) d + 8); + vec_st(vfiller, 0, (uint32_t *) d + 12); + + d += 64; + w -= 64; + } + + if (w >= 32) + { + vec_st(vfiller, 0, (uint32_t *) d); + vec_st(vfiller, 0, (uint32_t *) d + 4); + + d += 32; + w -= 32; + } + + if (w >= 16) + { + vec_st(vfiller, 0, (uint32_t *) d); + + d += 16; + w -= 16; + } + + while (w >= 4) + { + *(uint32_t *)d = filler; + + w -= 4; + d += 4; + } + + if (w >= 2) + { + *(uint16_t *)d = filler; + w -= 2; + d += 2; + } + + if (w >= 1) + { + *(uint8_t *)d = filler; + w -= 1; + d += 1; + } + } + + return TRUE; +} + +static void +vmx_composite_src_x888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int32_t w; + int dst_stride, src_stride; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + w = width; + + while (w && (uintptr_t)dst & 15) + { + *dst++ = *src++ | 0xff000000; + w--; + } + + while (w >= 16) + { + vector unsigned int vmx_src1, vmx_src2, vmx_src3, vmx_src4; + + vmx_src1 = load_128_unaligned (src); + vmx_src2 = load_128_unaligned (src + 4); + vmx_src3 = load_128_unaligned (src + 8); + vmx_src4 = load_128_unaligned (src + 12); + + save_128_aligned (dst, vec_or (vmx_src1, mask_ff000000)); + save_128_aligned (dst + 4, vec_or (vmx_src2, mask_ff000000)); + save_128_aligned (dst + 8, vec_or (vmx_src3, mask_ff000000)); + save_128_aligned (dst + 12, vec_or (vmx_src4, mask_ff000000)); + + dst += 16; + src += 16; + w -= 16; + } + + while (w) + { + *dst++ = *src++ | 0xff000000; + w--; + } + } +} + +static void +vmx_composite_over_n_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t src, ia; + int i, w, dst_stride; + vector unsigned int vdst, vsrc, via; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + vsrc = (vector unsigned int){src, src, src, src}; + via = negate (splat_alpha (vsrc)); + ia = ALPHA_8 (~src); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + w = width; + + while (w && ((uintptr_t)dst & 15)) + { + uint32_t d = *dst; + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, src); + *dst++ = d; + w--; + } + + for (i = w / 4; i > 0; i--) + { + vdst = pix_multiply (load_128_aligned (dst), via); + save_128_aligned (dst, pix_add (vsrc, vdst)); + dst += 4; + } + + for (i = w % 4; --i >= 0;) + { + uint32_t d = dst[i]; + UN8x4_MUL_UN8_ADD_UN8x4 (d, ia, src); + dst[i] = d; + } + } +} + +static void +vmx_composite_over_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + int dst_stride, src_stride; + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + + dst = dst_line; + src = src_line; + + while (height--) + { + vmx_combine_over_u (imp, op, dst, src, NULL, width); + + dst += dst_stride; + src += src_stride; + } +} + +static void +vmx_composite_over_n_8888_8888_ca (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t src, ia; + uint32_t *dst_line, d; + uint32_t *mask_line, m; + uint32_t pack_cmp; + int dst_stride, mask_stride; + + vector unsigned int vsrc, valpha, vmask, vdest; + + src = _pixman_image_get_solid (imp, src_image, dest_image->bits.format); + + if (src == 0) + return; + + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + PIXMAN_IMAGE_GET_LINE ( + mask_image, mask_x, mask_y, uint32_t, mask_stride, mask_line, 1); + + vsrc = (vector unsigned int) {src, src, src, src}; + valpha = splat_alpha(vsrc); + ia = ALPHA_8 (src); + + while (height--) + { + int w = width; + const uint32_t *pm = (uint32_t *)mask_line; + uint32_t *pd = (uint32_t *)dst_line; + uint32_t s; + + dst_line += dst_stride; + mask_line += mask_stride; + + while (w && (uintptr_t)pd & 15) + { + s = src; + m = *pm++; + + if (m) + { + d = *pd; + UN8x4_MUL_UN8x4 (s, m); + UN8x4_MUL_UN8 (m, ia); + m = ~m; + UN8x4_MUL_UN8x4_ADD_UN8x4 (d, m, s); + *pd = d; + } + + pd++; + w--; + } + + while (w >= 4) + { + /* pm is NOT necessarily 16-byte aligned */ + vmask = load_128_unaligned (pm); + + pack_cmp = vec_all_eq(vmask, (vector unsigned int) AVV(0)); + + /* if all bits in mask are zero, pack_cmp is not 0 */ + if (pack_cmp == 0) + { + /* pd is 16-byte aligned */ + vdest = in_over (vsrc, valpha, vmask, load_128_aligned (pd)); + + save_128_aligned(pd, vdest); + } + + pd += 4; + pm += 4; + w -= 4; + } + + while (w) + { + s = src; + m = *pm++; + + if (m) + { + d = *pd; + UN8x4_MUL_UN8x4 (s, m); + UN8x4_MUL_UN8 (m, ia); + m = ~m; + UN8x4_MUL_UN8x4_ADD_UN8x4 (d, m, s); + *pd = d; + } + + pd++; + w--; + } + } +} + +static void +vmx_composite_add_8_8 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint8_t *dst_line, *dst; + uint8_t *src_line, *src; + int dst_stride, src_stride; + int32_t w; + uint16_t t; + + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint8_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint8_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + src = src_line; + + dst_line += dst_stride; + src_line += src_stride; + w = width; + + /* Small head */ + while (w && (uintptr_t)dst & 3) + { + t = (*dst) + (*src++); + *dst++ = t | (0 - (t >> 8)); + w--; + } + + vmx_combine_add_u (imp, op, + (uint32_t*)dst, (uint32_t*)src, NULL, w >> 2); + + /* Small tail */ + dst += w & 0xfffc; + src += w & 0xfffc; + + w &= 3; + + while (w) + { + t = (*dst) + (*src++); + *dst++ = t | (0 - (t >> 8)); + w--; + } + } +} + +static void +vmx_composite_add_8888_8888 (pixman_implementation_t *imp, + pixman_composite_info_t *info) +{ + PIXMAN_COMPOSITE_ARGS (info); + uint32_t *dst_line, *dst; + uint32_t *src_line, *src; + int dst_stride, src_stride; + + PIXMAN_IMAGE_GET_LINE ( + src_image, src_x, src_y, uint32_t, src_stride, src_line, 1); + PIXMAN_IMAGE_GET_LINE ( + dest_image, dest_x, dest_y, uint32_t, dst_stride, dst_line, 1); + + while (height--) + { + dst = dst_line; + dst_line += dst_stride; + src = src_line; + src_line += src_stride; + + vmx_combine_add_u (imp, op, dst, src, NULL, width); + } +} + +static force_inline void +scaled_nearest_scanline_vmx_8888_8888_OVER (uint32_t* pd, + const uint32_t* ps, + int32_t w, + pixman_fixed_t vx, + pixman_fixed_t unit_x, + pixman_fixed_t src_width_fixed, + pixman_bool_t fully_transparent_src) +{ + uint32_t s, d; + const uint32_t* pm = NULL; + + vector unsigned int vsrc, vdst; + + if (fully_transparent_src) + return; + + /* Align dst on a 16-byte boundary */ + while (w && ((uintptr_t)pd & 15)) + { + d = *pd; + s = combine1 (ps + pixman_fixed_to_int (vx), pm); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + *pd++ = core_combine_over_u_pixel_vmx (s, d); + if (pm) + pm++; + w--; + } + + while (w >= 4) + { + vector unsigned int tmp; + uint32_t tmp1, tmp2, tmp3, tmp4; + + tmp1 = *(ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp2 = *(ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp3 = *(ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + tmp4 = *(ps + pixman_fixed_to_int (vx)); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + tmp[0] = tmp1; + tmp[1] = tmp2; + tmp[2] = tmp3; + tmp[3] = tmp4; + + vsrc = combine4 ((const uint32_t *) &tmp, pm); + + if (is_opaque (vsrc)) + { + save_128_aligned (pd, vsrc); + } + else if (!is_zero (vsrc)) + { + vdst = over(vsrc, splat_alpha(vsrc), load_128_aligned (pd)); + + save_128_aligned (pd, vdst); + } + + w -= 4; + pd += 4; + if (pm) + pm += 4; + } + + while (w) + { + d = *pd; + s = combine1 (ps + pixman_fixed_to_int (vx), pm); + vx += unit_x; + while (vx >= 0) + vx -= src_width_fixed; + + *pd++ = core_combine_over_u_pixel_vmx (s, d); + if (pm) + pm++; + + w--; + } +} + +FAST_NEAREST_MAINLOOP (vmx_8888_8888_cover_OVER, + scaled_nearest_scanline_vmx_8888_8888_OVER, + uint32_t, uint32_t, COVER) +FAST_NEAREST_MAINLOOP (vmx_8888_8888_none_OVER, + scaled_nearest_scanline_vmx_8888_8888_OVER, + uint32_t, uint32_t, NONE) +FAST_NEAREST_MAINLOOP (vmx_8888_8888_pad_OVER, + scaled_nearest_scanline_vmx_8888_8888_OVER, + uint32_t, uint32_t, PAD) +FAST_NEAREST_MAINLOOP (vmx_8888_8888_normal_OVER, + scaled_nearest_scanline_vmx_8888_8888_OVER, + uint32_t, uint32_t, NORMAL) + +static const pixman_fast_path_t vmx_fast_paths[] = +{ + PIXMAN_STD_FAST_PATH (OVER, solid, null, a8r8g8b8, vmx_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, null, x8r8g8b8, vmx_composite_over_n_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, a8r8g8b8, vmx_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8r8g8b8, null, x8r8g8b8, vmx_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, a8b8g8r8, vmx_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, a8b8g8r8, null, x8b8g8r8, vmx_composite_over_8888_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8r8g8b8, vmx_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8r8g8b8, vmx_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, a8b8g8r8, vmx_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH (OVER, solid, a8, x8b8g8r8, vmx_composite_over_n_8_8888), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, a8r8g8b8, vmx_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8r8g8b8, x8r8g8b8, vmx_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, a8b8g8r8, vmx_composite_over_n_8888_8888_ca), + PIXMAN_STD_FAST_PATH_CA (OVER, solid, a8b8g8r8, x8b8g8r8, vmx_composite_over_n_8888_8888_ca), + + /* PIXMAN_OP_ADD */ + PIXMAN_STD_FAST_PATH (ADD, a8, null, a8, vmx_composite_add_8_8), + PIXMAN_STD_FAST_PATH (ADD, a8r8g8b8, null, a8r8g8b8, vmx_composite_add_8888_8888), + PIXMAN_STD_FAST_PATH (ADD, a8b8g8r8, null, a8b8g8r8, vmx_composite_add_8888_8888), + + /* PIXMAN_OP_SRC */ + PIXMAN_STD_FAST_PATH (SRC, x8r8g8b8, null, a8r8g8b8, vmx_composite_src_x888_8888), + PIXMAN_STD_FAST_PATH (SRC, x8b8g8r8, null, a8b8g8r8, vmx_composite_src_x888_8888), + + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, vmx_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, vmx_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, vmx_8888_8888), + SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, vmx_8888_8888), + + { PIXMAN_OP_NONE }, +}; + +static uint32_t * +vmx_fetch_x8r8g8b8 (pixman_iter_t *iter, const uint32_t *mask) +{ + int w = iter->width; + vector unsigned int ff000000 = mask_ff000000; + uint32_t *dst = iter->buffer; + uint32_t *src = (uint32_t *)iter->bits; + + iter->bits += iter->stride; + + while (w && ((uintptr_t)dst) & 0x0f) + { + *dst++ = (*src++) | 0xff000000; + w--; + } + + while (w >= 4) + { + save_128_aligned(dst, vec_or(load_128_unaligned(src), ff000000)); + + dst += 4; + src += 4; + w -= 4; + } + + while (w) + { + *dst++ = (*src++) | 0xff000000; + w--; + } + + return iter->buffer; +} + +static uint32_t * +vmx_fetch_a8 (pixman_iter_t *iter, const uint32_t *mask) +{ + int w = iter->width; + uint32_t *dst = iter->buffer; + uint8_t *src = iter->bits; + vector unsigned int vmx0, vmx1, vmx2, vmx3, vmx4, vmx5, vmx6; + + iter->bits += iter->stride; + + while (w && (((uintptr_t)dst) & 15)) + { + *dst++ = *(src++) << 24; + w--; + } + + while (w >= 16) + { + vmx0 = load_128_unaligned((uint32_t *) src); + + unpack_128_2x128((vector unsigned int) AVV(0), vmx0, &vmx1, &vmx2); + unpack_128_2x128_16((vector unsigned int) AVV(0), vmx1, &vmx3, &vmx4); + unpack_128_2x128_16((vector unsigned int) AVV(0), vmx2, &vmx5, &vmx6); + + save_128_aligned(dst, vmx6); + save_128_aligned((dst + 4), vmx5); + save_128_aligned((dst + 8), vmx4); + save_128_aligned((dst + 12), vmx3); + + dst += 16; + src += 16; + w -= 16; + } + + while (w) + { + *dst++ = *(src++) << 24; + w--; + } + + return iter->buffer; +} + +#define IMAGE_FLAGS \ + (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \ + FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) + +static const pixman_iter_info_t vmx_iters[] = +{ + { PIXMAN_x8r8g8b8, IMAGE_FLAGS, ITER_NARROW, + _pixman_iter_init_bits_stride, vmx_fetch_x8r8g8b8, NULL + }, + { PIXMAN_a8, IMAGE_FLAGS, ITER_NARROW, + _pixman_iter_init_bits_stride, vmx_fetch_a8, NULL + }, + { PIXMAN_null }, +}; + +pixman_implementation_t * +_pixman_implementation_create_vmx (pixman_implementation_t *fallback) +{ + pixman_implementation_t *imp = _pixman_implementation_create (fallback, vmx_fast_paths); + + /* VMX constants */ + mask_ff000000 = create_mask_32_128 (0xff000000); + mask_red = create_mask_32_128 (0x00f80000); + mask_green = create_mask_32_128 (0x0000fc00); + mask_blue = create_mask_32_128 (0x000000f8); + mask_565_fix_rb = create_mask_32_128 (0x00e000e0); + mask_565_fix_g = create_mask_32_128 (0x0000c000); + + /* Set up function pointers */ + + imp->combine_32[PIXMAN_OP_OVER] = vmx_combine_over_u; + imp->combine_32[PIXMAN_OP_OVER_REVERSE] = vmx_combine_over_reverse_u; + imp->combine_32[PIXMAN_OP_IN] = vmx_combine_in_u; + imp->combine_32[PIXMAN_OP_IN_REVERSE] = vmx_combine_in_reverse_u; + imp->combine_32[PIXMAN_OP_OUT] = vmx_combine_out_u; + imp->combine_32[PIXMAN_OP_OUT_REVERSE] = vmx_combine_out_reverse_u; + imp->combine_32[PIXMAN_OP_ATOP] = vmx_combine_atop_u; + imp->combine_32[PIXMAN_OP_ATOP_REVERSE] = vmx_combine_atop_reverse_u; + imp->combine_32[PIXMAN_OP_XOR] = vmx_combine_xor_u; + + imp->combine_32[PIXMAN_OP_ADD] = vmx_combine_add_u; + + imp->combine_32_ca[PIXMAN_OP_SRC] = vmx_combine_src_ca; + imp->combine_32_ca[PIXMAN_OP_OVER] = vmx_combine_over_ca; + imp->combine_32_ca[PIXMAN_OP_OVER_REVERSE] = vmx_combine_over_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_IN] = vmx_combine_in_ca; + imp->combine_32_ca[PIXMAN_OP_IN_REVERSE] = vmx_combine_in_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_OUT] = vmx_combine_out_ca; + imp->combine_32_ca[PIXMAN_OP_OUT_REVERSE] = vmx_combine_out_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_ATOP] = vmx_combine_atop_ca; + imp->combine_32_ca[PIXMAN_OP_ATOP_REVERSE] = vmx_combine_atop_reverse_ca; + imp->combine_32_ca[PIXMAN_OP_XOR] = vmx_combine_xor_ca; + imp->combine_32_ca[PIXMAN_OP_ADD] = vmx_combine_add_ca; + + imp->fill = vmx_fill; + + imp->iter_info = vmx_iters; + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman-x86.c b/gfx/cairo/libpixman/src/pixman-x86.c new file mode 100644 index 0000000000..2c702e5c3b --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-x86.c @@ -0,0 +1,253 @@ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "pixman-private.h" + +#if defined(USE_X86_MMX) || defined (USE_SSE2) || defined (USE_SSSE3) + +/* The CPU detection code needs to be in a file not compiled with + * "-mmmx -msse", as gcc would generate CMOV instructions otherwise + * that would lead to SIGILL instructions on old CPUs that don't have + * it. + */ + +typedef enum +{ + X86_MMX = (1 << 0), + X86_MMX_EXTENSIONS = (1 << 1), + X86_SSE = (1 << 2) | X86_MMX_EXTENSIONS, + X86_SSE2 = (1 << 3), + X86_CMOV = (1 << 4), + X86_SSSE3 = (1 << 5) +} cpu_features_t; + +#ifdef HAVE_GETISAX + +#include + +static cpu_features_t +detect_cpu_features (void) +{ + cpu_features_t features = 0; + unsigned int result = 0; + + if (getisax (&result, 1)) + { + if (result & AV_386_CMOV) + features |= X86_CMOV; + if (result & AV_386_MMX) + features |= X86_MMX; + if (result & AV_386_AMD_MMX) + features |= X86_MMX_EXTENSIONS; + if (result & AV_386_SSE) + features |= X86_SSE; + if (result & AV_386_SSE2) + features |= X86_SSE2; + if (result & AV_386_SSSE3) + features |= X86_SSSE3; + } + + return features; +} + +#else + +#define _PIXMAN_X86_64 \ + (defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)) + +static pixman_bool_t +have_cpuid (void) +{ +#if _PIXMAN_X86_64 || defined (_MSC_VER) + + return TRUE; + +#elif defined (__GNUC__) + uint32_t result; + + __asm__ volatile ( + "pushf" "\n\t" + "pop %%eax" "\n\t" + "mov %%eax, %%ecx" "\n\t" + "xor $0x00200000, %%eax" "\n\t" + "push %%eax" "\n\t" + "popf" "\n\t" + "pushf" "\n\t" + "pop %%eax" "\n\t" + "xor %%ecx, %%eax" "\n\t" + "mov %%eax, %0" "\n\t" + : "=r" (result) + : + : "%eax", "%ecx"); + + return !!result; + +#else +#error "Unknown compiler" +#endif +} + +#ifdef _MSC_VER +#include /* for __cpuid */ +#endif + +static void +pixman_cpuid (uint32_t feature, + uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) +{ +#if defined (__GNUC__) + +#if _PIXMAN_X86_64 + __asm__ volatile ( + "cpuid" "\n\t" + : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) + : "a" (feature)); +#else + /* On x86-32 we need to be careful about the handling of %ebx + * and %esp. We can't declare either one as clobbered + * since they are special registers (%ebx is the "PIC + * register" holding an offset to global data, %esp the + * stack pointer), so we need to make sure that %ebx is + * preserved, and that %esp has its original value when + * accessing the output operands. + */ + __asm__ volatile ( + "xchg %%ebx, %1" "\n\t" + "cpuid" "\n\t" + "xchg %%ebx, %1" "\n\t" + : "=a" (*a), "=r" (*b), "=c" (*c), "=d" (*d) + : "a" (feature)); +#endif + +#elif defined (_MSC_VER) + int info[4]; + + __cpuid (info, feature); + + *a = info[0]; + *b = info[1]; + *c = info[2]; + *d = info[3]; +#else +#error Unknown compiler +#endif +} + +static cpu_features_t +detect_cpu_features (void) +{ + uint32_t a, b, c, d; + cpu_features_t features = 0; + + if (!have_cpuid()) + return features; + + /* Get feature bits */ + pixman_cpuid (0x01, &a, &b, &c, &d); + if (d & (1 << 15)) + features |= X86_CMOV; + if (d & (1 << 23)) + features |= X86_MMX; + if (d & (1 << 25)) + features |= X86_SSE; + if (d & (1 << 26)) + features |= X86_SSE2; + if (c & (1 << 9)) + features |= X86_SSSE3; + + /* Check for AMD specific features */ + if ((features & X86_MMX) && !(features & X86_SSE)) + { + char vendor[13]; + + /* Get vendor string */ + memset (vendor, 0, sizeof vendor); + + pixman_cpuid (0x00, &a, &b, &c, &d); + memcpy (vendor + 0, &b, 4); + memcpy (vendor + 4, &d, 4); + memcpy (vendor + 8, &c, 4); + + if (strcmp (vendor, "AuthenticAMD") == 0 || + strcmp (vendor, "HygonGenuine") == 0 || + strcmp (vendor, "Geode by NSC") == 0) + { + pixman_cpuid (0x80000000, &a, &b, &c, &d); + if (a >= 0x80000001) + { + pixman_cpuid (0x80000001, &a, &b, &c, &d); + + if (d & (1 << 22)) + features |= X86_MMX_EXTENSIONS; + } + } + } + + return features; +} + +#endif + +static pixman_bool_t +have_feature (cpu_features_t feature) +{ + static pixman_bool_t initialized; + static cpu_features_t features; + + if (!initialized) + { + features = detect_cpu_features(); + initialized = TRUE; + } + + return (features & feature) == feature; +} + +#endif + +pixman_implementation_t * +_pixman_x86_get_implementations (pixman_implementation_t *imp) +{ +#define MMX_BITS (X86_MMX | X86_MMX_EXTENSIONS) +#define SSE2_BITS (X86_MMX | X86_MMX_EXTENSIONS | X86_SSE | X86_SSE2) +#define SSSE3_BITS (X86_SSE | X86_SSE2 | X86_SSSE3) + +#ifdef USE_X86_MMX + if (!_pixman_disabled ("mmx") && have_feature (MMX_BITS)) + imp = _pixman_implementation_create_mmx (imp); +#endif + +#ifdef USE_SSE2 + if (!_pixman_disabled ("sse2") && have_feature (SSE2_BITS)) + imp = _pixman_implementation_create_sse2 (imp); +#endif + +#ifdef USE_SSSE3 + if (!_pixman_disabled ("ssse3") && have_feature (SSSE3_BITS)) + imp = _pixman_implementation_create_ssse3 (imp); +#endif + + return imp; +} diff --git a/gfx/cairo/libpixman/src/pixman.c b/gfx/cairo/libpixman/src/pixman.c new file mode 100644 index 0000000000..c09b528083 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman.c @@ -0,0 +1,1133 @@ +/* -*- Mode: c; c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t; -*- */ +/* + * Copyright © 2000 SuSE, Inc. + * Copyright © 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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: Keith Packard, SuSE, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include "pixman-private.h" + +#include + +pixman_implementation_t *global_implementation; + +#ifdef TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR +static void __attribute__((constructor)) +pixman_constructor (void) +{ + global_implementation = _pixman_choose_implementation (); +} +#endif + +typedef struct operator_info_t operator_info_t; + +struct operator_info_t +{ + uint8_t opaque_info[4]; +}; + +#define PACK(neither, src, dest, both) \ + {{ (uint8_t)PIXMAN_OP_ ## neither, \ + (uint8_t)PIXMAN_OP_ ## src, \ + (uint8_t)PIXMAN_OP_ ## dest, \ + (uint8_t)PIXMAN_OP_ ## both }} + +static const operator_info_t operator_table[] = +{ + /* Neither Opaque Src Opaque Dst Opaque Both Opaque */ + PACK (CLEAR, CLEAR, CLEAR, CLEAR), + PACK (SRC, SRC, SRC, SRC), + PACK (DST, DST, DST, DST), + PACK (OVER, SRC, OVER, SRC), + PACK (OVER_REVERSE, OVER_REVERSE, DST, DST), + PACK (IN, IN, SRC, SRC), + PACK (IN_REVERSE, DST, IN_REVERSE, DST), + PACK (OUT, OUT, CLEAR, CLEAR), + PACK (OUT_REVERSE, CLEAR, OUT_REVERSE, CLEAR), + PACK (ATOP, IN, OVER, SRC), + PACK (ATOP_REVERSE, OVER_REVERSE, IN_REVERSE, DST), + PACK (XOR, OUT, OUT_REVERSE, CLEAR), + PACK (ADD, ADD, ADD, ADD), + PACK (SATURATE, OVER_REVERSE, DST, DST), + + {{ 0 /* 0x0e */ }}, + {{ 0 /* 0x0f */ }}, + + PACK (CLEAR, CLEAR, CLEAR, CLEAR), + PACK (SRC, SRC, SRC, SRC), + PACK (DST, DST, DST, DST), + PACK (DISJOINT_OVER, DISJOINT_OVER, DISJOINT_OVER, DISJOINT_OVER), + PACK (DISJOINT_OVER_REVERSE, DISJOINT_OVER_REVERSE, DISJOINT_OVER_REVERSE, DISJOINT_OVER_REVERSE), + PACK (DISJOINT_IN, DISJOINT_IN, DISJOINT_IN, DISJOINT_IN), + PACK (DISJOINT_IN_REVERSE, DISJOINT_IN_REVERSE, DISJOINT_IN_REVERSE, DISJOINT_IN_REVERSE), + PACK (DISJOINT_OUT, DISJOINT_OUT, DISJOINT_OUT, DISJOINT_OUT), + PACK (DISJOINT_OUT_REVERSE, DISJOINT_OUT_REVERSE, DISJOINT_OUT_REVERSE, DISJOINT_OUT_REVERSE), + PACK (DISJOINT_ATOP, DISJOINT_ATOP, DISJOINT_ATOP, DISJOINT_ATOP), + PACK (DISJOINT_ATOP_REVERSE, DISJOINT_ATOP_REVERSE, DISJOINT_ATOP_REVERSE, DISJOINT_ATOP_REVERSE), + PACK (DISJOINT_XOR, DISJOINT_XOR, DISJOINT_XOR, DISJOINT_XOR), + + {{ 0 /* 0x1c */ }}, + {{ 0 /* 0x1d */ }}, + {{ 0 /* 0x1e */ }}, + {{ 0 /* 0x1f */ }}, + + PACK (CLEAR, CLEAR, CLEAR, CLEAR), + PACK (SRC, SRC, SRC, SRC), + PACK (DST, DST, DST, DST), + PACK (CONJOINT_OVER, CONJOINT_OVER, CONJOINT_OVER, CONJOINT_OVER), + PACK (CONJOINT_OVER_REVERSE, CONJOINT_OVER_REVERSE, CONJOINT_OVER_REVERSE, CONJOINT_OVER_REVERSE), + PACK (CONJOINT_IN, CONJOINT_IN, CONJOINT_IN, CONJOINT_IN), + PACK (CONJOINT_IN_REVERSE, CONJOINT_IN_REVERSE, CONJOINT_IN_REVERSE, CONJOINT_IN_REVERSE), + PACK (CONJOINT_OUT, CONJOINT_OUT, CONJOINT_OUT, CONJOINT_OUT), + PACK (CONJOINT_OUT_REVERSE, CONJOINT_OUT_REVERSE, CONJOINT_OUT_REVERSE, CONJOINT_OUT_REVERSE), + PACK (CONJOINT_ATOP, CONJOINT_ATOP, CONJOINT_ATOP, CONJOINT_ATOP), + PACK (CONJOINT_ATOP_REVERSE, CONJOINT_ATOP_REVERSE, CONJOINT_ATOP_REVERSE, CONJOINT_ATOP_REVERSE), + PACK (CONJOINT_XOR, CONJOINT_XOR, CONJOINT_XOR, CONJOINT_XOR), + + {{ 0 /* 0x2c */ }}, + {{ 0 /* 0x2d */ }}, + {{ 0 /* 0x2e */ }}, + {{ 0 /* 0x2f */ }}, + + PACK (MULTIPLY, MULTIPLY, MULTIPLY, MULTIPLY), + PACK (SCREEN, SCREEN, SCREEN, SCREEN), + PACK (OVERLAY, OVERLAY, OVERLAY, OVERLAY), + PACK (DARKEN, DARKEN, DARKEN, DARKEN), + PACK (LIGHTEN, LIGHTEN, LIGHTEN, LIGHTEN), + PACK (COLOR_DODGE, COLOR_DODGE, COLOR_DODGE, COLOR_DODGE), + PACK (COLOR_BURN, COLOR_BURN, COLOR_BURN, COLOR_BURN), + PACK (HARD_LIGHT, HARD_LIGHT, HARD_LIGHT, HARD_LIGHT), + PACK (SOFT_LIGHT, SOFT_LIGHT, SOFT_LIGHT, SOFT_LIGHT), + PACK (DIFFERENCE, DIFFERENCE, DIFFERENCE, DIFFERENCE), + PACK (EXCLUSION, EXCLUSION, EXCLUSION, EXCLUSION), + PACK (HSL_HUE, HSL_HUE, HSL_HUE, HSL_HUE), + PACK (HSL_SATURATION, HSL_SATURATION, HSL_SATURATION, HSL_SATURATION), + PACK (HSL_COLOR, HSL_COLOR, HSL_COLOR, HSL_COLOR), + PACK (HSL_LUMINOSITY, HSL_LUMINOSITY, HSL_LUMINOSITY, HSL_LUMINOSITY), +}; + +/* + * Optimize the current operator based on opacity of source or destination + * The output operator should be mathematically equivalent to the source. + */ +static pixman_op_t +optimize_operator (pixman_op_t op, + uint32_t src_flags, + uint32_t mask_flags, + uint32_t dst_flags) +{ + pixman_bool_t is_source_opaque, is_dest_opaque; + +#define OPAQUE_SHIFT 13 + + COMPILE_TIME_ASSERT (FAST_PATH_IS_OPAQUE == (1 << OPAQUE_SHIFT)); + + is_dest_opaque = (dst_flags & FAST_PATH_IS_OPAQUE); + is_source_opaque = ((src_flags & mask_flags) & FAST_PATH_IS_OPAQUE); + + is_dest_opaque >>= OPAQUE_SHIFT - 1; + is_source_opaque >>= OPAQUE_SHIFT; + + return operator_table[op].opaque_info[is_dest_opaque | is_source_opaque]; +} + +/* + * Computing composite region + */ +static inline pixman_bool_t +clip_general_image (pixman_region32_t * region, + pixman_region32_t * clip, + int dx, + int dy) +{ + if (pixman_region32_n_rects (region) == 1 && + pixman_region32_n_rects (clip) == 1) + { + pixman_box32_t * rbox = pixman_region32_rectangles (region, NULL); + pixman_box32_t * cbox = pixman_region32_rectangles (clip, NULL); + int v; + + if (rbox->x1 < (v = cbox->x1 + dx)) + rbox->x1 = v; + if (rbox->x2 > (v = cbox->x2 + dx)) + rbox->x2 = v; + if (rbox->y1 < (v = cbox->y1 + dy)) + rbox->y1 = v; + if (rbox->y2 > (v = cbox->y2 + dy)) + rbox->y2 = v; + if (rbox->x1 >= rbox->x2 || rbox->y1 >= rbox->y2) + { + pixman_region32_init (region); + return FALSE; + } + } + else if (!pixman_region32_not_empty (clip)) + { + return FALSE; + } + else + { + if (dx || dy) + pixman_region32_translate (region, -dx, -dy); + + if (!pixman_region32_intersect (region, region, clip)) + return FALSE; + + if (dx || dy) + pixman_region32_translate (region, dx, dy); + } + + return pixman_region32_not_empty (region); +} + +static inline pixman_bool_t +clip_source_image (pixman_region32_t * region, + pixman_image_t * image, + int dx, + int dy) +{ + /* Source clips are ignored, unless they are explicitly turned on + * and the clip in question was set by an X client. (Because if + * the clip was not set by a client, then it is a hierarchy + * clip and those should always be ignored for sources). + */ + if (!image->common.clip_sources || !image->common.client_clip) + return TRUE; + + return clip_general_image (region, + &image->common.clip_region, + dx, dy); +} + +/* + * returns FALSE if the final region is empty. Indistinguishable from + * an allocation failure, but rendering ignores those anyways. + */ +pixman_bool_t +_pixman_compute_composite_region32 (pixman_region32_t * region, + pixman_image_t * src_image, + pixman_image_t * mask_image, + pixman_image_t * dest_image, + int32_t src_x, + int32_t src_y, + int32_t mask_x, + int32_t mask_y, + int32_t dest_x, + int32_t dest_y, + int32_t width, + int32_t height) +{ + region->extents.x1 = dest_x; + region->extents.x2 = dest_x + width; + region->extents.y1 = dest_y; + region->extents.y2 = dest_y + height; + + region->extents.x1 = MAX (region->extents.x1, 0); + region->extents.y1 = MAX (region->extents.y1, 0); + region->extents.x2 = MIN (region->extents.x2, dest_image->bits.width); + region->extents.y2 = MIN (region->extents.y2, dest_image->bits.height); + + region->data = 0; + + /* Check for empty operation */ + if (region->extents.x1 >= region->extents.x2 || + region->extents.y1 >= region->extents.y2) + { + region->extents.x1 = 0; + region->extents.x2 = 0; + region->extents.y1 = 0; + region->extents.y2 = 0; + return FALSE; + } + + if (dest_image->common.have_clip_region) + { + if (!clip_general_image (region, &dest_image->common.clip_region, 0, 0)) + return FALSE; + } + + if (dest_image->common.alpha_map) + { + if (!pixman_region32_intersect_rect (region, region, + dest_image->common.alpha_origin_x, + dest_image->common.alpha_origin_y, + dest_image->common.alpha_map->width, + dest_image->common.alpha_map->height)) + { + return FALSE; + } + if (!pixman_region32_not_empty (region)) + return FALSE; + if (dest_image->common.alpha_map->common.have_clip_region) + { + if (!clip_general_image (region, &dest_image->common.alpha_map->common.clip_region, + -dest_image->common.alpha_origin_x, + -dest_image->common.alpha_origin_y)) + { + return FALSE; + } + } + } + + /* clip against src */ + if (src_image->common.have_clip_region) + { + if (!clip_source_image (region, src_image, dest_x - src_x, dest_y - src_y)) + return FALSE; + } + if (src_image->common.alpha_map && src_image->common.alpha_map->common.have_clip_region) + { + if (!clip_source_image (region, (pixman_image_t *)src_image->common.alpha_map, + dest_x - (src_x - src_image->common.alpha_origin_x), + dest_y - (src_y - src_image->common.alpha_origin_y))) + { + return FALSE; + } + } + /* clip against mask */ + if (mask_image && mask_image->common.have_clip_region) + { + if (!clip_source_image (region, mask_image, dest_x - mask_x, dest_y - mask_y)) + return FALSE; + + if (mask_image->common.alpha_map && mask_image->common.alpha_map->common.have_clip_region) + { + if (!clip_source_image (region, (pixman_image_t *)mask_image->common.alpha_map, + dest_x - (mask_x - mask_image->common.alpha_origin_x), + dest_y - (mask_y - mask_image->common.alpha_origin_y))) + { + return FALSE; + } + } + } + + return TRUE; +} + +typedef struct box_48_16 box_48_16_t; + +struct box_48_16 +{ + pixman_fixed_48_16_t x1; + pixman_fixed_48_16_t y1; + pixman_fixed_48_16_t x2; + pixman_fixed_48_16_t y2; +}; + +static pixman_bool_t +compute_transformed_extents (pixman_transform_t *transform, + const pixman_box32_t *extents, + box_48_16_t *transformed) +{ + pixman_fixed_48_16_t tx1, ty1, tx2, ty2; + pixman_fixed_t x1, y1, x2, y2; + int i; + + x1 = pixman_int_to_fixed (extents->x1) + pixman_fixed_1 / 2; + y1 = pixman_int_to_fixed (extents->y1) + pixman_fixed_1 / 2; + x2 = pixman_int_to_fixed (extents->x2) - pixman_fixed_1 / 2; + y2 = pixman_int_to_fixed (extents->y2) - pixman_fixed_1 / 2; + + if (!transform) + { + transformed->x1 = x1; + transformed->y1 = y1; + transformed->x2 = x2; + transformed->y2 = y2; + + return TRUE; + } + + tx1 = ty1 = INT64_MAX; + tx2 = ty2 = INT64_MIN; + + for (i = 0; i < 4; ++i) + { + pixman_fixed_48_16_t tx, ty; + pixman_vector_t v; + + v.vector[0] = (i & 0x01)? x1 : x2; + v.vector[1] = (i & 0x02)? y1 : y2; + v.vector[2] = pixman_fixed_1; + + if (!pixman_transform_point (transform, &v)) + return FALSE; + + tx = (pixman_fixed_48_16_t)v.vector[0]; + ty = (pixman_fixed_48_16_t)v.vector[1]; + + if (tx < tx1) + tx1 = tx; + if (ty < ty1) + ty1 = ty; + if (tx > tx2) + tx2 = tx; + if (ty > ty2) + ty2 = ty; + } + + transformed->x1 = tx1; + transformed->y1 = ty1; + transformed->x2 = tx2; + transformed->y2 = ty2; + + return TRUE; +} + +#define IS_16BIT(x) (((x) >= INT16_MIN) && ((x) <= INT16_MAX)) +#define ABS(f) (((f) < 0)? (-(f)) : (f)) +#define IS_16_16(f) (((f) >= pixman_min_fixed_48_16 && ((f) <= pixman_max_fixed_48_16))) + +static pixman_bool_t +analyze_extent (pixman_image_t *image, + const pixman_box32_t *extents, + uint32_t *flags) +{ + pixman_transform_t *transform; + pixman_fixed_t x_off, y_off; + pixman_fixed_t width, height; + pixman_fixed_t *params; + box_48_16_t transformed; + pixman_box32_t exp_extents; + + if (!image) + return TRUE; + + /* Some compositing functions walk one step + * outside the destination rectangle, so we + * check here that the expanded-by-one source + * extents in destination space fits in 16 bits + */ + if (!IS_16BIT (extents->x1 - 1) || + !IS_16BIT (extents->y1 - 1) || + !IS_16BIT (extents->x2 + 1) || + !IS_16BIT (extents->y2 + 1)) + { + return FALSE; + } + + transform = image->common.transform; + if (image->common.type == BITS) + { + /* During repeat mode calculations we might convert the + * width/height of an image to fixed 16.16, so we need + * them to be smaller than 16 bits. + */ + if (image->bits.width >= 0x7fff || image->bits.height >= 0x7fff) + return FALSE; + + if ((image->common.flags & FAST_PATH_ID_TRANSFORM) == FAST_PATH_ID_TRANSFORM && + extents->x1 >= 0 && + extents->y1 >= 0 && + extents->x2 <= image->bits.width && + extents->y2 <= image->bits.height) + { + *flags |= FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; + return TRUE; + } + + switch (image->common.filter) + { + case PIXMAN_FILTER_CONVOLUTION: + params = image->common.filter_params; + x_off = - pixman_fixed_e - ((params[0] - pixman_fixed_1) >> 1); + y_off = - pixman_fixed_e - ((params[1] - pixman_fixed_1) >> 1); + width = params[0]; + height = params[1]; + break; + + case PIXMAN_FILTER_SEPARABLE_CONVOLUTION: + params = image->common.filter_params; + x_off = - pixman_fixed_e - ((params[0] - pixman_fixed_1) >> 1); + y_off = - pixman_fixed_e - ((params[1] - pixman_fixed_1) >> 1); + width = params[0]; + height = params[1]; + break; + + case PIXMAN_FILTER_GOOD: + case PIXMAN_FILTER_BEST: + case PIXMAN_FILTER_BILINEAR: + x_off = - pixman_fixed_1 / 2; + y_off = - pixman_fixed_1 / 2; + width = pixman_fixed_1; + height = pixman_fixed_1; + break; + + case PIXMAN_FILTER_FAST: + case PIXMAN_FILTER_NEAREST: + x_off = - pixman_fixed_e; + y_off = - pixman_fixed_e; + width = 0; + height = 0; + break; + + default: + return FALSE; + } + } + else + { + x_off = 0; + y_off = 0; + width = 0; + height = 0; + } + + if (!compute_transformed_extents (transform, extents, &transformed)) + return FALSE; + + if (image->common.type == BITS) + { + if (pixman_fixed_to_int (transformed.x1 - pixman_fixed_e) >= 0 && + pixman_fixed_to_int (transformed.y1 - pixman_fixed_e) >= 0 && + pixman_fixed_to_int (transformed.x2 - pixman_fixed_e) < image->bits.width && + pixman_fixed_to_int (transformed.y2 - pixman_fixed_e) < image->bits.height) + { + *flags |= FAST_PATH_SAMPLES_COVER_CLIP_NEAREST; + } + + if (pixman_fixed_to_int (transformed.x1 - pixman_fixed_1 / 2) >= 0 && + pixman_fixed_to_int (transformed.y1 - pixman_fixed_1 / 2) >= 0 && + pixman_fixed_to_int (transformed.x2 + pixman_fixed_1 / 2) < image->bits.width && + pixman_fixed_to_int (transformed.y2 + pixman_fixed_1 / 2) < image->bits.height) + { + *flags |= FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR; + } + } + + /* Check we don't overflow when the destination extents are expanded by one. + * This ensures that compositing functions can simply walk the source space + * using 16.16 variables without worrying about overflow. + */ + exp_extents = *extents; + exp_extents.x1 -= 1; + exp_extents.y1 -= 1; + exp_extents.x2 += 1; + exp_extents.y2 += 1; + + if (!compute_transformed_extents (transform, &exp_extents, &transformed)) + return FALSE; + + if (!IS_16_16 (transformed.x1 + x_off - 8 * pixman_fixed_e) || + !IS_16_16 (transformed.y1 + y_off - 8 * pixman_fixed_e) || + !IS_16_16 (transformed.x2 + x_off + 8 * pixman_fixed_e + width) || + !IS_16_16 (transformed.y2 + y_off + 8 * pixman_fixed_e + height)) + { + return FALSE; + } + + return TRUE; +} + +/* + * Work around GCC bug causing crashes in Mozilla with SSE2 + * + * When using -msse, gcc generates movdqa instructions assuming that + * the stack is 16 byte aligned. Unfortunately some applications, such + * as Mozilla and Mono, end up aligning the stack to 4 bytes, which + * causes the movdqa instructions to fail. + * + * The __force_align_arg_pointer__ makes gcc generate a prologue that + * realigns the stack pointer to 16 bytes. + * + * On x86-64 this is not necessary because the standard ABI already + * calls for a 16 byte aligned stack. + * + * See https://bugs.freedesktop.org/show_bug.cgi?id=15693 + */ +#if defined (USE_SSE2) && defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__) +__attribute__((__force_align_arg_pointer__)) +#endif +PIXMAN_EXPORT void +pixman_image_composite32 (pixman_op_t op, + pixman_image_t * src, + pixman_image_t * mask, + pixman_image_t * dest, + int32_t src_x, + int32_t src_y, + int32_t mask_x, + int32_t mask_y, + int32_t dest_x, + int32_t dest_y, + int32_t width, + int32_t height) +{ + pixman_format_code_t src_format, mask_format, dest_format; + pixman_region32_t region; + pixman_box32_t extents; + pixman_implementation_t *imp; + pixman_composite_func_t func; + pixman_composite_info_t info; + const pixman_box32_t *pbox; + int n; + + _pixman_image_validate (src); + if (mask) + _pixman_image_validate (mask); + _pixman_image_validate (dest); + + src_format = src->common.extended_format_code; + info.src_flags = src->common.flags; + + if (mask && !(mask->common.flags & FAST_PATH_IS_OPAQUE)) + { + mask_format = mask->common.extended_format_code; + info.mask_flags = mask->common.flags; + } + else + { + mask_format = PIXMAN_null; + info.mask_flags = FAST_PATH_IS_OPAQUE | FAST_PATH_NO_ALPHA_MAP; + } + + dest_format = dest->common.extended_format_code; + info.dest_flags = dest->common.flags; + + /* Check for pixbufs */ + if ((mask_format == PIXMAN_a8r8g8b8 || mask_format == PIXMAN_a8b8g8r8) && + (src->type == BITS && src->bits.bits == mask->bits.bits) && + (src->common.repeat == mask->common.repeat) && + (info.src_flags & info.mask_flags & FAST_PATH_ID_TRANSFORM) && + (src_x == mask_x && src_y == mask_y)) + { + if (src_format == PIXMAN_x8b8g8r8) + src_format = mask_format = PIXMAN_pixbuf; + else if (src_format == PIXMAN_x8r8g8b8) + src_format = mask_format = PIXMAN_rpixbuf; + } + + pixman_region32_init (®ion); + + if (!_pixman_compute_composite_region32 ( + ®ion, src, mask, dest, + src_x, src_y, mask_x, mask_y, dest_x, dest_y, width, height)) + { + goto out; + } + + extents = *pixman_region32_extents (®ion); + + extents.x1 -= dest_x - src_x; + extents.y1 -= dest_y - src_y; + extents.x2 -= dest_x - src_x; + extents.y2 -= dest_y - src_y; + + if (!analyze_extent (src, &extents, &info.src_flags)) + goto out; + + extents.x1 -= src_x - mask_x; + extents.y1 -= src_y - mask_y; + extents.x2 -= src_x - mask_x; + extents.y2 -= src_y - mask_y; + + if (!analyze_extent (mask, &extents, &info.mask_flags)) + goto out; + + /* If the clip is within the source samples, and the samples are + * opaque, then the source is effectively opaque. + */ +#define NEAREST_OPAQUE (FAST_PATH_SAMPLES_OPAQUE | \ + FAST_PATH_NEAREST_FILTER | \ + FAST_PATH_SAMPLES_COVER_CLIP_NEAREST) +#define BILINEAR_OPAQUE (FAST_PATH_SAMPLES_OPAQUE | \ + FAST_PATH_BILINEAR_FILTER | \ + FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR) + + if ((info.src_flags & NEAREST_OPAQUE) == NEAREST_OPAQUE || + (info.src_flags & BILINEAR_OPAQUE) == BILINEAR_OPAQUE) + { + info.src_flags |= FAST_PATH_IS_OPAQUE; + } + + if ((info.mask_flags & NEAREST_OPAQUE) == NEAREST_OPAQUE || + (info.mask_flags & BILINEAR_OPAQUE) == BILINEAR_OPAQUE) + { + info.mask_flags |= FAST_PATH_IS_OPAQUE; + } + + /* + * Check if we can replace our operator by a simpler one + * if the src or dest are opaque. The output operator should be + * mathematically equivalent to the source. + */ + info.op = optimize_operator (op, info.src_flags, info.mask_flags, info.dest_flags); + + _pixman_implementation_lookup_composite ( + get_implementation (), info.op, + src_format, info.src_flags, + mask_format, info.mask_flags, + dest_format, info.dest_flags, + &imp, &func); + + info.src_image = src; + info.mask_image = mask; + info.dest_image = dest; + + pbox = pixman_region32_rectangles (®ion, &n); + + while (n--) + { + info.src_x = pbox->x1 + src_x - dest_x; + info.src_y = pbox->y1 + src_y - dest_y; + info.mask_x = pbox->x1 + mask_x - dest_x; + info.mask_y = pbox->y1 + mask_y - dest_y; + info.dest_x = pbox->x1; + info.dest_y = pbox->y1; + info.width = pbox->x2 - pbox->x1; + info.height = pbox->y2 - pbox->y1; + + func (imp, &info); + + pbox++; + } + +out: + pixman_region32_fini (®ion); +} + +PIXMAN_EXPORT void +pixman_image_composite (pixman_op_t op, + pixman_image_t * src, + pixman_image_t * mask, + pixman_image_t * dest, + int16_t src_x, + int16_t src_y, + int16_t mask_x, + int16_t mask_y, + int16_t dest_x, + int16_t dest_y, + uint16_t width, + uint16_t height) +{ + pixman_image_composite32 (op, src, mask, dest, src_x, src_y, + mask_x, mask_y, dest_x, dest_y, width, height); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_blt (uint32_t *src_bits, + uint32_t *dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height) +{ + return _pixman_implementation_blt (get_implementation(), + src_bits, dst_bits, src_stride, dst_stride, + src_bpp, dst_bpp, + src_x, src_y, + dest_x, dest_y, + width, height); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_fill (uint32_t *bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t filler) +{ + return _pixman_implementation_fill ( + get_implementation(), bits, stride, bpp, x, y, width, height, filler); +} + +static uint32_t +color_to_uint32 (const pixman_color_t *color) +{ + return + (color->alpha >> 8 << 24) | + (color->red >> 8 << 16) | + (color->green & 0xff00) | + (color->blue >> 8); +} + +static pixman_bool_t +color_to_pixel (const pixman_color_t *color, + uint32_t * pixel, + pixman_format_code_t format) +{ + uint32_t c = color_to_uint32 (color); + + if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_RGBA_FLOAT) + { + return FALSE; + } + + if (!(format == PIXMAN_a8r8g8b8 || + format == PIXMAN_x8r8g8b8 || + format == PIXMAN_a8b8g8r8 || + format == PIXMAN_x8b8g8r8 || + format == PIXMAN_b8g8r8a8 || + format == PIXMAN_b8g8r8x8 || + format == PIXMAN_r8g8b8a8 || + format == PIXMAN_r8g8b8x8 || + format == PIXMAN_r5g6b5 || + format == PIXMAN_b5g6r5 || + format == PIXMAN_a8 || + format == PIXMAN_a1)) + { + return FALSE; + } + + 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 (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_RGBA) + c = ((c & 0xff000000) >> 24) | (c << 8); + + if (format == PIXMAN_a1) + c = c >> 31; + else if (format == PIXMAN_a8) + c = c >> 24; + else if (format == PIXMAN_r5g6b5 || + format == PIXMAN_b5g6r5) + c = convert_8888_to_0565 (c); + +#if 0 + printf ("color: %x %x %x %x\n", color->alpha, color->red, color->green, color->blue); + printf ("pixel: %x\n", c); +#endif + + *pixel = c; + return TRUE; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_image_fill_rectangles (pixman_op_t op, + pixman_image_t * dest, + const pixman_color_t * color, + int n_rects, + const pixman_rectangle16_t *rects) +{ + pixman_box32_t stack_boxes[6]; + pixman_box32_t *boxes; + pixman_bool_t result; + int i; + + if (n_rects > 6) + { + boxes = pixman_malloc_ab (sizeof (pixman_box32_t), n_rects); + if (boxes == NULL) + return FALSE; + } + else + { + boxes = stack_boxes; + } + + for (i = 0; i < n_rects; ++i) + { + boxes[i].x1 = rects[i].x; + boxes[i].y1 = rects[i].y; + boxes[i].x2 = boxes[i].x1 + rects[i].width; + boxes[i].y2 = boxes[i].y1 + rects[i].height; + } + + result = pixman_image_fill_boxes (op, dest, color, n_rects, boxes); + + if (boxes != stack_boxes) + free (boxes); + + return result; +} + +PIXMAN_EXPORT pixman_bool_t +pixman_image_fill_boxes (pixman_op_t op, + pixman_image_t * dest, + const pixman_color_t *color, + int n_boxes, + const pixman_box32_t *boxes) +{ + pixman_image_t *solid; + pixman_color_t c; + int i; + + _pixman_image_validate (dest); + + if (color->alpha == 0xffff) + { + if (op == PIXMAN_OP_OVER) + op = PIXMAN_OP_SRC; + } + + if (op == PIXMAN_OP_CLEAR) + { + c.red = 0; + c.green = 0; + c.blue = 0; + c.alpha = 0; + + color = &c; + + op = PIXMAN_OP_SRC; + } + + if (op == PIXMAN_OP_SRC) + { + uint32_t pixel; + + if (color_to_pixel (color, &pixel, dest->bits.format)) + { + pixman_region32_t fill_region; + int n_rects, j; + pixman_box32_t *rects; + + if (!pixman_region32_init_rects (&fill_region, boxes, n_boxes)) + return FALSE; + + if (dest->common.have_clip_region) + { + if (!pixman_region32_intersect (&fill_region, + &fill_region, + &dest->common.clip_region)) + return FALSE; + } + + rects = pixman_region32_rectangles (&fill_region, &n_rects); + for (j = 0; j < n_rects; ++j) + { + const pixman_box32_t *rect = &(rects[j]); + pixman_fill (dest->bits.bits, dest->bits.rowstride, PIXMAN_FORMAT_BPP (dest->bits.format), + rect->x1, rect->y1, rect->x2 - rect->x1, rect->y2 - rect->y1, + pixel); + } + + pixman_region32_fini (&fill_region); + return TRUE; + } + } + + solid = pixman_image_create_solid_fill (color); + if (!solid) + return FALSE; + + for (i = 0; i < n_boxes; ++i) + { + const pixman_box32_t *box = &(boxes[i]); + + pixman_image_composite32 (op, solid, NULL, dest, + 0, 0, 0, 0, + box->x1, box->y1, + box->x2 - box->x1, box->y2 - box->y1); + } + + pixman_image_unref (solid); + + return TRUE; +} + +/** + * pixman_version: + * + * Returns the version of the pixman library encoded in a single + * integer as per %PIXMAN_VERSION_ENCODE. The encoding ensures that + * later versions compare greater than earlier versions. + * + * A run-time comparison to check that pixman's version is greater than + * or equal to version X.Y.Z could be performed as follows: + * + * + * if (pixman_version() >= PIXMAN_VERSION_ENCODE(X,Y,Z)) {...} + * + * + * See also pixman_version_string() as well as the compile-time + * equivalents %PIXMAN_VERSION and %PIXMAN_VERSION_STRING. + * + * Return value: the encoded version. + **/ +PIXMAN_EXPORT int +pixman_version (void) +{ + return PIXMAN_VERSION; +} + +/** + * pixman_version_string: + * + * Returns the version of the pixman library as a human-readable string + * of the form "X.Y.Z". + * + * See also pixman_version() as well as the compile-time equivalents + * %PIXMAN_VERSION_STRING and %PIXMAN_VERSION. + * + * Return value: a string containing the version. + **/ +PIXMAN_EXPORT const char* +pixman_version_string (void) +{ + return PIXMAN_VERSION_STRING; +} + +/** + * pixman_format_supported_source: + * @format: A pixman_format_code_t format + * + * Return value: whether the provided format code is a supported + * format for a pixman surface used as a source in + * rendering. + * + * Currently, all pixman_format_code_t values are supported. + **/ +PIXMAN_EXPORT pixman_bool_t +pixman_format_supported_source (pixman_format_code_t format) +{ + switch (format) + { + /* 32 bpp formats */ + case PIXMAN_a2b10g10r10: + case PIXMAN_x2b10g10r10: + case PIXMAN_a2r10g10b10: + case PIXMAN_x2r10g10b10: + case PIXMAN_a8r8g8b8: + case PIXMAN_a8r8g8b8_sRGB: + case PIXMAN_x8r8g8b8: + case PIXMAN_a8b8g8r8: + case PIXMAN_x8b8g8r8: + case PIXMAN_b8g8r8a8: + case PIXMAN_b8g8r8x8: + case PIXMAN_r8g8b8a8: + case PIXMAN_r8g8b8x8: + case PIXMAN_r8g8b8: + case PIXMAN_b8g8r8: + case PIXMAN_r5g6b5: + case PIXMAN_b5g6r5: + case PIXMAN_x14r6g6b6: + /* 16 bpp formats */ + case PIXMAN_a1r5g5b5: + case PIXMAN_x1r5g5b5: + case PIXMAN_a1b5g5r5: + case PIXMAN_x1b5g5r5: + case PIXMAN_a4r4g4b4: + case PIXMAN_x4r4g4b4: + case PIXMAN_a4b4g4r4: + case PIXMAN_x4b4g4r4: + /* 8bpp formats */ + case PIXMAN_a8: + case PIXMAN_r3g3b2: + case PIXMAN_b2g3r3: + case PIXMAN_a2r2g2b2: + case PIXMAN_a2b2g2r2: + case PIXMAN_c8: + case PIXMAN_g8: + case PIXMAN_x4a4: + /* Collides with PIXMAN_c8 + case PIXMAN_x4c4: + */ + /* Collides with PIXMAN_g8 + case PIXMAN_x4g4: + */ + /* 4bpp formats */ + case PIXMAN_a4: + case PIXMAN_r1g2b1: + case PIXMAN_b1g2r1: + case PIXMAN_a1r1g1b1: + case PIXMAN_a1b1g1r1: + case PIXMAN_c4: + case PIXMAN_g4: + /* 1bpp formats */ + case PIXMAN_a1: + case PIXMAN_g1: + /* YUV formats */ + case PIXMAN_yuy2: + case PIXMAN_yv12: + return TRUE; + + default: + return FALSE; + } +} + +/** + * pixman_format_supported_destination: + * @format: A pixman_format_code_t format + * + * Return value: whether the provided format code is a supported + * format for a pixman surface used as a destination in + * rendering. + * + * Currently, all pixman_format_code_t values are supported + * except for the YUV formats. + **/ +PIXMAN_EXPORT pixman_bool_t +pixman_format_supported_destination (pixman_format_code_t format) +{ + /* YUV formats cannot be written to at the moment */ + if (format == PIXMAN_yuy2 || format == PIXMAN_yv12) + return FALSE; + + return pixman_format_supported_source (format); +} + +PIXMAN_EXPORT pixman_bool_t +pixman_compute_composite_region (pixman_region16_t * region, + pixman_image_t * src_image, + pixman_image_t * mask_image, + pixman_image_t * dest_image, + int16_t src_x, + int16_t src_y, + int16_t mask_x, + int16_t mask_y, + int16_t dest_x, + int16_t dest_y, + uint16_t width, + uint16_t height) +{ + pixman_region32_t r32; + pixman_bool_t retval; + + pixman_region32_init (&r32); + + retval = _pixman_compute_composite_region32 ( + &r32, src_image, mask_image, dest_image, + src_x, src_y, mask_x, mask_y, dest_x, dest_y, + width, height); + + if (retval) + { + if (!pixman_region16_copy_from_region32 (region, &r32)) + retval = FALSE; + } + + pixman_region32_fini (&r32); + return retval; +} diff --git a/gfx/cairo/libpixman/src/pixman.h b/gfx/cairo/libpixman/src/pixman.h new file mode 100644 index 0000000000..858955554a --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman.h @@ -0,0 +1,1423 @@ +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +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. + +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 +OPEN GROUP 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. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +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 Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL 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. + +******************************************************************/ +/* + * Copyright © 1998, 2004 Keith Packard + * Copyright 2007 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of 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. + */ + +#ifndef PIXMAN_H__ +#define PIXMAN_H__ + +#ifdef MOZILLA_VERSION +#include "cairo/pixman-rename.h" +#endif + +#include + +#ifdef __cplusplus +#define PIXMAN_BEGIN_DECLS extern "C" { +#define PIXMAN_END_DECLS } +#else +#define PIXMAN_BEGIN_DECLS +#define PIXMAN_END_DECLS +#endif + +PIXMAN_BEGIN_DECLS + +/* + * Standard integers + */ + +#if !defined (PIXMAN_DONT_DEFINE_STDINT) + +#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || defined (_sgi) || defined (__sun) || defined (sun) || defined (__digital__) || defined (__HP_cc) +# include +/* VS 2010 (_MSC_VER 1600) has stdint.h */ +#elif defined (_MSC_VER) && _MSC_VER < 1600 +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#elif defined (_AIX) +# include +#else +# include +#endif + +#endif + +/* + * Boolean + */ +typedef int pixman_bool_t; + +/* + * Fixpoint numbers + */ +typedef int64_t pixman_fixed_32_32_t; +typedef pixman_fixed_32_32_t pixman_fixed_48_16_t; +typedef uint32_t pixman_fixed_1_31_t; +typedef uint32_t pixman_fixed_1_16_t; +typedef int32_t pixman_fixed_16_16_t; +typedef pixman_fixed_16_16_t pixman_fixed_t; + +#define pixman_fixed_e ((pixman_fixed_t) 1) +#define pixman_fixed_1 (pixman_int_to_fixed(1)) +#define pixman_fixed_1_minus_e (pixman_fixed_1 - pixman_fixed_e) +#define pixman_fixed_minus_1 (pixman_int_to_fixed(-1)) +#define pixman_fixed_to_int(f) ((int) ((f) >> 16)) +#define pixman_int_to_fixed(i) ((pixman_fixed_t) ((uint32_t) (i) << 16)) +#define pixman_fixed_to_double(f) (double) ((f) / (double) pixman_fixed_1) +#define pixman_double_to_fixed(d) ((pixman_fixed_t) ((d) * 65536.0)) +#define pixman_fixed_frac(f) ((f) & pixman_fixed_1_minus_e) +#define pixman_fixed_floor(f) ((f) & ~pixman_fixed_1_minus_e) +#define pixman_fixed_ceil(f) pixman_fixed_floor ((f) + pixman_fixed_1_minus_e) +#define pixman_fixed_fraction(f) ((f) & pixman_fixed_1_minus_e) +#define pixman_fixed_mod_2(f) ((f) & (pixman_fixed1 | pixman_fixed_1_minus_e)) +#define pixman_max_fixed_48_16 ((pixman_fixed_48_16_t) 0x7fffffff) +#define pixman_min_fixed_48_16 (-((pixman_fixed_48_16_t) 1 << 31)) + +/* + * Misc structs + */ +typedef struct pixman_color pixman_color_t; +typedef struct pixman_point_fixed pixman_point_fixed_t; +typedef struct pixman_line_fixed pixman_line_fixed_t; +typedef struct pixman_vector pixman_vector_t; +typedef struct pixman_transform pixman_transform_t; + +struct pixman_color +{ + uint16_t red; + uint16_t green; + uint16_t blue; + uint16_t alpha; +}; + +struct pixman_point_fixed +{ + pixman_fixed_t x; + pixman_fixed_t y; +}; + +struct pixman_line_fixed +{ + pixman_point_fixed_t p1, p2; +}; + +/* + * Fixed point matrices + */ + +struct pixman_vector +{ + pixman_fixed_t vector[3]; +}; + +struct pixman_transform +{ + pixman_fixed_t matrix[3][3]; +}; + +/* forward declaration (sorry) */ +struct pixman_box16; +typedef union pixman_image pixman_image_t; + +PIXMAN_API +void pixman_transform_init_identity (struct pixman_transform *matrix); + +PIXMAN_API +pixman_bool_t pixman_transform_point_3d (const struct pixman_transform *transform, + struct pixman_vector *vector); + +PIXMAN_API +pixman_bool_t pixman_transform_point (const struct pixman_transform *transform, + struct pixman_vector *vector); + +PIXMAN_API +pixman_bool_t pixman_transform_multiply (struct pixman_transform *dst, + const struct pixman_transform *l, + const struct pixman_transform *r); + +PIXMAN_API +void pixman_transform_init_scale (struct pixman_transform *t, + pixman_fixed_t sx, + pixman_fixed_t sy); + +PIXMAN_API +pixman_bool_t pixman_transform_scale (struct pixman_transform *forward, + struct pixman_transform *reverse, + pixman_fixed_t sx, + pixman_fixed_t sy); + +PIXMAN_API +void pixman_transform_init_rotate (struct pixman_transform *t, + pixman_fixed_t cos, + pixman_fixed_t sin); + +PIXMAN_API +pixman_bool_t pixman_transform_rotate (struct pixman_transform *forward, + struct pixman_transform *reverse, + pixman_fixed_t c, + pixman_fixed_t s); + +PIXMAN_API +void pixman_transform_init_translate (struct pixman_transform *t, + pixman_fixed_t tx, + pixman_fixed_t ty); + +PIXMAN_API +pixman_bool_t pixman_transform_translate (struct pixman_transform *forward, + struct pixman_transform *reverse, + pixman_fixed_t tx, + pixman_fixed_t ty); + +PIXMAN_API +pixman_bool_t pixman_transform_bounds (const struct pixman_transform *matrix, + struct pixman_box16 *b); + +PIXMAN_API +pixman_bool_t pixman_transform_invert (struct pixman_transform *dst, + const struct pixman_transform *src); + +PIXMAN_API +pixman_bool_t pixman_transform_is_identity (const struct pixman_transform *t); + +PIXMAN_API +pixman_bool_t pixman_transform_is_scale (const struct pixman_transform *t); + +PIXMAN_API +pixman_bool_t pixman_transform_is_int_translate (const struct pixman_transform *t); + +PIXMAN_API +pixman_bool_t pixman_transform_is_inverse (const struct pixman_transform *a, + const struct pixman_transform *b); + +/* + * Floating point matrices + */ +typedef struct pixman_f_transform pixman_f_transform_t; +typedef struct pixman_f_vector pixman_f_vector_t; + +struct pixman_f_vector +{ + double v[3]; +}; + +struct pixman_f_transform +{ + double m[3][3]; +}; + + +PIXMAN_API +pixman_bool_t pixman_transform_from_pixman_f_transform (struct pixman_transform *t, + const struct pixman_f_transform *ft); + +PIXMAN_API +void pixman_f_transform_from_pixman_transform (struct pixman_f_transform *ft, + const struct pixman_transform *t); + +PIXMAN_API +pixman_bool_t pixman_f_transform_invert (struct pixman_f_transform *dst, + const struct pixman_f_transform *src); + +PIXMAN_API +pixman_bool_t pixman_f_transform_point (const struct pixman_f_transform *t, + struct pixman_f_vector *v); + +PIXMAN_API +void pixman_f_transform_point_3d (const struct pixman_f_transform *t, + struct pixman_f_vector *v); + +PIXMAN_API +void pixman_f_transform_multiply (struct pixman_f_transform *dst, + const struct pixman_f_transform *l, + const struct pixman_f_transform *r); + +PIXMAN_API +void pixman_f_transform_init_scale (struct pixman_f_transform *t, + double sx, + double sy); + +PIXMAN_API +pixman_bool_t pixman_f_transform_scale (struct pixman_f_transform *forward, + struct pixman_f_transform *reverse, + double sx, + double sy); + +PIXMAN_API +void pixman_f_transform_init_rotate (struct pixman_f_transform *t, + double cos, + double sin); + +PIXMAN_API +pixman_bool_t pixman_f_transform_rotate (struct pixman_f_transform *forward, + struct pixman_f_transform *reverse, + double c, + double s); + +PIXMAN_API +void pixman_f_transform_init_translate (struct pixman_f_transform *t, + double tx, + double ty); + +PIXMAN_API +pixman_bool_t pixman_f_transform_translate (struct pixman_f_transform *forward, + struct pixman_f_transform *reverse, + double tx, + double ty); + +PIXMAN_API +pixman_bool_t pixman_f_transform_bounds (const struct pixman_f_transform *t, + struct pixman_box16 *b); + +PIXMAN_API +void pixman_f_transform_init_identity (struct pixman_f_transform *t); + +typedef enum +{ + PIXMAN_REPEAT_NONE, + PIXMAN_REPEAT_NORMAL, + PIXMAN_REPEAT_PAD, + PIXMAN_REPEAT_REFLECT +} pixman_repeat_t; + +typedef enum +{ + PIXMAN_DITHER_NONE, + PIXMAN_DITHER_FAST, + PIXMAN_DITHER_GOOD, + PIXMAN_DITHER_BEST, + PIXMAN_DITHER_ORDERED_BAYER_8, + PIXMAN_DITHER_ORDERED_BLUE_NOISE_64, +} pixman_dither_t; + +typedef enum +{ + PIXMAN_FILTER_FAST, + PIXMAN_FILTER_GOOD, + PIXMAN_FILTER_BEST, + PIXMAN_FILTER_NEAREST, + PIXMAN_FILTER_BILINEAR, + PIXMAN_FILTER_CONVOLUTION, + + /* The SEPARABLE_CONVOLUTION filter takes the following parameters: + * + * width: integer given as 16.16 fixpoint number + * height: integer given as 16.16 fixpoint number + * x_phase_bits: integer given as 16.16 fixpoint + * y_phase_bits: integer given as 16.16 fixpoint + * xtables: (1 << x_phase_bits) tables of size width + * ytables: (1 << y_phase_bits) tables of size height + * + * When sampling at (x, y), the location is first rounded to one of + * n_x_phases * n_y_phases subpixel positions. These subpixel positions + * determine an xtable and a ytable to use. + * + * Conceptually a width x height matrix is then formed in which each entry + * is the product of the corresponding entries in the x and y tables. + * This matrix is then aligned with the image pixels such that its center + * is as close as possible to the subpixel location chosen earlier. Then + * the image is convolved with the matrix and the resulting pixel returned. + */ + PIXMAN_FILTER_SEPARABLE_CONVOLUTION +} pixman_filter_t; + +typedef enum +{ + PIXMAN_OP_CLEAR = 0x00, + PIXMAN_OP_SRC = 0x01, + PIXMAN_OP_DST = 0x02, + PIXMAN_OP_OVER = 0x03, + PIXMAN_OP_OVER_REVERSE = 0x04, + PIXMAN_OP_IN = 0x05, + PIXMAN_OP_IN_REVERSE = 0x06, + PIXMAN_OP_OUT = 0x07, + PIXMAN_OP_OUT_REVERSE = 0x08, + PIXMAN_OP_ATOP = 0x09, + PIXMAN_OP_ATOP_REVERSE = 0x0a, + PIXMAN_OP_XOR = 0x0b, + PIXMAN_OP_ADD = 0x0c, + PIXMAN_OP_SATURATE = 0x0d, + + PIXMAN_OP_DISJOINT_CLEAR = 0x10, + PIXMAN_OP_DISJOINT_SRC = 0x11, + PIXMAN_OP_DISJOINT_DST = 0x12, + PIXMAN_OP_DISJOINT_OVER = 0x13, + PIXMAN_OP_DISJOINT_OVER_REVERSE = 0x14, + PIXMAN_OP_DISJOINT_IN = 0x15, + PIXMAN_OP_DISJOINT_IN_REVERSE = 0x16, + PIXMAN_OP_DISJOINT_OUT = 0x17, + PIXMAN_OP_DISJOINT_OUT_REVERSE = 0x18, + PIXMAN_OP_DISJOINT_ATOP = 0x19, + PIXMAN_OP_DISJOINT_ATOP_REVERSE = 0x1a, + PIXMAN_OP_DISJOINT_XOR = 0x1b, + + PIXMAN_OP_CONJOINT_CLEAR = 0x20, + PIXMAN_OP_CONJOINT_SRC = 0x21, + PIXMAN_OP_CONJOINT_DST = 0x22, + PIXMAN_OP_CONJOINT_OVER = 0x23, + PIXMAN_OP_CONJOINT_OVER_REVERSE = 0x24, + PIXMAN_OP_CONJOINT_IN = 0x25, + PIXMAN_OP_CONJOINT_IN_REVERSE = 0x26, + PIXMAN_OP_CONJOINT_OUT = 0x27, + PIXMAN_OP_CONJOINT_OUT_REVERSE = 0x28, + PIXMAN_OP_CONJOINT_ATOP = 0x29, + PIXMAN_OP_CONJOINT_ATOP_REVERSE = 0x2a, + PIXMAN_OP_CONJOINT_XOR = 0x2b, + + PIXMAN_OP_MULTIPLY = 0x30, + PIXMAN_OP_SCREEN = 0x31, + PIXMAN_OP_OVERLAY = 0x32, + PIXMAN_OP_DARKEN = 0x33, + PIXMAN_OP_LIGHTEN = 0x34, + PIXMAN_OP_COLOR_DODGE = 0x35, + PIXMAN_OP_COLOR_BURN = 0x36, + PIXMAN_OP_HARD_LIGHT = 0x37, + PIXMAN_OP_SOFT_LIGHT = 0x38, + PIXMAN_OP_DIFFERENCE = 0x39, + PIXMAN_OP_EXCLUSION = 0x3a, + PIXMAN_OP_HSL_HUE = 0x3b, + PIXMAN_OP_HSL_SATURATION = 0x3c, + PIXMAN_OP_HSL_COLOR = 0x3d, + PIXMAN_OP_HSL_LUMINOSITY = 0x3e + +#ifdef PIXMAN_USE_INTERNAL_API + , + PIXMAN_N_OPERATORS, + PIXMAN_OP_NONE = PIXMAN_N_OPERATORS +#endif +} pixman_op_t; + +/* + * Regions + */ +typedef struct pixman_region16_data pixman_region16_data_t; +typedef struct pixman_box16 pixman_box16_t; +typedef struct pixman_rectangle16 pixman_rectangle16_t; +typedef struct pixman_region16 pixman_region16_t; + +struct pixman_region16_data { + long size; + long numRects; +/* pixman_box16_t rects[size]; in memory but not explicitly declared */ +}; + +struct pixman_rectangle16 +{ + int16_t x, y; + uint16_t width, height; +}; + +struct pixman_box16 +{ + int16_t x1, y1, x2, y2; +}; + +struct pixman_region16 +{ + pixman_box16_t extents; + pixman_region16_data_t *data; +}; + +typedef enum +{ + PIXMAN_REGION_OUT, + PIXMAN_REGION_IN, + PIXMAN_REGION_PART +} pixman_region_overlap_t; + +/* This function exists only to make it possible to preserve + * the X ABI - it should go away at first opportunity. + */ +PIXMAN_API +void pixman_region_set_static_pointers (pixman_box16_t *empty_box, + pixman_region16_data_t *empty_data, + pixman_region16_data_t *broken_data); + +/* creation/destruction */ +PIXMAN_API +void pixman_region_init (pixman_region16_t *region); + +PIXMAN_API +void pixman_region_init_rect (pixman_region16_t *region, + int x, + int y, + unsigned int width, + unsigned int height); + +PIXMAN_API +pixman_bool_t pixman_region_init_rects (pixman_region16_t *region, + const pixman_box16_t *boxes, + int count); + +PIXMAN_API +void pixman_region_init_with_extents (pixman_region16_t *region, + const pixman_box16_t *extents); + +PIXMAN_API +void pixman_region_init_from_image (pixman_region16_t *region, + pixman_image_t *image); + +PIXMAN_API +void pixman_region_fini (pixman_region16_t *region); + + +/* manipulation */ +PIXMAN_API +void pixman_region_translate (pixman_region16_t *region, + int x, + int y); + +PIXMAN_API +pixman_bool_t pixman_region_copy (pixman_region16_t *dest, + const pixman_region16_t *source); + +PIXMAN_API +pixman_bool_t pixman_region_intersect (pixman_region16_t *new_reg, + const pixman_region16_t *reg1, + const pixman_region16_t *reg2); + +PIXMAN_API +pixman_bool_t pixman_region_union (pixman_region16_t *new_reg, + const pixman_region16_t *reg1, + const pixman_region16_t *reg2); + +PIXMAN_API +pixman_bool_t pixman_region_union_rect (pixman_region16_t *dest, + const pixman_region16_t *source, + int x, + int y, + unsigned int width, + unsigned int height); + +PIXMAN_API +pixman_bool_t pixman_region_intersect_rect (pixman_region16_t *dest, + const pixman_region16_t *source, + int x, + int y, + unsigned int width, + unsigned int height); + +PIXMAN_API +pixman_bool_t pixman_region_subtract (pixman_region16_t *reg_d, + const pixman_region16_t *reg_m, + const pixman_region16_t *reg_s); + +PIXMAN_API +pixman_bool_t pixman_region_inverse (pixman_region16_t *new_reg, + const pixman_region16_t *reg1, + const pixman_box16_t *inv_rect); + +PIXMAN_API +pixman_bool_t pixman_region_contains_point (const pixman_region16_t *region, + int x, + int y, + pixman_box16_t *box); + +PIXMAN_API +pixman_region_overlap_t pixman_region_contains_rectangle (const pixman_region16_t *region, + const pixman_box16_t *prect); + +PIXMAN_API +pixman_bool_t pixman_region_not_empty (const pixman_region16_t *region); + +PIXMAN_API +pixman_box16_t * pixman_region_extents (const pixman_region16_t *region); + +PIXMAN_API +int pixman_region_n_rects (const pixman_region16_t *region); + +PIXMAN_API +pixman_box16_t * pixman_region_rectangles (const pixman_region16_t *region, + int *n_rects); + +PIXMAN_API +pixman_bool_t pixman_region_equal (const pixman_region16_t *region1, + const pixman_region16_t *region2); + +PIXMAN_API +pixman_bool_t pixman_region_selfcheck (pixman_region16_t *region); + +PIXMAN_API +void pixman_region_reset (pixman_region16_t *region, + const pixman_box16_t *box); + +PIXMAN_API +void pixman_region_clear (pixman_region16_t *region); +/* + * 32 bit regions + */ +typedef struct pixman_region32_data pixman_region32_data_t; +typedef struct pixman_box32 pixman_box32_t; +typedef struct pixman_rectangle32 pixman_rectangle32_t; +typedef struct pixman_region32 pixman_region32_t; + +struct pixman_region32_data { + long size; + long numRects; +/* pixman_box32_t rects[size]; in memory but not explicitly declared */ +}; + +struct pixman_rectangle32 +{ + int32_t x, y; + uint32_t width, height; +}; + +struct pixman_box32 +{ + int32_t x1, y1, x2, y2; +}; + +struct pixman_region32 +{ + pixman_box32_t extents; + pixman_region32_data_t *data; +}; + +/* creation/destruction */ +PIXMAN_API +void pixman_region32_init (pixman_region32_t *region); + +PIXMAN_API +void pixman_region32_init_rect (pixman_region32_t *region, + int x, + int y, + unsigned int width, + unsigned int height); + +PIXMAN_API +pixman_bool_t pixman_region32_init_rects (pixman_region32_t *region, + const pixman_box32_t *boxes, + int count); + +PIXMAN_API +void pixman_region32_init_with_extents (pixman_region32_t *region, + const pixman_box32_t *extents); + +PIXMAN_API +void pixman_region32_init_from_image (pixman_region32_t *region, + pixman_image_t *image); + +PIXMAN_API +void pixman_region32_fini (pixman_region32_t *region); + + +/* manipulation */ +PIXMAN_API +void pixman_region32_translate (pixman_region32_t *region, + int x, + int y); + +PIXMAN_API +pixman_bool_t pixman_region32_copy (pixman_region32_t *dest, + const pixman_region32_t *source); + +PIXMAN_API +pixman_bool_t pixman_region32_intersect (pixman_region32_t *new_reg, + const pixman_region32_t *reg1, + const pixman_region32_t *reg2); + +PIXMAN_API +pixman_bool_t pixman_region32_union (pixman_region32_t *new_reg, + const pixman_region32_t *reg1, + const pixman_region32_t *reg2); + +PIXMAN_API +pixman_bool_t pixman_region32_intersect_rect (pixman_region32_t *dest, + const pixman_region32_t *source, + int x, + int y, + unsigned int width, + unsigned int height); + +PIXMAN_API +pixman_bool_t pixman_region32_union_rect (pixman_region32_t *dest, + const pixman_region32_t *source, + int x, + int y, + unsigned int width, + unsigned int height); + +PIXMAN_API +pixman_bool_t pixman_region32_subtract (pixman_region32_t *reg_d, + const pixman_region32_t *reg_m, + const pixman_region32_t *reg_s); + +PIXMAN_API +pixman_bool_t pixman_region32_inverse (pixman_region32_t *new_reg, + const pixman_region32_t *reg1, + const pixman_box32_t *inv_rect); + +PIXMAN_API +pixman_bool_t pixman_region32_contains_point (const pixman_region32_t *region, + int x, + int y, + pixman_box32_t *box); + +PIXMAN_API +pixman_region_overlap_t pixman_region32_contains_rectangle (const pixman_region32_t *region, + const pixman_box32_t *prect); + +PIXMAN_API +pixman_bool_t pixman_region32_not_empty (const pixman_region32_t *region); + +PIXMAN_API +pixman_box32_t * pixman_region32_extents (const pixman_region32_t *region); + +PIXMAN_API +int pixman_region32_n_rects (const pixman_region32_t *region); + +PIXMAN_API +pixman_box32_t * pixman_region32_rectangles (const pixman_region32_t *region, + int *n_rects); + +PIXMAN_API +pixman_bool_t pixman_region32_equal (const pixman_region32_t *region1, + const pixman_region32_t *region2); + +PIXMAN_API +pixman_bool_t pixman_region32_selfcheck (pixman_region32_t *region); + +PIXMAN_API +void pixman_region32_reset (pixman_region32_t *region, + const pixman_box32_t *box); + +PIXMAN_API +void pixman_region32_clear (pixman_region32_t *region); + + +/* Copy / Fill / Misc */ +PIXMAN_API +pixman_bool_t pixman_blt (uint32_t *src_bits, + uint32_t *dst_bits, + int src_stride, + int dst_stride, + int src_bpp, + int dst_bpp, + int src_x, + int src_y, + int dest_x, + int dest_y, + int width, + int height); + +PIXMAN_API +pixman_bool_t pixman_fill (uint32_t *bits, + int stride, + int bpp, + int x, + int y, + int width, + int height, + uint32_t _xor); + + +PIXMAN_API +int pixman_version (void); + +PIXMAN_API +const char* pixman_version_string (void); + +/* + * Images + */ +typedef struct pixman_indexed pixman_indexed_t; +typedef struct pixman_gradient_stop pixman_gradient_stop_t; + +typedef uint32_t (* pixman_read_memory_func_t) (const void *src, int size); +typedef void (* pixman_write_memory_func_t) (void *dst, uint32_t value, int size); + +typedef void (* pixman_image_destroy_func_t) (pixman_image_t *image, void *data); + +struct pixman_gradient_stop { + pixman_fixed_t x; + pixman_color_t color; +}; + +#define PIXMAN_MAX_INDEXED 256 /* XXX depth must be <= 8 */ + +#if PIXMAN_MAX_INDEXED <= 256 +typedef uint8_t pixman_index_type; +#endif + +struct pixman_indexed +{ + pixman_bool_t color; + uint32_t rgba[PIXMAN_MAX_INDEXED]; + pixman_index_type ent[32768]; +}; + +/* + * While the protocol is generous in format support, the + * sample implementation allows only packed RGB and GBR + * representations for data to simplify software rendering, + */ +#define PIXMAN_FORMAT(bpp,type,a,r,g,b) (((bpp) << 24) | \ + ((type) << 16) | \ + ((a) << 12) | \ + ((r) << 8) | \ + ((g) << 4) | \ + ((b))) + +#define PIXMAN_FORMAT_BYTE(bpp,type,a,r,g,b) \ + (((bpp >> 3) << 24) | \ + (3 << 22) | ((type) << 16) | \ + ((a >> 3) << 12) | \ + ((r >> 3) << 8) | \ + ((g >> 3) << 4) | \ + ((b >> 3))) + +#define PIXMAN_FORMAT_RESHIFT(val, ofs, num) \ + (((val >> (ofs)) & ((1 << (num)) - 1)) << ((val >> 22) & 3)) + +#define PIXMAN_FORMAT_BPP(f) PIXMAN_FORMAT_RESHIFT(f, 24, 8) +#define PIXMAN_FORMAT_SHIFT(f) ((uint32_t)((f >> 22) & 3)) +#define PIXMAN_FORMAT_TYPE(f) (((f) >> 16) & 0x3f) +#define PIXMAN_FORMAT_A(f) PIXMAN_FORMAT_RESHIFT(f, 12, 4) +#define PIXMAN_FORMAT_R(f) PIXMAN_FORMAT_RESHIFT(f, 8, 4) +#define PIXMAN_FORMAT_G(f) PIXMAN_FORMAT_RESHIFT(f, 4, 4) +#define PIXMAN_FORMAT_B(f) PIXMAN_FORMAT_RESHIFT(f, 0, 4) +#define PIXMAN_FORMAT_RGB(f) (((f) ) & 0xfff) +#define PIXMAN_FORMAT_VIS(f) (((f) ) & 0xffff) +#define PIXMAN_FORMAT_DEPTH(f) (PIXMAN_FORMAT_A(f) + \ + PIXMAN_FORMAT_R(f) + \ + PIXMAN_FORMAT_G(f) + \ + PIXMAN_FORMAT_B(f)) + +#define PIXMAN_TYPE_OTHER 0 +#define PIXMAN_TYPE_A 1 +#define PIXMAN_TYPE_ARGB 2 +#define PIXMAN_TYPE_ABGR 3 +#define PIXMAN_TYPE_COLOR 4 +#define PIXMAN_TYPE_GRAY 5 +#define PIXMAN_TYPE_YUY2 6 +#define PIXMAN_TYPE_YV12 7 +#define PIXMAN_TYPE_BGRA 8 +#define PIXMAN_TYPE_RGBA 9 +#define PIXMAN_TYPE_ARGB_SRGB 10 +#define PIXMAN_TYPE_RGBA_FLOAT 11 + +#define PIXMAN_FORMAT_COLOR(f) \ + (PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_ARGB || \ + PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_ABGR || \ + PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_BGRA || \ + PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_RGBA || \ + PIXMAN_FORMAT_TYPE(f) == PIXMAN_TYPE_RGBA_FLOAT) + +typedef enum { +/* 128bpp formats */ + PIXMAN_rgba_float = PIXMAN_FORMAT_BYTE(128,PIXMAN_TYPE_RGBA_FLOAT,32,32,32,32), +/* 96bpp formats */ + PIXMAN_rgb_float = PIXMAN_FORMAT_BYTE(96,PIXMAN_TYPE_RGBA_FLOAT,0,32,32,32), + +/* 32bpp formats */ + PIXMAN_a8r8g8b8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,8,8,8,8), + PIXMAN_x8r8g8b8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,0,8,8,8), + PIXMAN_a8b8g8r8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,8,8,8,8), + PIXMAN_x8b8g8r8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,0,8,8,8), + PIXMAN_b8g8r8a8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_BGRA,8,8,8,8), + PIXMAN_b8g8r8x8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_BGRA,0,8,8,8), + PIXMAN_r8g8b8a8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_RGBA,8,8,8,8), + PIXMAN_r8g8b8x8 = PIXMAN_FORMAT(32,PIXMAN_TYPE_RGBA,0,8,8,8), + PIXMAN_x14r6g6b6 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,0,6,6,6), + PIXMAN_x2r10g10b10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,0,10,10,10), + PIXMAN_a2r10g10b10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB,2,10,10,10), + PIXMAN_x2b10g10r10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,0,10,10,10), + PIXMAN_a2b10g10r10 = PIXMAN_FORMAT(32,PIXMAN_TYPE_ABGR,2,10,10,10), + +/* sRGB formats */ + PIXMAN_a8r8g8b8_sRGB = PIXMAN_FORMAT(32,PIXMAN_TYPE_ARGB_SRGB,8,8,8,8), + +/* 24bpp formats */ + PIXMAN_r8g8b8 = PIXMAN_FORMAT(24,PIXMAN_TYPE_ARGB,0,8,8,8), + PIXMAN_b8g8r8 = PIXMAN_FORMAT(24,PIXMAN_TYPE_ABGR,0,8,8,8), + +/* 16bpp formats */ + PIXMAN_r5g6b5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,0,5,6,5), + PIXMAN_b5g6r5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,0,5,6,5), + + PIXMAN_a1r5g5b5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,1,5,5,5), + PIXMAN_x1r5g5b5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,0,5,5,5), + PIXMAN_a1b5g5r5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,1,5,5,5), + PIXMAN_x1b5g5r5 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,0,5,5,5), + PIXMAN_a4r4g4b4 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,4,4,4,4), + PIXMAN_x4r4g4b4 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ARGB,0,4,4,4), + PIXMAN_a4b4g4r4 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,4,4,4,4), + PIXMAN_x4b4g4r4 = PIXMAN_FORMAT(16,PIXMAN_TYPE_ABGR,0,4,4,4), + +/* 8bpp formats */ + PIXMAN_a8 = PIXMAN_FORMAT(8,PIXMAN_TYPE_A,8,0,0,0), + PIXMAN_r3g3b2 = PIXMAN_FORMAT(8,PIXMAN_TYPE_ARGB,0,3,3,2), + PIXMAN_b2g3r3 = PIXMAN_FORMAT(8,PIXMAN_TYPE_ABGR,0,3,3,2), + PIXMAN_a2r2g2b2 = PIXMAN_FORMAT(8,PIXMAN_TYPE_ARGB,2,2,2,2), + PIXMAN_a2b2g2r2 = PIXMAN_FORMAT(8,PIXMAN_TYPE_ABGR,2,2,2,2), + + PIXMAN_c8 = PIXMAN_FORMAT(8,PIXMAN_TYPE_COLOR,0,0,0,0), + PIXMAN_g8 = PIXMAN_FORMAT(8,PIXMAN_TYPE_GRAY,0,0,0,0), + + PIXMAN_x4a4 = PIXMAN_FORMAT(8,PIXMAN_TYPE_A,4,0,0,0), + + PIXMAN_x4c4 = PIXMAN_FORMAT(8,PIXMAN_TYPE_COLOR,0,0,0,0), + PIXMAN_x4g4 = PIXMAN_FORMAT(8,PIXMAN_TYPE_GRAY,0,0,0,0), + +/* 4bpp formats */ + PIXMAN_a4 = PIXMAN_FORMAT(4,PIXMAN_TYPE_A,4,0,0,0), + PIXMAN_r1g2b1 = PIXMAN_FORMAT(4,PIXMAN_TYPE_ARGB,0,1,2,1), + PIXMAN_b1g2r1 = PIXMAN_FORMAT(4,PIXMAN_TYPE_ABGR,0,1,2,1), + PIXMAN_a1r1g1b1 = PIXMAN_FORMAT(4,PIXMAN_TYPE_ARGB,1,1,1,1), + PIXMAN_a1b1g1r1 = PIXMAN_FORMAT(4,PIXMAN_TYPE_ABGR,1,1,1,1), + + PIXMAN_c4 = PIXMAN_FORMAT(4,PIXMAN_TYPE_COLOR,0,0,0,0), + PIXMAN_g4 = PIXMAN_FORMAT(4,PIXMAN_TYPE_GRAY,0,0,0,0), + +/* 1bpp formats */ + PIXMAN_a1 = PIXMAN_FORMAT(1,PIXMAN_TYPE_A,1,0,0,0), + + PIXMAN_g1 = PIXMAN_FORMAT(1,PIXMAN_TYPE_GRAY,0,0,0,0), + +/* YUV formats */ + PIXMAN_yuy2 = PIXMAN_FORMAT(16,PIXMAN_TYPE_YUY2,0,0,0,0), + PIXMAN_yv12 = PIXMAN_FORMAT(12,PIXMAN_TYPE_YV12,0,0,0,0) +} pixman_format_code_t; + +/* Querying supported format values. */ +PIXMAN_API +pixman_bool_t pixman_format_supported_destination (pixman_format_code_t format); + +PIXMAN_API +pixman_bool_t pixman_format_supported_source (pixman_format_code_t format); + +/* Constructors */ +PIXMAN_API +pixman_image_t *pixman_image_create_solid_fill (const pixman_color_t *color); + +PIXMAN_API +pixman_image_t *pixman_image_create_linear_gradient (const pixman_point_fixed_t *p1, + const pixman_point_fixed_t *p2, + const pixman_gradient_stop_t *stops, + int n_stops); + +PIXMAN_API +pixman_image_t *pixman_image_create_radial_gradient (const pixman_point_fixed_t *inner, + const pixman_point_fixed_t *outer, + pixman_fixed_t inner_radius, + pixman_fixed_t outer_radius, + const pixman_gradient_stop_t *stops, + int n_stops); + +PIXMAN_API +pixman_image_t *pixman_image_create_conical_gradient (const pixman_point_fixed_t *center, + pixman_fixed_t angle, + const pixman_gradient_stop_t *stops, + int n_stops); + +PIXMAN_API +pixman_image_t *pixman_image_create_bits (pixman_format_code_t format, + int width, + int height, + uint32_t *bits, + int rowstride_bytes); + +PIXMAN_API +pixman_image_t *pixman_image_create_bits_no_clear (pixman_format_code_t format, + int width, + int height, + uint32_t * bits, + int rowstride_bytes); + +/* Destructor */ +PIXMAN_API +pixman_image_t *pixman_image_ref (pixman_image_t *image); + +PIXMAN_API +pixman_bool_t pixman_image_unref (pixman_image_t *image); + + +PIXMAN_API +void pixman_image_set_destroy_function (pixman_image_t *image, + pixman_image_destroy_func_t function, + void *data); + +PIXMAN_API +void * pixman_image_get_destroy_data (pixman_image_t *image); + +/* Set properties */ +PIXMAN_API +pixman_bool_t pixman_image_set_clip_region (pixman_image_t *image, + pixman_region16_t *region); + +PIXMAN_API +pixman_bool_t pixman_image_set_clip_region32 (pixman_image_t *image, + pixman_region32_t *region); + +PIXMAN_API +void pixman_image_set_has_client_clip (pixman_image_t *image, + pixman_bool_t clien_clip); + +PIXMAN_API +pixman_bool_t pixman_image_set_transform (pixman_image_t *image, + const pixman_transform_t *transform); + +PIXMAN_API +void pixman_image_set_repeat (pixman_image_t *image, + pixman_repeat_t repeat); + +PIXMAN_API +void pixman_image_set_dither (pixman_image_t *image, + pixman_dither_t dither); + +PIXMAN_API +void pixman_image_set_dither_offset (pixman_image_t *image, + int offset_x, + int offset_y); + +PIXMAN_API +pixman_bool_t pixman_image_set_filter (pixman_image_t *image, + pixman_filter_t filter, + const pixman_fixed_t *filter_params, + int n_filter_params); + +PIXMAN_API +void pixman_image_set_source_clipping (pixman_image_t *image, + pixman_bool_t source_clipping); + +PIXMAN_API +void pixman_image_set_alpha_map (pixman_image_t *image, + pixman_image_t *alpha_map, + int16_t x, + int16_t y); + +PIXMAN_API +void pixman_image_set_component_alpha (pixman_image_t *image, + pixman_bool_t component_alpha); + +PIXMAN_API +pixman_bool_t pixman_image_get_component_alpha (pixman_image_t *image); + +PIXMAN_API +void pixman_image_set_accessors (pixman_image_t *image, + pixman_read_memory_func_t read_func, + pixman_write_memory_func_t write_func); + +PIXMAN_API +void pixman_image_set_indexed (pixman_image_t *image, + const pixman_indexed_t *indexed); + +PIXMAN_API +uint32_t *pixman_image_get_data (pixman_image_t *image); + +PIXMAN_API +int pixman_image_get_width (pixman_image_t *image); + +PIXMAN_API +int pixman_image_get_height (pixman_image_t *image); + +PIXMAN_API +int pixman_image_get_stride (pixman_image_t *image); /* in bytes */ + +PIXMAN_API +int pixman_image_get_depth (pixman_image_t *image); + +PIXMAN_API +pixman_format_code_t pixman_image_get_format (pixman_image_t *image); + +typedef enum +{ + PIXMAN_KERNEL_IMPULSE, + PIXMAN_KERNEL_BOX, + PIXMAN_KERNEL_LINEAR, + PIXMAN_KERNEL_CUBIC, + PIXMAN_KERNEL_GAUSSIAN, + PIXMAN_KERNEL_LANCZOS2, + PIXMAN_KERNEL_LANCZOS3, + PIXMAN_KERNEL_LANCZOS3_STRETCHED /* Jim Blinn's 'nice' filter */ +} pixman_kernel_t; + +/* Create the parameter list for a SEPARABLE_CONVOLUTION filter + * with the given kernels and scale parameters. + */ +PIXMAN_API +pixman_fixed_t * +pixman_filter_create_separable_convolution (int *n_values, + pixman_fixed_t scale_x, + pixman_fixed_t scale_y, + pixman_kernel_t reconstruct_x, + pixman_kernel_t reconstruct_y, + pixman_kernel_t sample_x, + pixman_kernel_t sample_y, + int subsample_bits_x, + int subsample_bits_y); + + +PIXMAN_API +pixman_bool_t pixman_image_fill_rectangles (pixman_op_t op, + pixman_image_t *image, + const pixman_color_t *color, + int n_rects, + const pixman_rectangle16_t *rects); + +PIXMAN_API +pixman_bool_t pixman_image_fill_boxes (pixman_op_t op, + pixman_image_t *dest, + const pixman_color_t *color, + int n_boxes, + const pixman_box32_t *boxes); + +/* Composite */ +PIXMAN_API +pixman_bool_t pixman_compute_composite_region (pixman_region16_t *region, + pixman_image_t *src_image, + pixman_image_t *mask_image, + pixman_image_t *dest_image, + int16_t src_x, + int16_t src_y, + int16_t mask_x, + int16_t mask_y, + int16_t dest_x, + int16_t dest_y, + uint16_t width, + uint16_t height); + +PIXMAN_API +void pixman_image_composite (pixman_op_t op, + pixman_image_t *src, + pixman_image_t *mask, + pixman_image_t *dest, + int16_t src_x, + int16_t src_y, + int16_t mask_x, + int16_t mask_y, + int16_t dest_x, + int16_t dest_y, + uint16_t width, + uint16_t height); + +PIXMAN_API +void pixman_image_composite32 (pixman_op_t op, + pixman_image_t *src, + pixman_image_t *mask, + pixman_image_t *dest, + int32_t src_x, + int32_t src_y, + int32_t mask_x, + int32_t mask_y, + int32_t dest_x, + int32_t dest_y, + int32_t width, + int32_t height); + +/* Executive Summary: This function is a no-op that only exists + * for historical reasons. + * + * There used to be a bug in the X server where it would rely on + * out-of-bounds accesses when it was asked to composite with a + * window as the source. It would create a pixman image pointing + * to some bogus position in memory, but then set a clip region + * to the position where the actual bits were. + * + * Due to a bug in old versions of pixman, where it would not clip + * against the image bounds when a clip region was set, this would + * actually work. So when the pixman bug was fixed, a workaround was + * added to allow certain out-of-bound accesses. This function disabled + * those workarounds. + * + * Since 0.21.2, pixman doesn't do these workarounds anymore, so now this + * function is a no-op. + */ +PIXMAN_API +void pixman_disable_out_of_bounds_workaround (void); + +/* + * Glyphs + */ +typedef struct pixman_glyph_cache_t pixman_glyph_cache_t; +typedef struct +{ + int x, y; + const void *glyph; +} pixman_glyph_t; + +PIXMAN_API +pixman_glyph_cache_t *pixman_glyph_cache_create (void); + +PIXMAN_API +void pixman_glyph_cache_destroy (pixman_glyph_cache_t *cache); + +PIXMAN_API +void pixman_glyph_cache_freeze (pixman_glyph_cache_t *cache); + +PIXMAN_API +void pixman_glyph_cache_thaw (pixman_glyph_cache_t *cache); + +PIXMAN_API +const void * pixman_glyph_cache_lookup (pixman_glyph_cache_t *cache, + void *font_key, + void *glyph_key); + +PIXMAN_API +const void * pixman_glyph_cache_insert (pixman_glyph_cache_t *cache, + void *font_key, + void *glyph_key, + int origin_x, + int origin_y, + pixman_image_t *glyph_image); + +PIXMAN_API +void pixman_glyph_cache_remove (pixman_glyph_cache_t *cache, + void *font_key, + void *glyph_key); + +PIXMAN_API +void pixman_glyph_get_extents (pixman_glyph_cache_t *cache, + int n_glyphs, + pixman_glyph_t *glyphs, + pixman_box32_t *extents); + +PIXMAN_API +pixman_format_code_t pixman_glyph_get_mask_format (pixman_glyph_cache_t *cache, + int n_glyphs, + const pixman_glyph_t *glyphs); + +PIXMAN_API +void pixman_composite_glyphs (pixman_op_t op, + pixman_image_t *src, + pixman_image_t *dest, + pixman_format_code_t mask_format, + int32_t src_x, + int32_t src_y, + int32_t mask_x, + int32_t mask_y, + int32_t dest_x, + int32_t dest_y, + int32_t width, + int32_t height, + pixman_glyph_cache_t *cache, + int n_glyphs, + const pixman_glyph_t *glyphs); + +PIXMAN_API +void pixman_composite_glyphs_no_mask (pixman_op_t op, + pixman_image_t *src, + pixman_image_t *dest, + int32_t src_x, + int32_t src_y, + int32_t dest_x, + int32_t dest_y, + pixman_glyph_cache_t *cache, + int n_glyphs, + const pixman_glyph_t *glyphs); + +/* + * Trapezoids + */ +typedef struct pixman_edge pixman_edge_t; +typedef struct pixman_trapezoid pixman_trapezoid_t; +typedef struct pixman_trap pixman_trap_t; +typedef struct pixman_span_fix pixman_span_fix_t; +typedef struct pixman_triangle pixman_triangle_t; + +/* + * An edge structure. This represents a single polygon edge + * and can be quickly stepped across small or large gaps in the + * sample grid + */ +struct pixman_edge +{ + pixman_fixed_t x; + pixman_fixed_t e; + pixman_fixed_t stepx; + pixman_fixed_t signdx; + pixman_fixed_t dy; + pixman_fixed_t dx; + + pixman_fixed_t stepx_small; + pixman_fixed_t stepx_big; + pixman_fixed_t dx_small; + pixman_fixed_t dx_big; +}; + +struct pixman_trapezoid +{ + pixman_fixed_t top, bottom; + pixman_line_fixed_t left, right; +}; + +struct pixman_triangle +{ + pixman_point_fixed_t p1, p2, p3; +}; + +/* whether 't' is a well defined not obviously empty trapezoid */ +#define pixman_trapezoid_valid(t) \ + ((t)->left.p1.y != (t)->left.p2.y && \ + (t)->right.p1.y != (t)->right.p2.y && \ + ((t)->bottom > (t)->top)) + +struct pixman_span_fix +{ + pixman_fixed_t l, r, y; +}; + +struct pixman_trap +{ + pixman_span_fix_t top, bot; +}; + +PIXMAN_API +pixman_fixed_t pixman_sample_ceil_y (pixman_fixed_t y, + int bpp); + +PIXMAN_API +pixman_fixed_t pixman_sample_floor_y (pixman_fixed_t y, + int bpp); + +PIXMAN_API +void pixman_edge_step (pixman_edge_t *e, + int n); + +PIXMAN_API +void pixman_edge_init (pixman_edge_t *e, + int bpp, + pixman_fixed_t y_start, + pixman_fixed_t x_top, + pixman_fixed_t y_top, + pixman_fixed_t x_bot, + pixman_fixed_t y_bot); + +PIXMAN_API +void pixman_line_fixed_edge_init (pixman_edge_t *e, + int bpp, + pixman_fixed_t y, + const pixman_line_fixed_t *line, + int x_off, + int y_off); + +PIXMAN_API +void pixman_rasterize_edges (pixman_image_t *image, + pixman_edge_t *l, + pixman_edge_t *r, + pixman_fixed_t t, + pixman_fixed_t b); + +PIXMAN_API +void pixman_add_traps (pixman_image_t *image, + int16_t x_off, + int16_t y_off, + int ntrap, + const pixman_trap_t *traps); + +PIXMAN_API +void pixman_add_trapezoids (pixman_image_t *image, + int16_t x_off, + int y_off, + int ntraps, + const pixman_trapezoid_t *traps); + +PIXMAN_API +void pixman_rasterize_trapezoid (pixman_image_t *image, + const pixman_trapezoid_t *trap, + int x_off, + int y_off); + +PIXMAN_API +void pixman_composite_trapezoids (pixman_op_t op, + pixman_image_t * src, + pixman_image_t * dst, + pixman_format_code_t mask_format, + int x_src, + int y_src, + int x_dst, + int y_dst, + int n_traps, + const pixman_trapezoid_t * traps); + +PIXMAN_API +void pixman_composite_triangles (pixman_op_t op, + pixman_image_t * src, + pixman_image_t * dst, + pixman_format_code_t mask_format, + int x_src, + int y_src, + int x_dst, + int y_dst, + int n_tris, + const pixman_triangle_t * tris); + +PIXMAN_API +void pixman_add_triangles (pixman_image_t *image, + int32_t x_off, + int32_t y_off, + int n_tris, + const pixman_triangle_t *tris); + +PIXMAN_END_DECLS + +#endif /* PIXMAN_H__ */ diff --git a/gfx/cairo/libpixman/src/solaris-hwcap.mapfile b/gfx/cairo/libpixman/src/solaris-hwcap.mapfile new file mode 100644 index 0000000000..87efce1e34 --- /dev/null +++ b/gfx/cairo/libpixman/src/solaris-hwcap.mapfile @@ -0,0 +1,30 @@ +############################################################################### +# +# Copyright 2009, Oracle and/or its affiliates. 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, 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. +# +############################################################################### +# +# Override the linker's detection of CMOV/MMX/SSE instructions so this +# library isn't flagged as only usable on CPU's with those ISA's, since it +# checks at runtime for availability before calling them + +hwcap_1 = V0x0 FPU OVERRIDE; diff --git a/gfx/cairo/moz.build b/gfx/cairo/moz.build new file mode 100644 index 0000000000..1774847539 --- /dev/null +++ b/gfx/cairo/moz.build @@ -0,0 +1,11 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ['cairo/src'] + +if CONFIG['MOZ_TREE_PIXMAN']: + DIRS += ['libpixman/src'] + diff --git a/gfx/cairo/pixman-arm32-clang.patch b/gfx/cairo/pixman-arm32-clang.patch new file mode 100644 index 0000000000..cd9d61e470 --- /dev/null +++ b/gfx/cairo/pixman-arm32-clang.patch @@ -0,0 +1,5205 @@ +https://gitlab.freedesktop.org/pixman/pixman/-/issues/74 + +diff --git a/gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S b/gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S +--- a/gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S ++++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S +@@ -77,206 +77,206 @@ + * format conversion, and interpolation as separate macros which can be used + * as the basic building blocks for constructing bilinear scanline functions. + */ + + .macro bilinear_load_8888 reg1, reg2, tmp + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 +- vld1.32 {reg1}, [TMP1], STRIDE +- vld1.32 {reg2}, [TMP1] ++ vld1.32 {\reg1}, [TMP1], STRIDE ++ vld1.32 {\reg2}, [TMP1] + .endm + + .macro bilinear_load_0565 reg1, reg2, tmp + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 +- vld1.32 {reg2[0]}, [TMP1], STRIDE +- vld1.32 {reg2[1]}, [TMP1] +- convert_four_0565_to_x888_packed reg2, reg1, reg2, tmp ++ vld1.32 {\reg2[0]}, [TMP1], STRIDE ++ vld1.32 {\reg2[1]}, [TMP1] ++ convert_four_0565_to_x888_packed \reg2, \reg1, \reg2, \tmp + .endm + + .macro bilinear_load_and_vertical_interpolate_two_8888 \ + acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 + +- bilinear_load_8888 reg1, reg2, tmp1 +- vmull.u8 acc1, reg1, d28 +- vmlal.u8 acc1, reg2, d29 +- bilinear_load_8888 reg3, reg4, tmp2 +- vmull.u8 acc2, reg3, d28 +- vmlal.u8 acc2, reg4, d29 ++ bilinear_load_8888 \reg1, \reg2, \tmp1 ++ vmull.u8 \acc1, \reg1, d28 ++ vmlal.u8 \acc1, \reg2, d29 ++ bilinear_load_8888 \reg3, \reg4, \tmp2 ++ vmull.u8 \acc2, \reg3, d28 ++ vmlal.u8 \acc2, \reg4, d29 + .endm + + .macro bilinear_load_and_vertical_interpolate_four_8888 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + bilinear_load_and_vertical_interpolate_two_8888 \ +- xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi ++ \xacc1, \xacc2, \xreg1, \xreg2, \xreg3, \xreg4, \xacc2lo, \xacc2hi + bilinear_load_and_vertical_interpolate_two_8888 \ +- yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi ++ \yacc1, \yacc2, \yreg1, \yreg2, \yreg3, \yreg4, \yacc2lo, \yacc2hi + .endm + + .macro bilinear_load_and_vertical_interpolate_two_0565 \ + acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 +- vld1.32 {acc2lo[0]}, [TMP1], STRIDE +- vld1.32 {acc2hi[0]}, [TMP2], STRIDE +- vld1.32 {acc2lo[1]}, [TMP1] +- vld1.32 {acc2hi[1]}, [TMP2] +- convert_0565_to_x888 acc2, reg3, reg2, reg1 +- vzip.u8 reg1, reg3 +- vzip.u8 reg2, reg4 +- vzip.u8 reg3, reg4 +- vzip.u8 reg1, reg2 +- vmull.u8 acc1, reg1, d28 +- vmlal.u8 acc1, reg2, d29 +- vmull.u8 acc2, reg3, d28 +- vmlal.u8 acc2, reg4, d29 ++ vld1.32 {\acc2lo[0]}, [TMP1], STRIDE ++ vld1.32 {\acc2hi[0]}, [TMP2], STRIDE ++ vld1.32 {\acc2lo[1]}, [TMP1] ++ vld1.32 {\acc2hi[1]}, [TMP2] ++ convert_0565_to_x888 \acc2, \reg3, \reg2, \reg1 ++ vzip.u8 \reg1, \reg3 ++ vzip.u8 \reg2, \reg4 ++ vzip.u8 \reg3, \reg4 ++ vzip.u8 \reg1, \reg2 ++ vmull.u8 \acc1, \reg1, d28 ++ vmlal.u8 \acc1, \reg2, d29 ++ vmull.u8 \acc2, \reg3, d28 ++ vmlal.u8 \acc2, \reg4, d29 + .endm + + .macro bilinear_load_and_vertical_interpolate_four_0565 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 +- vld1.32 {xacc2lo[0]}, [TMP1], STRIDE +- vld1.32 {xacc2hi[0]}, [TMP2], STRIDE +- vld1.32 {xacc2lo[1]}, [TMP1] +- vld1.32 {xacc2hi[1]}, [TMP2] +- convert_0565_to_x888 xacc2, xreg3, xreg2, xreg1 ++ vld1.32 {\xacc2lo[0]}, [TMP1], STRIDE ++ vld1.32 {\xacc2hi[0]}, [TMP2], STRIDE ++ vld1.32 {\xacc2lo[1]}, [TMP1] ++ vld1.32 {\xacc2hi[1]}, [TMP2] ++ convert_0565_to_x888 \xacc2, \xreg3, \xreg2, \xreg1 + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 +- vld1.32 {yacc2lo[0]}, [TMP1], STRIDE +- vzip.u8 xreg1, xreg3 +- vld1.32 {yacc2hi[0]}, [TMP2], STRIDE +- vzip.u8 xreg2, xreg4 +- vld1.32 {yacc2lo[1]}, [TMP1] +- vzip.u8 xreg3, xreg4 +- vld1.32 {yacc2hi[1]}, [TMP2] +- vzip.u8 xreg1, xreg2 +- convert_0565_to_x888 yacc2, yreg3, yreg2, yreg1 +- vmull.u8 xacc1, xreg1, d28 +- vzip.u8 yreg1, yreg3 +- vmlal.u8 xacc1, xreg2, d29 +- vzip.u8 yreg2, yreg4 +- vmull.u8 xacc2, xreg3, d28 +- vzip.u8 yreg3, yreg4 +- vmlal.u8 xacc2, xreg4, d29 +- vzip.u8 yreg1, yreg2 +- vmull.u8 yacc1, yreg1, d28 +- vmlal.u8 yacc1, yreg2, d29 +- vmull.u8 yacc2, yreg3, d28 +- vmlal.u8 yacc2, yreg4, d29 ++ vld1.32 {\yacc2lo[0]}, [TMP1], STRIDE ++ vzip.u8 \xreg1, \xreg3 ++ vld1.32 {\yacc2hi[0]}, [TMP2], STRIDE ++ vzip.u8 \xreg2, \xreg4 ++ vld1.32 {\yacc2lo[1]}, [TMP1] ++ vzip.u8 \xreg3, \xreg4 ++ vld1.32 {\yacc2hi[1]}, [TMP2] ++ vzip.u8 \xreg1, \xreg2 ++ convert_0565_to_x888 \yacc2, \yreg3, \yreg2, \yreg1 ++ vmull.u8 \xacc1, \xreg1, d28 ++ vzip.u8 \yreg1, \yreg3 ++ vmlal.u8 \xacc1, \xreg2, d29 ++ vzip.u8 \yreg2, \yreg4 ++ vmull.u8 \xacc2, \xreg3, d28 ++ vzip.u8 \yreg3, \yreg4 ++ vmlal.u8 \xacc2, \xreg4, d29 ++ vzip.u8 \yreg1, \yreg2 ++ vmull.u8 \yacc1, \yreg1, d28 ++ vmlal.u8 \yacc1, \yreg2, d29 ++ vmull.u8 \yacc2, \yreg3, d28 ++ vmlal.u8 \yacc2, \yreg4, d29 + .endm + + .macro bilinear_store_8888 numpix, tmp1, tmp2 +-.if numpix == 4 ++.if \numpix == 4 + vst1.32 {d0, d1}, [OUT]! +-.elseif numpix == 2 ++.elseif \numpix == 2 + vst1.32 {d0}, [OUT]! +-.elseif numpix == 1 ++.elseif \numpix == 1 + vst1.32 {d0[0]}, [OUT, :32]! + .else + .error bilinear_store_8888 numpix is unsupported + .endif + .endm + + .macro bilinear_store_0565 numpix, tmp1, tmp2 + vuzp.u8 d0, d1 + vuzp.u8 d2, d3 + vuzp.u8 d1, d3 + vuzp.u8 d0, d2 +- convert_8888_to_0565 d2, d1, d0, q1, tmp1, tmp2 +-.if numpix == 4 ++ convert_8888_to_0565 d2, d1, d0, q1, \tmp1, \tmp2 ++.if \numpix == 4 + vst1.16 {d2}, [OUT]! +-.elseif numpix == 2 ++.elseif \numpix == 2 + vst1.32 {d2[0]}, [OUT]! +-.elseif numpix == 1 ++.elseif \numpix == 1 + vst1.16 {d2[0]}, [OUT]! + .else + .error bilinear_store_0565 numpix is unsupported + .endif + .endm + + + /* + * Macros for loading mask pixels into register 'mask'. + * vdup must be done in somewhere else. + */ + .macro bilinear_load_mask_x numpix, mask + .endm + + .macro bilinear_load_mask_8 numpix, mask +-.if numpix == 4 +- vld1.32 {mask[0]}, [MASK]! +-.elseif numpix == 2 +- vld1.16 {mask[0]}, [MASK]! +-.elseif numpix == 1 +- vld1.8 {mask[0]}, [MASK]! ++.if \numpix == 4 ++ vld1.32 {\mask[0]}, [MASK]! ++.elseif \numpix == 2 ++ vld1.16 {\mask[0]}, [MASK]! ++.elseif \numpix == 1 ++ vld1.8 {\mask[0]}, [MASK]! + .else +- .error bilinear_load_mask_8 numpix is unsupported ++ .error bilinear_load_mask_8 \numpix is unsupported + .endif + pld [MASK, #prefetch_offset] + .endm + + .macro bilinear_load_mask mask_fmt, numpix, mask +- bilinear_load_mask_&mask_fmt numpix, mask ++ bilinear_load_mask_\()\mask_fmt \numpix, \mask + .endm + + + /* + * Macros for loading destination pixels into register 'dst0' and 'dst1'. + * Interleave should be done somewhere else. + */ + .macro bilinear_load_dst_0565_src numpix, dst0, dst1, dst01 + .endm + + .macro bilinear_load_dst_8888_src numpix, dst0, dst1, dst01 + .endm + + .macro bilinear_load_dst_8888 numpix, dst0, dst1, dst01 +-.if numpix == 4 +- vld1.32 {dst0, dst1}, [OUT] +-.elseif numpix == 2 +- vld1.32 {dst0}, [OUT] +-.elseif numpix == 1 +- vld1.32 {dst0[0]}, [OUT] ++.if \numpix == 4 ++ vld1.32 {\dst0, \dst1}, [OUT] ++.elseif \numpix == 2 ++ vld1.32 {\dst0}, [OUT] ++.elseif \numpix == 1 ++ vld1.32 {\dst0[0]}, [OUT] + .else +- .error bilinear_load_dst_8888 numpix is unsupported ++ .error bilinear_load_dst_8888 \numpix is unsupported + .endif + pld [OUT, #(prefetch_offset * 4)] + .endm + + .macro bilinear_load_dst_8888_over numpix, dst0, dst1, dst01 +- bilinear_load_dst_8888 numpix, dst0, dst1, dst01 ++ bilinear_load_dst_8888 \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_load_dst_8888_add numpix, dst0, dst1, dst01 +- bilinear_load_dst_8888 numpix, dst0, dst1, dst01 ++ bilinear_load_dst_8888 \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_load_dst dst_fmt, op, numpix, dst0, dst1, dst01 +- bilinear_load_dst_&dst_fmt&_&op numpix, dst0, dst1, dst01 ++ bilinear_load_dst_\()\dst_fmt\()_\()\op \numpix, \dst0, \dst1, \dst01 + .endm + + /* + * Macros for duplicating partially loaded mask to fill entire register. + * We will apply mask to interleaved source pixels, that is + * (r0, r1, r2, r3, g0, g1, g2, g3) x (m0, m1, m2, m3, m0, m1, m2, m3) + * (b0, b1, b2, b3, a0, a1, a2, a3) x (m0, m1, m2, m3, m0, m1, m2, m3) + * So, we need to duplicate loaded mask into whole register. +@@ -285,79 +285,79 @@ + * (r0, r1, x, x, g0, g1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) + * (b0, b1, x, x, a0, a1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) + * We can do some optimizations for this including last pixel cases. + */ + .macro bilinear_duplicate_mask_x numpix, mask + .endm + + .macro bilinear_duplicate_mask_8 numpix, mask +-.if numpix == 4 +- vdup.32 mask, mask[0] +-.elseif numpix == 2 +- vdup.16 mask, mask[0] +-.elseif numpix == 1 +- vdup.8 mask, mask[0] ++.if \numpix == 4 ++ vdup.32 \mask, \mask[0] ++.elseif \numpix == 2 ++ vdup.16 \mask, \mask[0] ++.elseif \numpix == 1 ++ vdup.8 \mask, \mask[0] + .else + .error bilinear_duplicate_mask_8 is unsupported + .endif + .endm + + .macro bilinear_duplicate_mask mask_fmt, numpix, mask +- bilinear_duplicate_mask_&mask_fmt numpix, mask ++ bilinear_duplicate_mask_\()\mask_fmt \numpix, \mask + .endm + + /* + * Macros for interleaving src and dst pixels to rrrr gggg bbbb aaaa form. + * Interleave should be done when maks is enabled or operator is 'over'. + */ + .macro bilinear_interleave src0, src1, dst0, dst1 +- vuzp.8 src0, src1 +- vuzp.8 dst0, dst1 +- vuzp.8 src0, src1 +- vuzp.8 dst0, dst1 ++ vuzp.8 \src0, \src1 ++ vuzp.8 \dst0, \dst1 ++ vuzp.8 \src0, \src1 ++ vuzp.8 \dst0, \dst1 + .endm + + .macro bilinear_interleave_src_dst_x_src \ + numpix, src0, src1, src01, dst0, dst1, dst01 + .endm + + .macro bilinear_interleave_src_dst_x_over \ + numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave src0, src1, dst0, dst1 ++ bilinear_interleave \src0, \src1, \dst0, \dst1 + .endm + + .macro bilinear_interleave_src_dst_x_add \ + numpix, src0, src1, src01, dst0, dst1, dst01 + .endm + + .macro bilinear_interleave_src_dst_8_src \ + numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave src0, src1, dst0, dst1 ++ bilinear_interleave \src0, \src1, \dst0, \dst1 + .endm + + .macro bilinear_interleave_src_dst_8_over \ + numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave src0, src1, dst0, dst1 ++ bilinear_interleave \src0, \src1, \dst0, \dst1 + .endm + + .macro bilinear_interleave_src_dst_8_add \ + numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave src0, src1, dst0, dst1 ++ bilinear_interleave \src0, \src1, \dst0, \dst1 + .endm + + .macro bilinear_interleave_src_dst \ + mask_fmt, op, numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave_src_dst_&mask_fmt&_&op \ +- numpix, src0, src1, src01, dst0, dst1, dst01 ++ bilinear_interleave_src_dst_\()\mask_fmt\()_\()\op \ ++ \numpix, \src0, \src1, \src01, \dst0, \dst1, \dst01 + .endm + + + /* + * Macros for applying masks to src pixels. (see combine_mask_u() function) + * src, dst should be in interleaved form. + * mask register should be in form (m0, m1, m2, m3). + */ +@@ -365,217 +365,217 @@ + numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + .endm + + .macro bilinear_apply_mask_to_src_8 \ + numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + +- vmull.u8 tmp01, src0, mask +- vmull.u8 tmp23, src1, mask ++ vmull.u8 \tmp01, \src0, \mask ++ vmull.u8 \tmp23, \src1, \mask + /* bubbles */ +- vrshr.u16 tmp45, tmp01, #8 +- vrshr.u16 tmp67, tmp23, #8 ++ vrshr.u16 \tmp45, \tmp01, #8 ++ vrshr.u16 \tmp67, \tmp23, #8 + /* bubbles */ +- vraddhn.u16 src0, tmp45, tmp01 +- vraddhn.u16 src1, tmp67, tmp23 ++ vraddhn.u16 \src0, \tmp45, \tmp01 ++ vraddhn.u16 \src1, \tmp67, \tmp23 + .endm + + .macro bilinear_apply_mask_to_src \ + mask_fmt, numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + +- bilinear_apply_mask_to_src_&mask_fmt \ +- numpix, src0, src1, src01, mask, \ +- tmp01, tmp23, tmp45, tmp67 ++ bilinear_apply_mask_to_src_\()\mask_fmt \ ++ \numpix, \src0, \src1, \src01, \mask, \ ++ \tmp01, \tmp23, \tmp45, \tmp67 + .endm + + + /* + * Macros for combining src and destination pixels. + * Interleave or not is depending on operator 'op'. + */ + .macro bilinear_combine_src \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + .endm + + .macro bilinear_combine_over \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + +- vdup.32 tmp8, src1[1] ++ vdup.32 \tmp8, \src1[1] + /* bubbles */ +- vmvn.8 tmp8, tmp8 ++ vmvn.8 \tmp8, \tmp8 + /* bubbles */ +- vmull.u8 tmp01, dst0, tmp8 ++ vmull.u8 \tmp01, \dst0, \tmp8 + /* bubbles */ +- vmull.u8 tmp23, dst1, tmp8 ++ vmull.u8 \tmp23, \dst1, \tmp8 + /* bubbles */ +- vrshr.u16 tmp45, tmp01, #8 +- vrshr.u16 tmp67, tmp23, #8 ++ vrshr.u16 \tmp45, \tmp01, #8 ++ vrshr.u16 \tmp67, \tmp23, #8 + /* bubbles */ +- vraddhn.u16 dst0, tmp45, tmp01 +- vraddhn.u16 dst1, tmp67, tmp23 ++ vraddhn.u16 \dst0, \tmp45, \tmp01 ++ vraddhn.u16 \dst1, \tmp67, \tmp23 + /* bubbles */ +- vqadd.u8 src01, dst01, src01 ++ vqadd.u8 \src01, \dst01, \src01 + .endm + + .macro bilinear_combine_add \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + +- vqadd.u8 src01, dst01, src01 ++ vqadd.u8 \src01, \dst01, \src01 + .endm + + .macro bilinear_combine \ + op, numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + +- bilinear_combine_&op \ +- numpix, src0, src1, src01, dst0, dst1, dst01, \ +- tmp01, tmp23, tmp45, tmp67, tmp8 ++ bilinear_combine_\()\op \ ++ \numpix, \src0, \src1, \src01, \dst0, \dst1, \dst01, \ ++ \tmp01, \tmp23, \tmp45, \tmp67, \tmp8 + .endm + + /* + * Macros for final deinterleaving of destination pixels if needed. + */ + .macro bilinear_deinterleave numpix, dst0, dst1, dst01 +- vuzp.8 dst0, dst1 ++ vuzp.8 \dst0, \dst1 + /* bubbles */ +- vuzp.8 dst0, dst1 ++ vuzp.8 \dst0, \dst1 + .endm + + .macro bilinear_deinterleave_dst_x_src numpix, dst0, dst1, dst01 + .endm + + .macro bilinear_deinterleave_dst_x_over numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst_x_add numpix, dst0, dst1, dst01 + .endm + + .macro bilinear_deinterleave_dst_8_src numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst_8_over numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst_8_add numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst mask_fmt, op, numpix, dst0, dst1, dst01 +- bilinear_deinterleave_dst_&mask_fmt&_&op numpix, dst0, dst1, dst01 ++ bilinear_deinterleave_dst_\()\mask_fmt\()_\()\op \numpix, \dst0, \dst1, \dst01 + .endm + + + .macro bilinear_interpolate_last_pixel src_fmt, mask_fmt, dst_fmt, op +- bilinear_load_&src_fmt d0, d1, d2 +- bilinear_load_mask mask_fmt, 1, d4 +- bilinear_load_dst dst_fmt, op, 1, d18, d19, q9 ++ bilinear_load_\()\src_fmt d0, d1, d2 ++ bilinear_load_mask \mask_fmt, 1, d4 ++ bilinear_load_dst \dst_fmt, \op, 1, d18, d19, q9 + vmull.u8 q1, d0, d28 + vmlal.u8 q1, d1, d29 + /* 5 cycles bubble */ + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + /* 5 cycles bubble */ +- bilinear_duplicate_mask mask_fmt, 1, d4 ++ bilinear_duplicate_mask \mask_fmt, 1, d4 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + /* 3 cycles bubble */ + vmovn.u16 d0, q0 + /* 1 cycle bubble */ + bilinear_interleave_src_dst \ +- mask_fmt, op, 1, d0, d1, q0, d18, d19, q9 ++ \mask_fmt, \op, 1, d0, d1, q0, d18, d19, q9 + bilinear_apply_mask_to_src \ +- mask_fmt, 1, d0, d1, q0, d4, \ ++ \mask_fmt, 1, d0, d1, q0, d4, \ + q3, q8, q10, q11 + bilinear_combine \ +- op, 1, d0, d1, q0, d18, d19, q9, \ ++ \op, 1, d0, d1, q0, d18, d19, q9, \ + q3, q8, q10, q11, d5 +- bilinear_deinterleave_dst mask_fmt, op, 1, d0, d1, q0 +- bilinear_store_&dst_fmt 1, q2, q3 ++ bilinear_deinterleave_dst \mask_fmt, \op, 1, d0, d1, q0 ++ bilinear_store_\()\dst_fmt 1, q2, q3 + .endm + + .macro bilinear_interpolate_two_pixels src_fmt, mask_fmt, dst_fmt, op +- bilinear_load_and_vertical_interpolate_two_&src_fmt \ ++ bilinear_load_and_vertical_interpolate_two_\()\src_fmt \ + q1, q11, d0, d1, d20, d21, d22, d23 +- bilinear_load_mask mask_fmt, 2, d4 +- bilinear_load_dst dst_fmt, op, 2, d18, d19, q9 ++ bilinear_load_mask \mask_fmt, 2, d4 ++ bilinear_load_dst \dst_fmt, \op, 2, d18, d19, q9 + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q10, d22, d31 + vmlal.u16 q10, d23, d31 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) +- bilinear_duplicate_mask mask_fmt, 2, d4 ++ bilinear_duplicate_mask \mask_fmt, 2, d4 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vmovn.u16 d0, q0 + bilinear_interleave_src_dst \ +- mask_fmt, op, 2, d0, d1, q0, d18, d19, q9 ++ \mask_fmt, \op, 2, d0, d1, q0, d18, d19, q9 + bilinear_apply_mask_to_src \ +- mask_fmt, 2, d0, d1, q0, d4, \ ++ \mask_fmt, 2, d0, d1, q0, d4, \ + q3, q8, q10, q11 + bilinear_combine \ +- op, 2, d0, d1, q0, d18, d19, q9, \ ++ \op, 2, d0, d1, q0, d18, d19, q9, \ + q3, q8, q10, q11, d5 +- bilinear_deinterleave_dst mask_fmt, op, 2, d0, d1, q0 +- bilinear_store_&dst_fmt 2, q2, q3 ++ bilinear_deinterleave_dst \mask_fmt, \op, 2, d0, d1, q0 ++ bilinear_store_\()\dst_fmt 2, q2, q3 + .endm + + .macro bilinear_interpolate_four_pixels src_fmt, mask_fmt, dst_fmt, op +- bilinear_load_and_vertical_interpolate_four_&src_fmt \ ++ bilinear_load_and_vertical_interpolate_four_\()\src_fmt \ + q1, q11, d0, d1, d20, d21, d22, d23 \ + q3, q9, d4, d5, d16, d17, d18, d19 + pld [TMP1, PF_OFFS] + sub TMP1, TMP1, STRIDE + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q10, d22, d31 + vmlal.u16 q10, d23, d31 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vshll.u16 q2, d6, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q2, d6, d30 + vmlal.u16 q2, d7, d30 + vshll.u16 q8, d18, #BILINEAR_INTERPOLATION_BITS +- bilinear_load_mask mask_fmt, 4, d22 +- bilinear_load_dst dst_fmt, op, 4, d2, d3, q1 ++ bilinear_load_mask \mask_fmt, 4, d22 ++ bilinear_load_dst \dst_fmt, \op, 4, d2, d3, q1 + pld [TMP1, PF_OFFS] + vmlsl.u16 q8, d18, d31 + vmlal.u16 q8, d19, d31 + vadd.u16 q12, q12, q13 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d5, q8, #(2 * BILINEAR_INTERPOLATION_BITS) +- bilinear_duplicate_mask mask_fmt, 4, d22 ++ bilinear_duplicate_mask \mask_fmt, 4, d22 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d0, q0 + vmovn.u16 d1, q2 + vadd.u16 q12, q12, q13 + bilinear_interleave_src_dst \ +- mask_fmt, op, 4, d0, d1, q0, d2, d3, q1 ++ \mask_fmt, \op, 4, d0, d1, q0, d2, d3, q1 + bilinear_apply_mask_to_src \ +- mask_fmt, 4, d0, d1, q0, d22, \ ++ \mask_fmt, 4, d0, d1, q0, d22, \ + q3, q8, q9, q10 + bilinear_combine \ +- op, 4, d0, d1, q0, d2, d3, q1, \ ++ \op, 4, d0, d1, q0, d2, d3, q1, \ + q3, q8, q9, q10, d23 +- bilinear_deinterleave_dst mask_fmt, op, 4, d0, d1, q0 +- bilinear_store_&dst_fmt 4, q2, q3 ++ bilinear_deinterleave_dst \mask_fmt, \op, 4, d0, d1, q0 ++ bilinear_store_\()\dst_fmt 4, q2, q3 + .endm + + .set BILINEAR_FLAG_USE_MASK, 1 + .set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 + + /* + * Main template macro for generating NEON optimized bilinear scanline functions. + * +@@ -605,24 +605,24 @@ + bilinear_process_four_pixels, \ + bilinear_process_pixblock_head, \ + bilinear_process_pixblock_tail, \ + bilinear_process_pixblock_tail_head, \ + pixblock_size, \ + prefetch_distance, \ + flags + +-pixman_asm_function fname +-.if pixblock_size == 8 +-.elseif pixblock_size == 4 ++pixman_asm_function \fname ++.if \pixblock_size == 8 ++.elseif \pixblock_size == 4 + .else + .error unsupported pixblock size + .endif + +-.if ((flags) & BILINEAR_FLAG_USE_MASK) == 0 ++.if ((\flags) & BILINEAR_FLAG_USE_MASK) == 0 + OUT .req r0 + TOP .req r1 + BOTTOM .req r2 + WT .req r3 + WB .req r4 + X .req r5 + UX .req r6 + WIDTH .req ip +@@ -630,17 +630,17 @@ pixman_asm_function fname + TMP2 .req r4 + PF_OFFS .req r7 + TMP3 .req r8 + TMP4 .req r9 + STRIDE .req r2 + + mov ip, sp + push {r4, r5, r6, r7, r8, r9} +- mov PF_OFFS, #prefetch_distance ++ mov PF_OFFS, #\prefetch_distance + ldmia ip, {WB, X, UX, WIDTH} + .else + OUT .req r0 + MASK .req r1 + TOP .req r2 + BOTTOM .req r3 + WT .req r4 + WB .req r5 +@@ -649,27 +649,27 @@ pixman_asm_function fname + WIDTH .req ip + TMP1 .req r4 + TMP2 .req r5 + PF_OFFS .req r8 + TMP3 .req r9 + TMP4 .req r10 + STRIDE .req r3 + +- .set prefetch_offset, prefetch_distance ++ .set prefetch_offset, \prefetch_distance + + mov ip, sp + push {r4, r5, r6, r7, r8, r9, r10, ip} +- mov PF_OFFS, #prefetch_distance ++ mov PF_OFFS, #\prefetch_distance + ldmia ip, {WT, WB, X, UX, WIDTH} + .endif + + mul PF_OFFS, PF_OFFS, UX + +-.if ((flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 ++.if ((\flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 + vpush {d8-d15} + .endif + + sub STRIDE, BOTTOM, TOP + .unreq BOTTOM + + cmp WIDTH, #0 + ble 3f +@@ -678,76 +678,76 @@ pixman_asm_function fname + vdup.u16 q13, UX + vdup.u8 d28, WT + vdup.u8 d29, WB + vadd.u16 d25, d25, d26 + + /* ensure good destination alignment */ + cmp WIDTH, #1 + blt 0f +- tst OUT, #(1 << dst_bpp_shift) ++ tst OUT, #(1 << \dst_bpp_shift) + beq 0f + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 +- bilinear_process_last_pixel ++ \bilinear_process_last_pixel + sub WIDTH, WIDTH, #1 + 0: + vadd.u16 q13, q13, q13 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + + cmp WIDTH, #2 + blt 0f +- tst OUT, #(1 << (dst_bpp_shift + 1)) ++ tst OUT, #(1 << (\dst_bpp_shift + 1)) + beq 0f +- bilinear_process_two_pixels ++ \bilinear_process_two_pixels + sub WIDTH, WIDTH, #2 + 0: +-.if pixblock_size == 8 ++.if \pixblock_size == 8 + cmp WIDTH, #4 + blt 0f +- tst OUT, #(1 << (dst_bpp_shift + 2)) ++ tst OUT, #(1 << (\dst_bpp_shift + 2)) + beq 0f +- bilinear_process_four_pixels ++ \bilinear_process_four_pixels + sub WIDTH, WIDTH, #4 + 0: + .endif +- subs WIDTH, WIDTH, #pixblock_size ++ subs WIDTH, WIDTH, #\pixblock_size + blt 1f +- mov PF_OFFS, PF_OFFS, asr #(16 - src_bpp_shift) +- bilinear_process_pixblock_head +- subs WIDTH, WIDTH, #pixblock_size ++ mov PF_OFFS, PF_OFFS, asr #(16 - \src_bpp_shift) ++ \bilinear_process_pixblock_head ++ subs WIDTH, WIDTH, #\pixblock_size + blt 5f + 0: +- bilinear_process_pixblock_tail_head +- subs WIDTH, WIDTH, #pixblock_size ++ \bilinear_process_pixblock_tail_head ++ subs WIDTH, WIDTH, #\pixblock_size + bge 0b + 5: +- bilinear_process_pixblock_tail ++ \bilinear_process_pixblock_tail + 1: +-.if pixblock_size == 8 ++.if \pixblock_size == 8 + tst WIDTH, #4 + beq 2f +- bilinear_process_four_pixels ++ \bilinear_process_four_pixels + 2: + .endif + /* handle the remaining trailing pixels */ + tst WIDTH, #2 + beq 2f +- bilinear_process_two_pixels ++ \bilinear_process_two_pixels + 2: + tst WIDTH, #1 + beq 3f +- bilinear_process_last_pixel ++ \bilinear_process_last_pixel + 3: +-.if ((flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 ++.if ((\flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 + vpop {d8-d15} + .endif + +-.if ((flags) & BILINEAR_FLAG_USE_MASK) == 0 ++.if ((\flags) & BILINEAR_FLAG_USE_MASK) == 0 + pop {r4, r5, r6, r7, r8, r9} + .else + pop {r4, r5, r6, r7, r8, r9, r10, ip} + .endif + bx lr + + .unreq OUT + .unreq TOP +@@ -757,21 +757,21 @@ 3: + .unreq UX + .unreq WIDTH + .unreq TMP1 + .unreq TMP2 + .unreq PF_OFFS + .unreq TMP3 + .unreq TMP4 + .unreq STRIDE +-.if ((flags) & BILINEAR_FLAG_USE_MASK) != 0 ++.if ((\flags) & BILINEAR_FLAG_USE_MASK) != 0 + .unreq MASK + .endif + +-.endfunc ++pixman_end_asm_function + + .endm + + /* src_8888_8_8888 */ + .macro bilinear_src_8888_8_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 8888, src + .endm + +diff --git a/gfx/cairo/libpixman/src/pixman-arm-neon-asm.S b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.S +--- a/gfx/cairo/libpixman/src/pixman-arm-neon-asm.S ++++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.S +@@ -29,16 +29,22 @@ + * (those which are exposing some new or interesting features) are + * extensively commented and can be used as examples. + * + * You may want to have a look at the comments for following functions: + * - pixman_composite_over_8888_0565_asm_neon + * - pixman_composite_over_n_8_0565_asm_neon + */ + ++#ifdef __clang__ ++#define ldrgeb ldrbge ++#define subges subsge ++#define subpls subspl ++#endif ++ + /* Prevent the stack from becoming executable for no reason... */ + #if defined(__linux__) && defined(__ELF__) + .section .note.GNU-stack,"",%progbits + #endif + + .text + .fpu neon + .arch armv7a +@@ -255,43 +261,43 @@ + vqadd.u8 d16, d2, d20 + vld1.16 {d4, d5}, [DST_R, :128]! + vqadd.u8 q9, q0, q11 + vshrn.u16 d6, q2, #8 + fetch_src_pixblock + vshrn.u16 d7, q2, #3 + vsli.u16 q2, q2, #5 + vshll.u8 q14, d16, #8 +- PF add PF_X, PF_X, #8 ++ PF add, PF_X, PF_X, #8 + vshll.u8 q8, d19, #8 +- PF tst PF_CTL, #0xF ++ PF tst, PF_CTL, #0xF + vsri.u8 d6, d6, #5 +- PF addne PF_X, PF_X, #8 ++ PF addne, PF_X, PF_X, #8 + vmvn.8 d3, d3 +- PF subne PF_CTL, PF_CTL, #1 ++ PF subne, PF_CTL, PF_CTL, #1 + vsri.u8 d7, d7, #6 + vshrn.u16 d30, q2, #2 + vmull.u8 q10, d3, d6 + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vmull.u8 q11, d3, d7 + vmull.u8 q12, d3, d30 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vsri.u16 q14, q8, #5 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + vshll.u8 q9, d18, #8 + vrshr.u16 q13, q10, #8 +- PF subge PF_X, PF_X, ORIG_W ++ PF subge, PF_X, PF_X, ORIG_W + vrshr.u16 q3, q11, #8 + vrshr.u16 q15, q12, #8 +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subges, PF_CTL, PF_CTL, #0x10 + vsri.u16 q14, q9, #11 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vraddhn.u16 d20, q10, q13 + vraddhn.u16 d23, q11, q3 +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vraddhn.u16 d22, q12, q15 + vst1.16 {d28, d29}, [DST_W, :128]! + .endm + + #else + + /* If we did not care much about the performance, we would just use this... */ + .macro pixman_composite_over_8888_0565_process_pixblock_tail_head +@@ -429,30 +435,30 @@ generate_composite_function \ + + .macro pixman_composite_src_8888_0565_process_pixblock_tail + vsri.u16 q14, q8, #5 + vsri.u16 q14, q9, #11 + .endm + + .macro pixman_composite_src_8888_0565_process_pixblock_tail_head + vsri.u16 q14, q8, #5 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + fetch_src_pixblock +- PF addne PF_X, PF_X, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF addne, PF_X, PF_X, #8 ++ PF subne, PF_CTL, PF_CTL, #1 + vsri.u16 q14, q9, #11 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vshll.u8 q8, d1, #8 + vst1.16 {d28, d29}, [DST_W, :128]! +- PF subge PF_X, PF_X, ORIG_W +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subge, PF_X, PF_X, ORIG_W ++ PF subges, PF_CTL, PF_CTL, #0x10 + vshll.u8 q14, d2, #8 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vshll.u8 q9, d0, #8 + .endm + + generate_composite_function \ + pixman_composite_src_8888_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ +@@ -504,30 +510,30 @@ generate_composite_function \ + vqadd.u8 q15, q1, q3 + .endm + + .macro pixman_composite_add_8_8_process_pixblock_tail + .endm + + .macro pixman_composite_add_8_8_process_pixblock_tail_head + fetch_src_pixblock +- PF add PF_X, PF_X, #32 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #32 ++ PF tst, PF_CTL, #0xF + vld1.8 {d4, d5, d6, d7}, [DST_R, :128]! +- PF addne PF_X, PF_X, #32 +- PF subne PF_CTL, PF_CTL, #1 ++ PF addne, PF_X, PF_X, #32 ++ PF subne, PF_CTL, PF_CTL, #1 + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] +- PF subge PF_X, PF_X, ORIG_W +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subge, PF_X, PF_X, ORIG_W ++ PF subges, PF_CTL, PF_CTL, #0x10 + vqadd.u8 q14, q0, q2 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vqadd.u8 q15, q1, q3 + .endm + + generate_composite_function \ + pixman_composite_add_8_8_asm_neon, 8, 0, 8, \ + FLAG_DST_READWRITE, \ + 32, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ +@@ -536,30 +542,30 @@ generate_composite_function \ + pixman_composite_add_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_process_pixblock_tail, \ + pixman_composite_add_8_8_process_pixblock_tail_head + + /******************************************************************************/ + + .macro pixman_composite_add_8888_8888_process_pixblock_tail_head + fetch_src_pixblock +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + vld1.32 {d4, d5, d6, d7}, [DST_R, :128]! +- PF addne PF_X, PF_X, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF addne, PF_X, PF_X, #8 ++ PF subne, PF_CTL, PF_CTL, #1 + vst1.32 {d28, d29, d30, d31}, [DST_W, :128]! +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] +- PF subge PF_X, PF_X, ORIG_W +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subge, PF_X, PF_X, ORIG_W ++ PF subges, PF_CTL, PF_CTL, #0x10 + vqadd.u8 q14, q0, q2 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vqadd.u8 q15, q1, q3 + .endm + + generate_composite_function \ + pixman_composite_add_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ +@@ -599,40 +605,40 @@ generate_composite_function_single_scanl + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + .endm + + .macro pixman_composite_out_reverse_8888_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q14, q8, #8 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 +- PF addne PF_X, PF_X, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF addne, PF_X, PF_X, #8 ++ PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + fetch_src_pixblock + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vmvn.8 d22, d3 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +- PF subge PF_X, PF_X, ORIG_W ++ PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q8, d22, d4 +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subsge, PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d22, d5 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vmull.u8 q10, d22, d6 +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vmull.u8 q11, d22, d7 + .endm + + generate_composite_function_single_scanline \ + pixman_composite_scanline_out_reverse_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init, \ +@@ -651,42 +657,42 @@ generate_composite_function_single_scanl + pixman_composite_out_reverse_8888_8888_process_pixblock_tail + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + .endm + + .macro pixman_composite_over_8888_8888_process_pixblock_tail_head + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q14, q8, #8 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 +- PF addne PF_X, PF_X, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF addne, PF_X, PF_X, #8 ++ PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + fetch_src_pixblock + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + vmvn.8 d22, d3 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +- PF subge PF_X, PF_X, ORIG_W ++ PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q8, d22, d4 +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d22, d5 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + vmull.u8 q10, d22, d6 +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vmull.u8 q11, d22, d7 + .endm + + generate_composite_function \ + pixman_composite_over_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ +@@ -737,30 +743,30 @@ generate_composite_function_single_scanl + vrshr.u16 q2, q10, #8 + vrshr.u16 q3, q11, #8 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 + vraddhn.u16 d30, q2, q10 + vraddhn.u16 d31, q3, q11 + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vqadd.u8 q14, q0, q14 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0x0F +- PF addne PF_X, PF_X, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0x0F ++ PF addne, PF_X, PF_X, #8 ++ PF subne, PF_CTL, PF_CTL, #1 + vqadd.u8 q15, q1, q15 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + vmull.u8 q8, d24, d4 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vmull.u8 q9, d24, d5 +- PF subge PF_X, PF_X, ORIG_W ++ PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q10, d24, d6 +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q11, d24, d7 +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + .endm + + .macro pixman_composite_over_n_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d3[0]}, [DUMMY] + vdup.8 d0, d3[0] + vdup.8 d1, d3[1] +@@ -779,40 +785,40 @@ generate_composite_function \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_n_8888_process_pixblock_tail_head + + /******************************************************************************/ + + .macro pixman_composite_over_reverse_n_8888_process_pixblock_tail_head + vrshr.u16 q14, q8, #8 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + vrshr.u16 q15, q9, #8 + vrshr.u16 q12, q10, #8 + vrshr.u16 q13, q11, #8 +- PF addne PF_X, PF_X, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF addne, PF_X, PF_X, #8 ++ PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d28, q14, q8 + vraddhn.u16 d29, q15, q9 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + vraddhn.u16 d30, q12, q10 + vraddhn.u16 d31, q13, q11 + vqadd.u8 q14, q0, q14 + vqadd.u8 q15, q1, q15 + vld4.8 {d0, d1, d2, d3}, [DST_R, :128]! + vmvn.8 d22, d3 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +- PF subge PF_X, PF_X, ORIG_W ++ PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q8, d22, d4 +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d22, d5 + vmull.u8 q10, d22, d6 +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vmull.u8 q11, d22, d7 + .endm + + .macro pixman_composite_over_reverse_n_8888_init + add DUMMY, sp, #ARGS_STACK_OFFSET + vld1.32 {d7[0]}, [DUMMY] + vdup.8 d4, d7[0] + vdup.8 d5, d7[1] +@@ -1240,33 +1246,33 @@ generate_composite_function \ + vrshrn.u16 d28, q8, #8 + vrshrn.u16 d29, q9, #8 + vrshrn.u16 d30, q10, #8 + vrshrn.u16 d31, q11, #8 + .endm + + .macro pixman_composite_src_n_8_8888_process_pixblock_tail_head + fetch_mask_pixblock +- PF add PF_X, PF_X, #8 ++ PF add, PF_X, PF_X, #8 + vrshrn.u16 d28, q8, #8 +- PF tst PF_CTL, #0x0F ++ PF tst, PF_CTL, #0x0F + vrshrn.u16 d29, q9, #8 +- PF addne PF_X, PF_X, #8 ++ PF addne, PF_X, PF_X, #8 + vrshrn.u16 d30, q10, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF subne, PF_CTL, PF_CTL, #1 + vrshrn.u16 d31, q11, #8 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + vmull.u8 q8, d24, d0 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] + vmull.u8 q9, d24, d1 +- PF subge PF_X, PF_X, ORIG_W ++ PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q10, d24, d2 +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q11, d24, d3 +- PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! + vrsra.u16 q8, q8, #8 + vrsra.u16 q9, q9, #8 + vrsra.u16 q10, q10, #8 + vrsra.u16 q11, q11, #8 + .endm + + .macro pixman_composite_src_n_8_8888_init +@@ -1309,33 +1315,33 @@ generate_composite_function \ + vrshrn.u16 d28, q0, #8 + vrshrn.u16 d29, q1, #8 + vrshrn.u16 d30, q2, #8 + vrshrn.u16 d31, q3, #8 + .endm + + .macro pixman_composite_src_n_8_8_process_pixblock_tail_head + fetch_mask_pixblock +- PF add PF_X, PF_X, #8 ++ PF add, PF_X, PF_X, #8 + vrshrn.u16 d28, q0, #8 +- PF tst PF_CTL, #0x0F ++ PF tst, PF_CTL, #0x0F + vrshrn.u16 d29, q1, #8 +- PF addne PF_X, PF_X, #8 ++ PF addne, PF_X, PF_X, #8 + vrshrn.u16 d30, q2, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF subne, PF_CTL, PF_CTL, #1 + vrshrn.u16 d31, q3, #8 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + vmull.u8 q0, d24, d16 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] + vmull.u8 q1, d25, d16 +- PF subge PF_X, PF_X, ORIG_W ++ PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q2, d26, d16 +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q3, d27, d16 +- PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! + vst1.8 {d28, d29, d30, d31}, [DST_W, :128]! + vrsra.u16 q0, q0, #8 + vrsra.u16 q1, q1, #8 + vrsra.u16 q2, q2, #8 + vrsra.u16 q3, q3, #8 + .endm + + .macro pixman_composite_src_n_8_8_init +@@ -1403,37 +1409,37 @@ generate_composite_function \ + .endm + + .macro pixman_composite_over_n_8_8888_process_pixblock_tail_head + vrshr.u16 q14, q8, #8 + vld4.8 {d4, d5, d6, d7}, [DST_R, :128]! + vrshr.u16 q15, q9, #8 + fetch_mask_pixblock + vrshr.u16 q6, q10, #8 +- PF add PF_X, PF_X, #8 ++ PF add, PF_X, PF_X, #8 + vrshr.u16 q7, q11, #8 +- PF tst PF_CTL, #0x0F ++ PF tst, PF_CTL, #0x0F + vraddhn.u16 d28, q14, q8 +- PF addne PF_X, PF_X, #8 ++ PF addne, PF_X, PF_X, #8 + vraddhn.u16 d29, q15, q9 +- PF subne PF_CTL, PF_CTL, #1 ++ PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d30, q6, q10 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + vraddhn.u16 d31, q7, q11 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + vmull.u8 q6, d24, d8 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] + vmull.u8 q7, d24, d9 +- PF subge PF_X, PF_X, ORIG_W ++ PF subge, PF_X, PF_X, ORIG_W + vmull.u8 q8, d24, d10 +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subges, PF_CTL, PF_CTL, #0x10 + vmull.u8 q9, d24, d11 +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + vqadd.u8 q14, q0, q14 +- PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! + vqadd.u8 q15, q1, q15 + vrshr.u16 q10, q6, #8 + vrshr.u16 q11, q7, #8 + vrshr.u16 q12, q8, #8 + vrshr.u16 q13, q9, #8 + vraddhn.u16 d0, q6, q10 + vraddhn.u16 d1, q7, q11 + vraddhn.u16 d2, q8, q12 +@@ -2420,31 +2426,31 @@ generate_composite_function \ + + .macro pixman_composite_src_pixbuf_8888_process_pixblock_tail_head + vrshr.u16 q11, q8, #8 + vswp d3, d31 + vrshr.u16 q12, q9, #8 + vrshr.u16 q13, q10, #8 + fetch_src_pixblock + vraddhn.u16 d30, q11, q8 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF +- PF addne PF_X, PF_X, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF ++ PF addne, PF_X, PF_X, #8 ++ PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d29, q12, q9 + vraddhn.u16 d28, q13, q10 + vmull.u8 q8, d3, d0 + vmull.u8 q9, d3, d1 + vmull.u8 q10, d3, d2 + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] +- PF subge PF_X, PF_X, ORIG_W +- PF subges PF_CTL, PF_CTL, #0x10 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF subge, PF_X, PF_X, ORIG_W ++ PF subges, PF_CTL, PF_CTL, #0x10 ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + .endm + + generate_composite_function \ + pixman_composite_src_pixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ +@@ -2477,31 +2483,31 @@ generate_composite_function \ + + .macro pixman_composite_src_rpixbuf_8888_process_pixblock_tail_head + vrshr.u16 q11, q8, #8 + vswp d3, d31 + vrshr.u16 q12, q9, #8 + vrshr.u16 q13, q10, #8 + fetch_src_pixblock + vraddhn.u16 d28, q11, q8 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF +- PF addne PF_X, PF_X, #8 +- PF subne PF_CTL, PF_CTL, #1 ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF ++ PF addne, PF_X, PF_X, #8 ++ PF subne, PF_CTL, PF_CTL, #1 + vraddhn.u16 d29, q12, q9 + vraddhn.u16 d30, q13, q10 + vmull.u8 q8, d3, d0 + vmull.u8 q9, d3, d1 + vmull.u8 q10, d3, d2 + vst4.8 {d28, d29, d30, d31}, [DST_W, :128]! +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] +- PF subge PF_X, PF_X, ORIG_W +- PF subges PF_CTL, PF_CTL, #0x10 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF subge, PF_X, PF_X, ORIG_W ++ PF subges, PF_CTL, PF_CTL, #0x10 ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + .endm + + generate_composite_function \ + pixman_composite_src_rpixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ + default_init, \ +@@ -2836,182 +2842,182 @@ generate_composite_function_nearest_scan + * format conversion, and interpolation as separate macros which can be used + * as the basic building blocks for constructing bilinear scanline functions. + */ + + .macro bilinear_load_8888 reg1, reg2, tmp + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #2 +- vld1.32 {reg1}, [TMP1], STRIDE +- vld1.32 {reg2}, [TMP1] ++ vld1.32 {\reg1}, [TMP1], STRIDE ++ vld1.32 {\reg2}, [TMP1] + .endm + + .macro bilinear_load_0565 reg1, reg2, tmp + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 +- vld1.32 {reg2[0]}, [TMP1], STRIDE +- vld1.32 {reg2[1]}, [TMP1] +- convert_four_0565_to_x888_packed reg2, reg1, reg2, tmp ++ vld1.32 {\reg2[0]}, [TMP1], STRIDE ++ vld1.32 {\reg2[1]}, [TMP1] ++ convert_four_0565_to_x888_packed \reg2, \reg1, \reg2, \tmp + .endm + + .macro bilinear_load_and_vertical_interpolate_two_8888 \ + acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 + +- bilinear_load_8888 reg1, reg2, tmp1 +- vmull.u8 acc1, reg1, d28 +- vmlal.u8 acc1, reg2, d29 +- bilinear_load_8888 reg3, reg4, tmp2 +- vmull.u8 acc2, reg3, d28 +- vmlal.u8 acc2, reg4, d29 ++ bilinear_load_8888 \reg1, \reg2, \tmp1 ++ vmull.u8 \acc1, \reg1, d28 ++ vmlal.u8 \acc1, \reg2, d29 ++ bilinear_load_8888 \reg3, \reg4, \tmp2 ++ vmull.u8 \acc2, \reg3, d28 ++ vmlal.u8 \acc2, \reg4, d29 + .endm + + .macro bilinear_load_and_vertical_interpolate_four_8888 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + bilinear_load_and_vertical_interpolate_two_8888 \ +- xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi ++ \xacc1, \xacc2, \xreg1, \xreg2, \xreg3, \xreg4, \xacc2lo, \xacc2hi + bilinear_load_and_vertical_interpolate_two_8888 \ +- yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi ++ \yacc1, \yacc2, \yreg1, \yreg2, \yreg3, \yreg4, \yacc2lo, \yacc2hi + .endm + + .macro bilinear_load_and_vertical_interpolate_two_0565 \ + acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 +- vld1.32 {acc2lo[0]}, [TMP1], STRIDE +- vld1.32 {acc2hi[0]}, [TMP2], STRIDE +- vld1.32 {acc2lo[1]}, [TMP1] +- vld1.32 {acc2hi[1]}, [TMP2] +- convert_0565_to_x888 acc2, reg3, reg2, reg1 +- vzip.u8 reg1, reg3 +- vzip.u8 reg2, reg4 +- vzip.u8 reg3, reg4 +- vzip.u8 reg1, reg2 +- vmull.u8 acc1, reg1, d28 +- vmlal.u8 acc1, reg2, d29 +- vmull.u8 acc2, reg3, d28 +- vmlal.u8 acc2, reg4, d29 ++ vld1.32 {\acc2lo[0]}, [TMP1], STRIDE ++ vld1.32 {\acc2hi[0]}, [TMP2], STRIDE ++ vld1.32 {\acc2lo[1]}, [TMP1] ++ vld1.32 {\acc2hi[1]}, [TMP2] ++ convert_0565_to_x888 \acc2, \reg3, \reg2, \reg1 ++ vzip.u8 \reg1, \reg3 ++ vzip.u8 \reg2, \reg4 ++ vzip.u8 \reg3, \reg4 ++ vzip.u8 \reg1, \reg2 ++ vmull.u8 \acc1, \reg1, d28 ++ vmlal.u8 \acc1, \reg2, d29 ++ vmull.u8 \acc2, \reg3, d28 ++ vmlal.u8 \acc2, \reg4, d29 + .endm + + .macro bilinear_load_and_vertical_interpolate_four_0565 \ + xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 +- vld1.32 {xacc2lo[0]}, [TMP1], STRIDE +- vld1.32 {xacc2hi[0]}, [TMP2], STRIDE +- vld1.32 {xacc2lo[1]}, [TMP1] +- vld1.32 {xacc2hi[1]}, [TMP2] +- convert_0565_to_x888 xacc2, xreg3, xreg2, xreg1 ++ vld1.32 {\xacc2lo[0]}, [TMP1], STRIDE ++ vld1.32 {\xacc2hi[0]}, [TMP2], STRIDE ++ vld1.32 {\xacc2lo[1]}, [TMP1] ++ vld1.32 {\xacc2hi[1]}, [TMP2] ++ convert_0565_to_x888 \xacc2, \xreg3, \xreg2, \xreg1 + mov TMP1, X, asr #16 + add X, X, UX + add TMP1, TOP, TMP1, asl #1 + mov TMP2, X, asr #16 + add X, X, UX + add TMP2, TOP, TMP2, asl #1 +- vld1.32 {yacc2lo[0]}, [TMP1], STRIDE +- vzip.u8 xreg1, xreg3 +- vld1.32 {yacc2hi[0]}, [TMP2], STRIDE +- vzip.u8 xreg2, xreg4 +- vld1.32 {yacc2lo[1]}, [TMP1] +- vzip.u8 xreg3, xreg4 +- vld1.32 {yacc2hi[1]}, [TMP2] +- vzip.u8 xreg1, xreg2 +- convert_0565_to_x888 yacc2, yreg3, yreg2, yreg1 +- vmull.u8 xacc1, xreg1, d28 +- vzip.u8 yreg1, yreg3 +- vmlal.u8 xacc1, xreg2, d29 +- vzip.u8 yreg2, yreg4 +- vmull.u8 xacc2, xreg3, d28 +- vzip.u8 yreg3, yreg4 +- vmlal.u8 xacc2, xreg4, d29 +- vzip.u8 yreg1, yreg2 +- vmull.u8 yacc1, yreg1, d28 +- vmlal.u8 yacc1, yreg2, d29 +- vmull.u8 yacc2, yreg3, d28 +- vmlal.u8 yacc2, yreg4, d29 ++ vld1.32 {\yacc2lo[0]}, [TMP1], STRIDE ++ vzip.u8 \xreg1, \xreg3 ++ vld1.32 {\yacc2hi[0]}, [TMP2], STRIDE ++ vzip.u8 \xreg2, \xreg4 ++ vld1.32 {\yacc2lo[1]}, [TMP1] ++ vzip.u8 \xreg3, \xreg4 ++ vld1.32 {\yacc2hi[1]}, [TMP2] ++ vzip.u8 \xreg1, \xreg2 ++ convert_0565_to_x888 \yacc2, \yreg3, \yreg2, \yreg1 ++ vmull.u8 \xacc1, \xreg1, d28 ++ vzip.u8 \yreg1, \yreg3 ++ vmlal.u8 \xacc1, \xreg2, d29 ++ vzip.u8 \yreg2, \yreg4 ++ vmull.u8 \xacc2, \xreg3, d28 ++ vzip.u8 \yreg3, \yreg4 ++ vmlal.u8 \xacc2, \xreg4, d29 ++ vzip.u8 \yreg1, \yreg2 ++ vmull.u8 \yacc1, \yreg1, d28 ++ vmlal.u8 \yacc1, \yreg2, d29 ++ vmull.u8 \yacc2, \yreg3, d28 ++ vmlal.u8 \yacc2, \yreg4, d29 + .endm + + .macro bilinear_store_8888 numpix, tmp1, tmp2 +-.if numpix == 4 ++.if \numpix == 4 + vst1.32 {d0, d1}, [OUT, :128]! +-.elseif numpix == 2 ++.elseif \numpix == 2 + vst1.32 {d0}, [OUT, :64]! +-.elseif numpix == 1 ++.elseif \numpix == 1 + vst1.32 {d0[0]}, [OUT, :32]! + .else +- .error bilinear_store_8888 numpix is unsupported ++ .error bilinear_store_8888 \numpix is unsupported + .endif + .endm + + .macro bilinear_store_0565 numpix, tmp1, tmp2 + vuzp.u8 d0, d1 + vuzp.u8 d2, d3 + vuzp.u8 d1, d3 + vuzp.u8 d0, d2 +- convert_8888_to_0565 d2, d1, d0, q1, tmp1, tmp2 +-.if numpix == 4 ++ convert_8888_to_0565 d2, d1, d0, q1, \tmp1, \tmp2 ++.if \numpix == 4 + vst1.16 {d2}, [OUT, :64]! +-.elseif numpix == 2 ++.elseif \numpix == 2 + vst1.32 {d2[0]}, [OUT, :32]! +-.elseif numpix == 1 ++.elseif \numpix == 1 + vst1.16 {d2[0]}, [OUT, :16]! + .else +- .error bilinear_store_0565 numpix is unsupported ++ .error bilinear_store_0565 \numpix is unsupported + .endif + .endm + + .macro bilinear_interpolate_last_pixel src_fmt, dst_fmt +- bilinear_load_&src_fmt d0, d1, d2 ++ bilinear_load_\()\src_fmt d0, d1, d2 + vmull.u8 q1, d0, d28 + vmlal.u8 q1, d1, d29 + /* 5 cycles bubble */ + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + /* 5 cycles bubble */ + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + /* 3 cycles bubble */ + vmovn.u16 d0, q0 + /* 1 cycle bubble */ +- bilinear_store_&dst_fmt 1, q2, q3 ++ bilinear_store_\()\dst_fmt 1, q2, q3 + .endm + + .macro bilinear_interpolate_two_pixels src_fmt, dst_fmt +- bilinear_load_and_vertical_interpolate_two_&src_fmt \ ++ bilinear_load_and_vertical_interpolate_two_\()\src_fmt \ + q1, q11, d0, d1, d20, d21, d22, d23 + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q10, d22, d31 + vmlal.u16 q10, d23, d31 + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + vmovn.u16 d0, q0 +- bilinear_store_&dst_fmt 2, q2, q3 ++ bilinear_store_\()\dst_fmt 2, q2, q3 + .endm + + .macro bilinear_interpolate_four_pixels src_fmt, dst_fmt +- bilinear_load_and_vertical_interpolate_four_&src_fmt \ ++ bilinear_load_and_vertical_interpolate_four_\()\src_fmt \ + q1, q11, d0, d1, d20, d21, d22, d23 \ + q3, q9, d4, d5, d16, d17, d18, d19 + pld [TMP1, PF_OFFS] + sub TMP1, TMP1, STRIDE + vshll.u16 q0, d2, #BILINEAR_INTERPOLATION_BITS + vmlsl.u16 q0, d2, d30 + vmlal.u16 q0, d3, d30 + vshll.u16 q10, d22, #BILINEAR_INTERPOLATION_BITS +@@ -3029,64 +3035,64 @@ generate_composite_function_nearest_scan + vshrn.u32 d0, q0, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d1, q10, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d4, q2, #(2 * BILINEAR_INTERPOLATION_BITS) + vshrn.u32 d5, q8, #(2 * BILINEAR_INTERPOLATION_BITS) + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vmovn.u16 d0, q0 + vmovn.u16 d1, q2 + vadd.u16 q12, q12, q13 +- bilinear_store_&dst_fmt 4, q2, q3 ++ bilinear_store_\()\dst_fmt 4, q2, q3 + .endm + + .macro bilinear_interpolate_four_pixels_head src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_head ++.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_head + .else +- bilinear_interpolate_four_pixels src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + .endif + .endm + + .macro bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_tail ++.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail + .endif + .endm + + .macro bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_tail_head ++.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail_head + .else +- bilinear_interpolate_four_pixels src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + .endif + .endm + + .macro bilinear_interpolate_eight_pixels_head src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_head ++.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_head + .else +- bilinear_interpolate_four_pixels_head src_fmt, dst_fmt +- bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_head \src_fmt, \dst_fmt ++ bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + .endif + .endm + + .macro bilinear_interpolate_eight_pixels_tail src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_tail ++.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail + .else +- bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_tail \src_fmt, \dst_fmt + .endif + .endm + + .macro bilinear_interpolate_eight_pixels_tail_head src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_tail_head ++.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail_head + .else +- bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt +- bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt ++ bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + .endif + .endm + + .set BILINEAR_FLAG_UNROLL_4, 0 + .set BILINEAR_FLAG_UNROLL_8, 1 + .set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 + + /* +@@ -3101,17 +3107,17 @@ generate_composite_function_nearest_scan + * prefetch_distance - prefetch in the source image by that many + * pixels ahead + */ + + .macro generate_bilinear_scanline_func fname, src_fmt, dst_fmt, \ + src_bpp_shift, dst_bpp_shift, \ + prefetch_distance, flags + +-pixman_asm_function fname ++pixman_asm_function \fname + OUT .req r0 + TOP .req r1 + BOTTOM .req r2 + WT .req r3 + WB .req r4 + X .req r5 + UX .req r6 + WIDTH .req ip +@@ -3119,21 +3125,21 @@ pixman_asm_function fname + TMP2 .req r4 + PF_OFFS .req r7 + TMP3 .req r8 + TMP4 .req r9 + STRIDE .req r2 + + mov ip, sp + push {r4, r5, r6, r7, r8, r9} +- mov PF_OFFS, #prefetch_distance ++ mov PF_OFFS, #\prefetch_distance + ldmia ip, {WB, X, UX, WIDTH} + mul PF_OFFS, PF_OFFS, UX + +-.if ((flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 ++.if ((\flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 + vpush {d8-d15} + .endif + + sub STRIDE, BOTTOM, TOP + .unreq BOTTOM + + cmp WIDTH, #0 + ble 3f +@@ -3146,83 +3152,83 @@ pixman_asm_function fname + + /* ensure good destination alignment */ + cmp WIDTH, #1 + blt 0f + tst OUT, #(1 << dst_bpp_shift) + beq 0f + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 +- bilinear_interpolate_last_pixel src_fmt, dst_fmt ++ bilinear_interpolate_last_pixel \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #1 + 0: + vadd.u16 q13, q13, q13 + vshr.u16 q15, q12, #(16 - BILINEAR_INTERPOLATION_BITS) + vadd.u16 q12, q12, q13 + + cmp WIDTH, #2 + blt 0f + tst OUT, #(1 << (dst_bpp_shift + 1)) + beq 0f +- bilinear_interpolate_two_pixels src_fmt, dst_fmt ++ bilinear_interpolate_two_pixels \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #2 + 0: +-.if ((flags) & BILINEAR_FLAG_UNROLL_8) != 0 ++.if ((\flags) & BILINEAR_FLAG_UNROLL_8) != 0 + /*********** 8 pixels per iteration *****************/ + cmp WIDTH, #4 + blt 0f + tst OUT, #(1 << (dst_bpp_shift + 2)) + beq 0f +- bilinear_interpolate_four_pixels src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #4 + 0: + subs WIDTH, WIDTH, #8 + blt 1f + mov PF_OFFS, PF_OFFS, asr #(16 - src_bpp_shift) +- bilinear_interpolate_eight_pixels_head src_fmt, dst_fmt ++ bilinear_interpolate_eight_pixels_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #8 + blt 5f + 0: +- bilinear_interpolate_eight_pixels_tail_head src_fmt, dst_fmt ++ bilinear_interpolate_eight_pixels_tail_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #8 + bge 0b + 5: +- bilinear_interpolate_eight_pixels_tail src_fmt, dst_fmt ++ bilinear_interpolate_eight_pixels_tail \src_fmt, \dst_fmt + 1: + tst WIDTH, #4 + beq 2f +- bilinear_interpolate_four_pixels src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + 2: + .else + /*********** 4 pixels per iteration *****************/ + subs WIDTH, WIDTH, #4 + blt 1f + mov PF_OFFS, PF_OFFS, asr #(16 - src_bpp_shift) +- bilinear_interpolate_four_pixels_head src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #4 + blt 5f + 0: +- bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #4 + bge 0b + 5: +- bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_tail \src_fmt, \dst_fmt + 1: + /****************************************************/ + .endif + /* handle the remaining trailing pixels */ + tst WIDTH, #2 + beq 2f +- bilinear_interpolate_two_pixels src_fmt, dst_fmt ++ bilinear_interpolate_two_pixels \src_fmt, \dst_fmt + 2: + tst WIDTH, #1 + beq 3f +- bilinear_interpolate_last_pixel src_fmt, dst_fmt ++ bilinear_interpolate_last_pixel \src_fmt, \dst_fmt + 3: +-.if ((flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 ++.if ((\flags) & BILINEAR_FLAG_USE_ALL_NEON_REGS) != 0 + vpop {d8-d15} + .endif + pop {r4, r5, r6, r7, r8, r9} + bx lr + + .unreq OUT + .unreq TOP + .unreq WT +@@ -3231,17 +3237,17 @@ 3: + .unreq UX + .unreq WIDTH + .unreq TMP1 + .unreq TMP2 + .unreq PF_OFFS + .unreq TMP3 + .unreq TMP4 + .unreq STRIDE +-.endfunc ++pixman_end_asm_function + + .endm + + /*****************************************************************************/ + + .set have_bilinear_interpolate_four_pixels_8888_8888, 1 + + .macro bilinear_interpolate_four_pixels_8888_8888_head +diff --git a/gfx/cairo/libpixman/src/pixman-arm-neon-asm.h b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.h +--- a/gfx/cairo/libpixman/src/pixman-arm-neon-asm.h ++++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.h +@@ -69,303 +69,303 @@ + .set PREFETCH_TYPE_ADVANCED, 2 /* Advanced fine-grained prefetch */ + + /* + * Definitions of supplementary pixld/pixst macros (for partial load/store of + * pixel data). + */ + + .macro pixldst1 op, elem_size, reg1, mem_operand, abits +-.if abits > 0 +- op&.&elem_size {d®1}, [&mem_operand&, :&abits&]! ++.if \abits > 0 ++ \op\().\()\elem_size {d\()\reg1}, [\()\mem_operand\(), :\()\abits\()]! + .else +- op&.&elem_size {d®1}, [&mem_operand&]! ++ \op\().\()\elem_size {d\()\reg1}, [\()\mem_operand\()]! + .endif + .endm + + .macro pixldst2 op, elem_size, reg1, reg2, mem_operand, abits +-.if abits > 0 +- op&.&elem_size {d®1, d®2}, [&mem_operand&, :&abits&]! ++.if \abits > 0 ++ \op\().\()\elem_size {d\()\reg1, d\()\reg2}, [\()\mem_operand\(), :\()\abits\()]! + .else +- op&.&elem_size {d®1, d®2}, [&mem_operand&]! ++ \op\().\()\elem_size {d\()\reg1, d\()\reg2}, [\()\mem_operand\()]! + .endif + .endm + + .macro pixldst4 op, elem_size, reg1, reg2, reg3, reg4, mem_operand, abits +-.if abits > 0 +- op&.&elem_size {d®1, d®2, d®3, d®4}, [&mem_operand&, :&abits&]! ++.if \abits > 0 ++ \op\().\()\elem_size {d\()\reg1, d\()\reg2, d\()\reg3, d\()\reg4}, [\()\mem_operand\(), :\()\abits\()]! + .else +- op&.&elem_size {d®1, d®2, d®3, d®4}, [&mem_operand&]! ++ \op\().\()\elem_size {d\()\reg1, d\()\reg2, d\()\reg3, d\()\reg4}, [\()\mem_operand\()]! + .endif + .endm + + .macro pixldst0 op, elem_size, reg1, idx, mem_operand, abits +- op&.&elem_size {d®1[idx]}, [&mem_operand&]! ++ \op\().\()\elem_size {d\()\reg1[\idx]}, [\()\mem_operand\()]! + .endm + + .macro pixldst3 op, elem_size, reg1, reg2, reg3, mem_operand +- op&.&elem_size {d®1, d®2, d®3}, [&mem_operand&]! ++ \op\().\()\elem_size {d\()\reg1, d\()\reg2, d\()\reg3}, [\()\mem_operand\()]! + .endm + + .macro pixldst30 op, elem_size, reg1, reg2, reg3, idx, mem_operand +- op&.&elem_size {d®1[idx], d®2[idx], d®3[idx]}, [&mem_operand&]! ++ \op\().\()\elem_size {d\()\reg1[\idx], d\()\reg2[\idx], d\()\reg3[\idx]}, [\()\mem_operand\()]! + .endm + + .macro pixldst numbytes, op, elem_size, basereg, mem_operand, abits +-.if numbytes == 32 +- pixldst4 op, elem_size, %(basereg+4), %(basereg+5), \ +- %(basereg+6), %(basereg+7), mem_operand, abits +-.elseif numbytes == 16 +- pixldst2 op, elem_size, %(basereg+2), %(basereg+3), mem_operand, abits +-.elseif numbytes == 8 +- pixldst1 op, elem_size, %(basereg+1), mem_operand, abits +-.elseif numbytes == 4 +- .if !RESPECT_STRICT_ALIGNMENT || (elem_size == 32) +- pixldst0 op, 32, %(basereg+0), 1, mem_operand, abits +- .elseif elem_size == 16 +- pixldst0 op, 16, %(basereg+0), 2, mem_operand, abits +- pixldst0 op, 16, %(basereg+0), 3, mem_operand, abits ++.if \numbytes == 32 ++ pixldst4 \op, \elem_size, %(\basereg+4), %(\basereg+5), \ ++ %(\basereg+6), %(\basereg+7), \mem_operand, \abits ++.elseif \numbytes == 16 ++ pixldst2 \op, \elem_size, %(\basereg+2), %(\basereg+3), \mem_operand, \abits ++.elseif \numbytes == 8 ++ pixldst1 \op, \elem_size, %(\basereg+1), \mem_operand, \abits ++.elseif \numbytes == 4 ++ .if !RESPECT_STRICT_ALIGNMENT || (\elem_size == 32) ++ pixldst0 \op, 32, %(\basereg+0), 1, \mem_operand, \abits ++ .elseif \elem_size == 16 ++ pixldst0 \op, 16, %(\basereg+0), 2, \mem_operand, \abits ++ pixldst0 \op, 16, %(\basereg+0), 3, \mem_operand, \abits + .else +- pixldst0 op, 8, %(basereg+0), 4, mem_operand, abits +- pixldst0 op, 8, %(basereg+0), 5, mem_operand, abits +- pixldst0 op, 8, %(basereg+0), 6, mem_operand, abits +- pixldst0 op, 8, %(basereg+0), 7, mem_operand, abits ++ pixldst0 \op, 8, %(\basereg+0), 4, \mem_operand, \abits ++ pixldst0 \op, 8, %(\basereg+0), 5, \mem_operand, \abits ++ pixldst0 \op, 8, %(\basereg+0), 6, \mem_operand, \abits ++ pixldst0 \op, 8, %(\basereg+0), 7, \mem_operand, \abits + .endif +-.elseif numbytes == 2 +- .if !RESPECT_STRICT_ALIGNMENT || (elem_size == 16) +- pixldst0 op, 16, %(basereg+0), 1, mem_operand, abits ++.elseif \numbytes == 2 ++ .if !RESPECT_STRICT_ALIGNMENT || (\elem_size == 16) ++ pixldst0 \op, 16, %(\basereg+0), 1, \mem_operand, \abits + .else +- pixldst0 op, 8, %(basereg+0), 2, mem_operand, abits +- pixldst0 op, 8, %(basereg+0), 3, mem_operand, abits ++ pixldst0 \op, 8, %(\basereg+0), 2, \mem_operand, \abits ++ pixldst0 \op, 8, %(\basereg+0), 3, \mem_operand, \abits + .endif +-.elseif numbytes == 1 +- pixldst0 op, 8, %(basereg+0), 1, mem_operand, abits ++.elseif \numbytes == 1 ++ pixldst0 \op, 8, %(\basereg+0), 1, \mem_operand, \abits + .else +- .error "unsupported size: numbytes" ++ .error "unsupported size: \numbytes" + .endif + .endm + + .macro pixld numpix, bpp, basereg, mem_operand, abits=0 +-.if bpp > 0 +-.if (bpp == 32) && (numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) +- pixldst4 vld4, 8, %(basereg+4), %(basereg+5), \ +- %(basereg+6), %(basereg+7), mem_operand, abits +-.elseif (bpp == 24) && (numpix == 8) +- pixldst3 vld3, 8, %(basereg+3), %(basereg+4), %(basereg+5), mem_operand +-.elseif (bpp == 24) && (numpix == 4) +- pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 4, mem_operand +- pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 5, mem_operand +- pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 6, mem_operand +- pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 7, mem_operand +-.elseif (bpp == 24) && (numpix == 2) +- pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 2, mem_operand +- pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 3, mem_operand +-.elseif (bpp == 24) && (numpix == 1) +- pixldst30 vld3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 1, mem_operand ++.if \bpp > 0 ++.if (\bpp == 32) && (\numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) ++ pixldst4 vld4, 8, %(\basereg+4), %(\basereg+5), \ ++ %(\basereg+6), %(\basereg+7), \mem_operand, \abits ++.elseif (\bpp == 24) && (\numpix == 8) ++ pixldst3 vld3, 8, %(\basereg+3), %(\basereg+4), %(\basereg+5), \mem_operand ++.elseif (\bpp == 24) && (\numpix == 4) ++ pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 4, \mem_operand ++ pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 5, \mem_operand ++ pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 6, \mem_operand ++ pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 7, \mem_operand ++.elseif (\bpp == 24) && (\numpix == 2) ++ pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 2, \mem_operand ++ pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 3, \mem_operand ++.elseif (\bpp == 24) && (\numpix == 1) ++ pixldst30 vld3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 1, \mem_operand + .else +- pixldst %(numpix * bpp / 8), vld1, %(bpp), basereg, mem_operand, abits ++ pixldst %(\numpix * \bpp / 8), vld1, %(\bpp), \basereg, \mem_operand, \abits + .endif + .endif + .endm + + .macro pixst numpix, bpp, basereg, mem_operand, abits=0 +-.if bpp > 0 +-.if (bpp == 32) && (numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) +- pixldst4 vst4, 8, %(basereg+4), %(basereg+5), \ +- %(basereg+6), %(basereg+7), mem_operand, abits +-.elseif (bpp == 24) && (numpix == 8) +- pixldst3 vst3, 8, %(basereg+3), %(basereg+4), %(basereg+5), mem_operand +-.elseif (bpp == 24) && (numpix == 4) +- pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 4, mem_operand +- pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 5, mem_operand +- pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 6, mem_operand +- pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 7, mem_operand +-.elseif (bpp == 24) && (numpix == 2) +- pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 2, mem_operand +- pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 3, mem_operand +-.elseif (bpp == 24) && (numpix == 1) +- pixldst30 vst3, 8, %(basereg+0), %(basereg+1), %(basereg+2), 1, mem_operand ++.if \bpp > 0 ++.if (\bpp == 32) && (\numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) ++ pixldst4 vst4, 8, %(\basereg+4), %(\basereg+5), \ ++ %(\basereg+6), %(\basereg+7), \mem_operand, \abits ++.elseif (\bpp == 24) && (\numpix == 8) ++ pixldst3 vst3, 8, %(\basereg+3), %(\basereg+4), %(\basereg+5), \mem_operand ++.elseif (\bpp == 24) && (\numpix == 4) ++ pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 4, \mem_operand ++ pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 5, \mem_operand ++ pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 6, \mem_operand ++ pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 7, \mem_operand ++.elseif (\bpp == 24) && (\numpix == 2) ++ pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 2, \mem_operand ++ pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 3, \mem_operand ++.elseif (\bpp == 24) && (\numpix == 1) ++ pixldst30 vst3, 8, %(\basereg+0), %(\basereg+1), %(\basereg+2), 1, \mem_operand + .else +- pixldst %(numpix * bpp / 8), vst1, %(bpp), basereg, mem_operand, abits ++ pixldst %(\numpix * \bpp / 8), vst1, %(\bpp), \basereg, \mem_operand, \abits + .endif + .endif + .endm + + .macro pixld_a numpix, bpp, basereg, mem_operand +-.if (bpp * numpix) <= 128 +- pixld numpix, bpp, basereg, mem_operand, %(bpp * numpix) ++.if (\bpp * \numpix) <= 128 ++ pixld \numpix, \bpp, \basereg, \mem_operand, %(\bpp * \numpix) + .else +- pixld numpix, bpp, basereg, mem_operand, 128 ++ pixld \numpix, \bpp, \basereg, \mem_operand, 128 + .endif + .endm + + .macro pixst_a numpix, bpp, basereg, mem_operand +-.if (bpp * numpix) <= 128 +- pixst numpix, bpp, basereg, mem_operand, %(bpp * numpix) ++.if (\bpp * \numpix) <= 128 ++ pixst \numpix, \bpp, \basereg, \mem_operand, %(\bpp * \numpix) + .else +- pixst numpix, bpp, basereg, mem_operand, 128 ++ pixst \numpix, \bpp, \basereg, \mem_operand, 128 + .endif + .endm + + /* + * Pixel fetcher for nearest scaling (needs TMP1, TMP2, VX, UNIT_X register + * aliases to be defined) + */ + .macro pixld1_s elem_size, reg1, mem_operand +-.if elem_size == 16 ++.if \elem_size == 16 + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X + 5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b +- add TMP1, mem_operand, TMP1, asl #1 ++ add TMP1, \mem_operand, TMP1, asl #1 + mov TMP2, VX, asr #16 + adds VX, VX, UNIT_X + 5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b +- add TMP2, mem_operand, TMP2, asl #1 +- vld1.16 {d®1&[0]}, [TMP1, :16] ++ add TMP2, \mem_operand, TMP2, asl #1 ++ vld1.16 {d\()\reg1\()[0]}, [TMP1, :16] + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X + 5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b +- add TMP1, mem_operand, TMP1, asl #1 +- vld1.16 {d®1&[1]}, [TMP2, :16] ++ add TMP1, \mem_operand, TMP1, asl #1 ++ vld1.16 {d\()\reg1\()[1]}, [TMP2, :16] + mov TMP2, VX, asr #16 + adds VX, VX, UNIT_X + 5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b +- add TMP2, mem_operand, TMP2, asl #1 +- vld1.16 {d®1&[2]}, [TMP1, :16] +- vld1.16 {d®1&[3]}, [TMP2, :16] +-.elseif elem_size == 32 ++ add TMP2, \mem_operand, TMP2, asl #1 ++ vld1.16 {d\()\reg1\()[2]}, [TMP1, :16] ++ vld1.16 {d\()\reg1\()[3]}, [TMP2, :16] ++.elseif \elem_size == 32 + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X + 5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b +- add TMP1, mem_operand, TMP1, asl #2 ++ add TMP1, \mem_operand, TMP1, asl #2 + mov TMP2, VX, asr #16 + adds VX, VX, UNIT_X + 5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b +- add TMP2, mem_operand, TMP2, asl #2 +- vld1.32 {d®1&[0]}, [TMP1, :32] +- vld1.32 {d®1&[1]}, [TMP2, :32] ++ add TMP2, \mem_operand, TMP2, asl #2 ++ vld1.32 {d\()\reg1\()[0]}, [TMP1, :32] ++ vld1.32 {d\()\reg1\()[1]}, [TMP2, :32] + .else + .error "unsupported" + .endif + .endm + + .macro pixld2_s elem_size, reg1, reg2, mem_operand + .if 0 /* elem_size == 32 */ + mov TMP1, VX, asr #16 + add VX, VX, UNIT_X, asl #1 +- add TMP1, mem_operand, TMP1, asl #2 ++ add TMP1, \mem_operand, TMP1, asl #2 + mov TMP2, VX, asr #16 + sub VX, VX, UNIT_X +- add TMP2, mem_operand, TMP2, asl #2 +- vld1.32 {d®1&[0]}, [TMP1, :32] ++ add TMP2, \mem_operand, TMP2, asl #2 ++ vld1.32 {d\()\reg1\()[0]}, [TMP1, :32] + mov TMP1, VX, asr #16 + add VX, VX, UNIT_X, asl #1 +- add TMP1, mem_operand, TMP1, asl #2 +- vld1.32 {d®2&[0]}, [TMP2, :32] ++ add TMP1, \mem_operand, TMP1, asl #2 ++ vld1.32 {d\()\reg2\()[0]}, [TMP2, :32] + mov TMP2, VX, asr #16 + add VX, VX, UNIT_X +- add TMP2, mem_operand, TMP2, asl #2 +- vld1.32 {d®1&[1]}, [TMP1, :32] +- vld1.32 {d®2&[1]}, [TMP2, :32] ++ add TMP2, \mem_operand, TMP2, asl #2 ++ vld1.32 {d\()\reg1\()[1]}, [TMP1, :32] ++ vld1.32 {d\()\reg2\()[1]}, [TMP2, :32] + .else +- pixld1_s elem_size, reg1, mem_operand +- pixld1_s elem_size, reg2, mem_operand ++ pixld1_s \elem_size, \reg1, \mem_operand ++ pixld1_s \elem_size, \reg2, \mem_operand + .endif + .endm + + .macro pixld0_s elem_size, reg1, idx, mem_operand +-.if elem_size == 16 ++.if \elem_size == 16 + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X + 5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b +- add TMP1, mem_operand, TMP1, asl #1 +- vld1.16 {d®1&[idx]}, [TMP1, :16] +-.elseif elem_size == 32 ++ add TMP1, \mem_operand, TMP1, asl #1 ++ vld1.16 {d\()\reg1\()[\idx]}, [TMP1, :16] ++.elseif \elem_size == 32 + mov TMP1, VX, asr #16 + adds VX, VX, UNIT_X + 5: subpls VX, VX, SRC_WIDTH_FIXED + bpl 5b +- add TMP1, mem_operand, TMP1, asl #2 +- vld1.32 {d®1&[idx]}, [TMP1, :32] ++ add TMP1, \mem_operand, TMP1, asl #2 ++ vld1.32 {d\()\reg1\()[\idx]}, [TMP1, :32] + .endif + .endm + + .macro pixld_s_internal numbytes, elem_size, basereg, mem_operand +-.if numbytes == 32 +- pixld2_s elem_size, %(basereg+4), %(basereg+5), mem_operand +- pixld2_s elem_size, %(basereg+6), %(basereg+7), mem_operand +- pixdeinterleave elem_size, %(basereg+4) +-.elseif numbytes == 16 +- pixld2_s elem_size, %(basereg+2), %(basereg+3), mem_operand +-.elseif numbytes == 8 +- pixld1_s elem_size, %(basereg+1), mem_operand +-.elseif numbytes == 4 +- .if elem_size == 32 +- pixld0_s elem_size, %(basereg+0), 1, mem_operand +- .elseif elem_size == 16 +- pixld0_s elem_size, %(basereg+0), 2, mem_operand +- pixld0_s elem_size, %(basereg+0), 3, mem_operand ++.if \numbytes == 32 ++ pixld2_s \elem_size, %(\basereg+4), %(\basereg+5), \mem_operand ++ pixld2_s \elem_size, %(\basereg+6), %(\basereg+7), \mem_operand ++ pixdeinterleave \elem_size, %(\basereg+4) ++.elseif \numbytes == 16 ++ pixld2_s \elem_size, %(\basereg+2), %(\basereg+3), \mem_operand ++.elseif \numbytes == 8 ++ pixld1_s \elem_size, %(\basereg+1), \mem_operand ++.elseif \numbytes == 4 ++ .if \elem_size == 32 ++ pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand ++ .elseif \elem_size == 16 ++ pixld0_s \elem_size, %(\basereg+0), 2, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 3, \mem_operand + .else +- pixld0_s elem_size, %(basereg+0), 4, mem_operand +- pixld0_s elem_size, %(basereg+0), 5, mem_operand +- pixld0_s elem_size, %(basereg+0), 6, mem_operand +- pixld0_s elem_size, %(basereg+0), 7, mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 4, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 5, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 6, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 7, \mem_operand + .endif +-.elseif numbytes == 2 +- .if elem_size == 16 +- pixld0_s elem_size, %(basereg+0), 1, mem_operand ++.elseif \numbytes == 2 ++ .if \elem_size == 16 ++ pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand + .else +- pixld0_s elem_size, %(basereg+0), 2, mem_operand +- pixld0_s elem_size, %(basereg+0), 3, mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 2, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 3, \mem_operand + .endif +-.elseif numbytes == 1 +- pixld0_s elem_size, %(basereg+0), 1, mem_operand ++.elseif \numbytes == 1 ++ pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand + .else +- .error "unsupported size: numbytes" ++ .error "unsupported size: \numbytes" + .endif + .endm + + .macro pixld_s numpix, bpp, basereg, mem_operand +-.if bpp > 0 +- pixld_s_internal %(numpix * bpp / 8), %(bpp), basereg, mem_operand ++.if \bpp > 0 ++ pixld_s_internal %(\numpix * \bpp / 8), %(\bpp), \basereg, \mem_operand + .endif + .endm + + .macro vuzp8 reg1, reg2 +- vuzp.8 d®1, d®2 ++ vuzp.8 d\()\reg1, d\()\reg2 + .endm + + .macro vzip8 reg1, reg2 +- vzip.8 d®1, d®2 ++ vzip.8 d\()\reg1, d\()\reg2 + .endm + + /* deinterleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ + .macro pixdeinterleave bpp, basereg +-.if (bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) +- vuzp8 %(basereg+0), %(basereg+1) +- vuzp8 %(basereg+2), %(basereg+3) +- vuzp8 %(basereg+1), %(basereg+3) +- vuzp8 %(basereg+0), %(basereg+2) ++.if (\bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) ++ vuzp8 %(\basereg+0), %(\basereg+1) ++ vuzp8 %(\basereg+2), %(\basereg+3) ++ vuzp8 %(\basereg+1), %(\basereg+3) ++ vuzp8 %(\basereg+0), %(\basereg+2) + .endif + .endm + + /* interleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ + .macro pixinterleave bpp, basereg +-.if (bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) +- vzip8 %(basereg+0), %(basereg+2) +- vzip8 %(basereg+1), %(basereg+3) +- vzip8 %(basereg+2), %(basereg+3) +- vzip8 %(basereg+0), %(basereg+1) ++.if (\bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) ++ vzip8 %(\basereg+0), %(\basereg+2) ++ vzip8 %(\basereg+1), %(\basereg+3) ++ vzip8 %(\basereg+2), %(\basereg+3) ++ vzip8 %(\basereg+0), %(\basereg+1) + .endif + .endm + + /* + * This is a macro for implementing cache preload. The main idea is that + * cache preload logic is mostly independent from the rest of pixels + * processing code. It starts at the top left pixel and moves forward + * across pixels and can jump across scanlines. Prefetch distance is +@@ -389,51 +389,51 @@ 5: subpls VX, VX, SRC_WIDTH_FIXED + * for almost zero cost! + * + * (*) The overhead of the prefetcher is visible when running some trivial + * pixels processing like simple copy. Anyway, having prefetch is a must + * when working with the graphics data. + */ + .macro PF a, x:vararg + .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_ADVANCED) +- a x ++ \a \x + .endif + .endm + + .macro cache_preload std_increment, boost_increment + .if (src_bpp_shift >= 0) || (dst_r_bpp != 0) || (mask_bpp_shift >= 0) + .if regs_shortage +- PF ldr ORIG_W, [sp] /* If we are short on regs, ORIG_W is kept on stack */ ++ PF ldr, ORIG_W, [sp] /* If we are short on regs, ORIG_W is kept on stack */ + .endif +-.if std_increment != 0 +- PF add PF_X, PF_X, #std_increment ++.if \std_increment != 0 ++ PF add, PF_X, PF_X, #\std_increment + .endif +- PF tst PF_CTL, #0xF +- PF addne PF_X, PF_X, #boost_increment +- PF subne PF_CTL, PF_CTL, #1 +- PF cmp PF_X, ORIG_W ++ PF tst, PF_CTL, #0xF ++ PF addne, PF_X, PF_X, #\boost_increment ++ PF subne, PF_CTL, PF_CTL, #1 ++ PF cmp, PF_X, ORIG_W + .if src_bpp_shift >= 0 + PF pld, [PF_SRC, PF_X, lsl #src_bpp_shift] + .endif + .if dst_r_bpp != 0 + PF pld, [PF_DST, PF_X, lsl #dst_bpp_shift] + .endif + .if mask_bpp_shift >= 0 + PF pld, [PF_MASK, PF_X, lsl #mask_bpp_shift] + .endif +- PF subge PF_X, PF_X, ORIG_W +- PF subges PF_CTL, PF_CTL, #0x10 ++ PF subge, PF_X, PF_X, ORIG_W ++ PF subges, PF_CTL, PF_CTL, #0x10 + .if src_bpp_shift >= 0 +- PF ldrgeb DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_SRC, SRC_STRIDE, lsl #src_bpp_shift]! + .endif + .if dst_r_bpp != 0 +- PF ldrgeb DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_DST, DST_STRIDE, lsl #dst_bpp_shift]! + .endif + .if mask_bpp_shift >= 0 +- PF ldrgeb DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! ++ PF ldrgeb, DUMMY, [PF_MASK, MASK_STRIDE, lsl #mask_bpp_shift]! + .endif + .endif + .endm + + .macro cache_preload_simple + .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_SIMPLE) + .if src_bpp > 0 + pld [SRC, #(PREFETCH_DISTANCE_SIMPLE * src_bpp / 8)] +@@ -460,51 +460,53 @@ 5: subpls VX, VX, SRC_WIDTH_FIXED + .macro ensure_destination_ptr_alignment process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + .if dst_w_bpp != 24 + tst DST_R, #0xF + beq 2f + + .irp lowbit, 1, 2, 4, 8, 16 ++#ifndef __clang__ + local skip1 +-.if (dst_w_bpp <= (lowbit * 8)) && ((lowbit * 8) < (pixblock_size * dst_w_bpp)) +-.if lowbit < 16 /* we don't need more than 16-byte alignment */ +- tst DST_R, #lowbit ++#endif ++.if (dst_w_bpp <= (\lowbit * 8)) && ((\lowbit * 8) < (pixblock_size * dst_w_bpp)) ++.if \lowbit < 16 /* we don't need more than 16-byte alignment */ ++ tst DST_R, #\lowbit + beq 1f + .endif +- pixld_src (lowbit * 8 / dst_w_bpp), src_bpp, src_basereg, SRC +- pixld (lowbit * 8 / dst_w_bpp), mask_bpp, mask_basereg, MASK ++ pixld_src (\lowbit * 8 / dst_w_bpp), src_bpp, src_basereg, SRC ++ pixld (\lowbit * 8 / dst_w_bpp), mask_bpp, mask_basereg, MASK + .if dst_r_bpp > 0 +- pixld_a (lowbit * 8 / dst_r_bpp), dst_r_bpp, dst_r_basereg, DST_R ++ pixld_a (\lowbit * 8 / dst_r_bpp), dst_r_bpp, dst_r_basereg, DST_R + .else +- add DST_R, DST_R, #lowbit ++ add DST_R, DST_R, #\lowbit + .endif +- PF add PF_X, PF_X, #(lowbit * 8 / dst_w_bpp) +- sub W, W, #(lowbit * 8 / dst_w_bpp) ++ PF add, PF_X, PF_X, #(\lowbit * 8 / dst_w_bpp) ++ sub W, W, #(\lowbit * 8 / dst_w_bpp) + 1: + .endif + .endr + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + +- process_pixblock_head ++ \process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple +- process_pixblock_tail ++ \process_pixblock_tail + + pixinterleave dst_w_bpp, dst_w_basereg + .irp lowbit, 1, 2, 4, 8, 16 +-.if (dst_w_bpp <= (lowbit * 8)) && ((lowbit * 8) < (pixblock_size * dst_w_bpp)) +-.if lowbit < 16 /* we don't need more than 16-byte alignment */ +- tst DST_W, #lowbit ++.if (dst_w_bpp <= (\lowbit * 8)) && ((\lowbit * 8) < (pixblock_size * dst_w_bpp)) ++.if \lowbit < 16 /* we don't need more than 16-byte alignment */ ++ tst DST_W, #\lowbit + beq 1f + .endif +- pixst_a (lowbit * 8 / dst_w_bpp), dst_w_bpp, dst_w_basereg, DST_W ++ pixst_a (\lowbit * 8 / dst_w_bpp), dst_w_bpp, dst_w_basereg, DST_W + 1: + .endif + .endr + .endif + 2: + .endm + + /* +@@ -525,51 +527,51 @@ 2: + .macro process_trailing_pixels cache_preload_flag, \ + dst_aligned_flag, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + tst W, #(pixblock_size - 1) + beq 2f + .irp chunk_size, 16, 8, 4, 2, 1 +-.if pixblock_size > chunk_size +- tst W, #chunk_size ++.if pixblock_size > \chunk_size ++ tst W, #\chunk_size + beq 1f +- pixld_src chunk_size, src_bpp, src_basereg, SRC +- pixld chunk_size, mask_bpp, mask_basereg, MASK +-.if dst_aligned_flag != 0 +- pixld_a chunk_size, dst_r_bpp, dst_r_basereg, DST_R ++ pixld_src \chunk_size, src_bpp, src_basereg, SRC ++ pixld \chunk_size, mask_bpp, mask_basereg, MASK ++.if \dst_aligned_flag != 0 ++ pixld_a \chunk_size, dst_r_bpp, dst_r_basereg, DST_R + .else +- pixld chunk_size, dst_r_bpp, dst_r_basereg, DST_R ++ pixld \chunk_size, dst_r_bpp, dst_r_basereg, DST_R + .endif +-.if cache_preload_flag != 0 +- PF add PF_X, PF_X, #chunk_size ++.if \cache_preload_flag != 0 ++ PF add, PF_X, PF_X, #\chunk_size + .endif + 1: + .endif + .endr + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + +- process_pixblock_head +-.if cache_preload_flag != 0 ++ \process_pixblock_head ++.if \cache_preload_flag != 0 + cache_preload 0, pixblock_size + cache_preload_simple + .endif +- process_pixblock_tail ++ \process_pixblock_tail + pixinterleave dst_w_bpp, dst_w_basereg + .irp chunk_size, 16, 8, 4, 2, 1 +-.if pixblock_size > chunk_size +- tst W, #chunk_size ++.if pixblock_size > \chunk_size ++ tst W, #\chunk_size + beq 1f +-.if dst_aligned_flag != 0 +- pixst_a chunk_size, dst_w_bpp, dst_w_basereg, DST_W ++.if \dst_aligned_flag != 0 ++ pixst_a \chunk_size, dst_w_bpp, dst_w_basereg, DST_W + .else +- pixst chunk_size, dst_w_bpp, dst_w_basereg, DST_W ++ pixst \chunk_size, dst_w_bpp, dst_w_basereg, DST_W + .endif + 1: + .endif + .endr + 2: + .endm + + /* +@@ -599,17 +601,17 @@ 2: + .if (mask_bpp != 24) && (mask_bpp != 0) + sub MASK, MASK, W, lsl #mask_bpp_shift + .endif + subs H, H, #1 + mov DST_R, DST_W + .if regs_shortage + str H, [sp, #4] /* save updated height to stack */ + .endif +- bge start_of_loop_label ++ bge \start_of_loop_label + .endm + + /* + * Registers are allocated in the following way by default: + * d0, d1, d2, d3 - reserved for loading source pixel data + * d4, d5, d6, d7 - reserved for loading destination pixel data + * d24, d25, d26, d27 - reserved for loading mask pixel data + * d28, d29, d30, d31 - final destination pixel data for writeback to memory +@@ -626,48 +628,48 @@ 2: + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + +- pixman_asm_function fname ++ pixman_asm_function \fname + + push {r4-r12, lr} /* save all registers */ + + /* + * Select prefetch type for this function. If prefetch distance is + * set to 0 or one of the color formats is 24bpp, SIMPLE prefetch + * has to be used instead of ADVANCED. + */ + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_DEFAULT +-.if prefetch_distance == 0 ++.if \prefetch_distance == 0 + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE + .elseif (PREFETCH_TYPE_CURRENT > PREFETCH_TYPE_SIMPLE) && \ +- ((src_bpp_ == 24) || (mask_bpp_ == 24) || (dst_w_bpp_ == 24)) ++ ((\src_bpp_ == 24) || (\mask_bpp_ == 24) || (\dst_w_bpp_ == 24)) + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_SIMPLE + .endif + + /* + * Make some macro arguments globally visible and accessible + * from other macros + */ +- .set src_bpp, src_bpp_ +- .set mask_bpp, mask_bpp_ +- .set dst_w_bpp, dst_w_bpp_ +- .set pixblock_size, pixblock_size_ +- .set dst_w_basereg, dst_w_basereg_ +- .set dst_r_basereg, dst_r_basereg_ +- .set src_basereg, src_basereg_ +- .set mask_basereg, mask_basereg_ ++ .set src_bpp, \src_bpp_ ++ .set mask_bpp, \mask_bpp_ ++ .set dst_w_bpp, \dst_w_bpp_ ++ .set pixblock_size, \pixblock_size_ ++ .set dst_w_basereg, \dst_w_basereg_ ++ .set dst_r_basereg, \dst_r_basereg_ ++ .set src_basereg, \src_basereg_ ++ .set mask_basereg, \mask_basereg_ + + .macro pixld_src x:vararg +- pixld x ++ pixld \x + .endm + .macro fetch_src_pixblock + pixld_src pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + .endm + /* + * Assign symbolic names to registers + */ +@@ -750,38 +752,38 @@ 2: + .elseif dst_w_bpp == 16 + .set dst_bpp_shift, 1 + .elseif dst_w_bpp == 8 + .set dst_bpp_shift, 0 + .else + .error "requested dst bpp (dst_w_bpp) is not supported" + .endif + +-.if (((flags) & FLAG_DST_READWRITE) != 0) ++.if (((\flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp + .else + .set dst_r_bpp, 0 + .endif +-.if (((flags) & FLAG_DEINTERLEAVE_32BPP) != 0) ++.if (((\flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 + .else + .set DEINTERLEAVE_32BPP_ENABLED, 0 + .endif + +-.if prefetch_distance < 0 || prefetch_distance > 15 +- .error "invalid prefetch distance (prefetch_distance)" ++.if \prefetch_distance < 0 || \prefetch_distance > 15 ++ .error "invalid prefetch distance (\prefetch_distance)" + .endif + + .if src_bpp > 0 + ldr SRC, [sp, #40] + .endif + .if mask_bpp > 0 + ldr MASK, [sp, #48] + .endif +- PF mov PF_X, #0 ++ PF mov, PF_X, #0 + .if src_bpp > 0 + ldr SRC_STRIDE, [sp, #44] + .endif + .if mask_bpp > 0 + ldr MASK_STRIDE, [sp, #52] + .endif + mov DST_R, DST_W + +@@ -796,24 +798,24 @@ 2: + .if dst_w_bpp == 24 + sub DST_STRIDE, DST_STRIDE, W + sub DST_STRIDE, DST_STRIDE, W, lsl #1 + .endif + + /* + * Setup advanced prefetcher initial state + */ +- PF mov PF_SRC, SRC +- PF mov PF_DST, DST_R +- PF mov PF_MASK, MASK ++ PF mov, PF_SRC, SRC ++ PF mov, PF_DST, DST_R ++ PF mov, PF_MASK, MASK + /* PF_CTL = prefetch_distance | ((h - 1) << 4) */ +- PF mov PF_CTL, H, lsl #4 +- PF add PF_CTL, #(prefetch_distance - 0x10) ++ PF mov, PF_CTL, H, lsl #4 ++ PF add, PF_CTL, #(\prefetch_distance - 0x10) + +- init ++ \init + .if regs_shortage + push {r0, r1} + .endif + subs H, H, #1 + .if regs_shortage + str H, [sp, #4] /* save updated height to stack */ + .else + mov ORIG_W, W +@@ -821,84 +823,84 @@ 2: + blt 9f + cmp W, #(pixblock_size * 2) + blt 8f + /* + * This is the start of the pipelined loop, which if optimized for + * long scanlines + */ + 0: +- ensure_destination_ptr_alignment process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ ensure_destination_ptr_alignment \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK +- PF add PF_X, PF_X, #pixblock_size +- process_pixblock_head ++ PF add, PF_X, PF_X, #pixblock_size ++ \process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple + subs W, W, #(pixblock_size * 2) + blt 2f + 1: +- process_pixblock_tail_head ++ \process_pixblock_tail_head + cache_preload_simple + subs W, W, #pixblock_size + bge 1b + 2: +- process_pixblock_tail ++ \process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 1, 1, \ +- process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + advance_to_next_scanline 0b + + .if regs_shortage + pop {r0, r1} + .endif +- cleanup ++ \cleanup + pop {r4-r12, pc} /* exit */ + /* + * This is the start of the loop, designed to process images with small width + * (less than pixblock_size * 2 pixels). In this case neither pipelining + * nor prefetch are used. + */ + 8: + /* Process exactly pixblock_size pixels if needed */ + tst W, #pixblock_size + beq 1f + pixld pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK +- process_pixblock_head +- process_pixblock_tail ++ \process_pixblock_head ++ \process_pixblock_tail + pixst pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + 1: + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 0, 0, \ +- process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + advance_to_next_scanline 8b + 9: + .if regs_shortage + pop {r0, r1} + .endif +- cleanup ++ \cleanup + pop {r4-r12, pc} /* exit */ + + .purgem fetch_src_pixblock + .purgem pixld_src + + .unreq SRC + .unreq MASK + .unreq DST_R +@@ -910,17 +912,17 @@ 9: + .unreq DST_STRIDE + .unreq MASK_STRIDE + .unreq PF_CTL + .unreq PF_X + .unreq PF_SRC + .unreq PF_DST + .unreq PF_MASK + .unreq DUMMY +- .endfunc ++ pixman_end_asm_function + .endm + + /* + * A simplified variant of function generation template for a single + * scanline processing (for implementing pixman combine functions) + */ + .macro generate_composite_function_scanline use_nearest_scaling, \ + fname, \ +@@ -934,49 +936,49 @@ 9: + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + +- pixman_asm_function fname ++ pixman_asm_function \fname + + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE + /* + * Make some macro arguments globally visible and accessible + * from other macros + */ +- .set src_bpp, src_bpp_ +- .set mask_bpp, mask_bpp_ +- .set dst_w_bpp, dst_w_bpp_ +- .set pixblock_size, pixblock_size_ +- .set dst_w_basereg, dst_w_basereg_ +- .set dst_r_basereg, dst_r_basereg_ +- .set src_basereg, src_basereg_ +- .set mask_basereg, mask_basereg_ ++ .set src_bpp, \src_bpp_ ++ .set mask_bpp, \mask_bpp_ ++ .set dst_w_bpp, \dst_w_bpp_ ++ .set pixblock_size, \pixblock_size_ ++ .set dst_w_basereg, \dst_w_basereg_ ++ .set dst_r_basereg, \dst_r_basereg_ ++ .set src_basereg, \src_basereg_ ++ .set mask_basereg, \mask_basereg_ + +-.if use_nearest_scaling != 0 ++.if \use_nearest_scaling != 0 + /* + * Assign symbolic names to registers for nearest scaling + */ + W .req r0 + DST_W .req r1 + SRC .req r2 + VX .req r3 + UNIT_X .req ip + MASK .req lr + TMP1 .req r4 + TMP2 .req r5 + DST_R .req r6 + SRC_WIDTH_FIXED .req r7 + + .macro pixld_src x:vararg +- pixld_s x ++ pixld_s \x + .endm + + ldr UNIT_X, [sp] + push {r4-r8, lr} + ldr SRC_WIDTH_FIXED, [sp, #(24 + 4)] + .if mask_bpp != 0 + ldr MASK, [sp, #(24 + 8)] + .endif +@@ -986,89 +988,89 @@ 9: + */ + W .req r0 /* width (is updated during processing) */ + DST_W .req r1 /* destination buffer pointer for writes */ + SRC .req r2 /* source buffer pointer */ + DST_R .req ip /* destination buffer pointer for reads */ + MASK .req r3 /* mask pointer */ + + .macro pixld_src x:vararg +- pixld x ++ pixld \x + .endm + .endif + +-.if (((flags) & FLAG_DST_READWRITE) != 0) ++.if (((\flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp + .else + .set dst_r_bpp, 0 + .endif +-.if (((flags) & FLAG_DEINTERLEAVE_32BPP) != 0) ++.if (((\flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 + .else + .set DEINTERLEAVE_32BPP_ENABLED, 0 + .endif + + .macro fetch_src_pixblock + pixld_src pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + .endm + +- init ++ \init + mov DST_R, DST_W + + cmp W, #pixblock_size + blt 8f + +- ensure_destination_ptr_alignment process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ ensure_destination_ptr_alignment \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + + subs W, W, #pixblock_size + blt 7f + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK +- process_pixblock_head ++ \process_pixblock_head + subs W, W, #pixblock_size + blt 2f + 1: +- process_pixblock_tail_head ++ \process_pixblock_tail_head + subs W, W, #pixblock_size + bge 1b + 2: +- process_pixblock_tail ++ \process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + 7: + /* Process the remaining trailing pixels in the scanline (dst aligned) */ + process_trailing_pixels 0, 1, \ +- process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + +- cleanup +-.if use_nearest_scaling != 0 ++ \cleanup ++.if \use_nearest_scaling != 0 + pop {r4-r8, pc} /* exit */ + .else + bx lr /* exit */ + .endif + 8: + /* Process the remaining trailing pixels in the scanline (dst unaligned) */ + process_trailing_pixels 0, 0, \ +- process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + +- cleanup ++ \cleanup + +-.if use_nearest_scaling != 0 ++.if \use_nearest_scaling != 0 + pop {r4-r8, pc} /* exit */ + + .unreq DST_R + .unreq SRC + .unreq W + .unreq VX + .unreq UNIT_X + .unreq TMP1 +@@ -1085,25 +1087,25 @@ 8: + .unreq DST_R + .unreq DST_W + .unreq W + .endif + + .purgem fetch_src_pixblock + .purgem pixld_src + +- .endfunc ++ pixman_end_asm_function + .endm + + .macro generate_composite_function_single_scanline x:vararg +- generate_composite_function_scanline 0, x ++ generate_composite_function_scanline 0, \x + .endm + + .macro generate_composite_function_nearest_scanline x:vararg +- generate_composite_function_scanline 1, x ++ generate_composite_function_scanline 1, \x + .endm + + /* Default prologue/epilogue, nothing special needs to be done */ + + .macro default_init + .endm + + .macro default_cleanup +@@ -1129,56 +1131,56 @@ 8: + * Conversion of 8 r5g6b6 pixels packed in 128-bit register (in) + * into a planar a8r8g8b8 format (with a, r, g, b color components + * stored into 64-bit registers out_a, out_r, out_g, out_b respectively). + * + * Warning: the conversion is destructive and the original + * value (in) is lost. + */ + .macro convert_0565_to_8888 in, out_a, out_r, out_g, out_b +- vshrn.u16 out_r, in, #8 +- vshrn.u16 out_g, in, #3 +- vsli.u16 in, in, #5 +- vmov.u8 out_a, #255 +- vsri.u8 out_r, out_r, #5 +- vsri.u8 out_g, out_g, #6 +- vshrn.u16 out_b, in, #2 ++ vshrn.u16 \out_r, \in, #8 ++ vshrn.u16 \out_g, \in, #3 ++ vsli.u16 \in, \in, #5 ++ vmov.u8 \out_a, #255 ++ vsri.u8 \out_r, \out_r, #5 ++ vsri.u8 \out_g, \out_g, #6 ++ vshrn.u16 \out_b, \in, #2 + .endm + + .macro convert_0565_to_x888 in, out_r, out_g, out_b +- vshrn.u16 out_r, in, #8 +- vshrn.u16 out_g, in, #3 +- vsli.u16 in, in, #5 +- vsri.u8 out_r, out_r, #5 +- vsri.u8 out_g, out_g, #6 +- vshrn.u16 out_b, in, #2 ++ vshrn.u16 \out_r, \in, #8 ++ vshrn.u16 \out_g, \in, #3 ++ vsli.u16 \in, \in, #5 ++ vsri.u8 \out_r, \out_r, #5 ++ vsri.u8 \out_g, \out_g, #6 ++ vshrn.u16 \out_b, \in, #2 + .endm + + /* + * Conversion from planar a8r8g8b8 format (with a, r, g, b color components + * in 64-bit registers in_a, in_r, in_g, in_b respectively) into 8 r5g6b6 + * pixels packed in 128-bit register (out). Requires two temporary 128-bit + * registers (tmp1, tmp2) + */ + .macro convert_8888_to_0565 in_r, in_g, in_b, out, tmp1, tmp2 +- vshll.u8 tmp1, in_g, #8 +- vshll.u8 out, in_r, #8 +- vshll.u8 tmp2, in_b, #8 +- vsri.u16 out, tmp1, #5 +- vsri.u16 out, tmp2, #11 ++ vshll.u8 \tmp1, \in_g, #8 ++ vshll.u8 \out, \in_r, #8 ++ vshll.u8 \tmp2, \in_b, #8 ++ vsri.u16 \out, \tmp1, #5 ++ vsri.u16 \out, \tmp2, #11 + .endm + + /* + * Conversion of four r5g6b5 pixels (in) to four x8r8g8b8 pixels + * returned in (out0, out1) registers pair. Requires one temporary + * 64-bit register (tmp). 'out1' and 'in' may overlap, the original + * value from 'in' is lost + */ + .macro convert_four_0565_to_x888_packed in, out0, out1, tmp +- vshl.u16 out0, in, #5 /* G top 6 bits */ +- vshl.u16 tmp, in, #11 /* B top 5 bits */ +- vsri.u16 in, in, #5 /* R is ready in top bits */ +- vsri.u16 out0, out0, #6 /* G is ready in top bits */ +- vsri.u16 tmp, tmp, #5 /* B is ready in top bits */ +- vshr.u16 out1, in, #8 /* R is in place */ +- vsri.u16 out0, tmp, #8 /* G & B is in place */ +- vzip.u16 out0, out1 /* everything is in place */ ++ vshl.u16 \out0, \in, #5 /* G top 6 bits */ ++ vshl.u16 \tmp, \in, #11 /* B top 5 bits */ ++ vsri.u16 \in, \in, #5 /* R is ready in top bits */ ++ vsri.u16 \out0, \out0, #6 /* G is ready in top bits */ ++ vsri.u16 \tmp, \tmp, #5 /* B is ready in top bits */ ++ vshr.u16 \out1, \in, #8 /* R is in place */ ++ vsri.u16 \out0, \tmp, #8 /* G & B is in place */ ++ vzip.u16 \out0, \out1 /* everything is in place */ + .endm +diff --git a/gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S b/gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S +--- a/gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S ++++ b/gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S +@@ -20,16 +20,20 @@ + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * + * Author: Jeff Muizelaar (jeff@infidigm.net) + * + */ + ++#ifdef __clang__ ++#define subpls subspl ++#endif ++ + /* Prevent the stack from becoming executable */ + #if defined(__linux__) && defined(__ELF__) + .section .note.GNU-stack,"",%progbits + #endif + + .text + .arch armv6 + .object_arch armv4 +@@ -57,100 +61,105 @@ + * prefetch_braking_distance - stop prefetching when that many pixels are + * remaining before the end of scanline + */ + + .macro generate_nearest_scanline_func fname, bpp_shift, t, \ + prefetch_distance, \ + prefetch_braking_distance + +-pixman_asm_function fname ++pixman_asm_function \fname + W .req r0 + DST .req r1 + SRC .req r2 + VX .req r3 + UNIT_X .req ip + TMP1 .req r4 + TMP2 .req r5 + VXMASK .req r6 + PF_OFFS .req r7 + SRC_WIDTH_FIXED .req r8 + + ldr UNIT_X, [sp] + push {r4, r5, r6, r7, r8, r10} +- mvn VXMASK, #((1 << bpp_shift) - 1) ++ mvn VXMASK, #((1 << \bpp_shift) - 1) + ldr SRC_WIDTH_FIXED, [sp, #28] + + /* define helper macro */ + .macro scale_2_pixels +- ldr&t TMP1, [SRC, TMP1] +- and TMP2, VXMASK, VX, asr #(16 - bpp_shift) ++ ldr\()\t TMP1, [SRC, TMP1] ++ and TMP2, VXMASK, VX, asr #(16 - \bpp_shift) + adds VX, VX, UNIT_X +- str&t TMP1, [DST], #(1 << bpp_shift) ++ str\()\t TMP1, [DST], #(1 << \bpp_shift) + 9: subpls VX, VX, SRC_WIDTH_FIXED + bpl 9b + +- ldr&t TMP2, [SRC, TMP2] +- and TMP1, VXMASK, VX, asr #(16 - bpp_shift) ++ ldr\()\t TMP2, [SRC, TMP2] ++ and TMP1, VXMASK, VX, asr #(16 - \bpp_shift) + adds VX, VX, UNIT_X +- str&t TMP2, [DST], #(1 << bpp_shift) ++ str\()\t TMP2, [DST], #(1 << \bpp_shift) + 9: subpls VX, VX, SRC_WIDTH_FIXED + bpl 9b + .endm + + /* now do the scaling */ +- and TMP1, VXMASK, VX, asr #(16 - bpp_shift) ++ and TMP1, VXMASK, VX, asr #(16 - \bpp_shift) + adds VX, VX, UNIT_X + 9: subpls VX, VX, SRC_WIDTH_FIXED + bpl 9b +- subs W, W, #(8 + prefetch_braking_distance) ++ subs W, W, #(8 + \prefetch_braking_distance) + blt 2f + /* calculate prefetch offset */ +- mov PF_OFFS, #prefetch_distance ++ mov PF_OFFS, #\prefetch_distance + mla PF_OFFS, UNIT_X, PF_OFFS, VX + 1: /* main loop, process 8 pixels per iteration with prefetch */ +- pld [SRC, PF_OFFS, asr #(16 - bpp_shift)] ++ pld [SRC, PF_OFFS, asr #(16 - \bpp_shift)] + add PF_OFFS, UNIT_X, lsl #3 + scale_2_pixels + scale_2_pixels + scale_2_pixels + scale_2_pixels + subs W, W, #8 + bge 1b + 2: +- subs W, W, #(4 - 8 - prefetch_braking_distance) ++ subs W, W, #(4 - 8 - \prefetch_braking_distance) + blt 2f + 1: /* process the remaining pixels */ + scale_2_pixels + scale_2_pixels + subs W, W, #4 + bge 1b + 2: + tst W, #2 + beq 2f + scale_2_pixels + 2: + tst W, #1 +- ldrne&t TMP1, [SRC, TMP1] +- strne&t TMP1, [DST] ++#ifdef __clang__ ++ ldr\()\t\()ne TMP1, [SRC, TMP1] ++ str\()\t\()ne TMP1, [DST] ++#else ++ ldrne\()\t TMP1, [SRC, TMP1] ++ strne\()\t TMP1, [DST] ++#endif + /* cleanup helper macro */ + .purgem scale_2_pixels + .unreq DST + .unreq SRC + .unreq W + .unreq VX + .unreq UNIT_X + .unreq TMP1 + .unreq TMP2 + .unreq VXMASK + .unreq PF_OFFS + .unreq SRC_WIDTH_FIXED + /* return */ + pop {r4, r5, r6, r7, r8, r10} + bx lr +-.endfunc ++pixman_end_asm_function + .endm + + generate_nearest_scanline_func \ + pixman_scaled_nearest_scanline_0565_0565_SRC_asm_armv6, 1, h, 80, 32 + + generate_nearest_scanline_func \ + pixman_scaled_nearest_scanline_8888_8888_SRC_asm_armv6, 2, , 48, 32 +diff --git a/gfx/cairo/libpixman/src/pixman-arm-simd-asm.S b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.S +--- a/gfx/cairo/libpixman/src/pixman-arm-simd-asm.S ++++ b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.S +@@ -20,16 +20,21 @@ + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * + * Author: Ben Avison (bavison@riscosopen.org) + * + */ + ++#ifdef __clang__ ++#define adceqs adcseq ++#define ldmnedb ldmdbne ++#endif ++ + /* Prevent the stack from becoming executable */ + #if defined(__linux__) && defined(__ELF__) + .section .note.GNU-stack,"",%progbits + #endif + + .text + .arch armv6 + .object_arch armv4 +@@ -52,26 +57,26 @@ + * preload If outputting 16 bytes causes 64 bytes to be read, whether an extra preload should be output + */ + + .macro blit_init + line_saved_regs STRIDE_D, STRIDE_S + .endm + + .macro blit_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload +- pixld cond, numbytes, firstreg, SRC, unaligned_src ++ pixld \cond, \numbytes, \firstreg, SRC, \unaligned_src + .endm + + .macro blit_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, dst_alignment + WK4 .req STRIDE_D + WK5 .req STRIDE_S + WK6 .req MASK + WK7 .req STRIDE_M +-110: pixld , 16, 0, SRC, unaligned_src +- pixld , 16, 4, SRC, unaligned_src ++110: pixld , 16, 0, SRC, \unaligned_src ++ pixld , 16, 4, SRC, \unaligned_src + pld [SRC, SCRATCH] + pixst , 16, 0, DST + pixst , 16, 4, DST + subs X, X, #32*8/src_bpp + bhs 110b + .unreq WK4 + .unreq WK5 + .unreq WK6 +@@ -137,17 +142,17 @@ generate_composite_function \ + mov STRIDE_M, SRC + .endm + + .macro fill_process_tail cond, numbytes, firstreg + WK4 .req SRC + WK5 .req STRIDE_S + WK6 .req MASK + WK7 .req STRIDE_M +- pixst cond, numbytes, 4, DST ++ pixst \cond, \numbytes, 4, DST + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 + .endm + + generate_composite_function \ + pixman_composite_src_n_8888_asm_armv6, 0, 0, 32, \ +@@ -177,30 +182,30 @@ generate_composite_function \ + nop_macro, /* newline */ \ + nop_macro /* cleanup */ \ + nop_macro /* process head */ \ + fill_process_tail + + /******************************************************************************/ + + .macro src_x888_8888_pixel, cond, reg +- orr&cond WK®, WK®, #0xFF000000 ++ orr\()\cond WK\()\reg, WK\()\reg, #0xFF000000 + .endm + + .macro pixman_composite_src_x888_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload +- pixld cond, numbytes, firstreg, SRC, unaligned_src ++ pixld \cond, \numbytes, \firstreg, SRC, \unaligned_src + .endm + + .macro pixman_composite_src_x888_8888_process_tail cond, numbytes, firstreg +- src_x888_8888_pixel cond, %(firstreg+0) +- .if numbytes >= 8 +- src_x888_8888_pixel cond, %(firstreg+1) +- .if numbytes == 16 +- src_x888_8888_pixel cond, %(firstreg+2) +- src_x888_8888_pixel cond, %(firstreg+3) ++ src_x888_8888_pixel \cond, %(\firstreg+0) ++ .if \numbytes >= 8 ++ src_x888_8888_pixel \cond, %(\firstreg+1) ++ .if \numbytes == 16 ++ src_x888_8888_pixel \cond, %(\firstreg+2) ++ src_x888_8888_pixel \cond, %(\firstreg+3) + .endif + .endif + .endm + + generate_composite_function \ + pixman_composite_src_x888_8888_asm_armv6, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_COND_EXEC | FLAG_PROCESS_PRESERVES_SCRATCH, \ + 3, /* prefetch distance */ \ +@@ -217,83 +222,83 @@ generate_composite_function \ + ldr MASK, =0x07E007E0 + mov STRIDE_M, #0xFF000000 + /* Set GE[3:0] to 1010 so SEL instructions do what we want */ + ldr SCRATCH, =0x80008000 + uadd8 SCRATCH, SCRATCH, SCRATCH + .endm + + .macro src_0565_8888_2pixels, reg1, reg2 +- and SCRATCH, WK®1, MASK @ 00000GGGGGG0000000000gggggg00000 +- bic WK®2, WK®1, MASK @ RRRRR000000BBBBBrrrrr000000bbbbb +- orr SCRATCH, SCRATCH, SCRATCH, lsr #6 @ 00000GGGGGGGGGGGG0000ggggggggggg +- mov WK®1, WK®2, lsl #16 @ rrrrr000000bbbbb0000000000000000 +- mov SCRATCH, SCRATCH, ror #19 @ GGGG0000ggggggggggg00000GGGGGGGG +- bic WK®2, WK®2, WK®1, lsr #16 @ RRRRR000000BBBBB0000000000000000 +- orr WK®1, WK®1, WK®1, lsr #5 @ rrrrrrrrrr0bbbbbbbbbb00000000000 +- orr WK®2, WK®2, WK®2, lsr #5 @ RRRRRRRRRR0BBBBBBBBBB00000000000 +- pkhtb WK®1, WK®1, WK®1, asr #5 @ rrrrrrrr--------bbbbbbbb-------- +- sel WK®1, WK®1, SCRATCH @ rrrrrrrrggggggggbbbbbbbb-------- +- mov SCRATCH, SCRATCH, ror #16 @ ggg00000GGGGGGGGGGGG0000gggggggg +- pkhtb WK®2, WK®2, WK®2, asr #5 @ RRRRRRRR--------BBBBBBBB-------- +- sel WK®2, WK®2, SCRATCH @ RRRRRRRRGGGGGGGGBBBBBBBB-------- +- orr WK®1, STRIDE_M, WK®1, lsr #8 @ 11111111rrrrrrrrggggggggbbbbbbbb +- orr WK®2, STRIDE_M, WK®2, lsr #8 @ 11111111RRRRRRRRGGGGGGGGBBBBBBBB ++ and SCRATCH, WK\()\reg1, MASK @ 00000GGGGGG0000000000gggggg00000 ++ bic WK\()\reg2, WK\()\reg1, MASK @ RRRRR000000BBBBBrrrrr000000bbbbb ++ orr SCRATCH, SCRATCH, SCRATCH, lsr #6 @ 00000GGGGGGGGGGGG0000ggggggggggg ++ mov WK\()\reg1, WK\()\reg2, lsl #16 @ rrrrr000000bbbbb0000000000000000 ++ mov SCRATCH, SCRATCH, ror #19 @ GGGG0000ggggggggggg00000GGGGGGGG ++ bic WK\()\reg2, WK\()\reg2, WK\()\reg1, lsr #16 @ RRRRR000000BBBBB0000000000000000 ++ orr WK\()\reg1, WK\()\reg1, WK\()\reg1, lsr #5 @ rrrrrrrrrr0bbbbbbbbbb00000000000 ++ orr WK\()\reg2, WK\()\reg2, WK\()\reg2, lsr #5 @ RRRRRRRRRR0BBBBBBBBBB00000000000 ++ pkhtb WK\()\reg1, WK\()\reg1, WK\()\reg1, asr #5 @ rrrrrrrr--------bbbbbbbb-------- ++ sel WK\()\reg1, WK\()\reg1, SCRATCH @ rrrrrrrrggggggggbbbbbbbb-------- ++ mov SCRATCH, SCRATCH, ror #16 @ ggg00000GGGGGGGGGGGG0000gggggggg ++ pkhtb WK\()\reg2, WK\()\reg2, WK\()\reg2, asr #5 @ RRRRRRRR--------BBBBBBBB-------- ++ sel WK\()\reg2, WK\()\reg2, SCRATCH @ RRRRRRRRGGGGGGGGBBBBBBBB-------- ++ orr WK\()\reg1, STRIDE_M, WK\()\reg1, lsr #8 @ 11111111rrrrrrrrggggggggbbbbbbbb ++ orr WK\()\reg2, STRIDE_M, WK\()\reg2, lsr #8 @ 11111111RRRRRRRRGGGGGGGGBBBBBBBB + .endm + + /* This version doesn't need STRIDE_M, but is one instruction longer. + It would however be preferable for an XRGB target, since we could knock off the last 2 instructions, but is that a common case? +- and SCRATCH, WK®1, MASK @ 00000GGGGGG0000000000gggggg00000 +- bic WK®1, WK®1, MASK @ RRRRR000000BBBBBrrrrr000000bbbbb +- orr SCRATCH, SCRATCH, SCRATCH, lsr #6 @ 00000GGGGGGGGGGGG0000ggggggggggg +- mov WK®2, WK®1, lsr #16 @ 0000000000000000RRRRR000000BBBBB +- mov SCRATCH, SCRATCH, ror #27 @ GGGGGGGGGGGG0000ggggggggggg00000 +- bic WK®1, WK®1, WK®2, lsl #16 @ 0000000000000000rrrrr000000bbbbb +- mov WK®2, WK®2, lsl #3 @ 0000000000000RRRRR000000BBBBB000 +- mov WK®1, WK®1, lsl #3 @ 0000000000000rrrrr000000bbbbb000 +- orr WK®2, WK®2, WK®2, lsr #5 @ 0000000000000RRRRRRRRRR0BBBBBBBB +- orr WK®1, WK®1, WK®1, lsr #5 @ 0000000000000rrrrrrrrrr0bbbbbbbb +- pkhbt WK®2, WK®2, WK®2, lsl #5 @ --------RRRRRRRR--------BBBBBBBB +- pkhbt WK®1, WK®1, WK®1, lsl #5 @ --------rrrrrrrr--------bbbbbbbb +- sel WK®2, SCRATCH, WK®2 @ --------RRRRRRRRGGGGGGGGBBBBBBBB +- sel WK®1, SCRATCH, WK®1 @ --------rrrrrrrrggggggggbbbbbbbb +- orr WK®2, WK®2, #0xFF000000 @ 11111111RRRRRRRRGGGGGGGGBBBBBBBB +- orr WK®1, WK®1, #0xFF000000 @ 11111111rrrrrrrrggggggggbbbbbbbb ++ and SCRATCH, WK\()\reg1, MASK @ 00000GGGGGG0000000000gggggg00000 ++ bic WK\()\reg1, WK\()\reg1, MASK @ RRRRR000000BBBBBrrrrr000000bbbbb ++ orr SCRATCH, SCRATCH, SCRATCH, lsr #6 @ 00000GGGGGGGGGGGG0000ggggggggggg ++ mov WK\()\reg2, WK\()\reg1, lsr #16 @ 0000000000000000RRRRR000000BBBBB ++ mov SCRATCH, SCRATCH, ror #27 @ GGGGGGGGGGGG0000ggggggggggg00000 ++ bic WK\()\reg1, WK\()\reg1, WK\()\reg2, lsl #16 @ 0000000000000000rrrrr000000bbbbb ++ mov WK\()\reg2, WK\()\reg2, lsl #3 @ 0000000000000RRRRR000000BBBBB000 ++ mov WK\()\reg1, WK\()\reg1, lsl #3 @ 0000000000000rrrrr000000bbbbb000 ++ orr WK\()\reg2, WK\()\reg2, WK\()\reg2, lsr #5 @ 0000000000000RRRRRRRRRR0BBBBBBBB ++ orr WK\()\reg1, WK\()\reg1, WK\()\reg1, lsr #5 @ 0000000000000rrrrrrrrrr0bbbbbbbb ++ pkhbt WK\()\reg2, WK\()\reg2, WK\()\reg2, lsl #5 @ --------RRRRRRRR--------BBBBBBBB ++ pkhbt WK\()\reg1, WK\()\reg1, WK\()\reg1, lsl #5 @ --------rrrrrrrr--------bbbbbbbb ++ sel WK\()\reg2, SCRATCH, WK\()\reg2 @ --------RRRRRRRRGGGGGGGGBBBBBBBB ++ sel WK\()\reg1, SCRATCH, WK\()\reg1 @ --------rrrrrrrrggggggggbbbbbbbb ++ orr WK\()\reg2, WK\()\reg2, #0xFF000000 @ 11111111RRRRRRRRGGGGGGGGBBBBBBBB ++ orr WK\()\reg1, WK\()\reg1, #0xFF000000 @ 11111111rrrrrrrrggggggggbbbbbbbb + */ + + .macro src_0565_8888_1pixel, reg +- bic SCRATCH, WK®, MASK @ 0000000000000000rrrrr000000bbbbb +- and WK®, WK®, MASK @ 000000000000000000000gggggg00000 +- mov SCRATCH, SCRATCH, lsl #3 @ 0000000000000rrrrr000000bbbbb000 +- mov WK®, WK®, lsl #5 @ 0000000000000000gggggg0000000000 +- orr SCRATCH, SCRATCH, SCRATCH, lsr #5 @ 0000000000000rrrrrrrrrr0bbbbbbbb +- orr WK®, WK®, WK®, lsr #6 @ 000000000000000gggggggggggg00000 +- pkhbt SCRATCH, SCRATCH, SCRATCH, lsl #5 @ --------rrrrrrrr--------bbbbbbbb +- sel WK®, WK®, SCRATCH @ --------rrrrrrrrggggggggbbbbbbbb +- orr WK®, WK®, #0xFF000000 @ 11111111rrrrrrrrggggggggbbbbbbbb ++ bic SCRATCH, WK\()\reg, MASK @ 0000000000000000rrrrr000000bbbbb ++ and WK\()\reg, WK\()\reg, MASK @ 000000000000000000000gggggg00000 ++ mov SCRATCH, SCRATCH, lsl #3 @ 0000000000000rrrrr000000bbbbb000 ++ mov WK\()\reg, WK\()\reg, lsl #5 @ 0000000000000000gggggg0000000000 ++ orr SCRATCH, SCRATCH, SCRATCH, lsr #5 @ 0000000000000rrrrrrrrrr0bbbbbbbb ++ orr WK\()\reg, WK\()\reg, WK\()\reg, lsr #6 @ 000000000000000gggggggggggg00000 ++ pkhbt SCRATCH, SCRATCH, SCRATCH, lsl #5 @ --------rrrrrrrr--------bbbbbbbb ++ sel WK\()\reg, WK\()\reg, SCRATCH @ --------rrrrrrrrggggggggbbbbbbbb ++ orr WK\()\reg, WK\()\reg, #0xFF000000 @ 11111111rrrrrrrrggggggggbbbbbbbb + .endm + + .macro src_0565_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload +- .if numbytes == 16 +- pixldst ld,, 8, firstreg, %(firstreg+2),,, SRC, unaligned_src +- .elseif numbytes == 8 +- pixld , 4, firstreg, SRC, unaligned_src +- .elseif numbytes == 4 +- pixld , 2, firstreg, SRC, unaligned_src ++ .if \numbytes == 16 ++ pixldst ld,, 8, \firstreg, %(\firstreg+2),,, SRC, \unaligned_src ++ .elseif \numbytes == 8 ++ pixld , 4, \firstreg, SRC, \unaligned_src ++ .elseif \numbytes == 4 ++ pixld , 2, \firstreg, SRC, \unaligned_src + .endif + .endm + + .macro src_0565_8888_process_tail cond, numbytes, firstreg +- .if numbytes == 16 +- src_0565_8888_2pixels firstreg, %(firstreg+1) +- src_0565_8888_2pixels %(firstreg+2), %(firstreg+3) +- .elseif numbytes == 8 +- src_0565_8888_2pixels firstreg, %(firstreg+1) ++ .if \numbytes == 16 ++ src_0565_8888_2pixels \firstreg, %(\firstreg+1) ++ src_0565_8888_2pixels %(\firstreg+2), %(\firstreg+3) ++ .elseif \numbytes == 8 ++ src_0565_8888_2pixels \firstreg, %(\firstreg+1) + .else +- src_0565_8888_1pixel firstreg ++ src_0565_8888_1pixel \firstreg + .endif + .endm + + generate_composite_function \ + pixman_composite_src_0565_8888_asm_armv6, 16, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_BRANCH_OVER, \ + 3, /* prefetch distance */ \ + src_0565_8888_init, \ +@@ -306,67 +311,67 @@ generate_composite_function \ + + .macro src_x888_0565_init + /* Hold loop invariant in MASK */ + ldr MASK, =0x001F001F + line_saved_regs STRIDE_S, ORIG_W + .endm + + .macro src_x888_0565_1pixel s, d +- and WK&d, MASK, WK&s, lsr #3 @ 00000000000rrrrr00000000000bbbbb +- and STRIDE_S, WK&s, #0xFC00 @ 0000000000000000gggggg0000000000 +- orr WK&d, WK&d, WK&d, lsr #5 @ 00000000000-----rrrrr000000bbbbb +- orr WK&d, WK&d, STRIDE_S, lsr #5 @ 00000000000-----rrrrrggggggbbbbb ++ and WK\()\d, MASK, WK\()\s, lsr #3 @ 00000000000rrrrr00000000000bbbbb ++ and STRIDE_S, WK\()\s, #0xFC00 @ 0000000000000000gggggg0000000000 ++ orr WK\()\d, WK\()\d, WK\()\d, lsr #5 @ 00000000000-----rrrrr000000bbbbb ++ orr WK\()\d, WK\()\d, STRIDE_S, lsr #5 @ 00000000000-----rrrrrggggggbbbbb + /* Top 16 bits are discarded during the following STRH */ + .endm + + .macro src_x888_0565_2pixels slo, shi, d, tmp +- and SCRATCH, WK&shi, #0xFC00 @ 0000000000000000GGGGGG0000000000 +- and WK&tmp, MASK, WK&shi, lsr #3 @ 00000000000RRRRR00000000000BBBBB +- and WK&shi, MASK, WK&slo, lsr #3 @ 00000000000rrrrr00000000000bbbbb +- orr WK&tmp, WK&tmp, WK&tmp, lsr #5 @ 00000000000-----RRRRR000000BBBBB +- orr WK&tmp, WK&tmp, SCRATCH, lsr #5 @ 00000000000-----RRRRRGGGGGGBBBBB +- and SCRATCH, WK&slo, #0xFC00 @ 0000000000000000gggggg0000000000 +- orr WK&shi, WK&shi, WK&shi, lsr #5 @ 00000000000-----rrrrr000000bbbbb +- orr WK&shi, WK&shi, SCRATCH, lsr #5 @ 00000000000-----rrrrrggggggbbbbb +- pkhbt WK&d, WK&shi, WK&tmp, lsl #16 @ RRRRRGGGGGGBBBBBrrrrrggggggbbbbb ++ and SCRATCH, WK\()\shi, #0xFC00 @ 0000000000000000GGGGGG0000000000 ++ and WK\()\tmp, MASK, WK\()\shi, lsr #3 @ 00000000000RRRRR00000000000BBBBB ++ and WK\()\shi, MASK, WK\()\slo, lsr #3 @ 00000000000rrrrr00000000000bbbbb ++ orr WK\()\tmp, WK\()\tmp, WK\()\tmp, lsr #5 @ 00000000000-----RRRRR000000BBBBB ++ orr WK\()\tmp, WK\()\tmp, SCRATCH, lsr #5 @ 00000000000-----RRRRRGGGGGGBBBBB ++ and SCRATCH, WK\()\slo, #0xFC00 @ 0000000000000000gggggg0000000000 ++ orr WK\()\shi, WK\()\shi, WK\()\shi, lsr #5 @ 00000000000-----rrrrr000000bbbbb ++ orr WK\()\shi, WK\()\shi, SCRATCH, lsr #5 @ 00000000000-----rrrrrggggggbbbbb ++ pkhbt WK\()\d, WK\()\shi, WK\()\tmp, lsl #16 @ RRRRRGGGGGGBBBBBrrrrrggggggbbbbb + .endm + + .macro src_x888_0565_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req STRIDE_S + WK5 .req STRIDE_M + WK6 .req WK3 + WK7 .req ORIG_W +- .if numbytes == 16 ++ .if \numbytes == 16 + pixld , 16, 4, SRC, 0 + src_x888_0565_2pixels 4, 5, 0, 0 + pixld , 8, 4, SRC, 0 + src_x888_0565_2pixels 6, 7, 1, 1 + pixld , 8, 6, SRC, 0 + .else +- pixld , numbytes*2, 4, SRC, 0 ++ pixld , \numbytes*2, 4, SRC, 0 + .endif + .endm + + .macro src_x888_0565_process_tail cond, numbytes, firstreg +- .if numbytes == 16 ++ .if \numbytes == 16 + src_x888_0565_2pixels 4, 5, 2, 2 + src_x888_0565_2pixels 6, 7, 3, 4 +- .elseif numbytes == 8 ++ .elseif \numbytes == 8 + src_x888_0565_2pixels 4, 5, 1, 1 + src_x888_0565_2pixels 6, 7, 2, 2 +- .elseif numbytes == 4 ++ .elseif \numbytes == 4 + src_x888_0565_2pixels 4, 5, 1, 1 + .else + src_x888_0565_1pixel 4, 1 + .endif +- .if numbytes == 16 +- pixst , numbytes, 0, DST ++ .if \numbytes == 16 ++ pixst , \numbytes, 0, DST + .else +- pixst , numbytes, 1, DST ++ pixst , \numbytes, 1, DST + .endif + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 + .endm + + generate_composite_function \ +@@ -377,47 +382,47 @@ generate_composite_function \ + nop_macro, /* newline */ \ + nop_macro, /* cleanup */ \ + src_x888_0565_process_head, \ + src_x888_0565_process_tail + + /******************************************************************************/ + + .macro add_8_8_8pixels cond, dst1, dst2 +- uqadd8&cond WK&dst1, WK&dst1, MASK +- uqadd8&cond WK&dst2, WK&dst2, STRIDE_M ++ uqadd8\()\cond WK\()\dst1, WK\()\dst1, MASK ++ uqadd8\()\cond WK\()\dst2, WK\()\dst2, STRIDE_M + .endm + + .macro add_8_8_4pixels cond, dst +- uqadd8&cond WK&dst, WK&dst, MASK ++ uqadd8\()\cond WK\()\dst, WK\()\dst, MASK + .endm + + .macro add_8_8_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req MASK + WK5 .req STRIDE_M +- .if numbytes == 16 +- pixld cond, 8, 4, SRC, unaligned_src +- pixld cond, 16, firstreg, DST, 0 +- add_8_8_8pixels cond, firstreg, %(firstreg+1) +- pixld cond, 8, 4, SRC, unaligned_src ++ .if \numbytes == 16 ++ pixld \cond, 8, 4, SRC, \unaligned_src ++ pixld \cond, 16, \firstreg, DST, 0 ++ add_8_8_8pixels \cond, \firstreg, %(\firstreg+1) ++ pixld \cond, 8, 4, SRC, \unaligned_src + .else +- pixld cond, numbytes, 4, SRC, unaligned_src +- pixld cond, numbytes, firstreg, DST, 0 ++ pixld \cond, \numbytes, 4, SRC, \unaligned_src ++ pixld \cond, \numbytes, \firstreg, DST, 0 + .endif + .unreq WK4 + .unreq WK5 + .endm + + .macro add_8_8_process_tail cond, numbytes, firstreg +- .if numbytes == 16 +- add_8_8_8pixels cond, %(firstreg+2), %(firstreg+3) +- .elseif numbytes == 8 +- add_8_8_8pixels cond, firstreg, %(firstreg+1) ++ .if \numbytes == 16 ++ add_8_8_8pixels \cond, %(\firstreg+2), %(\firstreg+3) ++ .elseif \numbytes == 8 ++ add_8_8_8pixels \cond, \firstreg, %(\firstreg+1) + .else +- add_8_8_4pixels cond, firstreg ++ add_8_8_4pixels \cond, \firstreg + .endif + .endm + + generate_composite_function \ + pixman_composite_add_8_8_asm_armv6, 8, 0, 8, \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_PRESERVES_SCRATCH, \ + 2, /* prefetch distance */ \ + nop_macro, /* init */ \ +@@ -436,82 +441,82 @@ generate_composite_function \ + line_saved_regs STRIDE_D, STRIDE_S, ORIG_W + .endm + + .macro over_8888_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req STRIDE_D + WK5 .req STRIDE_S + WK6 .req STRIDE_M + WK7 .req ORIG_W +- pixld , numbytes, %(4+firstreg), SRC, unaligned_src +- pixld , numbytes, firstreg, DST, 0 ++ pixld , \numbytes, %(4+\firstreg), SRC, \unaligned_src ++ pixld , \numbytes, \firstreg, DST, 0 + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 + .endm + + .macro over_8888_8888_check_transparent numbytes, reg0, reg1, reg2, reg3 + /* Since these colours a premultiplied by alpha, only 0 indicates transparent (any other colour with 0 in the alpha byte is luminous) */ +- teq WK®0, #0 +- .if numbytes > 4 +- teqeq WK®1, #0 +- .if numbytes > 8 +- teqeq WK®2, #0 +- teqeq WK®3, #0 ++ teq WK\()\reg0, #0 ++ .if \numbytes > 4 ++ teqeq WK\()\reg1, #0 ++ .if \numbytes > 8 ++ teqeq WK\()\reg2, #0 ++ teqeq WK\()\reg3, #0 + .endif + .endif + .endm + + .macro over_8888_8888_prepare next +- mov WK&next, WK&next, lsr #24 ++ mov WK\()\next, WK\()\next, lsr #24 + .endm + + .macro over_8888_8888_1pixel src, dst, offset, next + /* src = destination component multiplier */ +- rsb WK&src, WK&src, #255 ++ rsb WK\()\src, WK\()\src, #255 + /* Split even/odd bytes of dst into SCRATCH/dst */ +- uxtb16 SCRATCH, WK&dst +- uxtb16 WK&dst, WK&dst, ror #8 ++ uxtb16 SCRATCH, WK\()\dst ++ uxtb16 WK\()\dst, WK\()\dst, ror #8 + /* Multiply through, adding 0.5 to the upper byte of result for rounding */ +- mla SCRATCH, SCRATCH, WK&src, MASK +- mla WK&dst, WK&dst, WK&src, MASK ++ mla SCRATCH, SCRATCH, WK\()\src, MASK ++ mla WK\()\dst, WK\()\dst, WK\()\src, MASK + /* Where we would have had a stall between the result of the first MLA and the shifter input, + * reload the complete source pixel */ +- ldr WK&src, [SRC, #offset] ++ ldr WK\()\src, [SRC, #\offset] + /* Multiply by 257/256 to approximate 256/255 */ + uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 + /* In this stall, start processing the next pixel */ +- .if offset < -4 +- mov WK&next, WK&next, lsr #24 ++ .if \offset < -4 ++ mov WK\()\next, WK\()\next, lsr #24 + .endif +- uxtab16 WK&dst, WK&dst, WK&dst, ror #8 ++ uxtab16 WK\()\dst, WK\()\dst, WK\()\dst, ror #8 + /* Recombine even/odd bytes of multiplied destination */ + mov SCRATCH, SCRATCH, ror #8 +- sel WK&dst, SCRATCH, WK&dst ++ sel WK\()\dst, SCRATCH, WK\()\dst + /* Saturated add of source to multiplied destination */ +- uqadd8 WK&dst, WK&dst, WK&src ++ uqadd8 WK\()\dst, WK\()\dst, WK\()\src + .endm + + .macro over_8888_8888_process_tail cond, numbytes, firstreg + WK4 .req STRIDE_D + WK5 .req STRIDE_S + WK6 .req STRIDE_M + WK7 .req ORIG_W +- over_8888_8888_check_transparent numbytes, %(4+firstreg), %(5+firstreg), %(6+firstreg), %(7+firstreg) ++ over_8888_8888_check_transparent \numbytes, %(4+\firstreg), %(5+\firstreg), %(6+\firstreg), %(7+\firstreg) + beq 10f +- over_8888_8888_prepare %(4+firstreg) +- .set PROCESS_REG, firstreg +- .set PROCESS_OFF, -numbytes +- .rept numbytes / 4 ++ over_8888_8888_prepare %(4+\firstreg) ++ .set PROCESS_REG, \firstreg ++ .set PROCESS_OFF, -\numbytes ++ .rept \numbytes / 4 + over_8888_8888_1pixel %(4+PROCESS_REG), %(0+PROCESS_REG), PROCESS_OFF, %(5+PROCESS_REG) + .set PROCESS_REG, PROCESS_REG+1 + .set PROCESS_OFF, PROCESS_OFF+4 + .endr +- pixst , numbytes, firstreg, DST ++ pixst , \numbytes, \firstreg, DST + 10: + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 + .endm + + generate_composite_function \ +@@ -531,26 +536,26 @@ generate_composite_function \ + * word Register containing 4 bytes + * byte Register containing byte multiplier (bits 8-31 must be 0) + * tmp Scratch register + * half Register containing the constant 0x00800080 + * GE[3:0] bits must contain 0101 + */ + .macro mul_8888_8 word, byte, tmp, half + /* Split even/odd bytes of word apart */ +- uxtb16 tmp, word +- uxtb16 word, word, ror #8 ++ uxtb16 \tmp, \word ++ uxtb16 \word, \word, ror #8 + /* Multiply bytes together with rounding, then by 257/256 */ +- mla tmp, tmp, byte, half +- mla word, word, byte, half /* 1 stall follows */ +- uxtab16 tmp, tmp, tmp, ror #8 /* 1 stall follows */ +- uxtab16 word, word, word, ror #8 ++ mla \tmp, \tmp, \byte, \half ++ mla \word, \word, \byte, \half /* 1 stall follows */ ++ uxtab16 \tmp, \tmp, \tmp, ror #8 /* 1 stall follows */ ++ uxtab16 \word, \word, \word, ror #8 + /* Recombine bytes */ +- mov tmp, tmp, ror #8 +- sel word, tmp, word ++ mov \tmp, \tmp, ror #8 ++ sel \word, \tmp, \word + .endm + + /******************************************************************************/ + + .macro over_8888_n_8888_init + /* Mask is constant */ + ldr MASK, [sp, #ARGS_STACK_OFFSET+8] + /* Hold loop invariant in STRIDE_M */ +@@ -562,51 +567,51 @@ generate_composite_function \ + line_saved_regs Y, STRIDE_D, STRIDE_S, ORIG_W + .endm + + .macro over_8888_n_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req Y + WK5 .req STRIDE_D + WK6 .req STRIDE_S + WK7 .req ORIG_W +- pixld , numbytes, %(4+(firstreg%2)), SRC, unaligned_src +- pixld , numbytes, firstreg, DST, 0 ++ pixld , \numbytes, %(4+(\firstreg%2)), SRC, \unaligned_src ++ pixld , \numbytes, \firstreg, DST, 0 + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 + .endm + + .macro over_8888_n_8888_1pixel src, dst +- mul_8888_8 WK&src, MASK, SCRATCH, STRIDE_M +- sub WK7, WK6, WK&src, lsr #24 +- mul_8888_8 WK&dst, WK7, SCRATCH, STRIDE_M +- uqadd8 WK&dst, WK&dst, WK&src ++ mul_8888_8 WK\()\src, MASK, SCRATCH, STRIDE_M ++ sub WK7, WK6, WK\()\src, lsr #24 ++ mul_8888_8 WK\()\dst, WK7, SCRATCH, STRIDE_M ++ uqadd8 WK\()\dst, WK\()\dst, WK\()\src + .endm + + .macro over_8888_n_8888_process_tail cond, numbytes, firstreg + WK4 .req Y + WK5 .req STRIDE_D + WK6 .req STRIDE_S + WK7 .req ORIG_W +- over_8888_8888_check_transparent numbytes, %(4+(firstreg%2)), %(5+(firstreg%2)), %(6+firstreg), %(7+firstreg) ++ over_8888_8888_check_transparent \numbytes, %(4+(\firstreg%2)), %(5+(\firstreg%2)), %(6+\firstreg), %(7+\firstreg) + beq 10f + mov WK6, #255 +- .set PROCESS_REG, firstreg +- .rept numbytes / 4 +- .if numbytes == 16 && PROCESS_REG == 2 ++ .set PROCESS_REG, \firstreg ++ .rept \numbytes / 4 ++ .if \numbytes == 16 && PROCESS_REG == 2 + /* We're using WK6 and WK7 as temporaries, so half way through + * 4 pixels, reload the second two source pixels but this time + * into WK4 and WK5 */ + ldmdb SRC, {WK4, WK5} + .endif + over_8888_n_8888_1pixel %(4+(PROCESS_REG%2)), %(PROCESS_REG) + .set PROCESS_REG, PROCESS_REG+1 + .endr +- pixst , numbytes, firstreg, DST ++ pixst , \numbytes, \firstreg, DST + 10: + .unreq WK4 + .unreq WK5 + .unreq WK6 + .unreq WK7 + .endm + + generate_composite_function \ +@@ -637,47 +642,47 @@ generate_composite_function \ + ldr STRIDE_D, =0x00800080 + b 1f + .ltorg + 1: + .endm + + .macro over_n_8_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload + WK4 .req STRIDE_M +- pixld , numbytes/4, 4, MASK, unaligned_mask +- pixld , numbytes, firstreg, DST, 0 ++ pixld , \numbytes/4, 4, MASK, \unaligned_mask ++ pixld , \numbytes, \firstreg, DST, 0 + .unreq WK4 + .endm + + .macro over_n_8_8888_1pixel src, dst +- uxtb Y, WK4, ror #src*8 ++ uxtb Y, WK4, ror #\src*8 + /* Trailing part of multiplication of source */ + mla SCRATCH, STRIDE_S, Y, STRIDE_D + mla Y, SRC, Y, STRIDE_D + mov ORIG_W, #255 + uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 + uxtab16 Y, Y, Y, ror #8 + mov SCRATCH, SCRATCH, ror #8 + sub ORIG_W, ORIG_W, Y, lsr #24 + sel Y, SCRATCH, Y + /* Then multiply the destination */ +- mul_8888_8 WK&dst, ORIG_W, SCRATCH, STRIDE_D +- uqadd8 WK&dst, WK&dst, Y ++ mul_8888_8 WK\()\dst, ORIG_W, SCRATCH, STRIDE_D ++ uqadd8 WK\()\dst, WK\()\dst, Y + .endm + + .macro over_n_8_8888_process_tail cond, numbytes, firstreg + WK4 .req STRIDE_M + teq WK4, #0 + beq 10f +- .set PROCESS_REG, firstreg +- .rept numbytes / 4 +- over_n_8_8888_1pixel %(PROCESS_REG-firstreg), %(PROCESS_REG) ++ .set PROCESS_REG, \firstreg ++ .rept \numbytes / 4 ++ over_n_8_8888_1pixel %(PROCESS_REG-\firstreg), %(PROCESS_REG) + .set PROCESS_REG, PROCESS_REG+1 + .endr +- pixst , numbytes, firstreg, DST ++ pixst , \numbytes, \firstreg, DST + 10: + .unreq WK4 + .endm + + generate_composite_function \ + pixman_composite_over_n_8_8888_asm_armv6, 0, 8, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS \ + 2, /* prefetch distance */ \ +@@ -700,64 +705,64 @@ generate_composite_function \ + line_saved_regs STRIDE_D, ORIG_W + .endm + + .macro over_reverse_n_8888_newline + mov STRIDE_D, #0xFF + .endm + + .macro over_reverse_n_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload +- pixld , numbytes, firstreg, DST, 0 ++ pixld , \numbytes, \firstreg, DST, 0 + .endm + + .macro over_reverse_n_8888_1pixel d, is_only +- teq WK&d, #0 ++ teq WK\()\d, #0 + beq 8f /* replace with source */ +- bics ORIG_W, STRIDE_D, WK&d, lsr #24 +- .if is_only == 1 ++ bics ORIG_W, STRIDE_D, WK\()\d, lsr #24 ++ .if \is_only == 1 + beq 49f /* skip store */ + .else + beq 9f /* write same value back */ + .endif + mla SCRATCH, STRIDE_S, ORIG_W, MASK /* red/blue */ + mla ORIG_W, STRIDE_M, ORIG_W, MASK /* alpha/green */ + uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 + uxtab16 ORIG_W, ORIG_W, ORIG_W, ror #8 + mov SCRATCH, SCRATCH, ror #8 + sel ORIG_W, SCRATCH, ORIG_W +- uqadd8 WK&d, WK&d, ORIG_W ++ uqadd8 WK\()\d, WK\()\d, ORIG_W + b 9f +-8: mov WK&d, SRC ++8: mov WK\()\d, SRC + 9: + .endm + + .macro over_reverse_n_8888_tail numbytes, reg1, reg2, reg3, reg4 +- .if numbytes == 4 +- over_reverse_n_8888_1pixel reg1, 1 ++ .if \numbytes == 4 ++ over_reverse_n_8888_1pixel \reg1, 1 + .else +- and SCRATCH, WK®1, WK®2 +- .if numbytes == 16 +- and SCRATCH, SCRATCH, WK®3 +- and SCRATCH, SCRATCH, WK®4 ++ and SCRATCH, WK\()\reg1, WK\()\reg2 ++ .if \numbytes == 16 ++ and SCRATCH, SCRATCH, WK\()\reg3 ++ and SCRATCH, SCRATCH, WK\()\reg4 + .endif + mvns SCRATCH, SCRATCH, asr #24 + beq 49f /* skip store if all opaque */ +- over_reverse_n_8888_1pixel reg1, 0 +- over_reverse_n_8888_1pixel reg2, 0 +- .if numbytes == 16 +- over_reverse_n_8888_1pixel reg3, 0 +- over_reverse_n_8888_1pixel reg4, 0 ++ over_reverse_n_8888_1pixel \reg1, 0 ++ over_reverse_n_8888_1pixel \reg2, 0 ++ .if \numbytes == 16 ++ over_reverse_n_8888_1pixel \reg3, 0 ++ over_reverse_n_8888_1pixel \reg4, 0 + .endif + .endif +- pixst , numbytes, reg1, DST ++ pixst , \numbytes, \reg1, DST + 49: + .endm + + .macro over_reverse_n_8888_process_tail cond, numbytes, firstreg +- over_reverse_n_8888_tail numbytes, firstreg, %(firstreg+1), %(firstreg+2), %(firstreg+3) ++ over_reverse_n_8888_tail \numbytes, \firstreg, %(\firstreg+1), %(\firstreg+2), %(\firstreg+3) + .endm + + generate_composite_function \ + pixman_composite_over_reverse_n_8888_asm_armv6, 0, 0, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS | FLAG_PROCESS_CORRUPTS_SCRATCH, \ + 3, /* prefetch distance */ \ + over_reverse_n_8888_init, \ + over_reverse_n_8888_newline, \ +@@ -789,30 +794,30 @@ generate_composite_function \ + .unreq TMP1 + .unreq TMP2 + .unreq TMP3 + .unreq WK4 + .endm + + .macro over_white_8888_8888_ca_combine m, d + uxtb16 TMP1, TMP0 /* rb_notmask */ +- uxtb16 TMP2, d /* rb_dest; 1 stall follows */ ++ uxtb16 TMP2, \d /* rb_dest; 1 stall follows */ + smlatt TMP3, TMP2, TMP1, HALF /* red */ + smlabb TMP2, TMP2, TMP1, HALF /* blue */ + uxtb16 TMP0, TMP0, ror #8 /* ag_notmask */ +- uxtb16 TMP1, d, ror #8 /* ag_dest; 1 stall follows */ +- smlatt d, TMP1, TMP0, HALF /* alpha */ ++ uxtb16 TMP1, \d, ror #8 /* ag_dest; 1 stall follows */ ++ smlatt \d, TMP1, TMP0, HALF /* alpha */ + smlabb TMP1, TMP1, TMP0, HALF /* green */ + pkhbt TMP0, TMP2, TMP3, lsl #16 /* rb; 1 stall follows */ +- pkhbt TMP1, TMP1, d, lsl #16 /* ag */ ++ pkhbt TMP1, TMP1, \d, lsl #16 /* ag */ + uxtab16 TMP0, TMP0, TMP0, ror #8 + uxtab16 TMP1, TMP1, TMP1, ror #8 + mov TMP0, TMP0, ror #8 +- sel d, TMP0, TMP1 +- uqadd8 d, d, m /* d is a late result */ ++ sel \d, TMP0, TMP1 ++ uqadd8 \d, \d, \m /* d is a late result */ + .endm + + .macro over_white_8888_8888_ca_1pixel_head + pixld , 4, 1, MASK, 0 + pixld , 4, 3, DST, 0 + .endm + + .macro over_white_8888_8888_ca_1pixel_tail +@@ -848,29 +853,29 @@ 02: mvn TMP0, WK2 + movcs WK4, WK2 + b 04f + 03: over_white_8888_8888_ca_combine WK2, WK4 + 04: pixst , 8, 3, DST + 05: + .endm + + .macro over_white_8888_8888_ca_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload +- .if numbytes == 4 ++ .if \numbytes == 4 + over_white_8888_8888_ca_1pixel_head + .else +- .if numbytes == 16 ++ .if \numbytes == 16 + over_white_8888_8888_ca_2pixels_head + over_white_8888_8888_ca_2pixels_tail + .endif + over_white_8888_8888_ca_2pixels_head + .endif + .endm + + .macro over_white_8888_8888_ca_process_tail cond, numbytes, firstreg +- .if numbytes == 4 ++ .if \numbytes == 4 + over_white_8888_8888_ca_1pixel_tail + .else + over_white_8888_8888_ca_2pixels_tail + .endif + .endm + + generate_composite_function \ + pixman_composite_over_white_8888_8888_ca_asm_armv6, 0, 32, 32 \ +@@ -999,33 +1004,33 @@ 20: /* No simplifications possible - + uqadd8 WK0, WK1, WK2 /* followed by 1 stall */ + 30: /* The destination buffer is already in the L1 cache, so + * there's little point in amalgamating writes */ + pixst , 4, 0, DST + 40: + .endm + + .macro over_n_8888_8888_ca_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload +- .rept (numbytes / 4) - 1 ++ .rept (\numbytes / 4) - 1 + over_n_8888_8888_ca_1pixel_head + over_n_8888_8888_ca_1pixel_tail + .endr + over_n_8888_8888_ca_1pixel_head + .endm + + .macro over_n_8888_8888_ca_process_tail cond, numbytes, firstreg + over_n_8888_8888_ca_1pixel_tail + .endm + + pixman_asm_function pixman_composite_over_n_8888_8888_ca_asm_armv6 + ldr ip, [sp] + cmp ip, #-1 + beq pixman_composite_over_white_8888_8888_ca_asm_armv6 + /* else drop through... */ +- .endfunc ++ pixman_end_asm_function + generate_composite_function \ + pixman_composite_over_n_8888_8888_ca_asm_armv6_helper, 0, 32, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS | FLAG_PROCESS_CORRUPTS_SCRATCH | FLAG_PROCESS_CORRUPTS_WK0 \ + 2, /* prefetch distance */ \ + over_n_8888_8888_ca_init, \ + nop_macro, /* newline */ \ + over_n_8888_8888_ca_cleanup, \ + over_n_8888_8888_ca_process_head, \ +@@ -1040,94 +1045,94 @@ generate_composite_function \ + uadd8 SCRATCH, MASK, MASK + /* Offset the source pointer: we only need the alpha bytes */ + add SRC, SRC, #3 + line_saved_regs ORIG_W + .endm + + .macro in_reverse_8888_8888_head numbytes, reg1, reg2, reg3 + ldrb ORIG_W, [SRC], #4 +- .if numbytes >= 8 +- ldrb WK®1, [SRC], #4 +- .if numbytes == 16 +- ldrb WK®2, [SRC], #4 +- ldrb WK®3, [SRC], #4 ++ .if \numbytes >= 8 ++ ldrb WK\()\reg1, [SRC], #4 ++ .if \numbytes == 16 ++ ldrb WK\()\reg2, [SRC], #4 ++ ldrb WK\()\reg3, [SRC], #4 + .endif + .endif +- add DST, DST, #numbytes ++ add DST, DST, #\numbytes + .endm + + .macro in_reverse_8888_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload +- in_reverse_8888_8888_head numbytes, firstreg, %(firstreg+1), %(firstreg+2) ++ in_reverse_8888_8888_head \numbytes, \firstreg, %(\firstreg+1), %(\firstreg+2) + .endm + + .macro in_reverse_8888_8888_1pixel s, d, offset, is_only +- .if is_only != 1 +- movs s, ORIG_W +- .if offset != 0 +- ldrb ORIG_W, [SRC, #offset] ++ .if \is_only != 1 ++ movs \s, ORIG_W ++ .if \offset != 0 ++ ldrb ORIG_W, [SRC, #\offset] + .endif + beq 01f + teq STRIDE_M, #0xFF + beq 02f + .endif +- uxtb16 SCRATCH, d /* rb_dest */ +- uxtb16 d, d, ror #8 /* ag_dest */ +- mla SCRATCH, SCRATCH, s, MASK +- mla d, d, s, MASK ++ uxtb16 SCRATCH, \d /* rb_dest */ ++ uxtb16 \d, \d, ror #8 /* ag_dest */ ++ mla SCRATCH, SCRATCH, \s, MASK ++ mla \d, \d, \s, MASK + uxtab16 SCRATCH, SCRATCH, SCRATCH, ror #8 +- uxtab16 d, d, d, ror #8 ++ uxtab16 \d, \d, \d, ror #8 + mov SCRATCH, SCRATCH, ror #8 +- sel d, SCRATCH, d ++ sel \d, SCRATCH, \d + b 02f +- .if offset == 0 ++ .if \offset == 0 + 48: /* Last mov d,#0 of the set - used as part of shortcut for + * source values all 0 */ + .endif +-01: mov d, #0 ++01: mov \d, #0 + 02: + .endm + + .macro in_reverse_8888_8888_tail numbytes, reg1, reg2, reg3, reg4 +- .if numbytes == 4 ++ .if \numbytes == 4 + teq ORIG_W, ORIG_W, asr #32 +- ldrne WK®1, [DST, #-4] +- .elseif numbytes == 8 +- teq ORIG_W, WK®1 ++ ldrne WK\()\reg1, [DST, #-4] ++ .elseif \numbytes == 8 ++ teq ORIG_W, WK\()\reg1 + teqeq ORIG_W, ORIG_W, asr #32 /* all 0 or all -1? */ +- ldmnedb DST, {WK®1-WK®2} ++ ldmnedb DST, {WK\()\reg1-WK\()\reg2} + .else +- teq ORIG_W, WK®1 +- teqeq ORIG_W, WK®2 +- teqeq ORIG_W, WK®3 ++ teq ORIG_W, WK\()\reg1 ++ teqeq ORIG_W, WK\()\reg2 ++ teqeq ORIG_W, WK\()\reg3 + teqeq ORIG_W, ORIG_W, asr #32 /* all 0 or all -1? */ +- ldmnedb DST, {WK®1-WK®4} ++ ldmnedb DST, {WK\()\reg1-WK\()\reg4} + .endif + cmnne DST, #0 /* clear C if NE */ + bcs 49f /* no writes to dest if source all -1 */ + beq 48f /* set dest to all 0 if source all 0 */ +- .if numbytes == 4 +- in_reverse_8888_8888_1pixel ORIG_W, WK®1, 0, 1 +- str WK®1, [DST, #-4] +- .elseif numbytes == 8 +- in_reverse_8888_8888_1pixel STRIDE_M, WK®1, -4, 0 +- in_reverse_8888_8888_1pixel STRIDE_M, WK®2, 0, 0 +- stmdb DST, {WK®1-WK®2} ++ .if \numbytes == 4 ++ in_reverse_8888_8888_1pixel ORIG_W, WK\()\reg1, 0, 1 ++ str WK\()\reg1, [DST, #-4] ++ .elseif \numbytes == 8 ++ in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg1, -4, 0 ++ in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg2, 0, 0 ++ stmdb DST, {WK\()\reg1-WK\()\reg2} + .else +- in_reverse_8888_8888_1pixel STRIDE_M, WK®1, -12, 0 +- in_reverse_8888_8888_1pixel STRIDE_M, WK®2, -8, 0 +- in_reverse_8888_8888_1pixel STRIDE_M, WK®3, -4, 0 +- in_reverse_8888_8888_1pixel STRIDE_M, WK®4, 0, 0 +- stmdb DST, {WK®1-WK®4} ++ in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg1, -12, 0 ++ in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg2, -8, 0 ++ in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg3, -4, 0 ++ in_reverse_8888_8888_1pixel STRIDE_M, WK\()\reg4, 0, 0 ++ stmdb DST, {WK\()\reg1-WK\()\reg4} + .endif + 49: + .endm + + .macro in_reverse_8888_8888_process_tail cond, numbytes, firstreg +- in_reverse_8888_8888_tail numbytes, firstreg, %(firstreg+1), %(firstreg+2), %(firstreg+3) ++ in_reverse_8888_8888_tail \numbytes, \firstreg, %(\firstreg+1), %(\firstreg+2), %(\firstreg+3) + .endm + + generate_composite_function \ + pixman_composite_in_reverse_8888_8888_asm_armv6, 32, 0, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE | FLAG_SPILL_LINE_VARS | FLAG_PROCESS_CORRUPTS_SCRATCH | FLAG_NO_PRELOAD_DST \ + 2, /* prefetch distance */ \ + in_reverse_8888_8888_init, \ + nop_macro, /* newline */ \ +@@ -1144,31 +1149,31 @@ generate_composite_function \ + /* Hold multiplier for destination in STRIDE_M */ + mov STRIDE_M, #255 + sub STRIDE_M, STRIDE_M, SRC, lsr #24 + /* Set GE[3:0] to 0101 so SEL instructions do what we want */ + uadd8 SCRATCH, MASK, MASK + .endm + + .macro over_n_8888_process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, preload +- pixld , numbytes, firstreg, DST, 0 ++ pixld , \numbytes, \firstreg, DST, 0 + .endm + + .macro over_n_8888_1pixel dst +- mul_8888_8 WK&dst, STRIDE_M, SCRATCH, MASK +- uqadd8 WK&dst, WK&dst, SRC ++ mul_8888_8 WK\()\dst, STRIDE_M, SCRATCH, MASK ++ uqadd8 WK\()\dst, WK\()\dst, SRC + .endm + + .macro over_n_8888_process_tail cond, numbytes, firstreg +- .set PROCESS_REG, firstreg +- .rept numbytes / 4 ++ .set PROCESS_REG, \firstreg ++ .rept \numbytes / 4 + over_n_8888_1pixel %(PROCESS_REG) + .set PROCESS_REG, PROCESS_REG+1 + .endr +- pixst , numbytes, firstreg, DST ++ pixst , \numbytes, \firstreg, DST + .endm + + generate_composite_function \ + pixman_composite_over_n_8888_asm_armv6, 0, 0, 32 \ + FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_DOES_STORE \ + 2, /* prefetch distance */ \ + over_n_8888_init, \ + nop_macro, /* newline */ \ +diff --git a/gfx/cairo/libpixman/src/pixman-arm-simd-asm.h b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.h +--- a/gfx/cairo/libpixman/src/pixman-arm-simd-asm.h ++++ b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.h +@@ -107,88 +107,120 @@ + .set PREFETCH_TYPE_NONE, 0 + .set PREFETCH_TYPE_STANDARD, 1 + + /* + * Definitions of macros for load/store of pixel data. + */ + + .macro pixldst op, cond=al, numbytes, reg0, reg1, reg2, reg3, base, unaligned=0 +- .if numbytes == 16 +- .if unaligned == 1 +- op&r&cond WK®0, [base], #4 +- op&r&cond WK®1, [base], #4 +- op&r&cond WK®2, [base], #4 +- op&r&cond WK®3, [base], #4 ++ .if \numbytes == 16 ++ .if \unaligned == 1 ++ \op\()r\()\cond WK\()\reg0, [\base], #4 ++ \op\()r\()\cond WK\()\reg1, [\base], #4 ++ \op\()r\()\cond WK\()\reg2, [\base], #4 ++ \op\()r\()\cond WK\()\reg3, [\base], #4 + .else +- op&m&cond&ia base!, {WK®0,WK®1,WK®2,WK®3} ++#ifdef __clang__ ++ \op\()mia\()\cond \base!, {WK\()\reg0,WK\()\reg1,WK\()\reg2,WK\()\reg3} ++#else ++ \op\()m\()\cond\()ia \base!, {WK\()\reg0,WK\()\reg1,WK\()\reg2,WK\()\reg3} ++#endif + .endif +- .elseif numbytes == 8 +- .if unaligned == 1 +- op&r&cond WK®0, [base], #4 +- op&r&cond WK®1, [base], #4 ++ .elseif \numbytes == 8 ++ .if \unaligned == 1 ++ \op\()r\()\cond WK\()\reg0, [\base], #4 ++ \op\()r\()\cond WK\()\reg1, [\base], #4 + .else +- op&m&cond&ia base!, {WK®0,WK®1} ++#ifdef __clang__ ++ \op\()mia\()\cond \base!, {WK\()\reg0,WK\()\reg1} ++#else ++ \op\()m\()\cond\()ia \base!, {WK\()\reg0,WK\()\reg1} ++#endif + .endif +- .elseif numbytes == 4 +- op&r&cond WK®0, [base], #4 +- .elseif numbytes == 2 +- op&r&cond&h WK®0, [base], #2 +- .elseif numbytes == 1 +- op&r&cond&b WK®0, [base], #1 ++ .elseif \numbytes == 4 ++ \op\()r\()\cond WK\()\reg0, [\base], #4 ++ .elseif \numbytes == 2 ++#ifdef __clang__ ++ \op\()rh\()\cond WK\()\reg0, [\base], #2 ++#else ++ \op\()r\()\cond\()h WK\()\reg0, [\base], #2 ++#endif ++ .elseif \numbytes == 1 ++#ifdef __clang__ ++ \op\()rb\()\cond WK\()\reg0, [\base], #1 ++#else ++ \op\()r\()\cond\()b WK\()\reg0, [\base], #1 ++#endif + .else +- .error "unsupported size: numbytes" ++ .error "unsupported size: \numbytes" + .endif + .endm + + .macro pixst_baseupdated cond, numbytes, reg0, reg1, reg2, reg3, base +- .if numbytes == 16 +- stm&cond&db base, {WK®0,WK®1,WK®2,WK®3} +- .elseif numbytes == 8 +- stm&cond&db base, {WK®0,WK®1} +- .elseif numbytes == 4 +- str&cond WK®0, [base, #-4] +- .elseif numbytes == 2 +- str&cond&h WK®0, [base, #-2] +- .elseif numbytes == 1 +- str&cond&b WK®0, [base, #-1] ++ .if \numbytes == 16 ++#ifdef __clang__ ++ stm\()\cond\()db \base, {WK\()\reg0,WK\()\reg1,WK\()\reg2,WK\()\reg3} ++#else ++ stmdb\()\cond \base, {WK\()\reg0,WK\()\reg1,WK\()\reg2,WK\()\reg3} ++#endif ++ .elseif \numbytes == 8 ++#ifdef __clang__ ++ stmdb\()\cond \base, {WK\()\reg0,WK\()\reg1} ++#else ++ stm\()\cond\()db \base, {WK\()\reg0,WK\()\reg1} ++#endif ++ .elseif \numbytes == 4 ++ str\()\cond WK\()\reg0, [\base, #-4] ++ .elseif \numbytes == 2 ++#ifdef __clang__ ++ strh\()\cond WK\()\reg0, [\base, #-2] ++#else ++ str\()\cond\()h WK\()\reg0, [\base, #-2] ++#endif ++ .elseif \numbytes == 1 ++#ifdef __clang__ ++ strb\()\cond WK\()\reg0, [\base, #-1] ++#else ++ str\()\cond\()b WK\()\reg0, [\base, #-1] ++#endif + .else +- .error "unsupported size: numbytes" ++ .error "unsupported size: \numbytes" + .endif + .endm + + .macro pixld cond, numbytes, firstreg, base, unaligned +- pixldst ld, cond, numbytes, %(firstreg+0), %(firstreg+1), %(firstreg+2), %(firstreg+3), base, unaligned ++ pixldst ld, \cond, \numbytes, %(\firstreg+0), %(\firstreg+1), %(\firstreg+2), %(\firstreg+3), \base, \unaligned + .endm + + .macro pixst cond, numbytes, firstreg, base + .if (flags) & FLAG_DST_READWRITE +- pixst_baseupdated cond, numbytes, %(firstreg+0), %(firstreg+1), %(firstreg+2), %(firstreg+3), base ++ pixst_baseupdated \cond, \numbytes, %(\firstreg+0), %(\firstreg+1), %(\firstreg+2), %(\firstreg+3), \base + .else +- pixldst st, cond, numbytes, %(firstreg+0), %(firstreg+1), %(firstreg+2), %(firstreg+3), base ++ pixldst st, \cond, \numbytes, %(\firstreg+0), %(\firstreg+1), %(\firstreg+2), %(\firstreg+3), \base + .endif + .endm + + .macro PF a, x:vararg + .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_STANDARD) +- a x ++ \a \x + .endif + .endm + + + .macro preload_leading_step1 bpp, ptr, base + /* If the destination is already 16-byte aligned, then we need to preload + * between 0 and prefetch_distance (inclusive) cache lines ahead so there + * are no gaps when the inner loop starts. + */ +- .if bpp > 0 +- PF bic, ptr, base, #31 ++ .if \bpp > 0 ++ PF bic, \ptr, \base, #31 + .set OFFSET, 0 + .rept prefetch_distance+1 +- PF pld, [ptr, #OFFSET] ++ PF pld, [\ptr, #OFFSET] + .set OFFSET, OFFSET+32 + .endr + .endif + .endm + + .macro preload_leading_step2 bpp, bpp_shift, ptr, base + /* However, if the destination is not 16-byte aligned, we may need to + * preload more cache lines than that. The question we need to ask is: +@@ -196,81 +228,81 @@ + * by which the source pointer will be rounded down for preloading, and if + * so, by how many cache lines? Effectively, we want to calculate + * leading_bytes = ((-dst)&15)*src_bpp/dst_bpp + * inner_loop_offset = (src+leading_bytes)&31 + * extra_needed = leading_bytes - inner_loop_offset + * and test if extra_needed is <= 0, <= 32, or > 32 (where > 32 is only + * possible when there are 4 src bytes for every 1 dst byte). + */ +- .if bpp > 0 +- .ifc base,DST ++ .if \bpp > 0 ++ .ifc \base,DST + /* The test can be simplified further when preloading the destination */ +- PF tst, base, #16 ++ PF tst, \base, #16 + PF beq, 61f + .else +- .if bpp/dst_w_bpp == 4 +- PF add, SCRATCH, base, WK0, lsl #bpp_shift-dst_bpp_shift ++ .if \bpp/dst_w_bpp == 4 ++ PF add, SCRATCH, \base, WK0, lsl #\bpp_shift-dst_bpp_shift + PF and, SCRATCH, SCRATCH, #31 +- PF rsb, SCRATCH, SCRATCH, WK0, lsl #bpp_shift-dst_bpp_shift ++ PF rsb, SCRATCH, SCRATCH, WK0, lsl #\bpp_shift-dst_bpp_shift + PF sub, SCRATCH, SCRATCH, #1 /* so now ranges are -16..-1 / 0..31 / 32..63 */ + PF movs, SCRATCH, SCRATCH, lsl #32-6 /* so this sets NC / nc / Nc */ + PF bcs, 61f + PF bpl, 60f + PF pld, [ptr, #32*(prefetch_distance+2)] + .else +- PF mov, SCRATCH, base, lsl #32-5 +- PF add, SCRATCH, SCRATCH, WK0, lsl #32-5+bpp_shift-dst_bpp_shift +- PF rsbs, SCRATCH, SCRATCH, WK0, lsl #32-5+bpp_shift-dst_bpp_shift ++ PF mov, SCRATCH, \base, lsl #32-5 ++ PF add, SCRATCH, SCRATCH, WK0, lsl #32-5+\bpp_shift-dst_bpp_shift ++ PF rsbs, SCRATCH, SCRATCH, WK0, lsl #32-5+\bpp_shift-dst_bpp_shift + PF bls, 61f + .endif + .endif +-60: PF pld, [ptr, #32*(prefetch_distance+1)] ++60: PF pld, [\ptr, #32*(prefetch_distance+1)] + 61: + .endif + .endm + + #define IS_END_OF_GROUP(INDEX,SIZE) ((SIZE) < 2 || ((INDEX) & ~((INDEX)+1)) & ((SIZE)/2)) + .macro preload_middle bpp, base, scratch_holds_offset +- .if bpp > 0 ++ .if \bpp > 0 + /* prefetch distance = 256/bpp, stm distance = 128/dst_w_bpp */ +- .if IS_END_OF_GROUP(SUBBLOCK,256/128*dst_w_bpp/bpp) +- .if scratch_holds_offset +- PF pld, [base, SCRATCH] ++ .if IS_END_OF_GROUP(SUBBLOCK,256/128*dst_w_bpp/\bpp) ++ .if \scratch_holds_offset ++ PF pld, [\base, SCRATCH] + .else +- PF bic, SCRATCH, base, #31 ++ PF bic, SCRATCH, \base, #31 + PF pld, [SCRATCH, #32*prefetch_distance] + .endif + .endif + .endif + .endm + + .macro preload_trailing bpp, bpp_shift, base +- .if bpp > 0 +- .if bpp*pix_per_block > 256 ++ .if \bpp > 0 ++ .if \bpp*pix_per_block > 256 + /* Calculations are more complex if more than one fetch per block */ +- PF and, WK1, base, #31 +- PF add, WK1, WK1, WK0, lsl #bpp_shift +- PF add, WK1, WK1, #32*(bpp*pix_per_block/256-1)*(prefetch_distance+1) +- PF bic, SCRATCH, base, #31 ++ PF and, WK1, \base, #31 ++ PF add, WK1, WK1, WK0, lsl #\bpp_shift ++ PF add, WK1, WK1, #32*(\bpp*pix_per_block/256-1)*(prefetch_distance+1) ++ PF bic, SCRATCH, \base, #31 + 80: PF pld, [SCRATCH, #32*(prefetch_distance+1)] + PF add, SCRATCH, SCRATCH, #32 + PF subs, WK1, WK1, #32 + PF bhi, 80b + .else + /* If exactly one fetch per block, then we need either 0, 1 or 2 extra preloads */ +- PF mov, SCRATCH, base, lsl #32-5 +- PF adds, SCRATCH, SCRATCH, X, lsl #32-5+bpp_shift ++ PF mov, SCRATCH, \base, lsl #32-5 ++ PF adds, SCRATCH, SCRATCH, X, lsl #32-5+\bpp_shift + PF adceqs, SCRATCH, SCRATCH, #0 + /* The instruction above has two effects: ensures Z is only + * set if C was clear (so Z indicates that both shifted quantities + * were 0), and clears C if Z was set (so C indicates that the sum + * of the shifted quantities was greater and not equal to 32) */ + PF beq, 82f +- PF bic, SCRATCH, base, #31 ++ PF bic, SCRATCH, \base, #31 + PF bcc, 81f + PF pld, [SCRATCH, #32*(prefetch_distance+2)] + 81: PF pld, [SCRATCH, #32*(prefetch_distance+1)] + 82: + .endif + .endif + .endm + +@@ -283,97 +315,97 @@ 82: + * pixels) they cannot possibly straddle more than 2 32-byte cachelines, + * meaning there's no need for a loop. + * "bpp" - number of bits per pixel in the channel (source, mask or + * destination) that's being preloaded, or 0 if this channel is not used + * for reading + * "bpp_shift" - log2 of ("bpp"/8) (except if "bpp"=0 of course) + * "base" - base address register of channel to preload (SRC, MASK or DST) + */ +- .if bpp > 0 +- .if narrow_case && (bpp <= dst_w_bpp) ++ .if \bpp > 0 ++ .if \narrow_case && (\bpp <= dst_w_bpp) + /* In these cases, each line for each channel is in either 1 or 2 cache lines */ +- PF bic, WK0, base, #31 ++ PF bic, WK0, \base, #31 + PF pld, [WK0] +- PF add, WK1, base, X, LSL #bpp_shift ++ PF add, WK1, \base, X, LSL #\bpp_shift + PF sub, WK1, WK1, #1 + PF bic, WK1, WK1, #31 + PF cmp, WK1, WK0 + PF beq, 90f + PF pld, [WK1] + 90: + .else +- PF bic, WK0, base, #31 ++ PF bic, WK0, \base, #31 + PF pld, [WK0] +- PF add, WK1, base, X, lsl #bpp_shift ++ PF add, WK1, \base, X, lsl #\bpp_shift + PF sub, WK1, WK1, #1 + PF bic, WK1, WK1, #31 + PF cmp, WK1, WK0 + PF beq, 92f + 91: PF add, WK0, WK0, #32 + PF cmp, WK0, WK1 + PF pld, [WK0] + PF bne, 91b + 92: + .endif + .endif + .endm + + + .macro conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx +- process_head cond, numbytes, firstreg, unaligned_src, unaligned_mask, 0 +- .if decrementx +- sub&cond X, X, #8*numbytes/dst_w_bpp ++ \process_head \cond, \numbytes, \firstreg, \unaligned_src, \unaligned_mask, 0 ++ .if \decrementx ++ sub\()\cond X, X, #8*\numbytes/dst_w_bpp + .endif +- process_tail cond, numbytes, firstreg ++ \process_tail \cond, \numbytes, \firstreg + .if !((flags) & FLAG_PROCESS_DOES_STORE) +- pixst cond, numbytes, firstreg, DST ++ pixst \cond, \numbytes, \firstreg, DST + .endif + .endm + + .macro conditional_process1 cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx + .if (flags) & FLAG_BRANCH_OVER +- .ifc cond,mi ++ .ifc \cond,mi + bpl 100f + .endif +- .ifc cond,cs ++ .ifc \cond,cs + bcc 100f + .endif +- .ifc cond,ne ++ .ifc \cond,ne + beq 100f + .endif +- conditional_process1_helper , process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx ++ conditional_process1_helper , \process_head, \process_tail, \numbytes, \firstreg, \unaligned_src, \unaligned_mask, \decrementx + 100: + .else +- conditional_process1_helper cond, process_head, process_tail, numbytes, firstreg, unaligned_src, unaligned_mask, decrementx ++ conditional_process1_helper \cond, \process_head, \process_tail, \numbytes, \firstreg, \unaligned_src, \unaligned_mask, \decrementx + .endif + .endm + + .macro conditional_process2 test, cond1, cond2, process_head, process_tail, numbytes1, numbytes2, firstreg1, firstreg2, unaligned_src, unaligned_mask, decrementx + .if (flags) & (FLAG_DST_READWRITE | FLAG_BRANCH_OVER | FLAG_PROCESS_CORRUPTS_PSR | FLAG_PROCESS_DOES_STORE) + /* Can't interleave reads and writes */ +- test +- conditional_process1 cond1, process_head, process_tail, numbytes1, firstreg1, unaligned_src, unaligned_mask, decrementx ++ \test ++ conditional_process1 \cond1, \process_head, \process_tail, \numbytes1, \firstreg1, \unaligned_src, \unaligned_mask, \decrementx + .if (flags) & FLAG_PROCESS_CORRUPTS_PSR +- test ++ \test + .endif +- conditional_process1 cond2, process_head, process_tail, numbytes2, firstreg2, unaligned_src, unaligned_mask, decrementx ++ conditional_process1 \cond2, \process_head, \process_tail, \numbytes2, \firstreg2, \unaligned_src, \unaligned_mask, \decrementx + .else + /* Can interleave reads and writes for better scheduling */ +- test +- process_head cond1, numbytes1, firstreg1, unaligned_src, unaligned_mask, 0 +- process_head cond2, numbytes2, firstreg2, unaligned_src, unaligned_mask, 0 +- .if decrementx +- sub&cond1 X, X, #8*numbytes1/dst_w_bpp +- sub&cond2 X, X, #8*numbytes2/dst_w_bpp ++ \test ++ \process_head \cond1, \numbytes1, \firstreg1, \unaligned_src, \unaligned_mask, 0 ++ \process_head \cond2, \numbytes2, \firstreg2, \unaligned_src, \unaligned_mask, 0 ++ .if \decrementx ++ sub\()\cond1 X, X, #8*\numbytes1/dst_w_bpp ++ sub\()\cond2 X, X, #8*\numbytes2/dst_w_bpp + .endif +- process_tail cond1, numbytes1, firstreg1 +- process_tail cond2, numbytes2, firstreg2 +- pixst cond1, numbytes1, firstreg1, DST +- pixst cond2, numbytes2, firstreg2, DST ++ \process_tail \cond1, \numbytes1, \firstreg1 ++ \process_tail \cond2, \numbytes2, \firstreg2 ++ pixst \cond1, \numbytes1, \firstreg1, DST ++ pixst \cond2, \numbytes2, \firstreg2, DST + .endif + .endm + + + .macro test_bits_1_0_ptr + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + movs SCRATCH, X, lsl #32-1 /* C,N = bits 1,0 of DST */ + .else +@@ -395,22 +427,22 @@ 100: + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + .set DECREMENT_X, 0 + sub X, X, WK0, lsr #dst_bpp_shift + str X, [sp, #LINE_SAVED_REG_COUNT*4] + mov X, WK0 + .endif + /* Use unaligned loads in all cases for simplicity */ + .if dst_w_bpp == 8 +- conditional_process2 test_bits_1_0_ptr, mi, cs, process_head, process_tail, 1, 2, 1, 2, 1, 1, DECREMENT_X ++ conditional_process2 test_bits_1_0_ptr, mi, cs, \process_head, \process_tail, 1, 2, 1, 2, 1, 1, DECREMENT_X + .elseif dst_w_bpp == 16 + test_bits_1_0_ptr +- conditional_process1 cs, process_head, process_tail, 2, 2, 1, 1, DECREMENT_X ++ conditional_process1 cs, \process_head, \process_tail, 2, 2, 1, 1, DECREMENT_X + .endif +- conditional_process2 test_bits_3_2_ptr, mi, cs, process_head, process_tail, 4, 8, 1, 2, 1, 1, DECREMENT_X ++ conditional_process2 test_bits_3_2_ptr, mi, cs, \process_head, \process_tail, 4, 8, 1, 2, 1, 1, DECREMENT_X + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + ldr X, [sp, #LINE_SAVED_REG_COUNT*4] + .endif + .endm + + .macro test_bits_3_2_pix + movs SCRATCH, X, lsl #dst_bpp_shift+32-3 + .endm +@@ -419,169 +451,169 @@ 100: + .if dst_w_bpp == 8 + movs SCRATCH, X, lsl #dst_bpp_shift+32-1 + .else + movs SCRATCH, X, lsr #1 + .endif + .endm + + .macro trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask +- conditional_process2 test_bits_3_2_pix, cs, mi, process_head, process_tail, 8, 4, 0, 2, unaligned_src, unaligned_mask, 0 ++ conditional_process2 test_bits_3_2_pix, cs, mi, \process_head, \process_tail, 8, 4, 0, 2, \unaligned_src, \unaligned_mask, 0 + .if dst_w_bpp == 16 + test_bits_1_0_pix +- conditional_process1 cs, process_head, process_tail, 2, 0, unaligned_src, unaligned_mask, 0 ++ conditional_process1 cs, \process_head, \process_tail, 2, 0, \unaligned_src, \unaligned_mask, 0 + .elseif dst_w_bpp == 8 +- conditional_process2 test_bits_1_0_pix, cs, mi, process_head, process_tail, 2, 1, 0, 1, unaligned_src, unaligned_mask, 0 ++ conditional_process2 test_bits_1_0_pix, cs, mi, \process_head, \process_tail, 2, 1, 0, 1, \unaligned_src, \unaligned_mask, 0 + .endif + .endm + + + .macro wide_case_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, dst_alignment + 110: + .set SUBBLOCK, 0 /* this is a count of STMs; there can be up to 8 STMs per block */ + .rept pix_per_block*dst_w_bpp/128 +- process_head , 16, 0, unaligned_src, unaligned_mask, 1 ++ \process_head , 16, 0, \unaligned_src, \unaligned_mask, 1 + .if (src_bpp > 0) && (mask_bpp == 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) + preload_middle src_bpp, SRC, 1 + .elseif (src_bpp == 0) && (mask_bpp > 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) + preload_middle mask_bpp, MASK, 1 + .else + preload_middle src_bpp, SRC, 0 + preload_middle mask_bpp, MASK, 0 + .endif + .if (dst_r_bpp > 0) && ((SUBBLOCK % 2) == 0) && (((flags) & FLAG_NO_PRELOAD_DST) == 0) + /* Because we know that writes are 16-byte aligned, it's relatively easy to ensure that + * destination prefetches are 32-byte aligned. It's also the easiest channel to offset + * preloads for, to achieve staggered prefetches for multiple channels, because there are + * always two STMs per prefetch, so there is always an opposite STM on which to put the + * preload. Note, no need to BIC the base register here */ +- PF pld, [DST, #32*prefetch_distance - dst_alignment] ++ PF pld, [DST, #32*prefetch_distance - \dst_alignment] + .endif +- process_tail , 16, 0 ++ \process_tail , 16, 0 + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst , 16, 0, DST + .endif + .set SUBBLOCK, SUBBLOCK+1 + .endr + subs X, X, #pix_per_block + bhs 110b + .endm + + .macro wide_case_inner_loop_and_trailing_pixels process_head, process_tail, process_inner_loop, exit_label, unaligned_src, unaligned_mask + /* Destination now 16-byte aligned; we have at least one block before we have to stop preloading */ + .if dst_r_bpp > 0 + tst DST, #16 + bne 111f +- process_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, 16 + DST_PRELOAD_BIAS ++ \process_inner_loop \process_head, \process_tail, \unaligned_src, \unaligned_mask, 16 + DST_PRELOAD_BIAS + b 112f + 111: + .endif +- process_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, 0 + DST_PRELOAD_BIAS ++ \process_inner_loop \process_head, \process_tail, \unaligned_src, \unaligned_mask, 0 + DST_PRELOAD_BIAS + 112: + /* Just before the final (prefetch_distance+1) 32-byte blocks, deal with final preloads */ + .if (src_bpp*pix_per_block > 256) || (mask_bpp*pix_per_block > 256) || (dst_r_bpp*pix_per_block > 256) + PF and, WK0, X, #pix_per_block-1 + .endif + preload_trailing src_bpp, src_bpp_shift, SRC + preload_trailing mask_bpp, mask_bpp_shift, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_trailing dst_r_bpp, dst_bpp_shift, DST + .endif + add X, X, #(prefetch_distance+2)*pix_per_block - 128/dst_w_bpp + /* The remainder of the line is handled identically to the medium case */ +- medium_case_inner_loop_and_trailing_pixels process_head, process_tail,, exit_label, unaligned_src, unaligned_mask ++ medium_case_inner_loop_and_trailing_pixels \process_head, \process_tail,, \exit_label, \unaligned_src, \unaligned_mask + .endm + + .macro medium_case_inner_loop_and_trailing_pixels process_head, process_tail, unused, exit_label, unaligned_src, unaligned_mask + 120: +- process_head , 16, 0, unaligned_src, unaligned_mask, 0 +- process_tail , 16, 0 ++ \process_head , 16, 0, \unaligned_src, \unaligned_mask, 0 ++ \process_tail , 16, 0 + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst , 16, 0, DST + .endif + subs X, X, #128/dst_w_bpp + bhs 120b + /* Trailing pixels */ + tst X, #128/dst_w_bpp - 1 +- beq exit_label +- trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask ++ beq \exit_label ++ trailing_15bytes \process_head, \process_tail, \unaligned_src, \unaligned_mask + .endm + + .macro narrow_case_inner_loop_and_trailing_pixels process_head, process_tail, unused, exit_label, unaligned_src, unaligned_mask + tst X, #16*8/dst_w_bpp +- conditional_process1 ne, process_head, process_tail, 16, 0, unaligned_src, unaligned_mask, 0 ++ conditional_process1 ne, \process_head, \process_tail, 16, 0, \unaligned_src, \unaligned_mask, 0 + /* Trailing pixels */ + /* In narrow case, it's relatively unlikely to be aligned, so let's do without a branch here */ +- trailing_15bytes process_head, process_tail, unaligned_src, unaligned_mask ++ trailing_15bytes \process_head, \process_tail, \unaligned_src, \unaligned_mask + .endm + + .macro switch_on_alignment action, process_head, process_tail, process_inner_loop, exit_label + /* Note that if we're reading the destination, it's already guaranteed to be aligned at this point */ + .if mask_bpp == 8 || mask_bpp == 16 + tst MASK, #3 + bne 141f + .endif + .if src_bpp == 8 || src_bpp == 16 + tst SRC, #3 + bne 140f + .endif +- action process_head, process_tail, process_inner_loop, exit_label, 0, 0 ++ \action \process_head, \process_tail, \process_inner_loop, \exit_label, 0, 0 + .if src_bpp == 8 || src_bpp == 16 +- b exit_label ++ b \exit_label + 140: +- action process_head, process_tail, process_inner_loop, exit_label, 1, 0 ++ \action \process_head, \process_tail, \process_inner_loop, \exit_label, 1, 0 + .endif + .if mask_bpp == 8 || mask_bpp == 16 +- b exit_label ++ b \exit_label + 141: + .if src_bpp == 8 || src_bpp == 16 + tst SRC, #3 + bne 142f + .endif +- action process_head, process_tail, process_inner_loop, exit_label, 0, 1 ++ \action \process_head, \process_tail, \process_inner_loop, \exit_label, 0, 1 + .if src_bpp == 8 || src_bpp == 16 +- b exit_label ++ b \exit_label + 142: +- action process_head, process_tail, process_inner_loop, exit_label, 1, 1 ++ \action \process_head, \process_tail, \process_inner_loop, \exit_label, 1, 1 + .endif + .endif + .endm + + + .macro end_of_line restore_x, vars_spilled, loop_label, last_one +- .if vars_spilled ++ .if \vars_spilled + /* Sadly, GAS doesn't seem have an equivalent of the DCI directive? */ + /* This is ldmia sp,{} */ + .word 0xE89D0000 | LINE_SAVED_REGS + .endif + subs Y, Y, #1 +- .if vars_spilled ++ .if \vars_spilled + .if (LINE_SAVED_REGS) & (1<<1) + str Y, [sp] + .endif + .endif + add DST, DST, STRIDE_D + .if src_bpp > 0 + add SRC, SRC, STRIDE_S + .endif + .if mask_bpp > 0 + add MASK, MASK, STRIDE_M + .endif +- .if restore_x ++ .if \restore_x + mov X, ORIG_W + .endif +- bhs loop_label +- .ifc "last_one","" +- .if vars_spilled ++ bhs \loop_label ++ .ifc "\last_one","" ++ .if \vars_spilled + b 197f + .else + b 198f + .endif + .else +- .if (!vars_spilled) && ((flags) & FLAG_SPILL_LINE_VARS) ++ .if (!\vars_spilled) && ((flags) & FLAG_SPILL_LINE_VARS) + b 198f + .endif + .endif + .endm + + + .macro generate_composite_function fname, \ + src_bpp_, \ +@@ -591,27 +623,27 @@ 142: + prefetch_distance_, \ + init, \ + newline, \ + cleanup, \ + process_head, \ + process_tail, \ + process_inner_loop + +- pixman_asm_function fname ++ pixman_asm_function \fname + + /* + * Make some macro arguments globally visible and accessible + * from other macros + */ +- .set src_bpp, src_bpp_ +- .set mask_bpp, mask_bpp_ +- .set dst_w_bpp, dst_w_bpp_ +- .set flags, flags_ +- .set prefetch_distance, prefetch_distance_ ++ .set src_bpp, \src_bpp_ ++ .set mask_bpp, \mask_bpp_ ++ .set dst_w_bpp, \dst_w_bpp_ ++ .set flags, \flags_ ++ .set prefetch_distance, \prefetch_distance_ + + /* + * Select prefetch type for this function. + */ + .if prefetch_distance == 0 + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE + .else + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_STANDARD +@@ -727,17 +759,17 @@ 142: + .endif + + #ifdef DEBUG_PARAMS + add Y, Y, #1 + stmia sp, {r0-r7,pc} + sub Y, Y, #1 + #endif + +- init ++ \init + + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + /* Reserve a word in which to store X during leading pixels */ + sub sp, sp, #4 + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET+4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET+4 + .endif + +@@ -768,47 +800,47 @@ 142: + mov ORIG_W, X + .if (flags) & FLAG_SPILL_LINE_VARS_WIDE + /* This is stmdb sp!,{} */ + .word 0xE92D0000 | LINE_SAVED_REGS + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET + LINE_SAVED_REG_COUNT*4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET + LINE_SAVED_REG_COUNT*4 + .endif + 151: /* New line */ +- newline ++ \newline + preload_leading_step1 src_bpp, WK1, SRC + preload_leading_step1 mask_bpp, WK2, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_leading_step1 dst_r_bpp, WK3, DST + .endif + + ands WK0, DST, #15 + beq 154f + rsb WK0, WK0, #16 /* number of leading bytes until destination aligned */ + + preload_leading_step2 src_bpp, src_bpp_shift, WK1, SRC + preload_leading_step2 mask_bpp, mask_bpp_shift, WK2, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_leading_step2 dst_r_bpp, dst_bpp_shift, WK3, DST + .endif + +- leading_15bytes process_head, process_tail ++ leading_15bytes \process_head, \process_tail + + 154: /* Destination now 16-byte aligned; we have at least one prefetch on each channel as well as at least one 16-byte output block */ + .if (src_bpp > 0) && (mask_bpp == 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) + and SCRATCH, SRC, #31 + rsb SCRATCH, SCRATCH, #32*prefetch_distance + .elseif (src_bpp == 0) && (mask_bpp > 0) && ((flags) & FLAG_PROCESS_PRESERVES_SCRATCH) + and SCRATCH, MASK, #31 + rsb SCRATCH, SCRATCH, #32*prefetch_distance + .endif +- .ifc "process_inner_loop","" +- switch_on_alignment wide_case_inner_loop_and_trailing_pixels, process_head, process_tail, wide_case_inner_loop, 157f ++ .ifc "\process_inner_loop","" ++ switch_on_alignment wide_case_inner_loop_and_trailing_pixels, \process_head, \process_tail, wide_case_inner_loop, 157f + .else +- switch_on_alignment wide_case_inner_loop_and_trailing_pixels, process_head, process_tail, process_inner_loop, 157f ++ switch_on_alignment wide_case_inner_loop_and_trailing_pixels, \process_head, \process_tail, \process_inner_loop, 157f + .endif + + 157: /* Check for another line */ + end_of_line 1, %((flags) & FLAG_SPILL_LINE_VARS_WIDE), 151b + .if (flags) & FLAG_SPILL_LINE_VARS_WIDE + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET - LINE_SAVED_REG_COUNT*4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET - LINE_SAVED_REG_COUNT*4 + .endif +@@ -820,80 +852,80 @@ 160: /* Medium case */ + mov ORIG_W, X + .if (flags) & FLAG_SPILL_LINE_VARS_NON_WIDE + /* This is stmdb sp!,{} */ + .word 0xE92D0000 | LINE_SAVED_REGS + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET + LINE_SAVED_REG_COUNT*4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET + LINE_SAVED_REG_COUNT*4 + .endif + 161: /* New line */ +- newline ++ \newline + preload_line 0, src_bpp, src_bpp_shift, SRC /* in: X, corrupts: WK0-WK1 */ + preload_line 0, mask_bpp, mask_bpp_shift, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_line 0, dst_r_bpp, dst_bpp_shift, DST + .endif + + sub X, X, #128/dst_w_bpp /* simplifies inner loop termination */ + ands WK0, DST, #15 + beq 164f + rsb WK0, WK0, #16 /* number of leading bytes until destination aligned */ + +- leading_15bytes process_head, process_tail ++ leading_15bytes \process_head, \process_tail + + 164: /* Destination now 16-byte aligned; we have at least one 16-byte output block */ +- switch_on_alignment medium_case_inner_loop_and_trailing_pixels, process_head, process_tail,, 167f ++ switch_on_alignment medium_case_inner_loop_and_trailing_pixels, \process_head, \process_tail,, 167f + + 167: /* Check for another line */ + end_of_line 1, %((flags) & FLAG_SPILL_LINE_VARS_NON_WIDE), 161b + + .ltorg + + 170: /* Narrow case, less than 31 bytes, so no guarantee of at least one 16-byte block */ + .if dst_w_bpp < 32 + mov ORIG_W, X + .endif + .if (flags) & FLAG_SPILL_LINE_VARS_NON_WIDE + /* This is stmdb sp!,{} */ + .word 0xE92D0000 | LINE_SAVED_REGS + .endif + 171: /* New line */ +- newline ++ \newline + preload_line 1, src_bpp, src_bpp_shift, SRC /* in: X, corrupts: WK0-WK1 */ + preload_line 1, mask_bpp, mask_bpp_shift, MASK + .if ((flags) & FLAG_NO_PRELOAD_DST) == 0 + preload_line 1, dst_r_bpp, dst_bpp_shift, DST + .endif + + .if dst_w_bpp == 8 + tst DST, #3 + beq 174f + 172: subs X, X, #1 + blo 177f +- process_head , 1, 0, 1, 1, 0 +- process_tail , 1, 0 ++ \process_head , 1, 0, 1, 1, 0 ++ \process_tail , 1, 0 + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst , 1, 0, DST + .endif + tst DST, #3 + bne 172b + .elseif dst_w_bpp == 16 + tst DST, #2 + beq 174f + subs X, X, #1 + blo 177f +- process_head , 2, 0, 1, 1, 0 +- process_tail , 2, 0 ++ \process_head , 2, 0, 1, 1, 0 ++ \process_tail , 2, 0 + .if !((flags) & FLAG_PROCESS_DOES_STORE) + pixst , 2, 0, DST + .endif + .endif + + 174: /* Destination now 4-byte aligned; we have 0 or more output bytes to go */ +- switch_on_alignment narrow_case_inner_loop_and_trailing_pixels, process_head, process_tail,, 177f ++ switch_on_alignment narrow_case_inner_loop_and_trailing_pixels, \process_head, \process_tail,, 177f + + 177: /* Check for another line */ + end_of_line %(dst_w_bpp < 32), %((flags) & FLAG_SPILL_LINE_VARS_NON_WIDE), 171b, last_one + .if (flags) & FLAG_SPILL_LINE_VARS_NON_WIDE + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET - LINE_SAVED_REG_COUNT*4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET - LINE_SAVED_REG_COUNT*4 + .endif + +@@ -903,17 +935,17 @@ 197: + .endif + 198: + .if (flags) & FLAG_PROCESS_CORRUPTS_WK0 + .set ARGS_STACK_OFFSET, ARGS_STACK_OFFSET-4 + .set LOCALS_STACK_OFFSET, LOCALS_STACK_OFFSET-4 + add sp, sp, #4 + .endif + +- cleanup ++ \cleanup + + #ifdef DEBUG_PARAMS + add sp, sp, #9*4 /* junk the debug copy of arguments */ + #endif + 199: + pop {r4-r11, pc} /* exit */ + + .ltorg +@@ -927,23 +959,23 @@ 199: + .unreq MASK + .unreq STRIDE_M + .unreq WK0 + .unreq WK1 + .unreq WK2 + .unreq WK3 + .unreq SCRATCH + .unreq ORIG_W +- .endfunc ++ pixman_end_asm_function + .endm + + .macro line_saved_regs x:vararg + .set LINE_SAVED_REGS, 0 + .set LINE_SAVED_REG_COUNT, 0 +- .irp SAVED_REG,x ++ .irp SAVED_REG,\x + .ifc "SAVED_REG","Y" + .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<1) + .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 + .endif + .ifc "SAVED_REG","STRIDE_D" + .set LINE_SAVED_REGS, LINE_SAVED_REGS | (1<<3) + .set LINE_SAVED_REG_COUNT, LINE_SAVED_REG_COUNT + 1 + .endif diff --git a/gfx/cairo/pixman-arm64-clang.patch b/gfx/cairo/pixman-arm64-clang.patch new file mode 100644 index 0000000000..f059734531 --- /dev/null +++ b/gfx/cairo/pixman-arm64-clang.patch @@ -0,0 +1,3756 @@ +https://gitlab.freedesktop.org/pixman/pixman/-/merge_requests/71 + +diff --git a/gfx/cairo/libpixman/src/pixman-arm-asm.h b/gfx/cairo/libpixman/src/pixman-arm-asm.h +--- a/gfx/cairo/libpixman/src/pixman-arm-asm.h ++++ b/gfx/cairo/libpixman/src/pixman-arm-asm.h +@@ -21,17 +21,33 @@ + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + * + * Author: Jeff Muizelaar (jeff@infidigm.net) + * + */ + + /* Supplementary macro for setting function attributes */ +-.macro pixman_asm_function fname +- .func fname +- .global fname ++.macro pixman_asm_function_impl fname ++#ifdef ASM_HAVE_FUNC_DIRECTIVE ++ .func \fname ++#endif ++ .global \fname + #ifdef __ELF__ +- .hidden fname +- .type fname, %function ++ .hidden \fname ++ .type \fname, %function + #endif +-fname: ++\fname: + .endm ++ ++.macro pixman_asm_function fname ++#ifdef ASM_LEADING_UNDERSCORE ++ pixman_asm_function_impl _\fname ++#else ++ pixman_asm_function_impl \fname ++#endif ++.endm ++ ++.macro pixman_end_asm_function ++#ifdef ASM_HAVE_FUNC_DIRECTIVE ++ .endfunc ++#endif ++.endm +diff --git a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm-bilinear.S b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm-bilinear.S +--- a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm-bilinear.S ++++ b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm-bilinear.S +@@ -72,219 +72,219 @@ + * format conversion, and interpolation as separate macros which can be used + * as the basic building blocks for constructing bilinear scanline functions. + */ + + .macro bilinear_load_8888 reg1, reg2, tmp + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #2 +- ld1 {®1&.2s}, [TMP1], STRIDE +- ld1 {®2&.2s}, [TMP1] ++ ld1 {\()\reg1\().2s}, [TMP1], STRIDE ++ ld1 {\()\reg2\().2s}, [TMP1] + .endm + + .macro bilinear_load_0565 reg1, reg2, tmp + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 +- ld1 {®2&.s}[0], [TMP1], STRIDE +- ld1 {®2&.s}[1], [TMP1] +- convert_four_0565_to_x888_packed reg2, reg1, reg2, tmp ++ ld1 {\()\reg2\().s}[0], [TMP1], STRIDE ++ ld1 {\()\reg2\().s}[1], [TMP1] ++ convert_four_0565_to_x888_packed \reg2, \reg1, \reg2, \tmp + .endm + + .macro bilinear_load_and_vertical_interpolate_two_8888 \ + acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 + +- bilinear_load_8888 reg1, reg2, tmp1 +- umull &acc1&.8h, ®1&.8b, v28.8b +- umlal &acc1&.8h, ®2&.8b, v29.8b +- bilinear_load_8888 reg3, reg4, tmp2 +- umull &acc2&.8h, ®3&.8b, v28.8b +- umlal &acc2&.8h, ®4&.8b, v29.8b ++ bilinear_load_8888 \reg1, \reg2, \tmp1 ++ umull \()\acc1\().8h, \()\reg1\().8b, v28.8b ++ umlal \()\acc1\().8h, \()\reg2\().8b, v29.8b ++ bilinear_load_8888 \reg3, \reg4, \tmp2 ++ umull \()\acc2\().8h, \()\reg3\().8b, v28.8b ++ umlal \()\acc2\().8h, \()\reg4\().8b, v29.8b + .endm + + .macro bilinear_load_and_vertical_interpolate_four_8888 \ +- xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ ++ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi, \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + bilinear_load_and_vertical_interpolate_two_8888 \ +- xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi ++ \xacc1, \xacc2, \xreg1, \xreg2, \xreg3, \xreg4, \xacc2lo, xacc2hi + bilinear_load_and_vertical_interpolate_two_8888 \ +- yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi ++ \yacc1, \yacc2, \yreg1, \yreg2, \yreg3, \yreg4, \yacc2lo, \yacc2hi + .endm + + .macro vzip reg1, reg2 +- zip1 v24.8b, reg1, reg2 +- zip2 reg2, reg1, reg2 +- mov reg1, v24.8b ++ zip1 v24.8b, \reg1, \reg2 ++ zip2 \reg2, \reg1, \reg2 ++ mov \reg1, v24.8b + .endm + + .macro vuzp reg1, reg2 +- uzp1 v24.8b, reg1, reg2 +- uzp2 reg2, reg1, reg2 +- mov reg1, v24.8b ++ uzp1 v24.8b, \reg1, \reg2 ++ uzp2 \reg2, \reg1, \reg2 ++ mov \reg1, v24.8b + .endm + + .macro bilinear_load_and_vertical_interpolate_two_0565 \ + acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr WTMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 +- ld1 {&acc2&.s}[0], [TMP1], STRIDE +- ld1 {&acc2&.s}[2], [TMP2], STRIDE +- ld1 {&acc2&.s}[1], [TMP1] +- ld1 {&acc2&.s}[3], [TMP2] +- convert_0565_to_x888 acc2, reg3, reg2, reg1 +- vzip ®1&.8b, ®3&.8b +- vzip ®2&.8b, ®4&.8b +- vzip ®3&.8b, ®4&.8b +- vzip ®1&.8b, ®2&.8b +- umull &acc1&.8h, ®1&.8b, v28.8b +- umlal &acc1&.8h, ®2&.8b, v29.8b +- umull &acc2&.8h, ®3&.8b, v28.8b +- umlal &acc2&.8h, ®4&.8b, v29.8b ++ ld1 {\()\acc2\().s}[0], [TMP1], STRIDE ++ ld1 {\()\acc2\().s}[2], [TMP2], STRIDE ++ ld1 {\()\acc2\().s}[1], [TMP1] ++ ld1 {\()\acc2\().s}[3], [TMP2] ++ convert_0565_to_x888 \acc2, \reg3, \reg2, \reg1 ++ vzip \()\reg1\().8b, \()\reg3\().8b ++ vzip \()\reg2\().8b, \()\reg4\().8b ++ vzip \()\reg3\().8b, \()\reg4\().8b ++ vzip \()\reg1\().8b, \()\reg2\().8b ++ umull \()\acc1\().8h, \()\reg1\().8b, v28.8b ++ umlal \()\acc1\().8h, \()\reg2\().8b, v29.8b ++ umull \()\acc2\().8h, \()\reg3\().8b, v28.8b ++ umlal \()\acc2\().8h, \()\reg4\().8b, v29.8b + .endm + + .macro bilinear_load_and_vertical_interpolate_four_0565 \ +- xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ ++ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi, \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr WTMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 +- ld1 {&xacc2&.s}[0], [TMP1], STRIDE +- ld1 {&xacc2&.s}[2], [TMP2], STRIDE +- ld1 {&xacc2&.s}[1], [TMP1] +- ld1 {&xacc2&.s}[3], [TMP2] +- convert_0565_to_x888 xacc2, xreg3, xreg2, xreg1 ++ ld1 {\()\xacc2\().s}[0], [TMP1], STRIDE ++ ld1 {\()\xacc2\().s}[2], [TMP2], STRIDE ++ ld1 {\()\xacc2\().s}[1], [TMP1] ++ ld1 {\()\xacc2\().s}[3], [TMP2] ++ convert_0565_to_x888 \xacc2, \xreg3, \xreg2, \xreg1 + asr WTMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr WTMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 +- ld1 {&yacc2&.s}[0], [TMP1], STRIDE +- vzip &xreg1&.8b, &xreg3&.8b +- ld1 {&yacc2&.s}[2], [TMP2], STRIDE +- vzip &xreg2&.8b, &xreg4&.8b +- ld1 {&yacc2&.s}[1], [TMP1] +- vzip &xreg3&.8b, &xreg4&.8b +- ld1 {&yacc2&.s}[3], [TMP2] +- vzip &xreg1&.8b, &xreg2&.8b +- convert_0565_to_x888 yacc2, yreg3, yreg2, yreg1 +- umull &xacc1&.8h, &xreg1&.8b, v28.8b +- vzip &yreg1&.8b, &yreg3&.8b +- umlal &xacc1&.8h, &xreg2&.8b, v29.8b +- vzip &yreg2&.8b, &yreg4&.8b +- umull &xacc2&.8h, &xreg3&.8b, v28.8b +- vzip &yreg3&.8b, &yreg4&.8b +- umlal &xacc2&.8h, &xreg4&.8b, v29.8b +- vzip &yreg1&.8b, &yreg2&.8b +- umull &yacc1&.8h, &yreg1&.8b, v28.8b +- umlal &yacc1&.8h, &yreg2&.8b, v29.8b +- umull &yacc2&.8h, &yreg3&.8b, v28.8b +- umlal &yacc2&.8h, &yreg4&.8b, v29.8b ++ ld1 {\()\yacc2\().s}[0], [TMP1], STRIDE ++ vzip \()\xreg1\().8b, \()\xreg3\().8b ++ ld1 {\()\yacc2\().s}[2], [TMP2], STRIDE ++ vzip \()\xreg2\().8b, \()\xreg4\().8b ++ ld1 {\()\yacc2\().s}[1], [TMP1] ++ vzip \()\xreg3\().8b, \()\xreg4\().8b ++ ld1 {\()\yacc2\().s}[3], [TMP2] ++ vzip \()\xreg1\().8b, \()\xreg2\().8b ++ convert_0565_to_x888 \yacc2, \yreg3, \yreg2, \yreg1 ++ umull \()\xacc1\().8h, \()\xreg1\().8b, v28.8b ++ vzip \()\yreg1\().8b, \()\yreg3\().8b ++ umlal \()\xacc1\().8h, \()\xreg2\().8b, v29.8b ++ vzip \()\yreg2\().8b, \()\yreg4\().8b ++ umull \()\xacc2\().8h, \()\xreg3\().8b, v28.8b ++ vzip \()\yreg3\().8b, \()\yreg4\().8b ++ umlal \()\xacc2\().8h, \()\xreg4\().8b, v29.8b ++ vzip \()\yreg1\().8b, \()\yreg2\().8b ++ umull \()\yacc1\().8h, \()\yreg1\().8b, v28.8b ++ umlal \()\yacc1\().8h, \()\yreg2\().8b, v29.8b ++ umull \()\yacc2\().8h, \()\yreg3\().8b, v28.8b ++ umlal \()\yacc2\().8h, \()\yreg4\().8b, v29.8b + .endm + + .macro bilinear_store_8888 numpix, tmp1, tmp2 +-.if numpix == 4 ++.if \numpix == 4 + st1 {v0.2s, v1.2s}, [OUT], #16 +-.elseif numpix == 2 ++.elseif \numpix == 2 + st1 {v0.2s}, [OUT], #8 +-.elseif numpix == 1 ++.elseif \numpix == 1 + st1 {v0.s}[0], [OUT], #4 + .else +- .error bilinear_store_8888 numpix is unsupported ++ .error bilinear_store_8888 \numpix is unsupported + .endif + .endm + + .macro bilinear_store_0565 numpix, tmp1, tmp2 + vuzp v0.8b, v1.8b + vuzp v2.8b, v3.8b + vuzp v1.8b, v3.8b + vuzp v0.8b, v2.8b +- convert_8888_to_0565 v2, v1, v0, v1, tmp1, tmp2 +-.if numpix == 4 ++ convert_8888_to_0565 v2, v1, v0, v1, \tmp1, \tmp2 ++.if \numpix == 4 + st1 {v1.4h}, [OUT], #8 +-.elseif numpix == 2 ++.elseif \numpix == 2 + st1 {v1.s}[0], [OUT], #4 +-.elseif numpix == 1 ++.elseif \numpix == 1 + st1 {v1.h}[0], [OUT], #2 + .else +- .error bilinear_store_0565 numpix is unsupported ++ .error bilinear_store_0565 \numpix is unsupported + .endif + .endm + + + /* + * Macros for loading mask pixels into register 'mask'. + * dup must be done in somewhere else. + */ + .macro bilinear_load_mask_x numpix, mask + .endm + + .macro bilinear_load_mask_8 numpix, mask +-.if numpix == 4 +- ld1 {&mask&.s}[0], [MASK], #4 +-.elseif numpix == 2 +- ld1 {&mask&.h}[0], [MASK], #2 +-.elseif numpix == 1 +- ld1 {&mask&.b}[0], [MASK], #1 ++.if \numpix == 4 ++ ld1 {\()\mask\().s}[0], [MASK], #4 ++.elseif \numpix == 2 ++ ld1 {\()\mask\().h}[0], [MASK], #2 ++.elseif \numpix == 1 ++ ld1 {\()\mask\().b}[0], [MASK], #1 + .else +- .error bilinear_load_mask_8 numpix is unsupported ++ .error bilinear_load_mask_8 \numpix is unsupported + .endif +- prfm PREFETCH_MODE, [MASK, #prefetch_offset] ++ prfum PREFETCH_MODE, [MASK, #(prefetch_offset)] + .endm + + .macro bilinear_load_mask mask_fmt, numpix, mask +- bilinear_load_mask_&mask_fmt numpix, mask ++ bilinear_load_mask_\mask_fmt \numpix, \mask + .endm + + + /* + * Macros for loading destination pixels into register 'dst0' and 'dst1'. + * Interleave should be done somewhere else. + */ + .macro bilinear_load_dst_0565_src numpix, dst0, dst1, dst01 + .endm + + .macro bilinear_load_dst_8888_src numpix, dst0, dst1, dst01 + .endm + + .macro bilinear_load_dst_8888 numpix, dst0, dst1, dst01 +-.if numpix == 4 +- ld1 {&dst0&.2s, &dst1&.2s}, [OUT] +-.elseif numpix == 2 +- ld1 {&dst0&.2s}, [OUT] +-.elseif numpix == 1 +- ld1 {&dst0&.s}[0], [OUT] ++.if \numpix == 4 ++ ld1 {\()\dst0\().2s, \()\dst1\().2s}, [OUT] ++.elseif \numpix == 2 ++ ld1 {\()\dst0\().2s}, [OUT] ++.elseif \numpix == 1 ++ ld1 {\()\dst0\().s}[0], [OUT] + .else +- .error bilinear_load_dst_8888 numpix is unsupported ++ .error bilinear_load_dst_8888 \numpix is unsupported + .endif +- mov &dst01&.d[0], &dst0&.d[0] +- mov &dst01&.d[1], &dst1&.d[0] ++ mov \()\dst01\().d[0], \()\dst0\().d[0] ++ mov \()\dst01\().d[1], \()\dst1\().d[0] + prfm PREFETCH_MODE, [OUT, #(prefetch_offset * 4)] + .endm + + .macro bilinear_load_dst_8888_over numpix, dst0, dst1, dst01 +- bilinear_load_dst_8888 numpix, dst0, dst1, dst01 ++ bilinear_load_dst_8888 \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_load_dst_8888_add numpix, dst0, dst1, dst01 +- bilinear_load_dst_8888 numpix, dst0, dst1, dst01 ++ bilinear_load_dst_8888 \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_load_dst dst_fmt, op, numpix, dst0, dst1, dst01 +- bilinear_load_dst_&dst_fmt&_&op numpix, dst0, dst1, dst01 ++ bilinear_load_dst_\()\dst_fmt\()_\()\op \numpix, \dst0, \dst1, \dst01 + .endm + + /* + * Macros for duplicating partially loaded mask to fill entire register. + * We will apply mask to interleaved source pixels, that is + * (r0, r1, r2, r3, g0, g1, g2, g3) x (m0, m1, m2, m3, m0, m1, m2, m3) + * (b0, b1, b2, b3, a0, a1, a2, a3) x (m0, m1, m2, m3, m0, m1, m2, m3) + * So, we need to duplicate loaded mask into whole register. +@@ -293,84 +293,85 @@ + * (r0, r1, x, x, g0, g1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) + * (b0, b1, x, x, a0, a1, x, x) x (m0, m1, m0, m1, m0, m1, m0, m1) + * We can do some optimizations for this including last pixel cases. + */ + .macro bilinear_duplicate_mask_x numpix, mask + .endm + + .macro bilinear_duplicate_mask_8 numpix, mask +-.if numpix == 4 +- dup &mask&.2s, &mask&.s[0] +-.elseif numpix == 2 +- dup &mask&.4h, &mask&.h[0] +-.elseif numpix == 1 +- dup &mask&.8b, &mask&.b[0] ++.if \numpix == 4 ++ dup \()\mask\().2s, \()\mask\().s[0] ++.elseif \numpix == 2 ++ dup \()\mask\().4h, \()\mask\().h[0] ++.elseif \numpix == 1 ++ dup \()\mask\().8b, \()\mask\().b[0] + .else +- .error bilinear_duplicate_mask_8 is unsupported ++ .error bilinear_duplicate_\mask_8 is unsupported + .endif + .endm + + .macro bilinear_duplicate_mask mask_fmt, numpix, mask +- bilinear_duplicate_mask_&mask_fmt numpix, mask ++ bilinear_duplicate_mask_\()\mask_fmt \numpix, \mask + .endm + + /* + * Macros for interleaving src and dst pixels to rrrr gggg bbbb aaaa form. + * Interleave should be done when maks is enabled or operator is 'over'. + */ + .macro bilinear_interleave src0, src1, src01, dst0, dst1, dst01 +- vuzp &src0&.8b, &src1&.8b +- vuzp &dst0&.8b, &dst1&.8b +- vuzp &src0&.8b, &src1&.8b +- vuzp &dst0&.8b, &dst1&.8b +- mov &src01&.d[1], &src1&.d[0] +- mov &src01&.d[0], &src0&.d[0] +- mov &dst01&.d[1], &dst1&.d[0] +- mov &dst01&.d[0], &dst0&.d[0] ++ vuzp \()\src0\().8b, \()\src1\().8b ++ vuzp \()\dst0\().8b, \()\dst1\().8b ++ vuzp \()\src0\().8b, \()\src1\().8b ++ vuzp \()\dst0\().8b, \()\dst1\().8b ++ mov \()\src01\().d[1], \()\src1\().d[0] ++ mov \()\src01\().d[0], \()\src0\().d[0] ++ mov \()\dst01\().d[1], \()\dst1\().d[0] ++ mov \()\dst01\().d[0], \()\dst0\().d[0] + .endm + + .macro bilinear_interleave_src_dst_x_src \ + numpix, src0, src1, src01, dst0, dst1, dst01 + .endm + + .macro bilinear_interleave_src_dst_x_over \ + numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave src0, src1, src01, dst0, dst1, dst01 ++ bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_interleave_src_dst_x_add \ + numpix, src0, src1, src01, dst0, dst1, dst01 +- bilinear_interleave src0, src1, src01, dst0, dst1, dst01 ++ ++ bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_interleave_src_dst_8_src \ + numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave src0, src1, src01, dst0, dst1, dst01 ++ bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_interleave_src_dst_8_over \ + numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave src0, src1, src01, dst0, dst1, dst01 ++ bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_interleave_src_dst_8_add \ + numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave src0, src1, src01, dst0, dst1, dst01 ++ bilinear_interleave \src0, \src1, \src01, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_interleave_src_dst \ + mask_fmt, op, numpix, src0, src1, src01, dst0, dst1, dst01 + +- bilinear_interleave_src_dst_&mask_fmt&_&op \ +- numpix, src0, src1, src01, dst0, dst1, dst01 ++ bilinear_interleave_src_dst_\()\mask_fmt\()_\()\op \ ++ \numpix, \src0, \src1, \src01, \dst0, \dst1, \dst01 + .endm + + + /* + * Macros for applying masks to src pixels. (see combine_mask_u() function) + * src, dst should be in interleaved form. + * mask register should be in form (m0, m1, m2, m3). + */ +@@ -378,191 +379,191 @@ + numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + .endm + + .macro bilinear_apply_mask_to_src_8 \ + numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + +- umull &tmp01&.8h, &src0&.8b, &mask&.8b +- umull &tmp23&.8h, &src1&.8b, &mask&.8b ++ umull \()\tmp01\().8h, \()\src0\().8b, \()\mask\().8b ++ umull \()\tmp23\().8h, \()\src1\().8b, \()\mask\().8b + /* bubbles */ +- urshr &tmp45&.8h, &tmp01&.8h, #8 +- urshr &tmp67&.8h, &tmp23&.8h, #8 ++ urshr \()\tmp45\().8h, \()\tmp01\().8h, #8 ++ urshr \()\tmp67\().8h, \()\tmp23\().8h, #8 + /* bubbles */ +- raddhn &src0&.8b, &tmp45&.8h, &tmp01&.8h +- raddhn &src1&.8b, &tmp67&.8h, &tmp23&.8h +- mov &src01&.d[0], &src0&.d[0] +- mov &src01&.d[1], &src1&.d[0] ++ raddhn \()\src0\().8b, \()\tmp45\().8h, \()\tmp01\().8h ++ raddhn \()\src1\().8b, \()\tmp67\().8h, \()\tmp23\().8h ++ mov \()\src01\().d[0], \()\src0\().d[0] ++ mov \()\src01\().d[1], \()\src1\().d[0] + .endm + + .macro bilinear_apply_mask_to_src \ + mask_fmt, numpix, src0, src1, src01, mask, \ + tmp01, tmp23, tmp45, tmp67 + +- bilinear_apply_mask_to_src_&mask_fmt \ +- numpix, src0, src1, src01, mask, \ +- tmp01, tmp23, tmp45, tmp67 ++ bilinear_apply_mask_to_src_\()\mask_fmt \ ++ \numpix, \src0, \src1, \src01, \mask, \ ++ \tmp01, \tmp23, \tmp45, \tmp67 + .endm + + + /* + * Macros for combining src and destination pixels. + * Interleave or not is depending on operator 'op'. + */ + .macro bilinear_combine_src \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + .endm + + .macro bilinear_combine_over \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + +- dup &tmp8&.2s, &src1&.s[1] ++ dup \()\tmp8\().2s, \()\src1\().s[1] + /* bubbles */ +- mvn &tmp8&.8b, &tmp8&.8b ++ mvn \()\tmp8\().8b, \()\tmp8\().8b + /* bubbles */ +- umull &tmp01&.8h, &dst0&.8b, &tmp8&.8b ++ umull \()\tmp01\().8h, \()\dst0\().8b, \()\tmp8\().8b + /* bubbles */ +- umull &tmp23&.8h, &dst1&.8b, &tmp8&.8b ++ umull \()\tmp23\().8h, \()\dst1\().8b, \()\tmp8\().8b + /* bubbles */ +- urshr &tmp45&.8h, &tmp01&.8h, #8 +- urshr &tmp67&.8h, &tmp23&.8h, #8 ++ urshr \()\tmp45\().8h, \()\tmp01\().8h, #8 ++ urshr \()\tmp67\().8h, \()\tmp23\().8h, #8 + /* bubbles */ +- raddhn &dst0&.8b, &tmp45&.8h, &tmp01&.8h +- raddhn &dst1&.8b, &tmp67&.8h, &tmp23&.8h +- mov &dst01&.d[0], &dst0&.d[0] +- mov &dst01&.d[1], &dst1&.d[0] ++ raddhn \()\dst0\().8b, \()\tmp45\().8h, \()\tmp01\().8h ++ raddhn \()\dst1\().8b, \()\tmp67\().8h, \()\tmp23\().8h ++ mov \()\dst01\().d[0], \()\dst0\().d[0] ++ mov \()\dst01\().d[1], \()\dst1\().d[0] + /* bubbles */ +- uqadd &src0&.8b, &dst0&.8b, &src0&.8b +- uqadd &src1&.8b, &dst1&.8b, &src1&.8b +- mov &src01&.d[0], &src0&.d[0] +- mov &src01&.d[1], &src1&.d[0] ++ uqadd \()\src0\().8b, \()\dst0\().8b, \()\src0\().8b ++ uqadd \()\src1\().8b, \()\dst1\().8b, \()\src1\().8b ++ mov \()\src01\().d[0], \()\src0\().d[0] ++ mov \()\src01\().d[1], \()\src1\().d[0] + .endm + + .macro bilinear_combine_add \ + numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + +- uqadd &src0&.8b, &dst0&.8b, &src0&.8b +- uqadd &src1&.8b, &dst1&.8b, &src1&.8b +- mov &src01&.d[0], &src0&.d[0] +- mov &src01&.d[1], &src1&.d[0] ++ uqadd \()\src0\().8b, \()\dst0\().8b, \()\src0\().8b ++ uqadd \()\src1\().8b, \()\dst1\().8b, \()\src1\().8b ++ mov \()\src01\().d[0], \()\src0\().d[0] ++ mov \()\src01\().d[1], \()\src1\().d[0] + .endm + + .macro bilinear_combine \ + op, numpix, src0, src1, src01, dst0, dst1, dst01, \ + tmp01, tmp23, tmp45, tmp67, tmp8 + +- bilinear_combine_&op \ +- numpix, src0, src1, src01, dst0, dst1, dst01, \ +- tmp01, tmp23, tmp45, tmp67, tmp8 ++ bilinear_combine_\()\op \ ++ \numpix, \src0, \src1, \src01, \dst0, \dst1, \dst01, \ ++ \tmp01, \tmp23, \tmp45, \tmp67, \tmp8 + .endm + + /* + * Macros for final deinterleaving of destination pixels if needed. + */ + .macro bilinear_deinterleave numpix, dst0, dst1, dst01 +- vuzp &dst0&.8b, &dst1&.8b ++ vuzp \()\dst0\().8b, \()\dst1\().8b + /* bubbles */ +- vuzp &dst0&.8b, &dst1&.8b +- mov &dst01&.d[0], &dst0&.d[0] +- mov &dst01&.d[1], &dst1&.d[0] ++ vuzp \()\dst0\().8b, \()\dst1\().8b ++ mov \()\dst01\().d[0], \()\dst0\().d[0] ++ mov \()\dst01\().d[1], \()\dst1\().d[0] + .endm + + .macro bilinear_deinterleave_dst_x_src numpix, dst0, dst1, dst01 + .endm + + .macro bilinear_deinterleave_dst_x_over numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst_x_add numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst_8_src numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst_8_over numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst_8_add numpix, dst0, dst1, dst01 +- bilinear_deinterleave numpix, dst0, dst1, dst01 ++ bilinear_deinterleave \numpix, \dst0, \dst1, \dst01 + .endm + + .macro bilinear_deinterleave_dst mask_fmt, op, numpix, dst0, dst1, dst01 +- bilinear_deinterleave_dst_&mask_fmt&_&op numpix, dst0, dst1, dst01 ++ bilinear_deinterleave_dst_\()\mask_fmt\()_\()\op \numpix, \dst0, \dst1, \dst01 + .endm + + + .macro bilinear_interpolate_last_pixel src_fmt, mask_fmt, dst_fmt, op +- bilinear_load_&src_fmt v0, v1, v2 +- bilinear_load_mask mask_fmt, 1, v4 +- bilinear_load_dst dst_fmt, op, 1, v18, v19, v9 ++ bilinear_load_\()\src_fmt v0, v1, v2 ++ bilinear_load_mask \mask_fmt, 1, v4 ++ bilinear_load_dst \dst_fmt, \op, 1, v18, v19, v9 + umull v2.8h, v0.8b, v28.8b + umlal v2.8h, v1.8b, v29.8b + /* 5 cycles bubble */ + ushll v0.4s, v2.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v2.4h, v15.h[0] + umlal2 v0.4s, v2.8h, v15.h[0] + /* 5 cycles bubble */ +- bilinear_duplicate_mask mask_fmt, 1, v4 ++ bilinear_duplicate_mask \mask_fmt, 1, v4 + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + /* 3 cycles bubble */ + xtn v0.8b, v0.8h + /* 1 cycle bubble */ + bilinear_interleave_src_dst \ +- mask_fmt, op, 1, v0, v1, v0, v18, v19, v9 ++ \mask_fmt, \op, 1, v0, v1, v0, v18, v19, v9 + bilinear_apply_mask_to_src \ +- mask_fmt, 1, v0, v1, v0, v4, \ ++ \mask_fmt, 1, v0, v1, v0, v4, \ + v3, v8, v10, v11 + bilinear_combine \ +- op, 1, v0, v1, v0, v18, v19, v9, \ ++ \op, 1, v0, v1, v0, v18, v19, v9, \ + v3, v8, v10, v11, v5 +- bilinear_deinterleave_dst mask_fmt, op, 1, v0, v1, v0 +- bilinear_store_&dst_fmt 1, v17, v18 ++ bilinear_deinterleave_dst \mask_fmt, \op, 1, v0, v1, v0 ++ bilinear_store_\()\dst_fmt 1, v17, v18 + .endm + + .macro bilinear_interpolate_two_pixels src_fmt, mask_fmt, dst_fmt, op +- bilinear_load_and_vertical_interpolate_two_&src_fmt \ ++ bilinear_load_and_vertical_interpolate_two_\()\src_fmt \ + v1, v11, v18, v19, v20, v21, v22, v23 +- bilinear_load_mask mask_fmt, 2, v4 +- bilinear_load_dst dst_fmt, op, 2, v18, v19, v9 ++ bilinear_load_mask \mask_fmt, 2, v4 ++ bilinear_load_dst \dst_fmt, \op, 2, v18, v19, v9 + ushll v0.4s, v1.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v1.4h, v15.h[0] + umlal2 v0.4s, v1.8h, v15.h[0] + ushll v10.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v10.4s, v11.4h, v15.h[4] + umlal2 v10.4s, v11.8h, v15.h[4] + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v10.4s, #(2 * BILINEAR_INTERPOLATION_BITS) +- bilinear_duplicate_mask mask_fmt, 2, v4 ++ bilinear_duplicate_mask \mask_fmt, 2, v4 + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + xtn v0.8b, v0.8h + bilinear_interleave_src_dst \ +- mask_fmt, op, 2, v0, v1, v0, v18, v19, v9 ++ \mask_fmt, \op, 2, v0, v1, v0, v18, v19, v9 + bilinear_apply_mask_to_src \ +- mask_fmt, 2, v0, v1, v0, v4, \ ++ \mask_fmt, 2, v0, v1, v0, v4, \ + v3, v8, v10, v11 + bilinear_combine \ +- op, 2, v0, v1, v0, v18, v19, v9, \ ++ \op, 2, v0, v1, v0, v18, v19, v9, \ + v3, v8, v10, v11, v5 +- bilinear_deinterleave_dst mask_fmt, op, 2, v0, v1, v0 +- bilinear_store_&dst_fmt 2, v16, v17 ++ bilinear_deinterleave_dst \mask_fmt, \op, 2, v0, v1, v0 ++ bilinear_store_\()\dst_fmt 2, v16, v17 + .endm + + .macro bilinear_interpolate_four_pixels src_fmt, mask_fmt, dst_fmt, op +- bilinear_load_and_vertical_interpolate_four_&src_fmt \ +- v1, v11, v4, v5, v6, v7, v22, v23 \ ++ bilinear_load_and_vertical_interpolate_four_\()\src_fmt \ ++ v1, v11, v4, v5, v6, v7, v22, v23, \ + v3, v9, v16, v17, v20, v21, v18, v19 + prfm PREFETCH_MODE, [TMP1, PF_OFFS] + sub TMP1, TMP1, STRIDE + prfm PREFETCH_MODE, [TMP1, PF_OFFS] + ushll v0.4s, v1.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v1.4h, v15.h[0] + umlal2 v0.4s, v1.8h, v15.h[0] + ushll v10.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS +@@ -575,33 +576,33 @@ + ushll v8.4s, v9.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v8.4s, v9.4h, v15.h[4] + umlal2 v8.4s, v9.8h, v15.h[4] + add v12.8h, v12.8h, v13.8h + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v10.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn v2.4h, v2.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v2.8h, v8.4s, #(2 * BILINEAR_INTERPOLATION_BITS) +- bilinear_load_mask mask_fmt, 4, v4 +- bilinear_duplicate_mask mask_fmt, 4, v4 ++ bilinear_load_mask \mask_fmt, 4, v4 ++ bilinear_duplicate_mask \mask_fmt, 4, v4 + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + xtn v0.8b, v0.8h + xtn v1.8b, v2.8h + add v12.8h, v12.8h, v13.8h +- bilinear_load_dst dst_fmt, op, 4, v2, v3, v21 ++ bilinear_load_dst \dst_fmt, \op, 4, v2, v3, v21 + bilinear_interleave_src_dst \ +- mask_fmt, op, 4, v0, v1, v0, v2, v3, v11 ++ \mask_fmt, \op, 4, v0, v1, v0, v2, v3, v11 + bilinear_apply_mask_to_src \ +- mask_fmt, 4, v0, v1, v0, v4, \ ++ \mask_fmt, 4, v0, v1, v0, v4, \ + v6, v8, v9, v10 + bilinear_combine \ +- op, 4, v0, v1, v0, v2, v3, v1, \ ++ \op, 4, v0, v1, v0, v2, v3, v1, \ + v6, v8, v9, v10, v23 +- bilinear_deinterleave_dst mask_fmt, op, 4, v0, v1, v0 +- bilinear_store_&dst_fmt 4, v6, v7 ++ bilinear_deinterleave_dst \mask_fmt, \op, 4, v0, v1, v0 ++ bilinear_store_\()\dst_fmt 4, v6, v7 + .endm + + .set BILINEAR_FLAG_USE_MASK, 1 + .set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 + + /* + * Main template macro for generating NEON optimized bilinear scanline functions. + * +@@ -631,24 +632,24 @@ + bilinear_process_four_pixels, \ + bilinear_process_pixblock_head, \ + bilinear_process_pixblock_tail, \ + bilinear_process_pixblock_tail_head, \ + pixblock_size, \ + prefetch_distance, \ + flags + +-pixman_asm_function fname +-.if pixblock_size == 8 +-.elseif pixblock_size == 4 ++pixman_asm_function \fname ++.if \pixblock_size == 8 ++.elseif \pixblock_size == 4 + .else + .error unsupported pixblock size + .endif + +-.if ((flags) & BILINEAR_FLAG_USE_MASK) == 0 ++.if ((\flags) & BILINEAR_FLAG_USE_MASK) == 0 + OUT .req x0 + TOP .req x1 + BOTTOM .req x2 + WT .req x3 + WWT .req w3 + WB .req x4 + WWB .req w4 + X .req w5 +@@ -694,32 +695,32 @@ pixman_asm_function fname + PF_OFFS .req x12 + TMP3 .req x13 + WTMP3 .req w13 + TMP4 .req x14 + WTMP4 .req w14 + STRIDE .req x15 + DUMMY .req x30 + +- .set prefetch_offset, prefetch_distance ++ .set prefetch_offset, \prefetch_distance + + stp x29, x30, [sp, -16]! + mov x29, sp + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + stp x10, x11, [x29, -80] + stp x12, x13, [x29, -96] + stp x14, x15, [x29, -112] + str x8, [x29, -120] + ldr w8, [x29, 16] + sub sp, sp, 120 + .endif + +- mov WTMP1, #prefetch_distance ++ mov WTMP1, #\prefetch_distance + umull PF_OFFS, WTMP1, UX + + sub STRIDE, BOTTOM, TOP + .unreq BOTTOM + + cmp WIDTH, #0 + ble 300f + +@@ -730,73 +731,73 @@ pixman_asm_function fname + mov v25.d[0], v12.d[1] + mov v26.d[0], v13.d[0] + add v25.4h, v25.4h, v26.4h + mov v12.d[1], v25.d[0] + + /* ensure good destination alignment */ + cmp WIDTH, #1 + blt 100f +- tst OUT, #(1 << dst_bpp_shift) ++ tst OUT, #(1 << \dst_bpp_shift) + beq 100f + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h +- bilinear_process_last_pixel ++ \bilinear_process_last_pixel + sub WIDTH, WIDTH, #1 + 100: + add v13.8h, v13.8h, v13.8h + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + + cmp WIDTH, #2 + blt 100f +- tst OUT, #(1 << (dst_bpp_shift + 1)) ++ tst OUT, #(1 << (\dst_bpp_shift + 1)) + beq 100f +- bilinear_process_two_pixels ++ \bilinear_process_two_pixels + sub WIDTH, WIDTH, #2 + 100: +-.if pixblock_size == 8 ++.if \pixblock_size == 8 + cmp WIDTH, #4 + blt 100f +- tst OUT, #(1 << (dst_bpp_shift + 2)) ++ tst OUT, #(1 << (\dst_bpp_shift + 2)) + beq 100f +- bilinear_process_four_pixels ++ \bilinear_process_four_pixels + sub WIDTH, WIDTH, #4 + 100: + .endif +- subs WIDTH, WIDTH, #pixblock_size ++ subs WIDTH, WIDTH, #\pixblock_size + blt 100f +- asr PF_OFFS, PF_OFFS, #(16 - src_bpp_shift) +- bilinear_process_pixblock_head +- subs WIDTH, WIDTH, #pixblock_size ++ asr PF_OFFS, PF_OFFS, #(16 - \src_bpp_shift) ++ \bilinear_process_pixblock_head ++ subs WIDTH, WIDTH, #\pixblock_size + blt 500f + 0: +- bilinear_process_pixblock_tail_head +- subs WIDTH, WIDTH, #pixblock_size ++ \bilinear_process_pixblock_tail_head ++ subs WIDTH, WIDTH, #\pixblock_size + bge 0b + 500: +- bilinear_process_pixblock_tail ++ \bilinear_process_pixblock_tail + 100: +-.if pixblock_size == 8 ++.if \pixblock_size == 8 + tst WIDTH, #4 + beq 200f +- bilinear_process_four_pixels ++ \bilinear_process_four_pixels + 200: + .endif + /* handle the remaining trailing pixels */ + tst WIDTH, #2 + beq 200f +- bilinear_process_two_pixels ++ \bilinear_process_two_pixels + 200: + tst WIDTH, #1 + beq 300f +- bilinear_process_last_pixel ++ \bilinear_process_last_pixel + 300: + +-.if ((flags) & BILINEAR_FLAG_USE_MASK) == 0 ++.if ((\flags) & BILINEAR_FLAG_USE_MASK) == 0 + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x10, x11, [x29, -80] + ldp x12, x13, [x29, -96] + ldp x14, x15, [x29, -112] + mov sp, x29 + ldp x29, x30, [sp], 16 +@@ -824,21 +825,21 @@ 300: + .unreq WIDTH + .unreq TMP1 + .unreq WTMP1 + .unreq TMP2 + .unreq PF_OFFS + .unreq TMP3 + .unreq TMP4 + .unreq STRIDE +-.if ((flags) & BILINEAR_FLAG_USE_MASK) != 0 ++.if ((\flags) & BILINEAR_FLAG_USE_MASK) != 0 + .unreq MASK + .endif + +-.endfunc ++pixman_end_asm_function + + .endm + + /* src_8888_8_8888 */ + .macro bilinear_src_8888_8_8888_process_last_pixel + bilinear_interpolate_last_pixel 8888, 8, 8888, src + .endm + +diff --git a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S +--- a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S ++++ b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.S +@@ -262,64 +262,64 @@ + uqadd v18.8b, v0.8b, v22.8b + uqadd v19.8b, v1.8b, v23.8b + shrn v6.8b, v4.8h, #8 + fetch_src_pixblock + shrn v7.8b, v4.8h, #3 + sli v4.8h, v4.8h, #5 + ushll v14.8h, v17.8b, #7 + sli v14.8h, v14.8h, #1 +- PF add PF_X, PF_X, #8 ++ PF add, PF_X, PF_X, #8 + ushll v8.8h, v19.8b, #7 + sli v8.8h, v8.8h, #1 +- PF tst PF_CTL, #0xF ++ PF tst, PF_CTL, #0xF + sri v6.8b, v6.8b, #5 +- PF beq 10f +- PF add PF_X, PF_X, #8 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 + 10: + mvn v3.8b, v3.8b +- PF beq 10f +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + sri v7.8b, v7.8b, #6 + shrn v30.8b, v4.8h, #2 + umull v10.8h, v3.8b, v6.8b +- PF lsl DUMMY, PF_X, #src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF lsl, DUMMY, PF_X, #src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + umull v11.8h, v3.8b, v7.8b + umull v12.8h, v3.8b, v30.8b +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + sri v14.8h, v8.8h, #5 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + ushll v9.8h, v18.8b, #7 + sli v9.8h, v9.8h, #1 + urshr v17.8h, v10.8h, #8 +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W + 10: + urshr v19.8h, v11.8h, #8 + urshr v18.8h, v12.8h, #8 +- PF ble 10f +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +- PF ble 10f +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 + 10: + raddhn v20.8b, v10.8h, v17.8h + raddhn v23.8b, v11.8h, v19.8h +- PF ble 10f +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_SRC, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_SRC, #1 + 10: + raddhn v22.8b, v12.8h, v18.8h + st1 {v14.8h}, [DST_W], #16 + .endm + + #else + + /* If we did not care much about the performance, we would just use this... */ +@@ -469,42 +469,42 @@ generate_composite_function \ + sri v14.8h, v8.8h, #5 + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] + .endm + + .macro pixman_composite_src_8888_0565_process_pixblock_tail_head + sri v14.8h, v8.8h, #5 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + fetch_src_pixblock +- PF beq 10f +- PF add PF_X, PF_X, #8 +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + sri v14.8h, v9.8h, #11 + mov v28.d[0], v14.d[0] + mov v29.d[0], v14.d[1] +- PF cmp PF_X, ORIG_W +- PF lsl DUMMY, PF_X, #src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF cmp, PF_X, ORIG_W ++ PF lsl, DUMMY, PF_X, #src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + ushll v8.8h, v1.8b, #7 + sli v8.8h, v8.8h, #1 + st1 {v14.8h}, [DST_W], #16 +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + ushll v14.8h, v2.8b, #7 + sli v14.8h, v14.8h, #1 +- PF ble 10f +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 + 10: + ushll v9.8h, v0.8b, #7 + sli v9.8h, v9.8h, #1 + .endm + + generate_composite_function \ + pixman_composite_src_8888_0565_asm_neon, 32, 0, 16, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ +@@ -561,41 +561,41 @@ generate_composite_function \ + uqadd v31.8b, v3.8b, v7.8b + .endm + + .macro pixman_composite_add_8_8_process_pixblock_tail + .endm + + .macro pixman_composite_add_8_8_process_pixblock_tail_head + fetch_src_pixblock +- PF add PF_X, PF_X, #32 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #32 ++ PF tst, PF_CTL, #0xF + ld1 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 +- PF beq 10f +- PF add PF_X, PF_X, #32 +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #32 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +- PF cmp PF_X, ORIG_W +- PF lsl DUMMY, PF_X, #src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF cmp, PF_X, ORIG_W ++ PF lsl, DUMMY, PF_X, #src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + uqadd v28.8b, v0.8b, v4.8b +- PF ble 10f +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_DST, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_DST, #1 + 10: + uqadd v29.8b, v1.8b, v5.8b + uqadd v30.8b, v2.8b, v6.8b + uqadd v31.8b, v3.8b, v7.8b + .endm + + generate_composite_function \ + pixman_composite_add_8_8_asm_neon, 8, 0, 8, \ +@@ -607,41 +607,41 @@ generate_composite_function \ + pixman_composite_add_8_8_process_pixblock_head, \ + pixman_composite_add_8_8_process_pixblock_tail, \ + pixman_composite_add_8_8_process_pixblock_tail_head + + /******************************************************************************/ + + .macro pixman_composite_add_8888_8888_process_pixblock_tail_head + fetch_src_pixblock +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + ld1 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 +- PF beq 10f +- PF add PF_X, PF_X, #8 +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +- PF cmp PF_X, ORIG_W +- PF lsl DUMMY, PF_X, #src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF cmp, PF_X, ORIG_W ++ PF lsl, DUMMY, PF_X, #src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + uqadd v28.8b, v0.8b, v4.8b +- PF ble 10f +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_DST, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_DST, #1 + 10: + uqadd v29.8b, v1.8b, v5.8b + uqadd v30.8b, v2.8b, v6.8b + uqadd v31.8b, v3.8b, v7.8b + .endm + + generate_composite_function \ + pixman_composite_add_8888_8888_asm_neon, 32, 0, 32, \ +@@ -684,55 +684,55 @@ generate_composite_function_single_scanl + raddhn v29.8b, v15.8h, v9.8h + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h + .endm + + .macro pixman_composite_out_reverse_8888_8888_process_pixblock_tail_head + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + urshr v14.8h, v8.8h, #8 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + urshr v15.8h, v9.8h, #8 + urshr v16.8h, v10.8h, #8 + urshr v17.8h, v11.8h, #8 +- PF beq 10f +- PF add PF_X, PF_X, #8 +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h + fetch_src_pixblock +- PF lsl DUMMY, PF_X, #src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF lsl, DUMMY, PF_X, #src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + mvn v22.8b, v3.8b +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W + 10: + umull v8.8h, v22.8b, v4.8b +- PF ble 10f +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + umull v9.8h, v22.8b, v5.8b +- PF ble 10f +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 + 10: + umull v10.8h, v22.8b, v6.8b +- PF ble 10f +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_DST, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_DST, #1 + 10: + umull v11.8h, v22.8b, v7.8b + .endm + + generate_composite_function_single_scanline \ + pixman_composite_scanline_out_reverse_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ +@@ -754,59 +754,59 @@ generate_composite_function_single_scanl + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + .endm + + .macro pixman_composite_over_8888_8888_process_pixblock_tail_head + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + urshr v14.8h, v8.8h, #8 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + urshr v15.8h, v9.8h, #8 + urshr v16.8h, v10.8h, #8 + urshr v17.8h, v11.8h, #8 +- PF beq 10f +- PF add PF_X, PF_X, #8 +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + fetch_src_pixblock +- PF lsl DUMMY, PF_X, #src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF lsl, DUMMY, PF_X, #src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + mvn v22.8b, v3.8b +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W + 10: + umull v8.8h, v22.8b, v4.8b +- PF ble 10f +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + umull v9.8h, v22.8b, v5.8b +- PF ble 10f +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 + 10: + umull v10.8h, v22.8b, v6.8b +- PF ble 10f +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_DST, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_DST, #1 + 10: + umull v11.8h, v22.8b, v7.8b + .endm + + generate_composite_function \ + pixman_composite_over_8888_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ +@@ -860,40 +860,40 @@ generate_composite_function_single_scanl + urshr v16.8h, v10.8h, #8 + urshr v17.8h, v11.8h, #8 + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h + raddhn v30.8b, v16.8h, v10.8h + raddhn v31.8b, v17.8h, v11.8h + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + uqadd v28.8b, v0.8b, v28.8b +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0x0F +- PF beq 10f +- PF add PF_X, PF_X, #8 +- PF sub PF_CTL, PF_CTL, #1 ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0x0F ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + umull v8.8h, v24.8b, v4.8b +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + umull v9.8h, v24.8b, v5.8b +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W + 10: + umull v10.8h, v24.8b, v6.8b +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF subs, PF_CTL, PF_CTL, #0x10 + umull v11.8h, v24.8b, v7.8b +- PF ble 10f +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_DST, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_DST, #1 + 10: + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + .endm + + .macro pixman_composite_over_n_8888_init + mov v3.s[0], w4 + dup v0.8b, v3.b[0] + dup v1.8b, v3.b[1] +@@ -912,52 +912,52 @@ generate_composite_function \ + pixman_composite_over_8888_8888_process_pixblock_head, \ + pixman_composite_over_8888_8888_process_pixblock_tail, \ + pixman_composite_over_n_8888_process_pixblock_tail_head + + /******************************************************************************/ + + .macro pixman_composite_over_reverse_n_8888_process_pixblock_tail_head + urshr v14.8h, v8.8h, #8 +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF + urshr v15.8h, v9.8h, #8 + urshr v12.8h, v10.8h, #8 + urshr v13.8h, v11.8h, #8 +- PF beq 10f +- PF add PF_X, PF_X, #8 +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + raddhn v28.8b, v14.8h, v8.8h + raddhn v29.8b, v15.8h, v9.8h +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + raddhn v30.8b, v12.8h, v10.8h + raddhn v31.8b, v13.8h, v11.8h + uqadd v28.8b, v0.8b, v28.8b + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + ld4 {v0.8b, v1.8b, v2.8b, v3.8b}, [DST_R], #32 + mvn v22.8b, v3.8b +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +- PF blt 10f +- PF sub PF_X, PF_X, ORIG_W ++ PF blt, 10f ++ PF sub, PF_X, PF_X, ORIG_W + 10: + umull v8.8h, v22.8b, v4.8b +- PF blt 10f +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF blt, 10f ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + umull v9.8h, v22.8b, v5.8b + umull v10.8h, v22.8b, v6.8b +- PF blt 10f +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_DST, #1 ++ PF blt, 10f ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_DST, #1 + 10: + umull v11.8h, v22.8b, v7.8b + .endm + + .macro pixman_composite_over_reverse_n_8888_init + mov v7.s[0], w4 + dup v4.8b, v7.b[0] + dup v5.8b, v7.b[1] +@@ -1405,45 +1405,45 @@ generate_composite_function \ + rshrn v28.8b, v8.8h, #8 + rshrn v29.8b, v9.8h, #8 + rshrn v30.8b, v10.8h, #8 + rshrn v31.8b, v11.8h, #8 + .endm + + .macro pixman_composite_src_n_8_8888_process_pixblock_tail_head + fetch_mask_pixblock +- PF add PF_X, PF_X, #8 ++ PF add, PF_X, PF_X, #8 + rshrn v28.8b, v8.8h, #8 +- PF tst PF_CTL, #0x0F ++ PF tst, PF_CTL, #0x0F + rshrn v29.8b, v9.8h, #8 +- PF beq 10f +- PF add PF_X, PF_X, #8 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 + 10: + rshrn v30.8b, v10.8h, #8 +- PF beq 10f +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + rshrn v31.8b, v11.8h, #8 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + umull v8.8h, v24.8b, v0.8b +- PF lsl DUMMY, PF_X, #mask_bpp_shift +- PF prfm PREFETCH_MODE, [PF_MASK, DUMMY] ++ PF lsl, DUMMY, PF_X, #mask_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_MASK, DUMMY] + umull v9.8h, v24.8b, v1.8b +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W + 10: + umull v10.8h, v24.8b, v2.8b +- PF ble 10f +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + umull v11.8h, v24.8b, v3.8b +- PF ble 10f +- PF lsl DUMMY, MASK_STRIDE, #mask_bpp_shift +- PF ldrsb DUMMY, [PF_MASK, DUMMY] +- PF add PF_MASK, PF_MASK, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift ++ PF ldrsb, DUMMY, [PF_MASK, DUMMY] ++ PF add, PF_MASK, PF_MASK, #1 + 10: + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + ursra v8.8h, v8.8h, #8 + ursra v9.8h, v9.8h, #8 + ursra v10.8h, v10.8h, #8 + ursra v11.8h, v11.8h, #8 + .endm + +@@ -1486,45 +1486,45 @@ generate_composite_function \ + rshrn v28.8b, v0.8h, #8 + rshrn v29.8b, v1.8h, #8 + rshrn v30.8b, v2.8h, #8 + rshrn v31.8b, v3.8h, #8 + .endm + + .macro pixman_composite_src_n_8_8_process_pixblock_tail_head + fetch_mask_pixblock +- PF add PF_X, PF_X, #8 ++ PF add, PF_X, PF_X, #8 + rshrn v28.8b, v0.8h, #8 +- PF tst PF_CTL, #0x0F ++ PF tst, PF_CTL, #0x0F + rshrn v29.8b, v1.8h, #8 +- PF beq 10f +- PF add PF_X, PF_X, #8 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 + 10: + rshrn v30.8b, v2.8h, #8 +- PF beq 10f +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + rshrn v31.8b, v3.8h, #8 +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + umull v0.8h, v24.8b, v16.8b +- PF lsl DUMMY, PF_X, mask_bpp_shift +- PF prfm PREFETCH_MODE, [PF_MASK, DUMMY] ++ PF lsl, DUMMY, PF_X, mask_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_MASK, DUMMY] + umull v1.8h, v25.8b, v16.8b +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W + 10: + umull v2.8h, v26.8b, v16.8b +- PF ble 10f +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + umull v3.8h, v27.8b, v16.8b +- PF ble 10f +- PF lsl DUMMY, MASK_STRIDE, #mask_bpp_shift +- PF ldrsb DUMMY, [PF_MASK, DUMMY] +- PF add PF_MASK, PF_MASK, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift ++ PF ldrsb, DUMMY, [PF_MASK, DUMMY] ++ PF add, PF_MASK, PF_MASK, #1 + 10: + st1 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 + ursra v0.8h, v0.8h, #8 + ursra v1.8h, v1.8h, #8 + ursra v2.8h, v2.8h, #8 + ursra v3.8h, v3.8h, #8 + .endm + +@@ -1594,54 +1594,54 @@ generate_composite_function \ + .endm + + .macro pixman_composite_over_n_8_8888_process_pixblock_tail_head + urshr v16.8h, v12.8h, #8 + ld4 {v4.8b, v5.8b, v6.8b, v7.8b}, [DST_R], #32 + urshr v17.8h, v13.8h, #8 + fetch_mask_pixblock + urshr v18.8h, v14.8h, #8 +- PF add PF_X, PF_X, #8 ++ PF add, PF_X, PF_X, #8 + urshr v19.8h, v15.8h, #8 +- PF tst PF_CTL, #0x0F ++ PF tst, PF_CTL, #0x0F + raddhn v28.8b, v16.8h, v12.8h +- PF beq 10f +- PF add PF_X, PF_X, #8 ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 + 10: + raddhn v29.8b, v17.8h, v13.8h +- PF beq 10f +- PF sub PF_CTL, PF_CTL, #1 ++ PF beq, 10f ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + raddhn v30.8b, v18.8h, v14.8h +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + raddhn v31.8b, v19.8h, v15.8h +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + umull v16.8h, v24.8b, v8.8b +- PF lsl DUMMY, PF_X, #mask_bpp_shift +- PF prfm PREFETCH_MODE, [PF_MASK, DUMMY] ++ PF lsl, DUMMY, PF_X, #mask_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_MASK, DUMMY] + umull v17.8h, v24.8b, v9.8b +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W + 10: + umull v18.8h, v24.8b, v10.8b +- PF ble 10f +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF subs, PF_CTL, PF_CTL, #0x10 + 10: + umull v19.8h, v24.8b, v11.8b +- PF ble 10f +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_DST, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_DST, #1 + 10: + uqadd v28.8b, v0.8b, v28.8b +- PF ble 10f +- PF lsl DUMMY, MASK_STRIDE, #mask_bpp_shift +- PF ldrsb DUMMY, [PF_MASK, DUMMY] +- PF add PF_MASK, PF_MASK, #1 ++ PF ble, 10f ++ PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift ++ PF ldrsb, DUMMY, [PF_MASK, DUMMY] ++ PF add, PF_MASK, PF_MASK, #1 + 10: + uqadd v29.8b, v1.8b, v29.8b + uqadd v30.8b, v2.8b, v30.8b + uqadd v31.8b, v3.8b, v31.8b + urshr v12.8h, v16.8h, #8 + urshr v13.8h, v17.8h, #8 + urshr v14.8h, v18.8h, #8 + urshr v15.8h, v19.8h, #8 +@@ -2407,17 +2407,17 @@ generate_composite_function \ + generate_composite_function_single_scanline \ + pixman_composite_scanline_out_reverse_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_out_reverse_8888_n_8888_process_pixblock_head, \ + pixman_composite_out_reverse_8888_n_8888_process_pixblock_tail, \ +- pixman_composite_out_reverse_8888_8888_8888_process_pixblock_tail_head \ ++ pixman_composite_out_reverse_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + + /******************************************************************************/ + + .macro pixman_composite_over_8888_n_8888_process_pixblock_head +@@ -2482,31 +2482,31 @@ generate_composite_function \ + pixman_composite_over_8888_8888_8888_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ +- pixman_composite_over_8888_8888_8888_process_pixblock_tail_head \ ++ pixman_composite_over_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + + generate_composite_function_single_scanline \ + pixman_composite_scanline_over_mask_asm_neon, 32, 32, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ +- pixman_composite_over_8888_8888_8888_process_pixblock_tail_head \ ++ pixman_composite_over_8888_8888_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 12 /* mask_basereg */ + + /******************************************************************************/ + + /* TODO: expand macros and do better instructions scheduling */ +@@ -2524,17 +2524,17 @@ generate_composite_function \ + pixman_composite_over_8888_8_8888_asm_neon, 32, 8, 32, \ + FLAG_DST_READWRITE | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 5, /* prefetch distance */ \ + default_init_need_all_regs, \ + default_cleanup_need_all_regs, \ + pixman_composite_over_8888_n_8888_process_pixblock_head, \ + pixman_composite_over_8888_n_8888_process_pixblock_tail, \ +- pixman_composite_over_8888_8_8888_process_pixblock_tail_head \ ++ pixman_composite_over_8888_8_8888_process_pixblock_tail_head, \ + 28, /* dst_w_basereg */ \ + 4, /* dst_r_basereg */ \ + 0, /* src_basereg */ \ + 15 /* mask_basereg */ + + /******************************************************************************/ + + .macro pixman_composite_src_0888_0888_process_pixblock_head +@@ -2675,38 +2675,38 @@ generate_composite_function \ + urshr v11.8h, v8.8h, #8 + mov v30.8b, v31.8b + mov v31.8b, v3.8b + mov v3.8b, v31.8b + urshr v12.8h, v9.8h, #8 + urshr v13.8h, v10.8h, #8 + fetch_src_pixblock + raddhn v30.8b, v11.8h, v8.8h +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF +- PF beq 10f +- PF add PF_X, PF_X, #8 +- PF sub PF_CTL, PF_CTL, #1 ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + raddhn v29.8b, v12.8h, v9.8h + raddhn v28.8b, v13.8h, v10.8h + umull v8.8h, v3.8b, v0.8b + umull v9.8h, v3.8b, v1.8b + umull v10.8h, v3.8b, v2.8b + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +- PF cmp PF_X, ORIG_W +- PF lsl DUMMY, PF_X, src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W +- PF subs PF_CTL, PF_CTL, #0x10 +- PF ble 10f +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 ++ PF cmp, PF_X, ORIG_W ++ PF lsl, DUMMY, PF_X, src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W ++ PF subs, PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 + 10: + .endm + + generate_composite_function \ + pixman_composite_src_pixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ +@@ -2744,38 +2744,38 @@ generate_composite_function \ + urshr v11.8h, v8.8h, #8 + mov v30.8b, v31.8b + mov v31.8b, v3.8b + mov v3.8b, v30.8b + urshr v12.8h, v9.8h, #8 + urshr v13.8h, v10.8h, #8 + fetch_src_pixblock + raddhn v28.8b, v11.8h, v8.8h +- PF add PF_X, PF_X, #8 +- PF tst PF_CTL, #0xF +- PF beq 10f +- PF add PF_X, PF_X, #8 +- PF sub PF_CTL, PF_CTL, #1 ++ PF add, PF_X, PF_X, #8 ++ PF tst, PF_CTL, #0xF ++ PF beq, 10f ++ PF add, PF_X, PF_X, #8 ++ PF sub, PF_CTL, PF_CTL, #1 + 10: + raddhn v29.8b, v12.8h, v9.8h + raddhn v30.8b, v13.8h, v10.8h + umull v8.8h, v3.8b, v0.8b + umull v9.8h, v3.8b, v1.8b + umull v10.8h, v3.8b, v2.8b + st4 {v28.8b, v29.8b, v30.8b, v31.8b}, [DST_W], #32 +- PF cmp PF_X, ORIG_W +- PF lsl DUMMY, PF_X, src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] +- PF ble 10f +- PF sub PF_X, PF_X, ORIG_W +- PF subs PF_CTL, PF_CTL, #0x10 +- PF ble 10f +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 ++ PF cmp, PF_X, ORIG_W ++ PF lsl, DUMMY, PF_X, src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF ble, 10f ++ PF sub, PF_X, PF_X, ORIG_W ++ PF subs, PF_CTL, PF_CTL, #0x10 ++ PF ble, 10f ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 + 10: + .endm + + generate_composite_function \ + pixman_composite_src_rpixbuf_8888_asm_neon, 32, 0, 32, \ + FLAG_DST_WRITEONLY | FLAG_DEINTERLEAVE_32BPP, \ + 8, /* number of pixels, processed in a single block */ \ + 10, /* prefetch distance */ \ +@@ -3126,197 +3126,197 @@ generate_composite_function_nearest_scan + * format conversion, and interpolation as separate macros which can be used + * as the basic building blocks for constructing bilinear scanline functions. + */ + + .macro bilinear_load_8888 reg1, reg2, tmp + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #2 +- ld1 {®1&.2s}, [TMP1], STRIDE +- ld1 {®2&.2s}, [TMP1] ++ ld1 {\()\reg1\().2s}, [TMP1], STRIDE ++ ld1 {\()\reg2\().2s}, [TMP1] + .endm + + .macro bilinear_load_0565 reg1, reg2, tmp + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 +- ld1 {®2&.s}[0], [TMP1], STRIDE +- ld1 {®2&.s}[1], [TMP1] +- convert_four_0565_to_x888_packed reg2, reg1, reg2, tmp ++ ld1 {\()\reg2\().s}[0], [TMP1], STRIDE ++ ld1 {\()\reg2\().s}[1], [TMP1] ++ convert_four_0565_to_x888_packed \reg2, \reg1, \reg2, \tmp + .endm + + .macro bilinear_load_and_vertical_interpolate_two_8888 \ + acc1, acc2, reg1, reg2, reg3, reg4, tmp1, tmp2 + +- bilinear_load_8888 reg1, reg2, tmp1 +- umull &acc1&.8h, ®1&.8b, v28.8b +- umlal &acc1&.8h, ®2&.8b, v29.8b +- bilinear_load_8888 reg3, reg4, tmp2 +- umull &acc2&.8h, ®3&.8b, v28.8b +- umlal &acc2&.8h, ®4&.8b, v29.8b ++ bilinear_load_8888 \reg1, \reg2, \tmp1 ++ umull \()\acc1\().8h, \()\reg1\().8b, v28.8b ++ umlal \()\acc1\().8h, \()\reg2\().8b, v29.8b ++ bilinear_load_8888 \reg3, \reg4, \tmp2 ++ umull \()\acc2\().8h, \()\reg3\().8b, v28.8b ++ umlal \()\acc2\().8h, \()\reg4\().8b, v29.8b + .endm + + .macro bilinear_load_and_vertical_interpolate_four_8888 \ +- xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ ++ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi, \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + + bilinear_load_and_vertical_interpolate_two_8888 \ +- xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi ++ \xacc1, \xacc2, \xreg1, \xreg2, \xreg3, \xreg4, \xacc2lo, \xacc2hi + bilinear_load_and_vertical_interpolate_two_8888 \ +- yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi ++ \yacc1, \yacc2, \yreg1, \yreg2, \yreg3, \yreg4, \yacc2lo, \yacc2hi + .endm + + .macro vzip reg1, reg2 + umov TMP4, v31.d[0] +- zip1 v31.8b, reg1, reg2 +- zip2 reg2, reg1, reg2 +- mov reg1, v31.8b ++ zip1 v31.8b, \reg1, \reg2 ++ zip2 \reg2, \reg1, \reg2 ++ mov \reg1, v31.8b + mov v31.d[0], TMP4 + .endm + + .macro vuzp reg1, reg2 + umov TMP4, v31.d[0] +- uzp1 v31.8b, reg1, reg2 +- uzp2 reg2, reg1, reg2 +- mov reg1, v31.8b ++ uzp1 v31.8b, \reg1, \reg2 ++ uzp2 \reg2, \reg1, \reg2 ++ mov \reg1, v31.8b + mov v31.d[0], TMP4 + .endm + + .macro bilinear_load_and_vertical_interpolate_two_0565 \ + acc1, acc2, reg1, reg2, reg3, reg4, acc2lo, acc2hi + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr TMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 +- ld1 {&acc2&.s}[0], [TMP1], STRIDE +- ld1 {&acc2&.s}[2], [TMP2], STRIDE +- ld1 {&acc2&.s}[1], [TMP1] +- ld1 {&acc2&.s}[3], [TMP2] +- convert_0565_to_x888 acc2, reg3, reg2, reg1 +- vzip ®1&.8b, ®3&.8b +- vzip ®2&.8b, ®4&.8b +- vzip ®3&.8b, ®4&.8b +- vzip ®1&.8b, ®2&.8b +- umull &acc1&.8h, ®1&.8b, v28.8b +- umlal &acc1&.8h, ®2&.8b, v29.8b +- umull &acc2&.8h, ®3&.8b, v28.8b +- umlal &acc2&.8h, ®4&.8b, v29.8b ++ ld1 {\()\acc2\().s}[0], [TMP1], STRIDE ++ ld1 {\()\acc2\().s}[2], [TMP2], STRIDE ++ ld1 {\()\acc2\().s}[1], [TMP1] ++ ld1 {\()\acc2\().s}[3], [TMP2] ++ convert_0565_to_x888 \acc2, \reg3, \reg2, \reg1 ++ vzip \()\reg1\().8b, \()\reg3\().8b ++ vzip \()\reg2\().8b, \()\reg4\().8b ++ vzip \()\reg3\().8b, \()\reg4\().8b ++ vzip \()\reg1\().8b, \()\reg2\().8b ++ umull \()\acc1\().8h, \()\reg1\().8b, v28.8b ++ umlal \()\acc1\().8h, \()\reg2\().8b, v29.8b ++ umull \()\acc2\().8h, \()\reg3\().8b, v28.8b ++ umlal \()\acc2\().8h, \()\reg4\().8b, v29.8b + .endm + + .macro bilinear_load_and_vertical_interpolate_four_0565 \ +- xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi \ ++ xacc1, xacc2, xreg1, xreg2, xreg3, xreg4, xacc2lo, xacc2hi, \ + yacc1, yacc2, yreg1, yreg2, yreg3, yreg4, yacc2lo, yacc2hi + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr TMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 +- ld1 {&xacc2&.s}[0], [TMP1], STRIDE +- ld1 {&xacc2&.s}[2], [TMP2], STRIDE +- ld1 {&xacc2&.s}[1], [TMP1] +- ld1 {&xacc2&.s}[3], [TMP2] +- convert_0565_to_x888 xacc2, xreg3, xreg2, xreg1 ++ ld1 {\()\xacc2\().s}[0], [TMP1], STRIDE ++ ld1 {\()\xacc2\().s}[2], [TMP2], STRIDE ++ ld1 {\()\xacc2\().s}[1], [TMP1] ++ ld1 {\()\xacc2\().s}[3], [TMP2] ++ convert_0565_to_x888 \xacc2, \xreg3, \xreg2, \xreg1 + asr TMP1, X, #16 + add X, X, UX + add TMP1, TOP, TMP1, lsl #1 + asr TMP2, X, #16 + add X, X, UX + add TMP2, TOP, TMP2, lsl #1 +- ld1 {&yacc2&.s}[0], [TMP1], STRIDE +- vzip &xreg1&.8b, &xreg3&.8b +- ld1 {&yacc2&.s}[2], [TMP2], STRIDE +- vzip &xreg2&.8b, &xreg4&.8b +- ld1 {&yacc2&.s}[1], [TMP1] +- vzip &xreg3&.8b, &xreg4&.8b +- ld1 {&yacc2&.s}[3], [TMP2] +- vzip &xreg1&.8b, &xreg2&.8b +- convert_0565_to_x888 yacc2, yreg3, yreg2, yreg1 +- umull &xacc1&.8h, &xreg1&.8b, v28.8b +- vzip &yreg1&.8b, &yreg3&.8b +- umlal &xacc1&.8h, &xreg2&.8b, v29.8b +- vzip &yreg2&.8b, &yreg4&.8b +- umull &xacc2&.8h, &xreg3&.8b, v28.8b +- vzip &yreg3&.8b, &yreg4&.8b +- umlal &xacc2&.8h, &xreg4&.8b, v29.8b +- vzip &yreg1&.8b, &yreg2&.8b +- umull &yacc1&.8h, &yreg1&.8b, v28.8b +- umlal &yacc1&.8h, &yreg2&.8b, v29.8b +- umull &yacc2&.8h, &yreg3&.8b, v28.8b +- umlal &yacc2&.8h, &yreg4&.8b, v29.8b ++ ld1 {\()\yacc2\().s}[0], [TMP1], STRIDE ++ vzip \()\xreg1\().8b, \()\xreg3\().8b ++ ld1 {\()\yacc2\().s}[2], [TMP2], STRIDE ++ vzip \()\xreg2\().8b, \()\xreg4\().8b ++ ld1 {\()\yacc2\().s}[1], [TMP1] ++ vzip \()\xreg3\().8b, \()\xreg4\().8b ++ ld1 {\()\yacc2\().s}[3], [TMP2] ++ vzip \()\xreg1\().8b, \()\xreg2\().8b ++ convert_0565_to_x888 \yacc2, \yreg3, \yreg2, \yreg1 ++ umull \()\xacc1\().8h, \()\xreg1\().8b, v28.8b ++ vzip \()\yreg1\().8b, \()\yreg3\().8b ++ umlal \()\xacc1\().8h, \()\xreg2\().8b, v29.8b ++ vzip \()\yreg2\().8b, \()\yreg4\().8b ++ umull \()\xacc2\().8h, \()\xreg3\().8b, v28.8b ++ vzip \()\yreg3\().8b, \()\yreg4\().8b ++ umlal \()\xacc2\().8h, \()\xreg4\().8b, v29.8b ++ vzip \()\yreg1\().8b, \()\yreg2\().8b ++ umull \()\yacc1\().8h, \()\yreg1\().8b, v28.8b ++ umlal \()\yacc1\().8h, \()\yreg2\().8b, v29.8b ++ umull \()\yacc2\().8h, \()\yreg3\().8b, v28.8b ++ umlal \()\yacc2\().8h, \()\yreg4\().8b, v29.8b + .endm + + .macro bilinear_store_8888 numpix, tmp1, tmp2 +-.if numpix == 4 ++.if \numpix == 4 + st1 {v0.2s, v1.2s}, [OUT], #16 +-.elseif numpix == 2 ++.elseif \numpix == 2 + st1 {v0.2s}, [OUT], #8 +-.elseif numpix == 1 ++.elseif \numpix == 1 + st1 {v0.s}[0], [OUT], #4 + .else +- .error bilinear_store_8888 numpix is unsupported ++ .error bilinear_store_8888 \numpix is unsupported + .endif + .endm + + .macro bilinear_store_0565 numpix, tmp1, tmp2 + vuzp v0.8b, v1.8b + vuzp v2.8b, v3.8b + vuzp v1.8b, v3.8b + vuzp v0.8b, v2.8b +- convert_8888_to_0565 v2, v1, v0, v1, tmp1, tmp2 +-.if numpix == 4 ++ convert_8888_to_0565 v2, v1, v0, v1, \tmp1, \tmp2 ++.if \numpix == 4 + st1 {v1.4h}, [OUT], #8 +-.elseif numpix == 2 ++.elseif \numpix == 2 + st1 {v1.s}[0], [OUT], #4 +-.elseif numpix == 1 ++.elseif \numpix == 1 + st1 {v1.h}[0], [OUT], #2 + .else +- .error bilinear_store_0565 numpix is unsupported ++ .error bilinear_store_0565 \numpix is unsupported + .endif + .endm + + .macro bilinear_interpolate_last_pixel src_fmt, dst_fmt +- bilinear_load_&src_fmt v0, v1, v2 ++ bilinear_load_\()\src_fmt v0, v1, v2 + umull v2.8h, v0.8b, v28.8b + umlal v2.8h, v1.8b, v29.8b + /* 5 cycles bubble */ + ushll v0.4s, v2.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v2.4h, v15.h[0] + umlal2 v0.4s, v2.8h, v15.h[0] + /* 5 cycles bubble */ + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + /* 3 cycles bubble */ + xtn v0.8b, v0.8h + /* 1 cycle bubble */ +- bilinear_store_&dst_fmt 1, v3, v4 ++ bilinear_store_\()\dst_fmt 1, v3, v4 + .endm + + .macro bilinear_interpolate_two_pixels src_fmt, dst_fmt +- bilinear_load_and_vertical_interpolate_two_&src_fmt \ ++ bilinear_load_and_vertical_interpolate_two_\()\src_fmt \ + v1, v11, v2, v3, v20, v21, v22, v23 + ushll v0.4s, v1.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v1.4h, v15.h[0] + umlal2 v0.4s, v1.8h, v15.h[0] + ushll v10.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v10.4s, v11.4h, v15.h[4] + umlal2 v10.4s, v11.8h, v15.h[4] + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v10.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + xtn v0.8b, v0.8h +- bilinear_store_&dst_fmt 2, v3, v4 ++ bilinear_store_\()\dst_fmt 2, v3, v4 + .endm + + .macro bilinear_interpolate_four_pixels src_fmt, dst_fmt +- bilinear_load_and_vertical_interpolate_four_&src_fmt \ +- v1, v11, v14, v20, v16, v17, v22, v23 \ ++ bilinear_load_and_vertical_interpolate_four_\()\src_fmt \ ++ v1, v11, v14, v20, v16, v17, v22, v23, \ + v3, v9, v24, v25, v26, v27, v18, v19 + prfm PREFETCH_MODE, [TMP1, PF_OFFS] + sub TMP1, TMP1, STRIDE + ushll v0.4s, v1.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v0.4s, v1.4h, v15.h[0] + umlal2 v0.4s, v1.8h, v15.h[0] + ushll v10.4s, v11.4h, #BILINEAR_INTERPOLATION_BITS + umlsl v10.4s, v11.4h, v15.h[4] +@@ -3333,64 +3333,64 @@ generate_composite_function_nearest_scan + shrn v0.4h, v0.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v0.8h, v10.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn v2.4h, v2.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + shrn2 v2.8h, v8.4s, #(2 * BILINEAR_INTERPOLATION_BITS) + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + xtn v0.8b, v0.8h + xtn v1.8b, v2.8h + add v12.8h, v12.8h, v13.8h +- bilinear_store_&dst_fmt 4, v3, v4 ++ bilinear_store_\()\dst_fmt 4, v3, v4 + .endm + + .macro bilinear_interpolate_four_pixels_head src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_head ++.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_head + .else +- bilinear_interpolate_four_pixels src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + .endif + .endm + + .macro bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_tail ++.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail + .endif + .endm + + .macro bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_four_pixels_&src_fmt&_&dst_fmt&_tail_head ++.ifdef have_bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_four_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail_head + .else +- bilinear_interpolate_four_pixels src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + .endif + .endm + + .macro bilinear_interpolate_eight_pixels_head src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_head ++.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_head + .else +- bilinear_interpolate_four_pixels_head src_fmt, dst_fmt +- bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_head \src_fmt, \dst_fmt ++ bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + .endif + .endm + + .macro bilinear_interpolate_eight_pixels_tail src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_tail ++.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail + .else +- bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_tail \src_fmt, \dst_fmt + .endif + .endm + + .macro bilinear_interpolate_eight_pixels_tail_head src_fmt, dst_fmt +-.ifdef have_bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt +- bilinear_interpolate_eight_pixels_&src_fmt&_&dst_fmt&_tail_head ++.ifdef have_bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt ++ bilinear_interpolate_eight_pixels_\()\src_fmt\()_\()\dst_fmt\()_tail_head + .else +- bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt +- bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt ++ bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + .endif + .endm + + .set BILINEAR_FLAG_UNROLL_4, 0 + .set BILINEAR_FLAG_UNROLL_8, 1 + .set BILINEAR_FLAG_USE_ALL_NEON_REGS, 2 + + /* +@@ -3405,17 +3405,17 @@ generate_composite_function_nearest_scan + * prefetch_distance - prefetch in the source image by that many + * pixels ahead + */ + + .macro generate_bilinear_scanline_func fname, src_fmt, dst_fmt, \ + src_bpp_shift, dst_bpp_shift, \ + prefetch_distance, flags + +-pixman_asm_function fname ++pixman_asm_function \fname + OUT .req x0 + TOP .req x1 + BOTTOM .req x2 + WT .req x3 + WB .req x4 + X .req x5 + UX .req x6 + WIDTH .req x7 +@@ -3437,17 +3437,17 @@ pixman_asm_function fname + sub sp, sp, 112 /* push all registers */ + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], #32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], #32 + stp x8, x9, [x29, -80] + stp x10, x11, [x29, -96] + stp x12, x13, [x29, -112] + +- mov PF_OFFS, #prefetch_distance ++ mov PF_OFFS, #\prefetch_distance + mul PF_OFFS, PF_OFFS, UX + + subs STRIDE, BOTTOM, TOP + .unreq BOTTOM + + cmp WIDTH, #0 + ble 300f + +@@ -3458,85 +3458,85 @@ pixman_asm_function fname + mov v25.d[0], v12.d[1] + mov v26.d[0], v13.d[0] + add v25.4h, v25.4h, v26.4h + mov v12.d[1], v25.d[0] + + /* ensure good destination alignment */ + cmp WIDTH, #1 + blt 100f +- tst OUT, #(1 << dst_bpp_shift) ++ tst OUT, #(1 << \dst_bpp_shift) + beq 100f + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h +- bilinear_interpolate_last_pixel src_fmt, dst_fmt ++ bilinear_interpolate_last_pixel \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #1 + 100: + add v13.8h, v13.8h, v13.8h + ushr v15.8h, v12.8h, #(16 - BILINEAR_INTERPOLATION_BITS) + add v12.8h, v12.8h, v13.8h + + cmp WIDTH, #2 + blt 100f +- tst OUT, #(1 << (dst_bpp_shift + 1)) ++ tst OUT, #(1 << (\dst_bpp_shift + 1)) + beq 100f +- bilinear_interpolate_two_pixels src_fmt, dst_fmt ++ bilinear_interpolate_two_pixels \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #2 + 100: +-.if ((flags) & BILINEAR_FLAG_UNROLL_8) != 0 ++.if ((\flags) & BILINEAR_FLAG_UNROLL_8) != 0 + /*********** 8 pixels per iteration *****************/ + cmp WIDTH, #4 + blt 100f +- tst OUT, #(1 << (dst_bpp_shift + 2)) ++ tst OUT, #(1 << (\dst_bpp_shift + 2)) + beq 100f +- bilinear_interpolate_four_pixels src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + sub WIDTH, WIDTH, #4 + 100: + subs WIDTH, WIDTH, #8 + blt 100f +- asr PF_OFFS, PF_OFFS, #(16 - src_bpp_shift) +- bilinear_interpolate_eight_pixels_head src_fmt, dst_fmt ++ asr PF_OFFS, PF_OFFS, #(16 - \src_bpp_shift) ++ bilinear_interpolate_eight_pixels_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #8 + blt 500f + 1000: +- bilinear_interpolate_eight_pixels_tail_head src_fmt, dst_fmt ++ bilinear_interpolate_eight_pixels_tail_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #8 + bge 1000b + 500: +- bilinear_interpolate_eight_pixels_tail src_fmt, dst_fmt ++ bilinear_interpolate_eight_pixels_tail \src_fmt, \dst_fmt + 100: + tst WIDTH, #4 + beq 200f +- bilinear_interpolate_four_pixels src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels \src_fmt, \dst_fmt + 200: + .else + /*********** 4 pixels per iteration *****************/ + subs WIDTH, WIDTH, #4 + blt 100f +- asr PF_OFFS, PF_OFFS, #(16 - src_bpp_shift) +- bilinear_interpolate_four_pixels_head src_fmt, dst_fmt ++ asr PF_OFFS, PF_OFFS, #(16 - \src_bpp_shift) ++ bilinear_interpolate_four_pixels_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #4 + blt 500f + 1000: +- bilinear_interpolate_four_pixels_tail_head src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_tail_head \src_fmt, \dst_fmt + subs WIDTH, WIDTH, #4 + bge 1000b + 500: +- bilinear_interpolate_four_pixels_tail src_fmt, dst_fmt ++ bilinear_interpolate_four_pixels_tail \src_fmt, \dst_fmt + 100: + /****************************************************/ + .endif + /* handle the remaining trailing pixels */ + tst WIDTH, #2 + beq 200f +- bilinear_interpolate_two_pixels src_fmt, dst_fmt ++ bilinear_interpolate_two_pixels \src_fmt, \dst_fmt + 200: + tst WIDTH, #1 + beq 300f +- bilinear_interpolate_last_pixel src_fmt, dst_fmt ++ bilinear_interpolate_last_pixel \src_fmt, \dst_fmt + 300: + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], #32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], #32 + ldp x8, x9, [x29, -80] + ldp x10, x11, [x29, -96] + ldp x12, x13, [x29, -104] + mov sp, x29 +@@ -3551,17 +3551,17 @@ 300: + .unreq UX + .unreq WIDTH + .unreq TMP1 + .unreq TMP2 + .unreq PF_OFFS + .unreq TMP3 + .unreq TMP4 + .unreq STRIDE +-.endfunc ++pixman_end_asm_function + + .endm + + /*****************************************************************************/ + + .set have_bilinear_interpolate_four_pixels_8888_8888, 1 + + .macro bilinear_interpolate_four_pixels_8888_8888_head +diff --git a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h +--- a/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h ++++ b/gfx/cairo/libpixman/src/pixman-arma64-neon-asm.h +@@ -75,340 +75,340 @@ + #define PREFETCH_MODE pldl1keep + + /* + * Definitions of supplementary pixld/pixst macros (for partial load/store of + * pixel data). + */ + + .macro pixldst1 op, elem_size, reg1, mem_operand, abits +- op {v®1&.&elem_size}, [&mem_operand&], #8 ++ \op {v\()\reg1\().\()\elem_size}, [\()\mem_operand\()], #8 + .endm + + .macro pixldst2 op, elem_size, reg1, reg2, mem_operand, abits +- op {v®1&.&elem_size, v®2&.&elem_size}, [&mem_operand&], #16 ++ \op {v\()\reg1\().\()\elem_size, v\()\reg2\().\()\elem_size}, [\()\mem_operand\()], #16 + .endm + + .macro pixldst4 op, elem_size, reg1, reg2, reg3, reg4, mem_operand, abits +- op {v®1&.&elem_size, v®2&.&elem_size, v®3&.&elem_size, v®4&.&elem_size}, [&mem_operand&], #32 ++ \op {v\()\reg1\().\()\elem_size, v\()\reg2\().\()\elem_size, v\()\reg3\().\()\elem_size, v\()\reg4\().\()\elem_size}, [\()\mem_operand\()], #32 + .endm + + .macro pixldst0 op, elem_size, reg1, idx, mem_operand, abits, bytes +- op {v®1&.&elem_size}[idx], [&mem_operand&], #&bytes& ++ \op {v\()\reg1\().\()\elem_size}[\idx], [\()\mem_operand\()], #\()\bytes\() + .endm + + .macro pixldst3 op, elem_size, reg1, reg2, reg3, mem_operand +- op {v®1&.&elem_size, v®2&.&elem_size, v®3&.&elem_size}, [&mem_operand&], #24 ++ \op {v\()\reg1\().\()\elem_size, v\()\reg2\().\()\elem_size, v\()\reg3\().\()\elem_size}, [\()\mem_operand\()], #24 + .endm + + .macro pixldst30 op, elem_size, reg1, reg2, reg3, idx, mem_operand +- op {v®1&.&elem_size, v®2&.&elem_size, v®3&.&elem_size}[idx], [&mem_operand&], #3 ++ \op {v\()\reg1\().\()\elem_size, v\()\reg2\().\()\elem_size, v\()\reg3\().\()\elem_size}[\idx], [\()\mem_operand\()], #3 + .endm + + .macro pixldst numbytes, op, elem_size, basereg, mem_operand, abits +-.if numbytes == 32 +- .if elem_size==32 +- pixldst4 op, 2s, %(basereg+4), %(basereg+5), \ +- %(basereg+6), %(basereg+7), mem_operand, abits +- .elseif elem_size==16 +- pixldst4 op, 4h, %(basereg+4), %(basereg+5), \ +- %(basereg+6), %(basereg+7), mem_operand, abits ++.if \numbytes == 32 ++ .if \elem_size==32 ++ pixldst4 \op, 2s, %(\basereg+4), %(\basereg+5), \ ++ %(\basereg+6), %(\basereg+7), \mem_operand, \abits ++ .elseif \elem_size==16 ++ pixldst4 \op, 4h, %(\basereg+4), %(\basereg+5), \ ++ %(\basereg+6), %(\basereg+7), \mem_operand, \abits + .else +- pixldst4 op, 8b, %(basereg+4), %(basereg+5), \ +- %(basereg+6), %(basereg+7), mem_operand, abits ++ pixldst4 \op, 8b, %(\basereg+4), %(\basereg+5), \ ++ %(\basereg+6), %(\basereg+7), \mem_operand, \abits + .endif +-.elseif numbytes == 16 +- .if elem_size==32 +- pixldst2 op, 2s, %(basereg+2), %(basereg+3), mem_operand, abits +- .elseif elem_size==16 +- pixldst2 op, 4h, %(basereg+2), %(basereg+3), mem_operand, abits ++.elseif \numbytes == 16 ++ .if \elem_size==32 ++ pixldst2 \op, 2s, %(\basereg+2), %(\basereg+3), \mem_operand, \abits ++ .elseif \elem_size==16 ++ pixldst2 \op, 4h, %(\basereg+2), %(\basereg+3), \mem_operand, \abits + .else +- pixldst2 op, 8b, %(basereg+2), %(basereg+3), mem_operand, abits ++ pixldst2 \op, 8b, %(\basereg+2), %(\basereg+3), \mem_operand, \abits + .endif +-.elseif numbytes == 8 +- .if elem_size==32 +- pixldst1 op, 2s, %(basereg+1), mem_operand, abits +- .elseif elem_size==16 +- pixldst1 op, 4h, %(basereg+1), mem_operand, abits ++.elseif \numbytes == 8 ++ .if \elem_size==32 ++ pixldst1 \op, 2s, %(\basereg+1), \mem_operand, \abits ++ .elseif \elem_size==16 ++ pixldst1 \op, 4h, %(\basereg+1), \mem_operand, \abits + .else +- pixldst1 op, 8b, %(basereg+1), mem_operand, abits ++ pixldst1 \op, 8b, %(\basereg+1), \mem_operand, \abits + .endif +-.elseif numbytes == 4 +- .if !RESPECT_STRICT_ALIGNMENT || (elem_size == 32) +- pixldst0 op, s, %(basereg+0), 1, mem_operand, abits, 4 +- .elseif elem_size == 16 +- pixldst0 op, h, %(basereg+0), 2, mem_operand, abits, 2 +- pixldst0 op, h, %(basereg+0), 3, mem_operand, abits, 2 ++.elseif \numbytes == 4 ++ .if !RESPECT_STRICT_ALIGNMENT || (\elem_size == 32) ++ pixldst0 \op, s, %(\basereg+0), 1, \mem_operand, \abits, 4 ++ .elseif \elem_size == 16 ++ pixldst0 \op, h, %(\basereg+0), 2, \mem_operand, \abits, 2 ++ pixldst0 \op, h, %(\basereg+0), 3, \mem_operand, \abits, 2 + .else +- pixldst0 op, b, %(basereg+0), 4, mem_operand, abits, 1 +- pixldst0 op, b, %(basereg+0), 5, mem_operand, abits, 1 +- pixldst0 op, b, %(basereg+0), 6, mem_operand, abits, 1 +- pixldst0 op, b, %(basereg+0), 7, mem_operand, abits, 1 ++ pixldst0 \op, b, %(\basereg+0), 4, \mem_operand, \abits, 1 ++ pixldst0 \op, b, %(\basereg+0), 5, \mem_operand, \abits, 1 ++ pixldst0 \op, b, %(\basereg+0), 6, \mem_operand, \abits, 1 ++ pixldst0 \op, b, %(\basereg+0), 7, \mem_operand, \abits, 1 + .endif +-.elseif numbytes == 2 +- .if !RESPECT_STRICT_ALIGNMENT || (elem_size == 16) +- pixldst0 op, h, %(basereg+0), 1, mem_operand, abits, 2 ++.elseif \numbytes == 2 ++ .if !RESPECT_STRICT_ALIGNMENT || (\elem_size == 16) ++ pixldst0 \op, h, %(\basereg+0), 1, \mem_operand, \abits, 2 + .else +- pixldst0 op, b, %(basereg+0), 2, mem_operand, abits, 1 +- pixldst0 op, b, %(basereg+0), 3, mem_operand, abits, 1 ++ pixldst0 \op, b, %(\basereg+0), 2, \mem_operand, \abits, 1 ++ pixldst0 \op, b, %(\basereg+0), 3, \mem_operand, \abits, 1 + .endif +-.elseif numbytes == 1 +- pixldst0 op, b, %(basereg+0), 1, mem_operand, abits, 1 ++.elseif \numbytes == 1 ++ pixldst0 \op, b, %(\basereg+0), 1, \mem_operand, \abits, 1 + .else +- .error "unsupported size: numbytes" ++ .error "unsupported size: \numbytes" + .endif + .endm + + .macro pixld numpix, bpp, basereg, mem_operand, abits=0 +-.if bpp > 0 +-.if (bpp == 32) && (numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) +- pixldst4 ld4, 8b, %(basereg+4), %(basereg+5), \ +- %(basereg+6), %(basereg+7), mem_operand, abits +-.elseif (bpp == 24) && (numpix == 8) +- pixldst3 ld3, 8b, %(basereg+3), %(basereg+4), %(basereg+5), mem_operand +-.elseif (bpp == 24) && (numpix == 4) +- pixldst30 ld3, b, %(basereg+0), %(basereg+1), %(basereg+2), 4, mem_operand +- pixldst30 ld3, b, %(basereg+0), %(basereg+1), %(basereg+2), 5, mem_operand +- pixldst30 ld3, b, %(basereg+0), %(basereg+1), %(basereg+2), 6, mem_operand +- pixldst30 ld3, b, %(basereg+0), %(basereg+1), %(basereg+2), 7, mem_operand +-.elseif (bpp == 24) && (numpix == 2) +- pixldst30 ld3, b, %(basereg+0), %(basereg+1), %(basereg+2), 2, mem_operand +- pixldst30 ld3, b, %(basereg+0), %(basereg+1), %(basereg+2), 3, mem_operand +-.elseif (bpp == 24) && (numpix == 1) +- pixldst30 ld3, b, %(basereg+0), %(basereg+1), %(basereg+2), 1, mem_operand ++.if \bpp > 0 ++.if (\bpp == 32) && (\numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) ++ pixldst4 ld4, 8b, %(\basereg+4), %(\basereg+5), \ ++ %(\basereg+6), %(\basereg+7), \mem_operand, \abits ++.elseif (\bpp == 24) && (\numpix == 8) ++ pixldst3 ld3, 8b, %(\basereg+3), %(\basereg+4), %(\basereg+5), \mem_operand ++.elseif (\bpp == 24) && (\numpix == 4) ++ pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 4, \mem_operand ++ pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 5, \mem_operand ++ pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 6, \mem_operand ++ pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 7, \mem_operand ++.elseif (\bpp == 24) && (\numpix == 2) ++ pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 2, \mem_operand ++ pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 3, \mem_operand ++.elseif (\bpp == 24) && (\numpix == 1) ++ pixldst30 ld3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 1, \mem_operand + .else +- pixldst %(numpix * bpp / 8), ld1, %(bpp), basereg, mem_operand, abits ++ pixldst %(\numpix * \bpp / 8), ld1, %(\bpp), \basereg, \mem_operand, \abits + .endif + .endif + .endm + + .macro pixst numpix, bpp, basereg, mem_operand, abits=0 +-.if bpp > 0 +-.if (bpp == 32) && (numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) +- pixldst4 st4, 8b, %(basereg+4), %(basereg+5), \ +- %(basereg+6), %(basereg+7), mem_operand, abits +-.elseif (bpp == 24) && (numpix == 8) +- pixldst3 st3, 8b, %(basereg+3), %(basereg+4), %(basereg+5), mem_operand +-.elseif (bpp == 24) && (numpix == 4) +- pixldst30 st3, b, %(basereg+0), %(basereg+1), %(basereg+2), 4, mem_operand +- pixldst30 st3, b, %(basereg+0), %(basereg+1), %(basereg+2), 5, mem_operand +- pixldst30 st3, b, %(basereg+0), %(basereg+1), %(basereg+2), 6, mem_operand +- pixldst30 st3, b, %(basereg+0), %(basereg+1), %(basereg+2), 7, mem_operand +-.elseif (bpp == 24) && (numpix == 2) +- pixldst30 st3, b, %(basereg+0), %(basereg+1), %(basereg+2), 2, mem_operand +- pixldst30 st3, b, %(basereg+0), %(basereg+1), %(basereg+2), 3, mem_operand +-.elseif (bpp == 24) && (numpix == 1) +- pixldst30 st3, b, %(basereg+0), %(basereg+1), %(basereg+2), 1, mem_operand +-.elseif numpix * bpp == 32 && abits == 32 +- pixldst 4, st1, 32, basereg, mem_operand, abits +-.elseif numpix * bpp == 16 && abits == 16 +- pixldst 2, st1, 16, basereg, mem_operand, abits ++.if \bpp > 0 ++.if (\bpp == 32) && (\numpix == 8) && (DEINTERLEAVE_32BPP_ENABLED != 0) ++ pixldst4 st4, 8b, %(\basereg+4), %(\basereg+5), \ ++ %(\basereg+6), %(\basereg+7), \mem_operand, \abits ++.elseif (\bpp == 24) && (\numpix == 8) ++ pixldst3 st3, 8b, %(\basereg+3), %(\basereg+4), %(\basereg+5), \mem_operand ++.elseif (\bpp == 24) && (\numpix == 4) ++ pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 4, \mem_operand ++ pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 5, \mem_operand ++ pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 6, \mem_operand ++ pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 7, \mem_operand ++.elseif (\bpp == 24) && (\numpix == 2) ++ pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 2, \mem_operand ++ pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 3, \mem_operand ++.elseif (\bpp == 24) && (\numpix == 1) ++ pixldst30 st3, b, %(\basereg+0), %(\basereg+1), %(\basereg+2), 1, \mem_operand ++.elseif \numpix * \bpp == 32 && \abits == 32 ++ pixldst 4, st1, 32, \basereg, \mem_operand, \abits ++.elseif \numpix * \bpp == 16 && \abits == 16 ++ pixldst 2, st1, 16, \basereg, \mem_operand, \abits + .else +- pixldst %(numpix * bpp / 8), st1, %(bpp), basereg, mem_operand, abits ++ pixldst %(\numpix * \bpp / 8), st1, %(\bpp), \basereg, \mem_operand, \abits + .endif + .endif + .endm + + .macro pixld_a numpix, bpp, basereg, mem_operand +-.if (bpp * numpix) <= 128 +- pixld numpix, bpp, basereg, mem_operand, %(bpp * numpix) ++.if (\bpp * \numpix) <= 128 ++ pixld \numpix, \bpp, \basereg, \mem_operand, %(\bpp * \numpix) + .else +- pixld numpix, bpp, basereg, mem_operand, 128 ++ pixld \numpix, \bpp, \basereg, \mem_operand, 128 + .endif + .endm + + .macro pixst_a numpix, bpp, basereg, mem_operand +-.if (bpp * numpix) <= 128 +- pixst numpix, bpp, basereg, mem_operand, %(bpp * numpix) ++.if (\bpp * \numpix) <= 128 ++ pixst \numpix, \bpp, \basereg, \mem_operand, %(\bpp * \numpix) + .else +- pixst numpix, bpp, basereg, mem_operand, 128 ++ pixst \numpix, \bpp, \basereg, \mem_operand, 128 + .endif + .endm + + /* + * Pixel fetcher for nearest scaling (needs TMP1, TMP2, VX, UNIT_X register + * aliases to be defined) + */ + .macro pixld1_s elem_size, reg1, mem_operand +-.if elem_size == 16 ++.if \elem_size == 16 + asr TMP1, VX, #16 + adds VX, VX, UNIT_X + bmi 55f + 5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b + 55: +- add TMP1, mem_operand, TMP1, lsl #1 ++ add TMP1, \mem_operand, TMP1, lsl #1 + asr TMP2, VX, #16 + adds VX, VX, UNIT_X + bmi 55f + 5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b + 55: +- add TMP2, mem_operand, TMP2, lsl #1 +- ld1 {v®1&.h}[0], [TMP1] ++ add TMP2, \mem_operand, TMP2, lsl #1 ++ ld1 {v\()\reg1\().h}[0], [TMP1] + asr TMP1, VX, #16 + adds VX, VX, UNIT_X + bmi 55f + 5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b + 55: +- add TMP1, mem_operand, TMP1, lsl #1 +- ld1 {v®1&.h}[1], [TMP2] ++ add TMP1, \mem_operand, TMP1, lsl #1 ++ ld1 {v\()\reg1\().h}[1], [TMP2] + asr TMP2, VX, #16 + adds VX, VX, UNIT_X + bmi 55f + 5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b + 55: +- add TMP2, mem_operand, TMP2, lsl #1 +- ld1 {v®1&.h}[2], [TMP1] +- ld1 {v®1&.h}[3], [TMP2] +-.elseif elem_size == 32 ++ add TMP2, \mem_operand, TMP2, lsl #1 ++ ld1 {v\()\reg1\().h}[2], [TMP1] ++ ld1 {v\()\reg1\().h}[3], [TMP2] ++.elseif \elem_size == 32 + asr TMP1, VX, #16 + adds VX, VX, UNIT_X + bmi 55f + 5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b + 55: +- add TMP1, mem_operand, TMP1, lsl #2 ++ add TMP1, \mem_operand, TMP1, lsl #2 + asr TMP2, VX, #16 + adds VX, VX, UNIT_X + bmi 55f + 5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b + 55: +- add TMP2, mem_operand, TMP2, lsl #2 +- ld1 {v®1&.s}[0], [TMP1] +- ld1 {v®1&.s}[1], [TMP2] ++ add TMP2, \mem_operand, TMP2, lsl #2 ++ ld1 {v\()\reg1\().s}[0], [TMP1] ++ ld1 {v\()\reg1\().s}[1], [TMP2] + .else + .error "unsupported" + .endif + .endm + + .macro pixld2_s elem_size, reg1, reg2, mem_operand +-.if 0 /* elem_size == 32 */ ++.if 0 /* \elem_size == 32 */ + mov TMP1, VX, asr #16 + add VX, VX, UNIT_X, asl #1 +- add TMP1, mem_operand, TMP1, asl #2 ++ add TMP1, \mem_operand, TMP1, asl #2 + mov TMP2, VX, asr #16 + sub VX, VX, UNIT_X +- add TMP2, mem_operand, TMP2, asl #2 +- ld1 {v®1&.s}[0], [TMP1] ++ add TMP2, \mem_operand, TMP2, asl #2 ++ ld1 {v\()\reg1\().s}[0], [TMP1] + mov TMP1, VX, asr #16 + add VX, VX, UNIT_X, asl #1 +- add TMP1, mem_operand, TMP1, asl #2 +- ld1 {v®2&.s}[0], [TMP2, :32] ++ add TMP1, \mem_operand, TMP1, asl #2 ++ ld1 {v\()\reg2\().s}[0], [TMP2, :32] + mov TMP2, VX, asr #16 + add VX, VX, UNIT_X +- add TMP2, mem_operand, TMP2, asl #2 +- ld1 {v®1&.s}[1], [TMP1] +- ld1 {v®2&.s}[1], [TMP2] ++ add TMP2, \mem_operand, TMP2, asl #2 ++ ld1 {v\()\reg1\().s}[1], [TMP1] ++ ld1 {v\()\reg2\().s}[1], [TMP2] + .else +- pixld1_s elem_size, reg1, mem_operand +- pixld1_s elem_size, reg2, mem_operand ++ pixld1_s \elem_size, \reg1, \mem_operand ++ pixld1_s \elem_size, \reg2, \mem_operand + .endif + .endm + + .macro pixld0_s elem_size, reg1, idx, mem_operand +-.if elem_size == 16 ++.if \elem_size == 16 + asr TMP1, VX, #16 + adds VX, VX, UNIT_X + bmi 55f + 5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b + 55: +- add TMP1, mem_operand, TMP1, lsl #1 +- ld1 {v®1&.h}[idx], [TMP1] +-.elseif elem_size == 32 ++ add TMP1, \mem_operand, TMP1, lsl #1 ++ ld1 {v\()\reg1\().h}[\idx], [TMP1] ++.elseif \elem_size == 32 + asr DUMMY, VX, #16 + mov TMP1, DUMMY + adds VX, VX, UNIT_X + bmi 55f + 5: subs VX, VX, SRC_WIDTH_FIXED + bpl 5b + 55: +- add TMP1, mem_operand, TMP1, lsl #2 +- ld1 {v®1&.s}[idx], [TMP1] ++ add TMP1, \mem_operand, TMP1, lsl #2 ++ ld1 {v\()\reg1\().s}[\idx], [TMP1] + .endif + .endm + + .macro pixld_s_internal numbytes, elem_size, basereg, mem_operand +-.if numbytes == 32 +- pixld2_s elem_size, %(basereg+4), %(basereg+5), mem_operand +- pixld2_s elem_size, %(basereg+6), %(basereg+7), mem_operand +- pixdeinterleave elem_size, %(basereg+4) +-.elseif numbytes == 16 +- pixld2_s elem_size, %(basereg+2), %(basereg+3), mem_operand +-.elseif numbytes == 8 +- pixld1_s elem_size, %(basereg+1), mem_operand +-.elseif numbytes == 4 +- .if elem_size == 32 +- pixld0_s elem_size, %(basereg+0), 1, mem_operand +- .elseif elem_size == 16 +- pixld0_s elem_size, %(basereg+0), 2, mem_operand +- pixld0_s elem_size, %(basereg+0), 3, mem_operand ++.if \numbytes == 32 ++ pixld2_s \elem_size, %(\basereg+4), %(\basereg+5), \mem_operand ++ pixld2_s \elem_size, %(\basereg+6), %(\basereg+7), \mem_operand ++ pixdeinterleave \elem_size, %(\basereg+4) ++.elseif \numbytes == 16 ++ pixld2_s \elem_size, %(\basereg+2), %(\basereg+3), \mem_operand ++.elseif \numbytes == 8 ++ pixld1_s \elem_size, %(\basereg+1), \mem_operand ++.elseif \numbytes == 4 ++ .if \elem_size == 32 ++ pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand ++ .elseif \elem_size == 16 ++ pixld0_s \elem_size, %(\basereg+0), 2, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 3, \mem_operand + .else +- pixld0_s elem_size, %(basereg+0), 4, mem_operand +- pixld0_s elem_size, %(basereg+0), 5, mem_operand +- pixld0_s elem_size, %(basereg+0), 6, mem_operand +- pixld0_s elem_size, %(basereg+0), 7, mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 4, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 5, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 6, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 7, \mem_operand + .endif +-.elseif numbytes == 2 +- .if elem_size == 16 +- pixld0_s elem_size, %(basereg+0), 1, mem_operand ++.elseif \numbytes == 2 ++ .if \elem_size == 16 ++ pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand + .else +- pixld0_s elem_size, %(basereg+0), 2, mem_operand +- pixld0_s elem_size, %(basereg+0), 3, mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 2, \mem_operand ++ pixld0_s \elem_size, %(\basereg+0), 3, \mem_operand + .endif +-.elseif numbytes == 1 +- pixld0_s elem_size, %(basereg+0), 1, mem_operand ++.elseif \numbytes == 1 ++ pixld0_s \elem_size, %(\basereg+0), 1, \mem_operand + .else +- .error "unsupported size: numbytes" ++ .error "unsupported size: \numbytes" + .endif + .endm + + .macro pixld_s numpix, bpp, basereg, mem_operand +-.if bpp > 0 +- pixld_s_internal %(numpix * bpp / 8), %(bpp), basereg, mem_operand ++.if \bpp > 0 ++ pixld_s_internal %(\numpix * \bpp / 8), %(\bpp), \basereg, \mem_operand + .endif + .endm + + .macro vuzp8 reg1, reg2 + umov DUMMY, v16.d[0] +- uzp1 v16.8b, v®1&.8b, v®2&.8b +- uzp2 v®2&.8b, v®1&.8b, v®2&.8b +- mov v®1&.8b, v16.8b ++ uzp1 v16.8b, v\()\reg1\().8b, v\()\reg2\().8b ++ uzp2 v\()\reg2\().8b, v\()\reg1\().8b, v\()\reg2\().8b ++ mov v\()\reg1\().8b, v16.8b + mov v16.d[0], DUMMY + .endm + + .macro vzip8 reg1, reg2 + umov DUMMY, v16.d[0] +- zip1 v16.8b, v®1&.8b, v®2&.8b +- zip2 v®2&.8b, v®1&.8b, v®2&.8b +- mov v®1&.8b, v16.8b ++ zip1 v16.8b, v\()\reg1\().8b, v\()\reg2\().8b ++ zip2 v\()\reg2\().8b, v\()\reg1\().8b, v\()\reg2\().8b ++ mov v\()\reg1\().8b, v16.8b + mov v16.d[0], DUMMY + .endm + + /* deinterleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ + .macro pixdeinterleave bpp, basereg +-.if (bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) +- vuzp8 %(basereg+0), %(basereg+1) +- vuzp8 %(basereg+2), %(basereg+3) +- vuzp8 %(basereg+1), %(basereg+3) +- vuzp8 %(basereg+0), %(basereg+2) ++.if (\bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) ++ vuzp8 %(\basereg+0), %(\basereg+1) ++ vuzp8 %(\basereg+2), %(\basereg+3) ++ vuzp8 %(\basereg+1), %(\basereg+3) ++ vuzp8 %(\basereg+0), %(\basereg+2) + .endif + .endm + + /* interleave B, G, R, A channels for eight 32bpp pixels in 4 registers */ + .macro pixinterleave bpp, basereg +-.if (bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) +- vzip8 %(basereg+0), %(basereg+2) +- vzip8 %(basereg+1), %(basereg+3) +- vzip8 %(basereg+2), %(basereg+3) +- vzip8 %(basereg+0), %(basereg+1) ++.if (\bpp == 32) && (DEINTERLEAVE_32BPP_ENABLED != 0) ++ vzip8 %(\basereg+0), %(\basereg+2) ++ vzip8 %(\basereg+1), %(\basereg+3) ++ vzip8 %(\basereg+2), %(\basereg+3) ++ vzip8 %(\basereg+0), %(\basereg+1) + .endif + .endm + + /* + * This is a macro for implementing cache preload. The main idea is that + * cache preload logic is mostly independent from the rest of pixels + * processing code. It starts at the top left pixel and moves forward + * across pixels and can jump across scanlines. Prefetch distance is +@@ -432,62 +432,62 @@ 55: + * for almost zero cost! + * + * (*) The overhead of the prefetcher is visible when running some trivial + * pixels processing like simple copy. Anyway, having prefetch is a must + * when working with the graphics data. + */ + .macro PF a, x:vararg + .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_ADVANCED) +- a x ++ \a \x + .endif + .endm + + .macro cache_preload std_increment, boost_increment + .if (src_bpp_shift >= 0) || (dst_r_bpp != 0) || (mask_bpp_shift >= 0) +-.if std_increment != 0 +- PF add PF_X, PF_X, #std_increment ++.if \std_increment != 0 ++ PF add, PF_X, PF_X, #\std_increment + .endif +- PF tst PF_CTL, #0xF +- PF beq 71f +- PF add PF_X, PF_X, #boost_increment +- PF sub PF_CTL, PF_CTL, #1 ++ PF tst, PF_CTL, #0xF ++ PF beq, 71f ++ PF add, PF_X, PF_X, #\boost_increment ++ PF sub, PF_CTL, PF_CTL, #1 + 71: +- PF cmp PF_X, ORIG_W ++ PF cmp, PF_X, ORIG_W + .if src_bpp_shift >= 0 +- PF lsl DUMMY, PF_X, #src_bpp_shift +- PF prfm PREFETCH_MODE, [PF_SRC, DUMMY] ++ PF lsl, DUMMY, PF_X, #src_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_SRC, DUMMY] + .endif + .if dst_r_bpp != 0 +- PF lsl DUMMY, PF_X, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [PF_DST, DUMMY] ++ PF lsl, DUMMY, PF_X, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_DST, DUMMY] + .endif + .if mask_bpp_shift >= 0 +- PF lsl DUMMY, PF_X, #mask_bpp_shift +- PF prfm PREFETCH_MODE, [PF_MASK, DUMMY] ++ PF lsl, DUMMY, PF_X, #mask_bpp_shift ++ PF prfm, PREFETCH_MODE, [PF_MASK, DUMMY] + .endif +- PF ble 71f +- PF sub PF_X, PF_X, ORIG_W +- PF subs PF_CTL, PF_CTL, #0x10 ++ PF ble, 71f ++ PF sub, PF_X, PF_X, ORIG_W ++ PF subs, PF_CTL, PF_CTL, #0x10 + 71: +- PF ble 72f ++ PF ble, 72f + .if src_bpp_shift >= 0 +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF ldrsb DUMMY, [PF_SRC, DUMMY] +- PF add PF_SRC, PF_SRC, #1 ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF ldrsb, DUMMY, [PF_SRC, DUMMY] ++ PF add, PF_SRC, PF_SRC, #1 + .endif + .if dst_r_bpp != 0 +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF ldrsb DUMMY, [PF_DST, DUMMY] +- PF add PF_DST, PF_DST, #1 ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF ldrsb, DUMMY, [PF_DST, DUMMY] ++ PF add, PF_DST, PF_DST, #1 + .endif + .if mask_bpp_shift >= 0 +- PF lsl DUMMY, MASK_STRIDE, #mask_bpp_shift +- PF ldrsb DUMMY, [PF_MASK, DUMMY] +- PF add PF_MASK, PF_MASK, #1 ++ PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift ++ PF ldrsb, DUMMY, [PF_MASK, DUMMY] ++ PF add, PF_MASK, PF_MASK, #1 + .endif + 72: + .endif + .endm + + .macro cache_preload_simple + .if (PREFETCH_TYPE_CURRENT == PREFETCH_TYPE_SIMPLE) + .if src_bpp > 0 +@@ -516,56 +516,56 @@ 72: + process_pixblock_tail, \ + process_pixblock_tail_head + .if dst_w_bpp != 24 + tst DST_R, #0xF + beq 52f + + .if src_bpp > 0 || mask_bpp > 0 || dst_r_bpp > 0 + .irp lowbit, 1, 2, 4, 8, 16 +-local skip1 +-.if (dst_w_bpp <= (lowbit * 8)) && ((lowbit * 8) < (pixblock_size * dst_w_bpp)) +-.if lowbit < 16 /* we don't need more than 16-byte alignment */ +- tst DST_R, #lowbit ++ ++.if (dst_w_bpp <= (\lowbit * 8)) && ((\lowbit * 8) < (pixblock_size * dst_w_bpp)) ++.if \lowbit < 16 /* we don't need more than 16-byte alignment */ ++ tst DST_R, #\lowbit + beq 51f + .endif +- pixld_src (lowbit * 8 / dst_w_bpp), src_bpp, src_basereg, SRC +- pixld (lowbit * 8 / dst_w_bpp), mask_bpp, mask_basereg, MASK ++ pixld_src (\lowbit * 8 / dst_w_bpp), src_bpp, src_basereg, SRC ++ pixld (\lowbit * 8 / dst_w_bpp), mask_bpp, mask_basereg, MASK + .if dst_r_bpp > 0 +- pixld_a (lowbit * 8 / dst_r_bpp), dst_r_bpp, dst_r_basereg, DST_R ++ pixld_a (\lowbit * 8 / dst_r_bpp), dst_r_bpp, dst_r_basereg, DST_R + .else +- add DST_R, DST_R, #lowbit ++ add DST_R, DST_R, #\lowbit + .endif +- PF add PF_X, PF_X, #(lowbit * 8 / dst_w_bpp) +- sub W, W, #(lowbit * 8 / dst_w_bpp) ++ PF add, PF_X, PF_X, #(\lowbit * 8 / dst_w_bpp) ++ sub W, W, #(\lowbit * 8 / dst_w_bpp) + 51: + .endif + .endr + .endif + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + +- process_pixblock_head ++ \process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple +- process_pixblock_tail ++ \process_pixblock_tail + + pixinterleave dst_w_bpp, dst_w_basereg + + .irp lowbit, 1, 2, 4, 8, 16 +-.if (dst_w_bpp <= (lowbit * 8)) && ((lowbit * 8) < (pixblock_size * dst_w_bpp)) +-.if lowbit < 16 /* we don't need more than 16-byte alignment */ +- tst DST_W, #lowbit ++.if (dst_w_bpp <= (\lowbit * 8)) && ((\lowbit * 8) < (pixblock_size * dst_w_bpp)) ++.if \lowbit < 16 /* we don't need more than 16-byte alignment */ ++ tst DST_W, #\lowbit + beq 51f + .endif + .if src_bpp == 0 && mask_bpp == 0 && dst_r_bpp == 0 +- sub W, W, #(lowbit * 8 / dst_w_bpp) ++ sub W, W, #(\lowbit * 8 / dst_w_bpp) + .endif +- pixst_a (lowbit * 8 / dst_w_bpp), dst_w_bpp, dst_w_basereg, DST_W ++ pixst_a (\lowbit * 8 / dst_w_bpp), dst_w_bpp, dst_w_basereg, DST_W + 51: + .endif + .endr + .endif + 52: + .endm + + /* +@@ -587,52 +587,52 @@ 52: + dst_aligned_flag, \ + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head + tst W, #(pixblock_size - 1) + beq 52f + .if src_bpp > 0 || mask_bpp > 0 || dst_r_bpp > 0 + .irp chunk_size, 16, 8, 4, 2, 1 +-.if pixblock_size > chunk_size +- tst W, #chunk_size ++.if pixblock_size > \chunk_size ++ tst W, #\chunk_size + beq 51f +- pixld_src chunk_size, src_bpp, src_basereg, SRC +- pixld chunk_size, mask_bpp, mask_basereg, MASK +-.if dst_aligned_flag != 0 +- pixld_a chunk_size, dst_r_bpp, dst_r_basereg, DST_R ++ pixld_src \chunk_size, src_bpp, src_basereg, SRC ++ pixld \chunk_size, mask_bpp, mask_basereg, MASK ++.if \dst_aligned_flag != 0 ++ pixld_a \chunk_size, dst_r_bpp, dst_r_basereg, DST_R + .else +- pixld chunk_size, dst_r_bpp, dst_r_basereg, DST_R ++ pixld \chunk_size, dst_r_bpp, dst_r_basereg, DST_R + .endif +-.if cache_preload_flag != 0 +- PF add PF_X, PF_X, #chunk_size ++.if \cache_preload_flag != 0 ++ PF add, PF_X, PF_X, #\chunk_size + .endif + 51: + .endif + .endr + .endif + pixdeinterleave src_bpp, src_basereg + pixdeinterleave mask_bpp, mask_basereg + pixdeinterleave dst_r_bpp, dst_r_basereg + +- process_pixblock_head +-.if cache_preload_flag != 0 ++ \process_pixblock_head ++.if \cache_preload_flag != 0 + cache_preload 0, pixblock_size + cache_preload_simple + .endif +- process_pixblock_tail ++ \process_pixblock_tail + pixinterleave dst_w_bpp, dst_w_basereg + .irp chunk_size, 16, 8, 4, 2, 1 +-.if pixblock_size > chunk_size +- tst W, #chunk_size ++.if pixblock_size > \chunk_size ++ tst W, #\chunk_size + beq 51f +-.if dst_aligned_flag != 0 +- pixst_a chunk_size, dst_w_bpp, dst_w_basereg, DST_W ++.if \dst_aligned_flag != 0 ++ pixst_a \chunk_size, dst_w_bpp, dst_w_basereg, DST_W + .else +- pixst chunk_size, dst_w_bpp, dst_w_basereg, DST_W ++ pixst \chunk_size, dst_w_bpp, dst_w_basereg, DST_W + .endif + 51: + .endif + .endr + 52: + .endm + + /* +@@ -655,17 +655,17 @@ 52: + .if (src_bpp != 24) && (src_bpp != 0) + sub SRC, SRC, W, lsl #src_bpp_shift + .endif + .if (mask_bpp != 24) && (mask_bpp != 0) + sub MASK, MASK, W, lsl #mask_bpp_shift + .endif + subs H, H, #1 + mov DST_R, DST_W +- bge start_of_loop_label ++ bge \start_of_loop_label + .endm + + /* + * Registers are allocated in the following way by default: + * v0, v1, v2, v3 - reserved for loading source pixel data + * v4, v5, v6, v7 - reserved for loading destination pixel data + * v24, v25, v26, v27 - reserved for loading mask pixel data + * v28, v29, v30, v31 - final destination pixel data for writeback to memory +@@ -682,17 +682,17 @@ 52: + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + +- pixman_asm_function fname ++ pixman_asm_function \fname + stp x29, x30, [sp, -16]! + mov x29, sp + sub sp, sp, 232 /* push all registers */ + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], #32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], #32 + stp x8, x9, [x29, -80] + stp x10, x11, [x29, -96] +@@ -707,38 +707,38 @@ 52: + str x28, [x29, -232] + + /* + * Select prefetch type for this function. If prefetch distance is + * set to 0 or one of the color formats is 24bpp, SIMPLE prefetch + * has to be used instead of ADVANCED. + */ + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_DEFAULT +-.if prefetch_distance == 0 ++.if \prefetch_distance == 0 + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE + .elseif (PREFETCH_TYPE_CURRENT > PREFETCH_TYPE_SIMPLE) && \ +- ((src_bpp_ == 24) || (mask_bpp_ == 24) || (dst_w_bpp_ == 24)) ++ ((\src_bpp_ == 24) || (\mask_bpp_ == 24) || (\dst_w_bpp_ == 24)) + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_SIMPLE + .endif + + /* + * Make some macro arguments globally visible and accessible + * from other macros + */ +- .set src_bpp, src_bpp_ +- .set mask_bpp, mask_bpp_ +- .set dst_w_bpp, dst_w_bpp_ +- .set pixblock_size, pixblock_size_ +- .set dst_w_basereg, dst_w_basereg_ +- .set dst_r_basereg, dst_r_basereg_ +- .set src_basereg, src_basereg_ +- .set mask_basereg, mask_basereg_ ++ .set src_bpp, \src_bpp_ ++ .set mask_bpp, \mask_bpp_ ++ .set dst_w_bpp, \dst_w_bpp_ ++ .set pixblock_size, \pixblock_size_ ++ .set dst_w_basereg, \dst_w_basereg_ ++ .set dst_r_basereg, \dst_r_basereg_ ++ .set src_basereg, \src_basereg_ ++ .set mask_basereg, \mask_basereg_ + + .macro pixld_src x:vararg +- pixld x ++ pixld \x + .endm + .macro fetch_src_pixblock + pixld_src pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + .endm + /* + * Assign symbolic names to registers + */ +@@ -805,32 +805,32 @@ 52: + .elseif dst_w_bpp == 16 + .set dst_bpp_shift, 1 + .elseif dst_w_bpp == 8 + .set dst_bpp_shift, 0 + .else + .error "requested dst bpp (dst_w_bpp) is not supported" + .endif + +-.if (((flags) & FLAG_DST_READWRITE) != 0) ++.if (((\flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp + .else + .set dst_r_bpp, 0 + .endif +-.if (((flags) & FLAG_DEINTERLEAVE_32BPP) != 0) ++.if (((\flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 + .else + .set DEINTERLEAVE_32BPP_ENABLED, 0 + .endif + +-.if prefetch_distance < 0 || prefetch_distance > 15 +- .error "invalid prefetch distance (prefetch_distance)" ++.if \prefetch_distance < 0 || \prefetch_distance > 15 ++ .error "invalid prefetch distance (\prefetch_distance)" + .endif + +- PF mov PF_X, #0 ++ PF mov, PF_X, #0 + mov DST_R, DST_W + + .if src_bpp == 24 + sub SRC_STRIDE, SRC_STRIDE, W + sub SRC_STRIDE, SRC_STRIDE, W, lsl #1 + .endif + .if mask_bpp == 24 + sub MASK_STRIDE, MASK_STRIDE, W +@@ -839,71 +839,71 @@ 52: + .if dst_w_bpp == 24 + sub DST_STRIDE, DST_STRIDE, W + sub DST_STRIDE, DST_STRIDE, W, lsl #1 + .endif + + /* + * Setup advanced prefetcher initial state + */ +- PF mov PF_SRC, SRC +- PF mov PF_DST, DST_R +- PF mov PF_MASK, MASK +- /* PF_CTL = prefetch_distance | ((h - 1) << 4) */ +- PF lsl DUMMY, H, #4 +- PF mov PF_CTL, DUMMY +- PF add PF_CTL, PF_CTL, #(prefetch_distance - 0x10) ++ PF mov, PF_SRC, SRC ++ PF mov, PF_DST, DST_R ++ PF mov, PF_MASK, MASK ++ /* PF_CTL = \prefetch_distance | ((h - 1) << 4) */ ++ PF lsl, DUMMY, H, #4 ++ PF mov, PF_CTL, DUMMY ++ PF add, PF_CTL, PF_CTL, #(\prefetch_distance - 0x10) + +- init ++ \init + subs H, H, #1 + mov ORIG_W, W + blt 9f + cmp W, #(pixblock_size * 2) + blt 800f + /* + * This is the start of the pipelined loop, which if optimized for + * long scanlines + */ + 0: +- ensure_destination_ptr_alignment process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ ensure_destination_ptr_alignment \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK +- PF add PF_X, PF_X, #pixblock_size +- process_pixblock_head ++ PF add, PF_X, PF_X, #pixblock_size ++ \process_pixblock_head + cache_preload 0, pixblock_size + cache_preload_simple + subs W, W, #(pixblock_size * 2) + blt 200f + + 100: +- process_pixblock_tail_head ++ \process_pixblock_tail_head + cache_preload_simple + subs W, W, #pixblock_size + bge 100b + + 200: +- process_pixblock_tail ++ \process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 1, 1, \ +- process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + advance_to_next_scanline 0b + +- cleanup ++ \cleanup + 1000: + /* pop all registers */ + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x8, x9, [x29, -80] + ldp x10, x11, [x29, -96] + ldp x12, x13, [x29, -112] +@@ -920,48 +920,48 @@ 1000: + ret /* exit */ + /* + * This is the start of the loop, designed to process images with small width + * (less than pixblock_size * 2 pixels). In this case neither pipelining + * nor prefetch are used. + */ + 800: + .if src_bpp_shift >= 0 +- PF lsl DUMMY, SRC_STRIDE, #src_bpp_shift +- PF prfm PREFETCH_MODE, [SRC, DUMMY] ++ PF lsl, DUMMY, SRC_STRIDE, #src_bpp_shift ++ PF prfm, PREFETCH_MODE, [SRC, DUMMY] + .endif + .if dst_r_bpp != 0 +- PF lsl DUMMY, DST_STRIDE, #dst_bpp_shift +- PF prfm PREFETCH_MODE, [DST_R, DUMMY] ++ PF lsl, DUMMY, DST_STRIDE, #dst_bpp_shift ++ PF prfm, PREFETCH_MODE, [DST_R, DUMMY] + .endif + .if mask_bpp_shift >= 0 +- PF lsl DUMMY, MASK_STRIDE, #mask_bpp_shift +- PF prfm PREFETCH_MODE, [MASK, DUMMY] ++ PF lsl, DUMMY, MASK_STRIDE, #mask_bpp_shift ++ PF prfm, PREFETCH_MODE, [MASK, DUMMY] + .endif + /* Process exactly pixblock_size pixels if needed */ + tst W, #pixblock_size + beq 100f + pixld pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK +- process_pixblock_head +- process_pixblock_tail ++ \process_pixblock_head ++ \process_pixblock_tail + pixst pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + 100: + /* Process the remaining trailing pixels in the scanline */ + process_trailing_pixels 0, 0, \ +- process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + advance_to_next_scanline 800b + 9: +- cleanup ++ \cleanup + /* pop all registers */ + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x8, x9, [x29, -80] + ldp x10, x11, [x29, -96] + ldp x12, x13, [x29, -112] + ldp x14, x15, [x29, -128] +@@ -990,17 +990,17 @@ 9: + .unreq DST_STRIDE + .unreq MASK_STRIDE + .unreq PF_CTL + .unreq PF_X + .unreq PF_SRC + .unreq PF_DST + .unreq PF_MASK + .unreq DUMMY +- .endfunc ++ pixman_end_asm_function + .endm + + /* + * A simplified variant of function generation template for a single + * scanline processing (for implementing pixman combine functions) + */ + .macro generate_composite_function_scanline use_nearest_scaling, \ + fname, \ +@@ -1014,50 +1014,50 @@ 9: + process_pixblock_head, \ + process_pixblock_tail, \ + process_pixblock_tail_head, \ + dst_w_basereg_ = 28, \ + dst_r_basereg_ = 4, \ + src_basereg_ = 0, \ + mask_basereg_ = 24 + +- pixman_asm_function fname ++ pixman_asm_function \fname + .set PREFETCH_TYPE_CURRENT, PREFETCH_TYPE_NONE + + /* + * Make some macro arguments globally visible and accessible + * from other macros + */ +- .set src_bpp, src_bpp_ +- .set mask_bpp, mask_bpp_ +- .set dst_w_bpp, dst_w_bpp_ +- .set pixblock_size, pixblock_size_ +- .set dst_w_basereg, dst_w_basereg_ +- .set dst_r_basereg, dst_r_basereg_ +- .set src_basereg, src_basereg_ +- .set mask_basereg, mask_basereg_ ++ .set src_bpp, \src_bpp_ ++ .set mask_bpp, \mask_bpp_ ++ .set dst_w_bpp, \dst_w_bpp_ ++ .set pixblock_size, \pixblock_size_ ++ .set dst_w_basereg, \dst_w_basereg_ ++ .set dst_r_basereg, \dst_r_basereg_ ++ .set src_basereg, \src_basereg_ ++ .set mask_basereg, \mask_basereg_ + +-.if use_nearest_scaling != 0 ++.if \use_nearest_scaling != 0 + /* + * Assign symbolic names to registers for nearest scaling + */ + W .req x0 + DST_W .req x1 + SRC .req x2 + VX .req x3 + UNIT_X .req x4 + SRC_WIDTH_FIXED .req x5 + MASK .req x6 + TMP1 .req x8 + TMP2 .req x9 + DST_R .req x10 + DUMMY .req x30 + + .macro pixld_src x:vararg +- pixld_s x ++ pixld_s \x + .endm + + sxtw x0, w0 + sxtw x3, w3 + sxtw x4, w4 + sxtw x5, w5 + + stp x29, x30, [sp, -16]! +@@ -1075,84 +1075,84 @@ 9: + W .req x0 /* width (is updated during processing) */ + DST_W .req x1 /* destination buffer pointer for writes */ + SRC .req x2 /* source buffer pointer */ + MASK .req x3 /* mask pointer */ + DST_R .req x4 /* destination buffer pointer for reads */ + DUMMY .req x30 + + .macro pixld_src x:vararg +- pixld x ++ pixld \x + .endm + + sxtw x0, w0 + + stp x29, x30, [sp, -16]! + mov x29, sp + sub sp, sp, 64 + sub x29, x29, 64 + st1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + st1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + .endif + +-.if (((flags) & FLAG_DST_READWRITE) != 0) ++.if (((\flags) & FLAG_DST_READWRITE) != 0) + .set dst_r_bpp, dst_w_bpp + .else + .set dst_r_bpp, 0 + .endif +-.if (((flags) & FLAG_DEINTERLEAVE_32BPP) != 0) ++.if (((\flags) & FLAG_DEINTERLEAVE_32BPP) != 0) + .set DEINTERLEAVE_32BPP_ENABLED, 1 + .else + .set DEINTERLEAVE_32BPP_ENABLED, 0 + .endif + + .macro fetch_src_pixblock + pixld_src pixblock_size, src_bpp, \ + (src_basereg - pixblock_size * src_bpp / 64), SRC + .endm + +- init ++ \init + mov DST_R, DST_W + + cmp W, #pixblock_size + blt 800f + +- ensure_destination_ptr_alignment process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ ensure_destination_ptr_alignment \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + + subs W, W, #pixblock_size + blt 700f + + /* Implement "head (tail_head) ... (tail_head) tail" loop pattern */ + pixld_a pixblock_size, dst_r_bpp, \ + (dst_r_basereg - pixblock_size * dst_r_bpp / 64), DST_R + fetch_src_pixblock + pixld pixblock_size, mask_bpp, \ + (mask_basereg - pixblock_size * mask_bpp / 64), MASK +- process_pixblock_head ++ \process_pixblock_head + subs W, W, #pixblock_size + blt 200f + 100: +- process_pixblock_tail_head ++ \process_pixblock_tail_head + subs W, W, #pixblock_size + bge 100b + 200: +- process_pixblock_tail ++ \process_pixblock_tail + pixst_a pixblock_size, dst_w_bpp, \ + (dst_w_basereg - pixblock_size * dst_w_bpp / 64), DST_W + 700: + /* Process the remaining trailing pixels in the scanline (dst aligned) */ + process_trailing_pixels 0, 1, \ +- process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + +- cleanup +-.if use_nearest_scaling != 0 ++ \cleanup ++.if \use_nearest_scaling != 0 + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x8, x9, [x29, -80] + ldr x10, [x29, -96] + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ +@@ -1162,22 +1162,22 @@ 700: + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ + .endif + 800: + /* Process the remaining trailing pixels in the scanline (dst unaligned) */ + process_trailing_pixels 0, 0, \ +- process_pixblock_head, \ +- process_pixblock_tail, \ +- process_pixblock_tail_head ++ \process_pixblock_head, \ ++ \process_pixblock_tail, \ ++ \process_pixblock_tail_head + +- cleanup +-.if use_nearest_scaling != 0 ++ \cleanup ++.if \use_nearest_scaling != 0 + sub x29, x29, 64 + ld1 {v8.8b, v9.8b, v10.8b, v11.8b}, [x29], 32 + ld1 {v12.8b, v13.8b, v14.8b, v15.8b}, [x29], 32 + ldp x8, x9, [x29, -80] + ldr x10, [x29, -88] + mov sp, x29 + ldp x29, x30, [sp], 16 + ret /* exit */ +@@ -1208,25 +1208,25 @@ 800: + .unreq DST_R + .unreq DST_W + .unreq W + .endif + + .purgem fetch_src_pixblock + .purgem pixld_src + +- .endfunc ++ pixman_end_asm_function + .endm + + .macro generate_composite_function_single_scanline x:vararg +- generate_composite_function_scanline 0, x ++ generate_composite_function_scanline 0, \x + .endm + + .macro generate_composite_function_nearest_scanline x:vararg +- generate_composite_function_scanline 1, x ++ generate_composite_function_scanline 1, \x + .endm + + /* Default prologue/epilogue, nothing special needs to be done */ + + .macro default_init + .endm + + .macro default_cleanup +@@ -1250,61 +1250,61 @@ 800: + * Conversion of 8 r5g6b6 pixels packed in 128-bit register (in) + * into a planar a8r8g8b8 format (with a, r, g, b color components + * stored into 64-bit registers out_a, out_r, out_g, out_b respectively). + * + * Warning: the conversion is destructive and the original + * value (in) is lost. + */ + .macro convert_0565_to_8888 in, out_a, out_r, out_g, out_b +- shrn &out_r&.8b, &in&.8h, #8 +- shrn &out_g&.8b, &in&.8h, #3 +- sli &in&.8h, &in&.8h, #5 +- movi &out_a&.8b, #255 +- sri &out_r&.8b, &out_r&.8b, #5 +- sri &out_g&.8b, &out_g&.8b, #6 +- shrn &out_b&.8b, &in&.8h, #2 ++ shrn \()\out_r\().8b, \()\in\().8h, #8 ++ shrn \()\out_g\().8b, \()\in\().8h, #3 ++ sli \()\in\().8h, \()\in\().8h, #5 ++ movi \()\out_a\().8b, #255 ++ sri \()\out_r\().8b, \()\out_r\().8b, #5 ++ sri \()\out_g\().8b, \()\out_g\().8b, #6 ++ shrn \()\out_b\().8b, \()\in\().8h, #2 + .endm + + .macro convert_0565_to_x888 in, out_r, out_g, out_b +- shrn &out_r&.8b, &in&.8h, #8 +- shrn &out_g&.8b, &in&.8h, #3 +- sli &in&.8h, &in&.8h, #5 +- sri &out_r&.8b, &out_r&.8b, #5 +- sri &out_g&.8b, &out_g&.8b, #6 +- shrn &out_b&.8b, &in&.8h, #2 ++ shrn \()\out_r\().8b, \()\in\().8h, #8 ++ shrn \()\out_g\().8b, \()\in\().8h, #3 ++ sli \()\in\().8h, \()\in\().8h, #5 ++ sri \()\out_r\().8b, \()\out_r\().8b, #5 ++ sri \()\out_g\().8b, \()\out_g\().8b, #6 ++ shrn \()\out_b\().8b, \()\in\().8h, #2 + .endm + + /* + * Conversion from planar a8r8g8b8 format (with a, r, g, b color components + * in 64-bit registers in_a, in_r, in_g, in_b respectively) into 8 r5g6b6 + * pixels packed in 128-bit register (out). Requires two temporary 128-bit + * registers (tmp1, tmp2) + */ + .macro convert_8888_to_0565 in_r, in_g, in_b, out, tmp1, tmp2 +- ushll &tmp1&.8h, &in_g&.8b, #7 +- shl &tmp1&.8h, &tmp1&.8h, #1 +- ushll &out&.8h, &in_r&.8b, #7 +- shl &out&.8h, &out&.8h, #1 +- ushll &tmp2&.8h, &in_b&.8b, #7 +- shl &tmp2&.8h, &tmp2&.8h, #1 +- sri &out&.8h, &tmp1&.8h, #5 +- sri &out&.8h, &tmp2&.8h, #11 ++ ushll \()\tmp1\().8h, \()\in_g\().8b, #7 ++ shl \()\tmp1\().8h, \()\tmp1\().8h, #1 ++ ushll \()\out\().8h, \()\in_r\().8b, #7 ++ shl \()\out\().8h, \()\out\().8h, #1 ++ ushll \()\tmp2\().8h, \()\in_b\().8b, #7 ++ shl \()\tmp2\().8h, \()\tmp2\().8h, #1 ++ sri \()\out\().8h, \()\tmp1\().8h, #5 ++ sri \()\out\().8h, \()\tmp2\().8h, #11 + .endm + + /* + * Conversion of four r5g6b5 pixels (in) to four x8r8g8b8 pixels + * returned in (out0, out1) registers pair. Requires one temporary + * 64-bit register (tmp). 'out1' and 'in' may overlap, the original + * value from 'in' is lost + */ + .macro convert_four_0565_to_x888_packed in, out0, out1, tmp +- shl &out0&.4h, &in&.4h, #5 /* G top 6 bits */ +- shl &tmp&.4h, &in&.4h, #11 /* B top 5 bits */ +- sri &in&.4h, &in&.4h, #5 /* R is ready in top bits */ +- sri &out0&.4h, &out0&.4h, #6 /* G is ready in top bits */ +- sri &tmp&.4h, &tmp&.4h, #5 /* B is ready in top bits */ +- ushr &out1&.4h, &in&.4h, #8 /* R is in place */ +- sri &out0&.4h, &tmp&.4h, #8 /* G & B is in place */ +- zip1 &tmp&.4h, &out0&.4h, &out1&.4h /* everything is in place */ +- zip2 &out1&.4h, &out0&.4h, &out1&.4h +- mov &out0&.d[0], &tmp&.d[0] ++ shl \()\out0\().4h, \()\in\().4h, #5 /* G top 6 bits */ ++ shl \()\tmp\().4h, \()\in\().4h, #11 /* B top 5 bits */ ++ sri \()\in\().4h, \()\in\().4h, #5 /* R is ready \in top bits */ ++ sri \()\out0\().4h, \()\out0\().4h, #6 /* G is ready \in top bits */ ++ sri \()\tmp\().4h, \()\tmp\().4h, #5 /* B is ready \in top bits */ ++ ushr \()\out1\().4h, \()\in\().4h, #8 /* R is \in place */ ++ sri \()\out0\().4h, \()\tmp\().4h, #8 /* G \() B is \in place */ ++ zip1 \()\tmp\().4h, \()\out0\().4h, \()\out1\().4h /* everything is \in place */ ++ zip2 \()\out1\().4h, \()\out0\().4h, \()\out1\().4h ++ mov \()\out0\().d[0], \()\tmp\().d[0] + .endm diff --git a/gfx/cairo/pixman-clangcl.patch b/gfx/cairo/pixman-clangcl.patch new file mode 100644 index 0000000000..efcbf75dc7 --- /dev/null +++ b/gfx/cairo/pixman-clangcl.patch @@ -0,0 +1,31 @@ +https://gitlab.freedesktop.org/pixman/pixman/-/issues/41 + +diff --git a/gfx/cairo/libpixman/src/pixman-mmx.c b/gfx/cairo/libpixman/src/pixman-mmx.c +--- a/gfx/cairo/libpixman/src/pixman-mmx.c ++++ b/gfx/cairo/libpixman/src/pixman-mmx.c +@@ -132,23 +132,23 @@ extern __inline __m64 __attribute__((__g + /* If __m64 is defined as a struct or union, then define M64_MEMBER to be + * the name of the member used to access the data. + * If __m64 requires using mm_cvt* intrinsics functions to convert between + * uint64_t and __m64 values, then define USE_CVT_INTRINSICS. + * If __m64 and uint64_t values can just be cast to each other directly, + * then define USE_M64_CASTS. + * If __m64 is a double datatype, then define USE_M64_DOUBLE. + */ +-#ifdef _MSC_VER ++#if defined(_MSC_VER) && !defined(__clang__) + # define M64_MEMBER m64_u64 + #elif defined(__ICC) + # define USE_CVT_INTRINSICS + #elif defined(USE_LOONGSON_MMI) + # define USE_M64_DOUBLE +-#elif defined(__GNUC__) ++#elif defined(__GNUC__) || defined(__clang__) + # define USE_M64_CASTS + #elif defined(__SUNPRO_C) + # if (__SUNPRO_C >= 0x5120) && !defined(__NOVECTORSIZE__) + /* Solaris Studio 12.3 (Sun C 5.12) introduces __attribute__(__vector_size__) + * support, and defaults to using it to define __m64, unless __NOVECTORSIZE__ + * is defined. If it is used, then the mm_cvt* intrinsics must be used. + */ + # define USE_CVT_INTRINSICS diff --git a/gfx/cairo/pixman-export.patch b/gfx/cairo/pixman-export.patch new file mode 100644 index 0000000000..ef3f619a4b --- /dev/null +++ b/gfx/cairo/pixman-export.patch @@ -0,0 +1,51 @@ +diff --git a/gfx/cairo/libpixman/src/pixman-compiler.h b/gfx/cairo/libpixman/src/pixman-compiler.h +--- a/gfx/cairo/libpixman/src/pixman-compiler.h ++++ b/gfx/cairo/libpixman/src/pixman-compiler.h +@@ -84,28 +84,47 @@ + # ifndef force_inline + # define force_inline inline + # endif + # ifndef noinline + # define noinline + # endif + #endif + ++/* In libxul builds we don't ever want to export pixman symbols */ ++#if 1 ++#include "prcpucfg.h" ++ ++#ifdef HAVE_VISIBILITY_HIDDEN_ATTRIBUTE ++#define CVISIBILITY_HIDDEN __attribute__((visibility("hidden"))) ++#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) ++#define CVISIBILITY_HIDDEN __hidden ++#else ++#define CVISIBILITY_HIDDEN ++#endif ++ ++/* In libxul builds we don't ever want to export cairo symbols */ ++#define PIXMAN_EXPORT extern CVISIBILITY_HIDDEN ++ ++#else ++ + /* GCC visibility */ + #if defined(__GNUC__) && __GNUC__ >= 4 && !defined(_WIN32) + # define PIXMAN_EXPORT __attribute__ ((visibility("default"))) + /* Sun Studio 8 visibility */ + #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) + # define PIXMAN_EXPORT __global + #elif defined (_MSC_VER) || defined(__MINGW32__) + # define PIXMAN_EXPORT PIXMAN_API + #else + # define PIXMAN_EXPORT + #endif + ++#endif ++ + /* member offsets */ + #define CONTAINER_OF(type, member, data) \ + ((type *)(((uint8_t *)data) - offsetof (type, member))) + + /* TLS */ + #if defined(PIXMAN_NO_TLS) + + # define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \ diff --git a/gfx/cairo/pixman-interp.patch b/gfx/cairo/pixman-interp.patch new file mode 100644 index 0000000000..263df0b9d8 --- /dev/null +++ b/gfx/cairo/pixman-interp.patch @@ -0,0 +1,31 @@ +diff --git a/gfx/cairo/libpixman/src/pixman-private.h b/gfx/cairo/libpixman/src/pixman-private.h +--- a/gfx/cairo/libpixman/src/pixman-private.h ++++ b/gfx/cairo/libpixman/src/pixman-private.h +@@ -1,17 +1,26 @@ + #ifndef PIXMAN_PRIVATE_H + #define PIXMAN_PRIVATE_H + + /* + * The defines which are shared between C and assembly code + */ + +-/* bilinear interpolation precision (must be < 8) */ ++/* bilinear interpolation precision (must be <= 8) */ ++#ifndef MOZILLA_VERSION ++#error "Need mozilla headers" ++#endif ++#ifdef MOZ_GFX_OPTIMIZE_MOBILE ++#define LOW_QUALITY_INTERPOLATION ++#define LOWER_QUALITY_INTERPOLATION ++#define BILINEAR_INTERPOLATION_BITS 4 ++#else + #define BILINEAR_INTERPOLATION_BITS 7 ++#endif + #define BILINEAR_INTERPOLATION_RANGE (1 << BILINEAR_INTERPOLATION_BITS) + + /* + * C specific part + */ + + #ifndef __ASSEMBLER__ + diff --git a/gfx/cairo/pixman-intrin.patch b/gfx/cairo/pixman-intrin.patch new file mode 100644 index 0000000000..66a4912cbd --- /dev/null +++ b/gfx/cairo/pixman-intrin.patch @@ -0,0 +1,24 @@ +diff --git a/gfx/cairo/libpixman/src/pixman-x86.c b/gfx/cairo/libpixman/src/pixman-x86.c +--- a/gfx/cairo/libpixman/src/pixman-x86.c ++++ b/gfx/cairo/libpixman/src/pixman-x86.c +@@ -104,16 +104,20 @@ have_cpuid (void) + + return !!result; + + #else + #error "Unknown compiler" + #endif + } + ++#ifdef _MSC_VER ++#include /* for __cpuid */ ++#endif ++ + static void + pixman_cpuid (uint32_t feature, + uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) + { + #if defined (__GNUC__) + + #if _PIXMAN_X86_64 + __asm__ volatile ( diff --git a/gfx/cairo/pixman-mingw32.patch b/gfx/cairo/pixman-mingw32.patch new file mode 100644 index 0000000000..f2093904c3 --- /dev/null +++ b/gfx/cairo/pixman-mingw32.patch @@ -0,0 +1,26 @@ +# HG changeset patch +# User Ryan VanderMeulen +# Parent 6d9741f4bafc0e3ff3fc50009960966f03386af5 + +diff --git a/gfx/cairo/libpixman/src/pixman-mmx.c b/gfx/cairo/libpixman/src/pixman-mmx.c +--- a/gfx/cairo/libpixman/src/pixman-mmx.c ++++ b/gfx/cairo/libpixman/src/pixman-mmx.c +@@ -55,17 +55,17 @@ + extern __inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + _mm_empty (void) + { + + } + #endif + + #ifdef USE_X86_MMX +-# if (defined(__SUNPRO_C) || defined(_MSC_VER) || defined(_WIN64)) ++# if (defined(__SUNPRO_C) || defined(_MSC_VER) || defined(_WIN64)) || defined(__MINGW32__) + # include + # else + /* We have to compile with -msse to use xmmintrin.h, but that causes SSE + * instructions to be generated that we don't want. Just duplicate the + * functions we want to use. */ + extern __inline int __attribute__((__gnu_inline__, __always_inline__, __artificial__)) + _mm_movemask_pi8 (__m64 __A) + { diff --git a/gfx/cairo/pixman-rename.patch b/gfx/cairo/pixman-rename.patch new file mode 100644 index 0000000000..03ee56f152 --- /dev/null +++ b/gfx/cairo/pixman-rename.patch @@ -0,0 +1,24 @@ +diff --git a/gfx/cairo/libpixman/src/pixman.h b/gfx/cairo/libpixman/src/pixman.h +--- a/gfx/cairo/libpixman/src/pixman.h ++++ b/gfx/cairo/libpixman/src/pixman.h +@@ -64,16 +64,20 @@ SOFTWARE. + * 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. + */ + + #ifndef PIXMAN_H__ + #define PIXMAN_H__ + ++#ifdef MOZILLA_VERSION ++#include "cairo/pixman-rename.h" ++#endif ++ + #include + + #ifdef __cplusplus + #define PIXMAN_BEGIN_DECLS extern "C" { + #define PIXMAN_END_DECLS } + #else + #define PIXMAN_BEGIN_DECLS + #define PIXMAN_END_DECLS -- cgit v1.2.3