summaryrefslogtreecommitdiffstats
path: root/gfx/cairo
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /gfx/cairo
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/cairo')
-rw-r--r--gfx/cairo/README256
-rw-r--r--gfx/cairo/add-a-stash-of-cairo_t-s.patch75
-rw-r--r--gfx/cairo/avoid-extend-none.patch39
-rw-r--r--gfx/cairo/bgr.patch104
-rw-r--r--gfx/cairo/buggy-repeat.patch39
-rw-r--r--gfx/cairo/cache-size.patch19
-rw-r--r--gfx/cairo/cairo-clamp-boundary.patch71
-rw-r--r--gfx/cairo/cairo-mask-extends-bug.patch41
-rw-r--r--gfx/cairo/cairo-qt-compile.patch21
-rw-r--r--gfx/cairo/cairo-region-clip.patch34
-rw-r--r--gfx/cairo/cairo-version-fixes.patch26
-rw-r--r--gfx/cairo/cairo-x-visual.patch160
-rw-r--r--gfx/cairo/cairo/AUTHORS99
-rw-r--r--gfx/cairo/cairo/COPYING17
-rw-r--r--gfx/cairo/cairo/COPYING-LGPL-2.1510
-rw-r--r--gfx/cairo/cairo/COPYING-MPL-1.1470
-rw-r--r--gfx/cairo/cairo/INSTALL187
-rw-r--r--gfx/cairo/cairo/NEWS5121
-rw-r--r--gfx/cairo/cairo/README198
-rw-r--r--gfx/cairo/cairo/src/cairo-analysis-surface-private.h75
-rw-r--r--gfx/cairo/cairo/src/cairo-analysis-surface.c925
-rw-r--r--gfx/cairo/cairo/src/cairo-arc-private.h57
-rw-r--r--gfx/cairo/cairo/src/cairo-arc.c292
-rw-r--r--gfx/cairo/cairo/src/cairo-array.c562
-rw-r--r--gfx/cairo/cairo/src/cairo-atomic-private.h419
-rw-r--r--gfx/cairo/cairo/src/cairo-atomic.c120
-rw-r--r--gfx/cairo/cairo/src/cairo-base64-stream.c144
-rw-r--r--gfx/cairo/cairo/src/cairo-base85-stream.c131
-rw-r--r--gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c838
-rw-r--r--gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c667
-rw-r--r--gfx/cairo/cairo/src/cairo-bentley-ottmann.c2133
-rw-r--r--gfx/cairo/cairo/src/cairo-beos-surface.cpp981
-rw-r--r--gfx/cairo/cairo/src/cairo-beos.h60
-rw-r--r--gfx/cairo/cairo/src/cairo-botor-scan-converter.c2199
-rw-r--r--gfx/cairo/cairo/src/cairo-boxes-private.h84
-rw-r--r--gfx/cairo/cairo/src/cairo-boxes.c300
-rw-r--r--gfx/cairo/cairo/src/cairo-cache-private.h145
-rw-r--r--gfx/cairo/cairo/src/cairo-cache.c338
-rw-r--r--gfx/cairo/cairo/src/cairo-cff-subset.c2278
-rw-r--r--gfx/cairo/cairo/src/cairo-clip-private.h151
-rw-r--r--gfx/cairo/cairo/src/cairo-clip.c1590
-rw-r--r--gfx/cairo/cairo/src/cairo-color.c211
-rw-r--r--gfx/cairo/cairo/src/cairo-combsort-private.h71
-rw-r--r--gfx/cairo/cairo/src/cairo-compiler-private.h277
-rw-r--r--gfx/cairo/cairo/src/cairo-composite-rectangles-private.h105
-rw-r--r--gfx/cairo/cairo/src/cairo-composite-rectangles.c195
-rw-r--r--gfx/cairo/cairo/src/cairo-d2d-private-fx.h1164
-rw-r--r--gfx/cairo/cairo/src/cairo-d2d-private.fx96
-rw-r--r--gfx/cairo/cairo/src/cairo-d2d-private.h191
-rw-r--r--gfx/cairo/cairo/src/cairo-d2d-surface.cpp4866
-rw-r--r--gfx/cairo/cairo/src/cairo-debug.c252
-rw-r--r--gfx/cairo/cairo/src/cairo-deflate-stream.c151
-rw-r--r--gfx/cairo/cairo/src/cairo-deprecated.h123
-rw-r--r--gfx/cairo/cairo/src/cairo-device-private.h86
-rw-r--r--gfx/cairo/cairo/src/cairo-device.c533
-rw-r--r--gfx/cairo/cairo/src/cairo-directfb-surface.c1964
-rw-r--r--gfx/cairo/cairo/src/cairo-directfb.h67
-rw-r--r--gfx/cairo/cairo/src/cairo-drm.h120
-rw-r--r--gfx/cairo/cairo/src/cairo-dwrite-font.cpp1622
-rw-r--r--gfx/cairo/cairo/src/cairo-dwrite-private.h224
-rw-r--r--gfx/cairo/cairo/src/cairo-eagle-context.c181
-rw-r--r--gfx/cairo/cairo/src/cairo-error-private.h60
-rw-r--r--gfx/cairo/cairo/src/cairo-features-win32.h16
-rw-r--r--gfx/cairo/cairo/src/cairo-features.h.in95
-rw-r--r--gfx/cairo/cairo/src/cairo-fixed-private.h358
-rw-r--r--gfx/cairo/cairo/src/cairo-fixed-type-private.h75
-rw-r--r--gfx/cairo/cairo/src/cairo-fixed.c39
-rw-r--r--gfx/cairo/cairo/src/cairo-font-face-twin-data.c1072
-rw-r--r--gfx/cairo/cairo/src/cairo-font-face-twin.c761
-rw-r--r--gfx/cairo/cairo/src/cairo-font-face.c308
-rw-r--r--gfx/cairo/cairo/src/cairo-font-options.c513
-rw-r--r--gfx/cairo/cairo/src/cairo-fontconfig-private.h78
-rw-r--r--gfx/cairo/cairo/src/cairo-freed-pool-private.h129
-rw-r--r--gfx/cairo/cairo/src/cairo-freed-pool.c93
-rw-r--r--gfx/cairo/cairo/src/cairo-freelist-private.h139
-rw-r--r--gfx/cairo/cairo/src/cairo-freelist-type-private.h54
-rw-r--r--gfx/cairo/cairo/src/cairo-freelist.c191
-rw-r--r--gfx/cairo/cairo/src/cairo-ft-font.c3355
-rw-r--r--gfx/cairo/cairo/src/cairo-ft-private.h73
-rw-r--r--gfx/cairo/cairo/src/cairo-ft.h82
-rw-r--r--gfx/cairo/cairo/src/cairo-gl-glyphs.c605
-rw-r--r--gfx/cairo/cairo/src/cairo-gl-private.h485
-rw-r--r--gfx/cairo/cairo/src/cairo-gl-shaders.c995
-rw-r--r--gfx/cairo/cairo/src/cairo-gl-surface.c1637
-rw-r--r--gfx/cairo/cairo/src/cairo-gl.h119
-rw-r--r--gfx/cairo/cairo/src/cairo-glitz-private.h41
-rw-r--r--gfx/cairo/cairo/src/cairo-glitz-surface.c2446
-rw-r--r--gfx/cairo/cairo/src/cairo-glitz.h57
-rw-r--r--gfx/cairo/cairo/src/cairo-glx-context.c260
-rw-r--r--gfx/cairo/cairo/src/cairo-gstate-private.h385
-rw-r--r--gfx/cairo/cairo/src/cairo-gstate.c2331
-rw-r--r--gfx/cairo/cairo/src/cairo-hash-private.h87
-rw-r--r--gfx/cairo/cairo/src/cairo-hash.c542
-rw-r--r--gfx/cairo/cairo/src/cairo-hull.c235
-rw-r--r--gfx/cairo/cairo/src/cairo-image-info-private.h63
-rw-r--r--gfx/cairo/cairo/src/cairo-image-info.c290
-rw-r--r--gfx/cairo/cairo/src/cairo-image-surface.c4788
-rw-r--r--gfx/cairo/cairo/src/cairo-list-private.h215
-rw-r--r--gfx/cairo/cairo/src/cairo-lzw.c404
-rw-r--r--gfx/cairo/cairo/src/cairo-malloc-private.h148
-rw-r--r--gfx/cairo/cairo/src/cairo-matrix.c1006
-rw-r--r--gfx/cairo/cairo/src/cairo-meta-surface-private.h187
-rw-r--r--gfx/cairo/cairo/src/cairo-misc.c931
-rw-r--r--gfx/cairo/cairo/src/cairo-mutex-impl-private.h278
-rw-r--r--gfx/cairo/cairo/src/cairo-mutex-list-private.h78
-rw-r--r--gfx/cairo/cairo/src/cairo-mutex-private.h67
-rw-r--r--gfx/cairo/cairo/src/cairo-mutex-type-private.h194
-rw-r--r--gfx/cairo/cairo/src/cairo-mutex.c82
-rw-r--r--gfx/cairo/cairo/src/cairo-no-features.h12
-rw-r--r--gfx/cairo/cairo/src/cairo-observer.c50
-rw-r--r--gfx/cairo/cairo/src/cairo-os2-private.h67
-rw-r--r--gfx/cairo/cairo/src/cairo-os2-surface.c1474
-rw-r--r--gfx/cairo/cairo/src/cairo-os2.h110
-rw-r--r--gfx/cairo/cairo/src/cairo-output-stream-private.h196
-rw-r--r--gfx/cairo/cairo/src/cairo-output-stream.c769
-rw-r--r--gfx/cairo/cairo/src/cairo-paginated-private.h165
-rw-r--r--gfx/cairo/cairo/src/cairo-paginated-surface-private.h62
-rw-r--r--gfx/cairo/cairo/src/cairo-paginated-surface.c651
-rw-r--r--gfx/cairo/cairo/src/cairo-path-bounds.c350
-rw-r--r--gfx/cairo/cairo/src/cairo-path-fill.c465
-rw-r--r--gfx/cairo/cairo/src/cairo-path-fixed-private.h165
-rw-r--r--gfx/cairo/cairo/src/cairo-path-fixed.c1424
-rw-r--r--gfx/cairo/cairo/src/cairo-path-in-fill.c291
-rw-r--r--gfx/cairo/cairo/src/cairo-path-private.h57
-rw-r--r--gfx/cairo/cairo/src/cairo-path-stroke.c2143
-rw-r--r--gfx/cairo/cairo/src/cairo-path.c536
-rw-r--r--gfx/cairo/cairo/src/cairo-pattern.c3228
-rw-r--r--gfx/cairo/cairo/src/cairo-pdf-operators-private.h171
-rw-r--r--gfx/cairo/cairo/src/cairo-pdf-operators.c1470
-rw-r--r--gfx/cairo/cairo/src/cairo-pdf-surface-private.h196
-rw-r--r--gfx/cairo/cairo/src/cairo-pdf-surface.c6250
-rw-r--r--gfx/cairo/cairo/src/cairo-pdf.h94
-rw-r--r--gfx/cairo/cairo/src/cairo-pen.c398
-rw-r--r--gfx/cairo/cairo/src/cairo-platform.h69
-rw-r--r--gfx/cairo/cairo/src/cairo-png.c798
-rw-r--r--gfx/cairo/cairo/src/cairo-polygon.c492
-rw-r--r--gfx/cairo/cairo/src/cairo-private.h57
-rw-r--r--gfx/cairo/cairo/src/cairo-ps-surface-private.h109
-rw-r--r--gfx/cairo/cairo/src/cairo-ps-surface.c3929
-rw-r--r--gfx/cairo/cairo/src/cairo-ps.h114
-rw-r--r--gfx/cairo/cairo/src/cairo-qt-surface.cpp1748
-rw-r--r--gfx/cairo/cairo/src/cairo-qt.h85
-rw-r--r--gfx/cairo/cairo/src/cairo-quartz-font.c843
-rw-r--r--gfx/cairo/cairo/src/cairo-quartz-image-surface.c290
-rw-r--r--gfx/cairo/cairo/src/cairo-quartz-image.h64
-rw-r--r--gfx/cairo/cairo/src/cairo-quartz-private.h119
-rw-r--r--gfx/cairo/cairo/src/cairo-quartz-surface.c3800
-rw-r--r--gfx/cairo/cairo/src/cairo-quartz.h112
-rw-r--r--gfx/cairo/cairo/src/cairo-recording-surface-private.h171
-rw-r--r--gfx/cairo/cairo/src/cairo-recording-surface.c1134
-rw-r--r--gfx/cairo/cairo/src/cairo-rectangle.c266
-rw-r--r--gfx/cairo/cairo/src/cairo-rectangular-scan-converter.c723
-rw-r--r--gfx/cairo/cairo/src/cairo-reference-count-private.h61
-rw-r--r--gfx/cairo/cairo/src/cairo-region-private.h71
-rw-r--r--gfx/cairo/cairo/src/cairo-region.c905
-rw-r--r--gfx/cairo/cairo/src/cairo-rename.h411
-rw-r--r--gfx/cairo/cairo/src/cairo-rtree-private.h134
-rw-r--r--gfx/cairo/cairo/src/cairo-rtree.c385
-rw-r--r--gfx/cairo/cairo/src/cairo-scaled-font-private.h131
-rw-r--r--gfx/cairo/cairo/src/cairo-scaled-font-subsets-private.h668
-rw-r--r--gfx/cairo/cairo/src/cairo-scaled-font-subsets.c1091
-rw-r--r--gfx/cairo/cairo/src/cairo-scaled-font.c2990
-rw-r--r--gfx/cairo/cairo/src/cairo-script-surface.c3622
-rw-r--r--gfx/cairo/cairo/src/cairo-script.h89
-rw-r--r--gfx/cairo/cairo/src/cairo-skia.h84
-rw-r--r--gfx/cairo/cairo/src/cairo-slope-private.h72
-rw-r--r--gfx/cairo/cairo/src/cairo-slope.c99
-rw-r--r--gfx/cairo/cairo/src/cairo-spans-private.h189
-rw-r--r--gfx/cairo/cairo/src/cairo-spans.c323
-rw-r--r--gfx/cairo/cairo/src/cairo-spline.c371
-rw-r--r--gfx/cairo/cairo/src/cairo-stroke-style.c310
-rw-r--r--gfx/cairo/cairo/src/cairo-supported-features.h25
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-clipper-private.h72
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-clipper.c135
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-fallback-private.h139
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-fallback.c1638
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-offset-private.h95
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-offset.c342
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-private.h103
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-snapshot-private.h48
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-snapshot.c255
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-subsurface-private.h49
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-subsurface.c552
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-wrapper-private.h171
-rw-r--r--gfx/cairo/cairo/src/cairo-surface-wrapper.c713
-rw-r--r--gfx/cairo/cairo/src/cairo-surface.c3326
-rw-r--r--gfx/cairo/cairo/src/cairo-svg-surface-private.h74
-rw-r--r--gfx/cairo/cairo/src/cairo-svg-surface.c2848
-rw-r--r--gfx/cairo/cairo/src/cairo-svg.h82
-rw-r--r--gfx/cairo/cairo/src/cairo-system.c97
-rw-r--r--gfx/cairo/cairo/src/cairo-tee-surface-private.h47
-rw-r--r--gfx/cairo/cairo/src/cairo-tee-surface.c718
-rw-r--r--gfx/cairo/cairo/src/cairo-tee.h66
-rw-r--r--gfx/cairo/cairo/src/cairo-tor-scan-converter.c2220
-rw-r--r--gfx/cairo/cairo/src/cairo-toy-font-face.c526
-rw-r--r--gfx/cairo/cairo/src/cairo-traps.c605
-rw-r--r--gfx/cairo/cairo/src/cairo-truetype-subset-private.h203
-rw-r--r--gfx/cairo/cairo/src/cairo-truetype-subset.c1433
-rw-r--r--gfx/cairo/cairo/src/cairo-type1-fallback.c887
-rw-r--r--gfx/cairo/cairo/src/cairo-type1-private.h51
-rw-r--r--gfx/cairo/cairo/src/cairo-type1-subset.c1434
-rw-r--r--gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h87
-rw-r--r--gfx/cairo/cairo/src/cairo-type3-glyph-surface.c563
-rw-r--r--gfx/cairo/cairo/src/cairo-types-private.h484
-rw-r--r--gfx/cairo/cairo/src/cairo-unicode.c422
-rw-r--r--gfx/cairo/cairo/src/cairo-user-font-private.h46
-rw-r--r--gfx/cairo/cairo/src/cairo-user-font.c827
-rw-r--r--gfx/cairo/cairo/src/cairo-version.c244
-rw-r--r--gfx/cairo/cairo/src/cairo-version.h16
-rw-r--r--gfx/cairo/cairo/src/cairo-vg-surface.c1926
-rw-r--r--gfx/cairo/cairo/src/cairo-vg.h103
-rw-r--r--gfx/cairo/cairo/src/cairo-wideint-private.h320
-rw-r--r--gfx/cairo/cairo/src/cairo-wideint-type-private.h162
-rw-r--r--gfx/cairo/cairo/src/cairo-wideint.c819
-rw-r--r--gfx/cairo/cairo/src/cairo-win32-font.c2358
-rw-r--r--gfx/cairo/cairo/src/cairo-win32-printing-surface.c1987
-rw-r--r--gfx/cairo/cairo/src/cairo-win32-private.h249
-rw-r--r--gfx/cairo/cairo/src/cairo-win32-refptr.h178
-rw-r--r--gfx/cairo/cairo/src/cairo-win32-surface.c4102
-rw-r--r--gfx/cairo/cairo/src/cairo-win32.h339
-rw-r--r--gfx/cairo/cairo/src/cairo-xcb-surface.c1368
-rw-r--r--gfx/cairo/cairo/src/cairo-xcb-xrender.h63
-rw-r--r--gfx/cairo/cairo/src/cairo-xcb.h96
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib-display.c669
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib-private.h200
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib-screen.c466
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib-surface-private.h112
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib-surface.c4933
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib-visual.c187
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib-xrender-private.h1164
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib-xrender.h66
-rw-r--r--gfx/cairo/cairo/src/cairo-xlib.h100
-rw-r--r--gfx/cairo/cairo/src/cairo-xml-surface.c1152
-rw-r--r--gfx/cairo/cairo/src/cairo-xml.h67
-rw-r--r--gfx/cairo/cairo/src/cairo.c4201
-rw-r--r--gfx/cairo/cairo/src/cairo.h2729
-rw-r--r--gfx/cairo/cairo/src/cairoint.h2593
-rw-r--r--gfx/cairo/cairo/src/check-has-hidden-symbols.c3
-rw-r--r--gfx/cairo/cairo/src/check-link.c24
-rw-r--r--gfx/cairo/cairo/src/filterpublic.awk22
-rw-r--r--gfx/cairo/cairo/src/moz.build251
-rw-r--r--gfx/cairo/cairo/src/pixman-rename.h145
-rw-r--r--gfx/cairo/cairo/src/test-fallback-surface.c237
-rw-r--r--gfx/cairo/cairo/src/test-fallback-surface.h50
-rw-r--r--gfx/cairo/cairo/src/test-meta-surface.c342
-rw-r--r--gfx/cairo/cairo/src/test-meta-surface.h50
-rw-r--r--gfx/cairo/cairo/src/test-paginated-surface.c291
-rw-r--r--gfx/cairo/cairo/src/test-paginated-surface.h48
-rw-r--r--gfx/cairo/cairo_qt_a8_fallback.diff68
-rw-r--r--gfx/cairo/cairo_qt_glyphs.patch256
-rw-r--r--gfx/cairo/clip-invariant.patch1255
-rw-r--r--gfx/cairo/clip-rects-surface-extents.patch163
-rw-r--r--gfx/cairo/copyarea-with-alpha.patch110
-rw-r--r--gfx/cairo/d2d-gradient-ensure-stops.patch32
-rw-r--r--gfx/cairo/d2d-repeating-gradients.patch271
-rw-r--r--gfx/cairo/d2d.patch465
-rw-r--r--gfx/cairo/dasharray-zero-gap.patch60
-rw-r--r--gfx/cairo/disable-previous-scaled-font-cache.patch16
-rw-r--r--gfx/cairo/disable-printing.patch27
-rw-r--r--gfx/cairo/disable-server-gradients.patch21
-rw-r--r--gfx/cairo/disable-subpixel-antialiasing.patch519
-rw-r--r--gfx/cairo/dwrite-font-match-robustness.patch26
-rw-r--r--gfx/cairo/dwrite-font-printing.patch157
-rw-r--r--gfx/cairo/dwrite-glyph-extents.patch44
-rw-r--r--gfx/cairo/empty-clip-extents.patch59
-rw-r--r--gfx/cairo/empty-clip-rectangles.patch28
-rw-r--r--gfx/cairo/ensure-text-flushed.patch16
-rw-r--r--gfx/cairo/expose-snapshot.patch528
-rw-r--r--gfx/cairo/fix-build-with-Werror=return-type.patch21
-rw-r--r--gfx/cairo/fix-cairo-surface-wrapper-flush-build-warning.patch19
-rw-r--r--gfx/cairo/fix-cairo-win32-print-gdi-error.diff26
-rw-r--r--gfx/cairo/fix-clip-copy.patch30
-rw-r--r--gfx/cairo/fix-clip-region-simplification.patch1
-rw-r--r--gfx/cairo/fix-clip-test.patch15
-rw-r--r--gfx/cairo/fix-ps-output.patch19
-rw-r--r--gfx/cairo/fix-unnecessary-fallback.patch14
-rw-r--r--gfx/cairo/fix-win32-font-assertion.patch27
-rw-r--r--gfx/cairo/fix-win32-show-glyphs-clipping.patch19
-rw-r--r--gfx/cairo/fix-xcopyarea-with-clips.patch38
-rw-r--r--gfx/cairo/fix-zero-length-gradient.patch1
-rw-r--r--gfx/cairo/fixup-unbounded.patch22
-rw-r--r--gfx/cairo/ft-no-subpixel-if-surface-disables.patch46
-rw-r--r--gfx/cairo/gdi-RGB24-ARGB32.patch141
-rw-r--r--gfx/cairo/glitz/src/glitz.def0
-rw-r--r--gfx/cairo/glitz/src/wgl/glitz-wgl.def0
-rw-r--r--gfx/cairo/handle-a1.patch25
-rw-r--r--gfx/cairo/handle-multi-path-clip.patch57
-rw-r--r--gfx/cairo/ignore-rank0.patch20
-rw-r--r--gfx/cairo/libpixman/AUTHORS0
-rw-r--r--gfx/cairo/libpixman/COPYING0
-rw-r--r--gfx/cairo/libpixman/INSTALL234
-rw-r--r--gfx/cairo/libpixman/NEWS0
-rw-r--r--gfx/cairo/libpixman/README0
-rw-r--r--gfx/cairo/libpixman/TODO139
-rw-r--r--gfx/cairo/libpixman/src/Makefile.in10
-rw-r--r--gfx/cairo/libpixman/src/make-combine.pl86
-rw-r--r--gfx/cairo/libpixman/src/moz.build163
-rw-r--r--gfx/cairo/libpixman/src/pixman-access-accessors.c3
-rw-r--r--gfx/cairo/libpixman/src/pixman-access.c1492
-rw-r--r--gfx/cairo/libpixman/src/pixman-accessor.h25
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-common.h428
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-detect-win32.asm21
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S1368
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-neon-asm.S3650
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-neon-asm.h1204
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-neon.c513
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-simd-asm-scaled.S165
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-simd-asm.S613
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-simd-asm.h912
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm-simd.c257
-rw-r--r--gfx/cairo/libpixman/src/pixman-arm.c229
-rw-r--r--gfx/cairo/libpixman/src/pixman-bits-image.c1849
-rw-r--r--gfx/cairo/libpixman/src/pixman-combine-float.c1018
-rw-r--r--gfx/cairo/libpixman/src/pixman-combine.c.template2461
-rw-r--r--gfx/cairo/libpixman/src/pixman-combine.h.template226
-rw-r--r--gfx/cairo/libpixman/src/pixman-combine16.c114
-rw-r--r--gfx/cairo/libpixman/src/pixman-combine32.c2504
-rw-r--r--gfx/cairo/libpixman/src/pixman-combine32.h272
-rw-r--r--gfx/cairo/libpixman/src/pixman-combine64.c2465
-rw-r--r--gfx/cairo/libpixman/src/pixman-combine64.h230
-rw-r--r--gfx/cairo/libpixman/src/pixman-compiler.h247
-rw-r--r--gfx/cairo/libpixman/src/pixman-conical-gradient.c212
-rw-r--r--gfx/cairo/libpixman/src/pixman-cpu.c799
-rw-r--r--gfx/cairo/libpixman/src/pixman-dither.h51
-rw-r--r--gfx/cairo/libpixman/src/pixman-edge-accessors.c4
-rw-r--r--gfx/cairo/libpixman/src/pixman-edge-imp.h183
-rw-r--r--gfx/cairo/libpixman/src/pixman-edge.c385
-rw-r--r--gfx/cairo/libpixman/src/pixman-fast-path.c2590
-rw-r--r--gfx/cairo/libpixman/src/pixman-fast-path.h1022
-rw-r--r--gfx/cairo/libpixman/src/pixman-filter.c350
-rw-r--r--gfx/cairo/libpixman/src/pixman-general.c243
-rw-r--r--gfx/cairo/libpixman/src/pixman-glyph.c670
-rw-r--r--gfx/cairo/libpixman/src/pixman-gradient-walker.c172
-rw-r--r--gfx/cairo/libpixman/src/pixman-image.c967
-rw-r--r--gfx/cairo/libpixman/src/pixman-implementation.c405
-rw-r--r--gfx/cairo/libpixman/src/pixman-inlines.h1421
-rw-r--r--gfx/cairo/libpixman/src/pixman-linear-gradient.c444
-rw-r--r--gfx/cairo/libpixman/src/pixman-matrix.c1073
-rw-r--r--gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.S3373
-rw-r--r--gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.h681
-rw-r--r--gfx/cairo/libpixman/src/pixman-mips-dspr2.c411
-rw-r--r--gfx/cairo/libpixman/src/pixman-mips-dspr2.h396
-rw-r--r--gfx/cairo/libpixman/src/pixman-mips-memcpy-asm.S382
-rw-r--r--gfx/cairo/libpixman/src/pixman-mips.c94
-rw-r--r--gfx/cairo/libpixman/src/pixman-mmx.c4084
-rw-r--r--gfx/cairo/libpixman/src/pixman-noop.c176
-rw-r--r--gfx/cairo/libpixman/src/pixman-ppc.c155
-rw-r--r--gfx/cairo/libpixman/src/pixman-private.h1187
-rw-r--r--gfx/cairo/libpixman/src/pixman-radial-gradient.c727
-rw-r--r--gfx/cairo/libpixman/src/pixman-region.c2808
-rw-r--r--gfx/cairo/libpixman/src/pixman-region16.c67
-rw-r--r--gfx/cairo/libpixman/src/pixman-region32.c47
-rw-r--r--gfx/cairo/libpixman/src/pixman-solid-fill.c67
-rw-r--r--gfx/cairo/libpixman/src/pixman-sse2.c6560
-rw-r--r--gfx/cairo/libpixman/src/pixman-timer.c66
-rw-r--r--gfx/cairo/libpixman/src/pixman-trap.c711
-rw-r--r--gfx/cairo/libpixman/src/pixman-utils.c315
-rw-r--r--gfx/cairo/libpixman/src/pixman-version.h50
-rw-r--r--gfx/cairo/libpixman/src/pixman-vmx.c1647
-rw-r--r--gfx/cairo/libpixman/src/pixman-x64-mmx-emulation.h263
-rw-r--r--gfx/cairo/libpixman/src/pixman-x86.c241
-rw-r--r--gfx/cairo/libpixman/src/pixman.c1135
-rw-r--r--gfx/cairo/libpixman/src/pixman.h1116
-rw-r--r--gfx/cairo/libpixman/src/refactor478
-rw-r--r--gfx/cairo/lround-c99-only.patch46
-rw-r--r--gfx/cairo/max-font-size.patch28
-rw-r--r--gfx/cairo/missing-cairo-clip-init.diff21
-rw-r--r--gfx/cairo/moz.build11
-rw-r--r--gfx/cairo/native-clipping.patch189
-rw-r--r--gfx/cairo/no-pixman-image-reuse-across-threads.patch242
-rw-r--r--gfx/cairo/nonfatal-assertions.patch17
-rw-r--r--gfx/cairo/on-edge.patch70
-rw-r--r--gfx/cairo/pattern_get_surface-no-error.patch29
-rw-r--r--gfx/cairo/pixman-16-bit-pipeline.patch1242
-rw-r--r--gfx/cairo/pixman-8888-over-565.patch712
-rw-r--r--gfx/cairo/pixman-android-cpu-detect.patch29
-rw-r--r--gfx/cairo/pixman-bilinear-fastpath.patch287
-rw-r--r--gfx/cairo/pixman-component-alpha.patch34
-rw-r--r--gfx/cairo/pixman-dither.patch310
-rw-r--r--gfx/cairo/pixman-enable-altivec-acceleration.patch38
-rw-r--r--gfx/cairo/pixman-export.patch37
-rw-r--r--gfx/cairo/pixman-image-transform.patch52
-rw-r--r--gfx/cairo/pixman-limits.patch18
-rw-r--r--gfx/cairo/pixman-lowres-interp.patch222
-rw-r--r--gfx/cairo/pixman-rename-and-endian.patch22
-rw-r--r--gfx/cairo/pixman-xp-dll-workaround27
-rw-r--r--gfx/cairo/premultiply-alpha-solid-gradients.patch46
-rw-r--r--gfx/cairo/quartz-cache-CGImageRef.patch173
-rw-r--r--gfx/cairo/quartz-cg-layers-fix-fallback.patch42
-rw-r--r--gfx/cairo/quartz-cglayers.patch715
-rw-r--r--gfx/cairo/quartz-check-imageSurfaceEquiv.patch36
-rw-r--r--gfx/cairo/quartz-const-globals.patch134
-rw-r--r--gfx/cairo/quartz-create-for-data.patch309
-rw-r--r--gfx/cairo/quartz-fallback.patch70
-rw-r--r--gfx/cairo/quartz-first-stop.patch57
-rw-r--r--gfx/cairo/quartz-fix-PAD.patch64
-rw-r--r--gfx/cairo/quartz-get-image-performance.patch43
-rw-r--r--gfx/cairo/quartz-get-image.patch127
-rw-r--r--gfx/cairo/quartz-glyph-extents.patch19
-rw-r--r--gfx/cairo/quartz-is-clear.patch28
-rw-r--r--gfx/cairo/quartz-layers-content.patch125
-rw-r--r--gfx/cairo/quartz-mark-dirty.patch56
-rw-r--r--gfx/cairo/quartz-mask-non-OVER.patch80
-rw-r--r--gfx/cairo/quartz-minimize-gradient-repeat.patch561
-rw-r--r--gfx/cairo/quartz-optimize-OVER.patch71
-rw-r--r--gfx/cairo/quartz-refactor-surface-setup.patch290
-rw-r--r--gfx/cairo/quartz-remove-snapshot.patch62
-rw-r--r--gfx/cairo/quartz-repeating-radial-gradients.patch305
-rw-r--r--gfx/cairo/quartz-state.patch1190
-rw-r--r--gfx/cairo/quartz-support-color-emoji-font.patch432
-rw-r--r--gfx/cairo/quartz-surface-mask-patch79
-rw-r--r--gfx/cairo/setlcdfilter_in_tree.patch30
-rw-r--r--gfx/cairo/support-new-style-atomic-primitives.patch121
-rw-r--r--gfx/cairo/surface-clipper.patch26
-rw-r--r--gfx/cairo/tee-surfaces-pointwise.patch278
-rw-r--r--gfx/cairo/text-path-filling-threshold.patch90
-rw-r--r--gfx/cairo/unicode-printing.patch333
-rw-r--r--gfx/cairo/use-show-text-glyphs-if-glyph-path-fails.patch42
-rw-r--r--gfx/cairo/win32-ExtCreatePen-zero-size.patch85
-rw-r--r--gfx/cairo/win32-avoid-extend-pad-fallback.patch109
-rw-r--r--gfx/cairo/win32-canvas-glyph-position.patch31
-rw-r--r--gfx/cairo/win32-cleartype-clipping.patch23
-rw-r--r--gfx/cairo/win32-composite-src-mod.patch44
-rw-r--r--gfx/cairo/win32-d3dsurface9.patch465
-rw-r--r--gfx/cairo/win32-ddb-dib.patch181
-rw-r--r--gfx/cairo/win32-ffs-gcc.patch25
-rw-r--r--gfx/cairo/win32-gdi-font-cache-no-HFONT.patch145
-rw-r--r--gfx/cairo/win32-gdi-font-cache.patch375
-rw-r--r--gfx/cairo/win32-inline-cpp-keyword.patch24
-rw-r--r--gfx/cairo/win32-logical-font-scale.patch12
-rw-r--r--gfx/cairo/win32-printing-axis-swap.patch292
-rw-r--r--gfx/cairo/win32-raster.patch262
-rw-r--r--gfx/cairo/win32-transparent-surface.patch129
-rw-r--r--gfx/cairo/win32-vertically-offset-glyph.patch23
-rw-r--r--gfx/cairo/wrap-source_image.patch105
-rw-r--r--gfx/cairo/xlib-flush-glyphs.patch66
-rw-r--r--gfx/cairo/xlib-glyph-clip-region.patch40
-rw-r--r--gfx/cairo/xlib-initialize-members.patch19
-rw-r--r--gfx/cairo/zero-sized.patch39
-rw-r--r--gfx/cairo/zombie-face.patch119
440 files changed, 242578 insertions, 0 deletions
diff --git a/gfx/cairo/README b/gfx/cairo/README
new file mode 100644
index 000000000..91e2d1982
--- /dev/null
+++ b/gfx/cairo/README
@@ -0,0 +1,256 @@
+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 (12d521df8acc483b2daa844d4f05dc2fe2765ba6)
+ pixman (0.24.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-android-cpu-detect.patch: Add CPU detection support for Android, where we can't reliably access /proc/self/auxv.
+
+pixman-rename-and-endian.patch: include cairo-platform.h for renaming of external symbols and endian macros
+
+NOTE: we previously supported ARM assembler on MSVC, this has been removed because of the maintenance burden
+
+pixman-export.patch: use cairo_public for PIXMAN_EXPORT to make sure pixman symbols are not exported in libxul
+
+pixman-limits.patch: include limits.h for SIZE_MAX
+
+pixman-lowres-interp.patch: Use lower quality interpolation for more speed.
+
+pixman-bilinear-fastpath.patch: Bilinear fast paths for non-neon
+
+pixman-16-bit-pipeline.patch: 16 bit pipeline for dithering
+
+pixman-dither.patch: Add dithering of 16 bit gradients
+
+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
+
+pixman-enable-altivec-acceleration.patch: enable building the altivec acceleration
+
+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
+
+==== 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
diff --git a/gfx/cairo/add-a-stash-of-cairo_t-s.patch b/gfx/cairo/add-a-stash-of-cairo_t-s.patch
new file mode 100644
index 000000000..c6fcdd9d8
--- /dev/null
+++ b/gfx/cairo/add-a-stash-of-cairo_t-s.patch
@@ -0,0 +1,75 @@
+commit dfec2c249915560cedd2b49326c6629ad8a0b0f2
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Tue Mar 2 16:01:41 2010 -0500
+
+ add a stash of cairo_t's
+
+diff --git a/src/cairo.c b/src/cairo.c
+index 3c9d892..4b27b83 100644
+--- a/src/cairo.c
++++ b/src/cairo.c
+@@ -119,7 +119,63 @@ _cairo_set_error (cairo_t *cr, cairo_status_t status)
+ _cairo_status_set_error (&cr->status, _cairo_error (status));
+ }
+
+-#if HAS_ATOMIC_OPS
++#if defined(_MSC_VER)
++#pragma intrinsic(_BitScanForward)
++static __forceinline int
++ffs(int x)
++{
++ unsigned long i;
++
++ if (_BitScanForward(&i, x) != 0)
++ return i + 1;
++
++ return 0;
++}
++#endif
++
++
++#if CAIRO_NO_MUTEX
++/* We keep a small stash of contexts to reduce malloc pressure */
++#define CAIRO_STASH_SIZE 4
++static struct {
++ cairo_t pool[CAIRO_STASH_SIZE];
++ int occupied;
++} _context_stash;
++
++static cairo_t *
++_context_get (void)
++{
++ int avail, old, new;
++
++ old = _context_stash.occupied;
++ avail = ffs (~old) - 1;
++ if (avail >= CAIRO_STASH_SIZE)
++ return malloc (sizeof (cairo_t));
++
++ new = old | (1 << avail);
++ _context_stash.occupied = new;
++
++ return &_context_stash.pool[avail];
++}
++
++static void
++_context_put (cairo_t *cr)
++{
++ int old, new, avail;
++
++ if (cr < &_context_stash.pool[0] ||
++ cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
++ {
++ free (cr);
++ return;
++ }
++
++ avail = ~(1 << (cr - &_context_stash.pool[0]));
++ old = _context_stash.occupied;
++ new = old & avail;
++ _context_stash.occupied = new;
++}
++#elif HAS_ATOMIC_OPS
+ /* We keep a small stash of contexts to reduce malloc pressure */
+ #define CAIRO_STASH_SIZE 4
+ static struct {
diff --git a/gfx/cairo/avoid-extend-none.patch b/gfx/cairo/avoid-extend-none.patch
new file mode 100644
index 000000000..b3606cc2e
--- /dev/null
+++ b/gfx/cairo/avoid-extend-none.patch
@@ -0,0 +1,39 @@
+changeset: 93076:25d0c8a38d7d
+tag: none
+tag: qbase
+tag: qtip
+tag: tip
+user: Jeff Muizelaar <jmuizelaar@mozilla.com>
+date: Thu May 03 15:21:52 2012 -0400
+summary: Bug 751668. Avoid incorrectly using EXTEND_NONE. r=joe
+
+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
+@@ -1390,25 +1390,16 @@ static pixman_image_t *
+ cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+ cairo_surface_type_t type;
+
+ if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+ source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
+
+ type = source->base.backend->type;
+ if (type == CAIRO_SURFACE_TYPE_IMAGE) {
+- if (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)
+ return _pixman_transparent_image ();
+
diff --git a/gfx/cairo/bgr.patch b/gfx/cairo/bgr.patch
new file mode 100644
index 000000000..af72fa237
--- /dev/null
+++ b/gfx/cairo/bgr.patch
@@ -0,0 +1,104 @@
+commit d2120bdb06c9aacc470bb346d6bc2071c2e0749d
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Fri Mar 12 15:32:09 2010 -0500
+
+ BGR
+
+diff --git a/src/cairo-surface.c b/src/cairo-surface.c
+index 332e3ab..4a1d6a0 100644
+--- a/src/cairo-surface.c
++++ b/src/cairo-surface.c
+@@ -1501,7 +1501,9 @@ static void
+ _wrap_release_source_image (void *data)
+ {
+ struct acquire_source_image_data *acquire_data = data;
+- _cairo_surface_release_source_image (acquire_data->src, acquire_data->image, acquire_data->image_extra);
++ _cairo_surface_release_source_image (acquire_data->src,
++ acquire_data->image,
++ acquire_data->image_extra);
+ free(data);
+ }
+
+@@ -1515,42 +1517,47 @@ _wrap_image (cairo_surface_t *src,
+ cairo_image_surface_t *surface;
+ cairo_status_t status;
+
+- struct acquire_source_image_data *data = malloc(sizeof(*data));
++ struct acquire_source_image_data *data = malloc (sizeof (*data));
++ if (unlikely (data == NULL))
++ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ data->src = src;
+ data->image = image;
+ data->image_extra = image_extra;
+
+- surface = (cairo_image_surface_t*)cairo_image_surface_create_for_data (image->data,
+- image->format,
+- image->width,
+- image->height,
+- image->stride);
++ surface = (cairo_image_surface_t*)
++ _cairo_image_surface_create_with_pixman_format (image->data,
++ image->pixman_format,
++ image->width,
++ image->height,
++ image->stride);
+ status = surface->base.status;
+- if (status)
++ if (status) {
++ free (data);
+ return status;
++ }
+
+ status = _cairo_user_data_array_set_data (&surface->base.user_data,
+- &wrap_image_key,
+- data,
+- _wrap_release_source_image);
++ &wrap_image_key,
++ data,
++ _wrap_release_source_image);
+ if (status) {
+ cairo_surface_destroy (&surface->base);
++ free (data);
+ return status;
+ }
+-/*
+- pixman_image_set_component_alpha (surface->pixman_image,
+- pixman_image_get_component_alpha (image->pixman_image));
+-*/
++
++ pixman_image_set_component_alpha (
++ surface->pixman_image,
++ pixman_image_get_component_alpha (image->pixman_image));
++
+ *out = surface;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+-
+ /**
+ * _cairo_surface_clone_similar:
+ * @surface: a #cairo_surface_t
+ * @src: the source image
+- * @content: target content mask
+ * @src_x: extent for the rectangle in src we actually care about
+ * @src_y: extent for the rectangle in src we actually care about
+ * @width: extent for the rectangle in src we actually care about
+@@ -1627,12 +1634,12 @@ _cairo_surface_clone_similar (cairo_surface_t *surface,
+ _cairo_surface_release_source_image (src, image, image_extra);
+ } else {
+ status =
+- surface->backend->clone_similar (surface, &image->base,
+- src_x, src_y,
+- width, height,
+- clone_offset_x,
+- clone_offset_y,
+- clone_out);
++ surface->backend->clone_similar (surface, &image->base,
++ src_x, src_y,
++ width, height,
++ clone_offset_x,
++ clone_offset_y,
++ clone_out);
+ cairo_surface_destroy(&image->base);
+ }
+ }
diff --git a/gfx/cairo/buggy-repeat.patch b/gfx/cairo/buggy-repeat.patch
new file mode 100644
index 000000000..3d27b8f9b
--- /dev/null
+++ b/gfx/cairo/buggy-repeat.patch
@@ -0,0 +1,39 @@
+diff --git a/gfx/cairo/cairo/src/cairo-xlib-display.c b/gfx/cairo/cairo/src/cairo-xlib-display.c
+--- a/gfx/cairo/cairo/src/cairo-xlib-display.c
++++ b/gfx/cairo/cairo/src/cairo-xlib-display.c
+@@ -216,6 +216,8 @@ _cairo_xlib_display_get (Display *dpy)
+ XExtCodes *codes;
+ int major_unused, minor_unused;
+
++ static int buggy_repeat_force = -1;
++
+ /* 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
+@@ -308,6 +310,26 @@ _cairo_xlib_display_get (Display *dpy)
+ if (VendorRelease (dpy) <= 40500000)
+ display->buggy_repeat = TRUE;
+ }
++
++ /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */
++ /* If buggy_repeat_force == -1, then initialize.
++ * - set to -2, meaning "nothing was specified", and we trust the above detection.
++ * - if MOZ_CAIRO_BUGGY_REPEAT is '0' (exactly), then force buggy repeat off
++ * - if MOZ_CAIRO_BUGGY_REPEAT is '1' (exactly), then force buggy repeat on
++ */
++ if (buggy_repeat_force == -1) {
++ const char *flag = getenv("MOZ_CAIRO_FORCE_BUGGY_REPEAT");
++
++ buggy_repeat_force = -2;
++
++ if (flag && flag[0] == '0')
++ buggy_repeat_force = 0;
++ else if (flag && flag[0] == '1')
++ buggy_repeat_force = 1;
++ }
++
++ if (buggy_repeat_force != -2)
++ display->buggy_repeat = (buggy_repeat_force == 1);
+
+ display->next = _cairo_xlib_display_list;
+ _cairo_xlib_display_list = display;
diff --git a/gfx/cairo/cache-size.patch b/gfx/cairo/cache-size.patch
new file mode 100644
index 000000000..2371046aa
--- /dev/null
+++ b/gfx/cairo/cache-size.patch
@@ -0,0 +1,19 @@
+commit c32b57b9ada7a57ec20648629ecb83de5688682a
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Mon Mar 23 11:28:12 2009 -0400
+
+ shrink cache size
+
+diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
+index 249ab6c..aa7fc11 100644
+--- a/src/cairo-scaled-font.c
++++ b/src/cairo-scaled-font.c
+@@ -63,7 +63,7 @@
+ */
+
+ /* XXX: This number is arbitrary---we've never done any measurement of this. */
+-#define MAX_GLYPH_PAGES_CACHED 512
++#define MAX_GLYPH_PAGES_CACHED 256
+ static cairo_cache_t *cairo_scaled_glyph_page_cache;
+
+ #define CAIRO_SCALED_GLYPH_PAGE_SIZE 32
diff --git a/gfx/cairo/cairo-clamp-boundary.patch b/gfx/cairo/cairo-clamp-boundary.patch
new file mode 100644
index 000000000..990f1161a
--- /dev/null
+++ b/gfx/cairo/cairo-clamp-boundary.patch
@@ -0,0 +1,71 @@
+# HG changeset patch
+# User Milan Sreckovic <msreckovic@mozilla.com>
+# Date 1362078121 18000
+# Node ID e9e6d97b153d8ec17ee03bb1deef1dec24c7a17c
+# Parent c65d59d33aa86b7e75bc420ea3beda6201e0aceb
+Bug 825721: clamp negative box starts and disallow negative sizes. r=jmuizelaar
+
+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
+@@ -1846,16 +1846,20 @@ static cairo_status_t
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
++ x1 = (x1 < 0 ? 0 : x1);
++ y1 = (y1 < 0 ? 0 : y1);
++ if (x2 <= x1 || y2 <= y1)
++ continue;
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ 0);
+ }
+ }
+ }
+
+@@ -2669,16 +2673,18 @@ static cairo_status_t
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_ceil (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_ceil (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_floor (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_floor (box[i].p2.y);
+
++ x1 = (x1 < 0 ? 0 : x1);
++ y1 = (y1 < 0 ? 0 : y1);
+ if (x2 > x1 && y2 > y1) {
+ cairo_box_t b;
+
+ pixman_fill ((uint32_t *) dst->data,
+ dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ pixel);
+@@ -2929,17 +2935,19 @@ static cairo_status_t
+ cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round_down (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_round_down (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_round_down (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_round_down (box[i].p2.y);
+
+- if (x2 == x1 || y2 == y1)
++ x1 = (x1 < 0 ? 0 : x1);
++ y1 = (y1 < 0 ? 0 : y1);
++ if (x2 <= x1 || y2 <= y1)
+ continue;
+
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ pixel);
+ }
+ }
diff --git a/gfx/cairo/cairo-mask-extends-bug.patch b/gfx/cairo/cairo-mask-extends-bug.patch
new file mode 100644
index 000000000..325772d82
--- /dev/null
+++ b/gfx/cairo/cairo-mask-extends-bug.patch
@@ -0,0 +1,41 @@
+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
+@@ -1788,18 +1788,35 @@ static cairo_status_t
+ 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_region == NULL)
+- return _cairo_image_surface_fixup_unbounded (dst, extents, NULL);
++ // If we have no boxes then we need to clear the entire extents
++ // because we have nothing to draw.
++ if (boxes->num_boxes < 1 && clip_region == NULL) {
++ int x = extents->unbounded.x;
++ int y = extents->unbounded.y;
++ int width = extents->unbounded.width;
++ int height = extents->unbounded.height;
++
++ pixman_color_t color = { 0 };
++ pixman_box32_t box = { x, y, x + width, y + height };
++
++ if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
++ dst->pixman_image,
++ &color,
++ 1, &box)) {
++ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
++ }
++ return CAIRO_STATUS_SUCCESS;
++ }
+
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
diff --git a/gfx/cairo/cairo-qt-compile.patch b/gfx/cairo/cairo-qt-compile.patch
new file mode 100644
index 000000000..f839c7988
--- /dev/null
+++ b/gfx/cairo/cairo-qt-compile.patch
@@ -0,0 +1,21 @@
+# HG changeset patch
+# Parent 2563fa2763b0ea83394e785340afa4c564ceab57
+diff -r 2563fa2763b0 -r 9ab15e95a354 gfx/cairo/cairo/src/cairo-qt-surface.cpp
+--- a/gfx/cairo/cairo/src/cairo-qt-surface.cpp Thu Apr 29 06:55:11 2010 +0300
++++ b/gfx/cairo/cairo/src/cairo-qt-surface.cpp Thu Apr 29 06:55:51 2010 +0300
+@@ -204,6 +204,7 @@
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ ASSERT_NOT_REACHED;
+ }
++ return QPainter::CompositionMode_Source;
+ }
+
+ static bool
+@@ -668,7 +669,6 @@
+ static cairo_status_t
+ _cairo_qt_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+- cairo_content_t content,
+ int src_x,
+ int src_y,
+ int width,
diff --git a/gfx/cairo/cairo-region-clip.patch b/gfx/cairo/cairo-region-clip.patch
new file mode 100644
index 000000000..a0eb2d265
--- /dev/null
+++ b/gfx/cairo/cairo-region-clip.patch
@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Matt Woodrow <mwoodrow@mozilla.com>
+# Date 1408674084 -43200
+# Fri Aug 22 14:21:24 2014 +1200
+# Node ID 2b819b882c3b26c02d821e8d713591a9b56f1728
+# Parent ffd1fc7e7d5a85e4823b5f2067b4a24d358a0e41
+Bug 1050788 - Fix cairo clip path region construction when the first path generates no traps. r=jrmuizel
+
+diff --git a/gfx/cairo/cairo/src/cairo-clip.c b/gfx/cairo/cairo/src/cairo-clip.c
+--- a/gfx/cairo/cairo/src/cairo-clip.c
++++ b/gfx/cairo/cairo/src/cairo-clip.c
+@@ -590,16 +590,22 @@ static cairo_int_status_t
+ status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
+ clip_path->fill_rule,
+ &traps);
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ goto UNSUPPORTED;
+
++ if (unlikely (traps.num_traps == 0)) {
++ clip_path->region = cairo_region_create ();
++ clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
++ return CAIRO_STATUS_SUCCESS;
++ }
++
+ if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
+ boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
+ if (unlikely (boxes == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (n = 0; n < traps.num_traps; n++) {
+ boxes[n].p1.x = traps.traps[n].left.p1.x;
diff --git a/gfx/cairo/cairo-version-fixes.patch b/gfx/cairo/cairo-version-fixes.patch
new file mode 100644
index 000000000..f55e85731
--- /dev/null
+++ b/gfx/cairo/cairo-version-fixes.patch
@@ -0,0 +1,26 @@
+diff --git a/gfx/cairo/cairo/src/cairo-version.c b/gfx/cairo/cairo/src/cairo-version.c
+--- a/gfx/cairo/cairo/src/cairo-version.c
++++ b/gfx/cairo/cairo/src/cairo-version.c
+@@ -42,7 +42,7 @@
+
+ /* get the "real" version info instead of dummy cairo-version.h */
+ #undef CAIRO_VERSION_H
+-#include "../cairo-version.h"
++#include "cairo-features.h"
+
+ /**
+ * cairo_version:
+diff --git a/gfx/cairo/cairo/src/cairo-version.h b/gfx/cairo/cairo/src/cairo-version.h
+--- a/gfx/cairo/cairo/src/cairo-version.h
++++ b/gfx/cairo/cairo/src/cairo-version.h
+@@ -7,8 +7,10 @@
+ #ifndef CAIRO_VERSION_H
+ #define CAIRO_VERSION_H
+
++#if 0
+ #define CAIRO_VERSION_MAJOR USE_cairo_version_OR_cairo_version_string_INSTEAD
+ #define CAIRO_VERSION_MINOR USE_cairo_version_OR_cairo_version_string_INSTEAD
+ #define CAIRO_VERSION_MICRO USE_cairo_version_OR_cairo_version_string_INSTEAD
++#endif
+
+ #endif
diff --git a/gfx/cairo/cairo-x-visual.patch b/gfx/cairo/cairo-x-visual.patch
new file mode 100644
index 000000000..29f6c737e
--- /dev/null
+++ b/gfx/cairo/cairo-x-visual.patch
@@ -0,0 +1,160 @@
+diff -r c1195334f839 gfx/cairo/cairo/src/cairo-xlib-surface.c
+--- a/gfx/cairo/cairo/src/cairo-xlib-surface.c Fri May 21 17:42:55 2010 +0300
++++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c Fri May 21 19:12:29 2010 +0300
+@@ -189,16 +189,57 @@ static const XTransform identity = { {
+
+ #define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11)
+
+ #define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \
+ ((op) <= CAIRO_OPERATOR_SATURATE || \
+ (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \
+ (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY))
+
++static Visual *
++_visual_for_xrender_format(Screen *screen,
++ XRenderPictFormat *xrender_format)
++{
++ int d, v;
++ 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_status_t
+ _cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface,
+ cairo_region_t *region)
+ {
+ cairo_bool_t had_clip_rects = surface->clip_region != NULL;
+
+ if (had_clip_rects == FALSE && region == NULL)
+ return CAIRO_STATUS_SUCCESS;
+@@ -313,16 +354,19 @@ _cairo_xlib_surface_create_similar (void
+ * visual/depth etc. as possible. */
+ pix = XCreatePixmap (src->dpy, src->drawable,
+ width <= 0 ? 1 : width, height <= 0 ? 1 : height,
+ xrender_format->depth);
+
+ visual = NULL;
+ 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);
+ }
+@@ -3178,28 +3222,32 @@ cairo_xlib_surface_create_with_xrender_f
+ Screen *scr,
+ XRenderPictFormat *format,
+ int width,
+ int height)
+ {
+ cairo_xlib_screen_t *screen;
+ cairo_surface_t *surface;
+ cairo_status_t status;
++ Visual *visual;
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return _cairo_surface_create_in_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));
+
++ if (format)
++ visual = _visual_for_xrender_format (scr, format);
++
+ surface = _cairo_xlib_surface_create_internal (screen, drawable,
+- NULL, format,
++ visual, format,
+ width, height, 0);
+ _cairo_xlib_screen_destroy (screen);
+
+ return surface;
+ }
+ slim_hidden_def (cairo_xlib_surface_create_with_xrender_format);
+
+ /**
+@@ -3413,33 +3461,37 @@ cairo_xlib_surface_get_screen (cairo_sur
+
+ return surface->screen->screen;
+ }
+
+ /**
+ * cairo_xlib_surface_get_visual:
+ * @surface: a #cairo_xlib_surface_t
+ *
+- * Get the X Visual used for underlying X Drawable.
++ * 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.
++ * 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 *abstract_surface)
++cairo_xlib_surface_get_visual (cairo_surface_t *surface)
+ {
+- cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
+-
+- if (! _cairo_surface_is_xlib (abstract_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 surface->visual;
++ 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.
+ *
diff --git a/gfx/cairo/cairo/AUTHORS b/gfx/cairo/cairo/AUTHORS
new file mode 100644
index 000000000..bdde62a31
--- /dev/null
+++ b/gfx/cairo/cairo/AUTHORS
@@ -0,0 +1,99 @@
+Josh Aas <joshmoz@gmail.com> Memory leak fix for quartz backend
+Daniel Amelang <dan@amelang.net> Many (magic) floating-point optimizations
+Shawn T. Amundson <amundson@gtk.org> Build fix
+Olivier Andrieu <oliv__a@users.sourceforge.net> PNG backend
+Peter Dennis Bartok <peter@novonyx.com> Bug fix for clipping
+Dave Beckett <dajobe@debian.org> Build fixes, Debian packaging
+Christian Biesinger <cbiesinger@web.de> BeOS backend
+Billy Biggs <vektor@dumbterm.net> Pixman code merge. Optimization. Fixes for subtle rendering bugs.
+Hans Breuer <hans@breuer.org> win32 bug fixes, build fixes, and improvements
+Brian Cameron <brian.cameron@sun.com> Flag bug in Sun's X server
+Damien Carbery <damien.carbery@sun.com> Build fixes
+Andrew Chant <andrew.chant@utoronto.ca> Adding const where needed
+Steve Chaplin <stevech1097@yahoo.com.au> Bug fixes for PNG reading
+Tomasz Cholewo <cholewo@ieee-cis.org> Bug fixes
+Manu Cornet <manu@manucornet.net> SVG build fix
+Frederic Crozat <fcrozat@mandriva.com> Fix test suite for OPD platforms (IA64 or PPC64)
+Radek Doulík <rodo@novell.com> Bug report and test case
+John Ehresman <jpe@wingide.com> Build fixes for win32
+John Ellson <ellson@research.att.com> First font/glyph extents functions
+Michael Emmel <mike.emmel@gmail.com> DirectFB backend
+Miklós Erdélyi <erdelyim@gmail.com> Fix typo leading to a crash
+Behdad Esfahbod <behdad@behdad.org> Huge piles of bug fixes, improvements, and general maintenance
+Brian Ewins <Brian.Ewins@gmail.com> ATSUI maintenance (first success at making it really work)
+Bertram Felgenhauer <int-e@gmx.de> Fixes for subtle arithmetic errors
+Bdale Garbee <bdale@gag.com> Provided essential support for cairo achitecture sessions
+Jens Granseuer <jensgr@gmx.net> Fixes to generate proper compiler flags
+Laxmi Harikumar <laxmi.harikumar@digital.com> Build fix
+J. Ali Harlow <ali@avrc.city.ac.uk> win32 backend updates
+Mathias Hasselmann <mathias.hasselmann@gmx.de> Significant reduction of calls to malloc
+Richard Henderson <rth@twiddle.net> "slim" macros for better shared libraries
+James Henstridge <james@daa.com.au> Build fixes related to freetype
+Graydon Hoare <graydon@redhat.com> Support for non-render X server, first real text support
+Thomas Hunger <info@teh-web.de> Initial version of cairo_in_stroke/fill
+Kristian Høgsberg <krh@redhat.com> PDF backend, PS backend with meta-surfaces
+Amaury Jacquot <sxpert@esitcom.org> Documentation review, appplication testing
+Adrian Johnson <ajohnson@redneon.com> PDF backend improvement
+Michael Johnson <ahze@ahze.net> Bug fix for pre-C99 compilers
+Jonathon Jongsma <jonathon.jongsma@gmail.com> Fix documentation typos
+Øyvind Kolås <pippin@freedesktop.org> Bug fixes. Better default values.
+Martin Kretzschmar <martink@gnome.org> Arithmetic fix for 64-bit architectures
+Mathieu Lacage <Mathieu.Lacage@sophia.inria.fr> several bug/typo fixes
+Dominic Lachowicz <domlachowicz@gmail.com> PDF conformance fix, fix image surface to zero out contents
+Alexander Larsson <alexl@redhat.com> Profiling and performance fixes.
+Tor Lillqvist <tml@novell.com> win32 build fixes, build scripts
+Jinghua Luo <sunmoon1997@gmail.com> Add bitmap glyph transformation, many freetype and glitz fixes
+Luke-Jr <luke-jr@utopios.org> Build fix for cross-compiling
+Kjartan Maraas <kmaraas@gnome.org> Several fixes for sparse, lots of debug help for multi-thread bugs
+Jordi Mas <jordi@ximian.com> Bug fix for cairo_show_text
+Nicholas Miell <nmiell@gmail.com> Fixes for linking bugs on AMD64
+Eugeniy Meshcheryakov <eugen@debian.org> PS/PDF font subsetting improvements
+Zakharov Mikhail <zmey20000@yahoo.com> Build fix for HP-UX
+Christopher (Monty) Montgomery <xiphmont@gmail.com> Performnace fix (subimage_copy), multi-thread testing
+Tim Mooney <enchanter@users.sourceforge.net> Fix test suite to compile with Solaris compiler
+Jeff Muizelaar <jeff@infidigm.net> Patient, painful, pixman code merge. Many fixes for intricacies of dashing.
+Yevgen Muntyan <muntyan@tamu.edu> win32 build fix
+Declan Naughton <piratepenguin@gmail.com> Fix documentation typos
+Peter Nilsson <c99pnn@cs.umu.se> Glitz backend
+Henning Noren <henning.noren.402@student.lu.se> Fix memory leak
+Geoff Norton <gnorton@customerdna.com> Build fixes
+Robert O'Callahan <rocallahan@novell.com> Const-correctness fixes, several new API functions for completeness (and to help mozilla)
+Ian Osgood <iano@quirkster.com> XCB backend maintenance
+Benjamin Otte <in7y118@public.uni-hamburg.de> Refinements to cairo/perf timing
+Mike Owens <etc@filespanker.com> Bug fixes
+Emmanuel Pacaud <emmanuel.pacaud@lapp.in2p3.fr> SVG backend
+Keith Packard <keithp@keithp.com> Original concept, polygon tessellation, dashing, font metrics rewrite
+Stuart Parmenter <pavlov@pavlov.net> Original GDI+ backend, win32 fixes
+Alfred Peng <alfred.peng@sun.com> Fixes for Sun compilers and for a memory leak
+Christof Petig <christof@petig-baender.de> Build fixes related to freetype
+Joonas Pihlaja <jpihlaja@cc.helsinki.fi> Huge improvements to the tessellator performance
+Mart Raudsepp <leio@dustbite.net> Build fixes
+David Reveman <davidr@novell.com> New pattern API, glitz backend
+Calum Robinson <calumr@mac.com> Quartz backend
+Pavel Roskin <proski@gnu.org> Several cleanups to eliminate warnings
+Tim Rowley <tim.rowley@gmail.com> Quartz/ATSUI fixes, X server workarounds, win32 glyph path support, test case to expose gradient regression
+Soeren Sandmann <sandmann@daimi.au.dk> Lots of MMX love for pixman compositing
+Torsten Schönfeld <kaffeetisch@gmx.de> Build fixes
+Jamey Sharp <jamey@minilop.net> Surface/font backend virtualization, XCB backend
+Jason Dorje Short <jdorje@users.sf.net> Build fixes and bug fixes
+Jeff Smith <whydoubt@yahoo.com> Fixes for intricacies of stroking code
+Travis Spencer <tspencer@cs.pdx.edu> XCB backend fix
+Bill Spitzak <spitzak@d2.com> Build fix to find Xrender.h without xrender.pc
+Zhe Su <james.su@gmail.com> Add support for fontconfig's embeddedbitmap option
+Owen Taylor <otaylor@redhat.com> Font rewrite, documentation, win32 backend
+Alp Toker <alp@atoker.com> Fix several code/comment typos
+Malcolm Tredinnick <malcolm@commsecure.com.au> Documentation fixes
+David Turner <david@freetype.org> Optimize gradient calculations
+Kalle Vahlman <kalle.vahlman@gmail.com> Allow perf reports to be compared across different platforms
+Sasha Vasko <sasha@aftercode.net> Build fix to compile without xlib backend
+Vladimir Vukicevic <vladimir@pobox.com> Quartz backend rewrite, win32/quartz maintenance
+Jonathan Watt <jwatt@jwatt.org> win32 fixes
+Peter Weilbacher <pmw@avila.aip.de> OS/2 backend
+Dan Williams <dcbw@redhat.com> Implemnt MMX function to help OLPC
+Chris Wilson <chris@chris-wilson.co.uk> Large-scale robustness improvements, (warn_unsed_result and malloc failure injection)
+Carl Worth <cworth@isi.edu> Original library, support for paths, images
+Richard D. Worth <richard@theworths.org> Build fixes for cygwin
+Kent Worsnop <kworsnop@accesswave.ca> Fix PDF dashing bug
+Dave Yeo <daveryeo@telus.net> 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 000000000..145e62966
--- /dev/null
+++ b/gfx/cairo/cairo/COPYING
@@ -0,0 +1,17 @@
+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.
diff --git a/gfx/cairo/cairo/COPYING-LGPL-2.1 b/gfx/cairo/cairo/COPYING-LGPL-2.1
new file mode 100644
index 000000000..b124cf581
--- /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.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+
+ <signature of Ty Coon>, 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 000000000..7714141d1
--- /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 000000000..dfff8bebb
--- /dev/null
+++ b/gfx/cairo/cairo/INSTALL
@@ -0,0 +1,187 @@
+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-quartz
+ --enable-atsui
+ --enable-xcb
+ --enable-glitz
+ --enable-beos
+ --enable-os2
+ --enable-directfb
+
+ Some of cairo's backends are marked as experimental and will
+ not be built by default. If you would like to build and
+ experiment with these backends, you will need to pass one of
+ the above options to the configure script. You may need to
+ have certain libraries installed first as discussed in the
+ dependencies section of the README file.
+
+ --disable-xlib
+ --disable-win32
+ --disable-png
+ --disable-freetype
+ --disable-ps
+ --disable-pdf
+ --disable-svg
+
+ Cairo's configure script detects the libraries needed to build
+ each stable backend, and when it finds them, enables each
+ backend. If you would like to override this detection and
+ disable a backend, (even when it would be possible to build
+ it), use one of the options above to disable the backend.
+
+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:
+
+ http://cairographics.org/releases
+
+or perhaps the latest (unstable) development snapshot:
+
+ http://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 (1.8 or newer)
+ autoconf
+ libtool
+
+Hopefully your platform of choice has packages readily available so
+that you can easily install things with your system's package
+management tool, (such as "apt-get install automake" on Debian or "yum
+install automake" on Fedora, etc.). Note that Mac OS X ships with it's
+own utility called libtool which is not what you want, (the one you do
+want goes by the name of glibtool).
+
+Once you have all of those packages installed, the next step is to run
+the autogen.sh script. That can be as simple as:
+
+ ./autogen.sh
+
+Or, if you're using Mac OS X, you'll have to let it know to use
+glibtool by instead doing:
+
+ LIBTOOLIZE=glibtoolize ./autogen.sh
+
+But before you run that command, note that the autogen.sh script
+accepts all the same arguments as the configure script, (and in fact,
+will generate the configure script and run it with the arguments you
+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 000000000..9be4062ca
--- /dev/null
+++ b/gfx/cairo/cairo/NEWS
@@ -0,0 +1,5121 @@
+Release 1.6.4 (2008-04-11 Carl Worth <cworth@cworth.org>)
+=========================================================
+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 <cworth@cworth.org>)
+=========================================================
+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
+ http://bugs.freedesktop.org/show_bug.cgi?id=15348
+
+ Cairo-generated postscript blocks Dell 5100cn
+ http://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 <cworth@cworth.org>)
+=========================================================
+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:
+
+ http://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
+ http://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 http://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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@cworth.org>)
+===========================================================
+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:
+
+ http://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 <cworth@cworth.org>)
+===========================================================
+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:
+
+ http://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 http://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 <cworth@cworth.org>)
+===========================================================
+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:
+
+ http://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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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 ExtendMode::REPEAT patterns for OS X 10.5.
+
+Snapshot 1.5.2 (2007-10-30 Carl Worth <cworth@cworth.org>)
+==========================================================
+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:
+
+ http://cairographics.org/releases/pixman-0.9.6.tar.gz
+
+ which can be verified with:
+
+ http://cairographics.org/releases/pixman-0.9.6.tar.gz.sha1
+ 66f01a682c64403a3d7a855ba5aa609ed93bcb9e pixman-0.9.6.tar.gz
+
+ http://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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+=========================================================
+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:
+
+ http://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 <cworth@cworth.org>)
+=========================================================
+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 <cworth@cworth.org>)
+=========================================================
+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
+ http://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 <cworth@cworth.org>)
+=========================================================
+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).
+
+ http://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).
+
+ http://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 <cworth@cworth.org>)
+=========================================================
+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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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?
+ http://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 <cworth@cworth.org>)
+==========================================================
+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:
+
+ http://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 <cworth@cworth.org>)
+==========================================================
+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 <behdad@behdad.org>)
+==============================================================
+This is the third bug fix release in the 1.2 series, coming less than
+two months after the 1.2.4 release made on August 18.
+
+The 1.2.4 release turned out to be a pretty solid one, except for a crasher
+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 <cworth@cworth.org>)
+=========================================================
+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)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=72e25648c4c4bc82ddd938aa4e05887a293f0d8b
+
+Fix a couple of corner cases in dashing degenerate paths (Jeff Muizelaar)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=fbb1758ba8384650157b2bbbc93d161b0c2a05f0
+
+Fix support for type1 fonts on win32 (Adrian Johnson)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=da1019c9138695cb838a54f8b871bbfd0e8996d7
+
+Fix assertion failure when rotating bitmap fonts (Carl Worth)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=0bfa6d4f33b8ddb5dc55bbe419c15df4af856ff9
+
+Fix assertion failure when calling cairo_text_path with bitmap fonts (Carl Worth)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=9878a033531e6b96b5f27e69e10e90dee7440cd9
+
+Fix mis-handling of cairo_close_path in some situations (Tim Rowley, Carl Worth)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=53f74e59faf1af78f2f0741ccf1f23aa5dad4efc
+
+Respect font_matrix translation in _cairo_gstate_glyph_path (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=f183b835b111d23e838889178aa8106ec84663b3
+
+Fix vertical metrics adjustment to work with non-identity shapes (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=b7bc263842a798d657a95e539e1693372448837f
+
+[PS] Set correct ImageMatrix in _cairo_ps_surface_emit_bitmap_glyph_data (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=d47388ad759b0a1a0869655a87d9b5eb6ae2445d
+
+Build system fixes
+------------------
+Fix xlib detection to prefer pkg-config to avoid false libXt dependency (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=0e78e7144353703cbd28aae6a67cd9ca261f1d68
+
+Fix typos causing win32 build problem with PS,PDF, and SVG backends (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=aea83b908d020e26732753830bb3056e6702a774
+
+Fix configure cache to not use stale results (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=6d0e3260444a2d5b6fb0cb223ac79f1c0e7b3a6e
+
+Fix to not pass unsupported warning options to the compiler (Jens Granseuer)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=97524a8fdb899de1ae4a3e920fb7bda6d76c5571
+
+Fix to allow env. variables such as png_REQUIRES to override configure detection (Jens Granseuer)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=abd16e47d6331bd3811c908e524b4dcb6bd23bf0
+
+Fix test suite to not use an old system cairo when converting svg2png (Behdad Esfahbod)
+http://gitweb.freedesktop.org/?p=cairo;a=commit;h=6122cc85c8f71b1ba2df3ab86907768edebe1781
+
+Fix test suite to not require signal.h to be present (Behdad Esfahbod)
+http://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 <cworth@cworth.org>)
+=========================================================
+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: http://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: http://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: http://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: http://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: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=9fcb3c32c1f16fe6ab913e27eb54d18b7d9a06b0
+ http://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: http://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: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=a0989f427be87c60415963dd6822b3c5c3781691
+
+PDF: Fix broken output on amd64 (Adrian Johnson)
+------------------------------------------------
+report: http://bugzilla.gnome.org/show_bug.cgi?id=349826
+fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=f4b12e497b7ac282b2f6831b8fb68deebc412e60
+
+PS: Fix broken output for truetype fonts > 64k (Adrian Johnson)
+---------------------------------------------------------------
+fix: http://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: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=778c4730a86296bf0a71080cf7008d7291792256
+
+Fix memory leaks in failure paths in gradient creation (Alfred Peng)
+--------------------------------------------------------------------
+fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=db06681b487873788b51a6766894fc619eb8d8f2
+
+Fix memory leak in _cairo_surface_show_glyphs (Chris Wilson)
+------------------------------------------------------------
+report: https://bugs.freedesktop.org/show_bug.cgi?id=7766
+fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=e2fddcccb43d06486d3680a19cfdd5a54963fcbd
+
+Solaris: Add definition of cairo_private for some Sun compilers (Alfred Peng)
+-----------------------------------------------------------------------------
+report: https://bugzilla.mozilla.org/show_bug.cgi?id=341874
+fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=04757a3aa8deeff3265719ebe01b021638990ec6
+
+Solaris: Change version number of Sun's Xorg server with buggy repeat (Brian Cameron)
+-------------------------------------------------------------------------------------
+report: https://bugs.freedesktop.org/show_bug.cgi?id=7483
+fix: http://gitweb.freedesktop.org/?p=cairo;a=commit;h=e0ad1aa995bcec4246c0b8ab0d5a5a79871ce235
+
+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 <cworth@cworth.org>)
+=========================================================
+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
+ http://bugzilla.gnome.org/show_bug.cgi?id=332266
+ * PS: Fix source surfaces with transformations
+ * PS: Fix to not draw BUTT-capped degnerate sub-paths
+ * PS: Don't walk off end of array when printing "~>"
+ * Fix some memory leaks in the test suite rig
+ * SVG: Fix memory leak when using cairo_mask
+ * Fix ExtendMode::REFLECT and EXTEND_PAD to not crash (though these are
+ 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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+==========================================================
+This is the third in a series of snapshots working toward the imminent
+1.2 release of cairo. For a list of items still needing work on the
+cairo 1.2 roadmap, please see:
+
+ http://cairographics.org/ROADMAP
+
+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 <cworth@cworth.org>)
+==========================================================
+This is the second in a series of snapshots working toward the
+upcoming 1.2 release of cairo. For a list of items still needing work
+on the cairo 1.2 roadmap, please see:
+
+ http://cairographics.org/ROADMAP
+
+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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+=========================================================
+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 - Accomodate 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 <cworth@cworth.org>)
+=========================================================
+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 mutiple 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 <cworth@cworth.org>)
+=========================================================
+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 <cworth@cworth.org>)
+=========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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_<object>_create ();
+ if (object == NULL)
+ goto BAILOUT;
+
+ /* act on object */
+
+ cairo_<object>_destroy (object);
+
+ to:
+
+ object = cairo_<object>_create ();
+ if (cairo_<object>_status (object))
+ goto BAILOUT;
+
+ /* act on object */
+
+ cairo_<object>_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_<object>_create ();
+
+ /* act on object */
+
+ cairo_<object>_destroy (object);
+
+ with cairo_<object>_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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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 <cworth@cworth.org>)
+==========================================================
+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:
+
+ http://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 <cairo-win32.h>
+
+ 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:
+
+http://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:
+
+ http://cairographics.org/manual/cairo-cairo-t.html#cairo-font-extents
+
+ cairo-ft.h
+ ----------
+ The cairo_ft_font API changed considerably. Please see the
+ documentation for details:
+
+ http://cairographics.org/manual/cairo-FreeType-Fonts.html
+
+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 <cworth@cworth.org>)
+==========================================================
+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:
+
+ http://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 <cworth@cworth.org>)
+===========================================================
+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 <cworth@isi.edu>)
+========================================================
+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 <cworth@isi.edu>)
+========================================================
+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 <c99drn@cs.umu.se>)
+=============================================================
+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 <cworth@isi.edu>)
+========================================================
+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 <cworth@isi.edu>)
+========================================================
+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 <cworth@isi.edu>)
+========================================================
+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 <cworth@isi.edu>)
+========================================================
+
+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 <dave.beckett@bristol.ac.uk> 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 <graydon@redhat.com> 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 <graydon@redhat.com> added the first real support for
+running cairo with a non-render-aware X server.
+
+Jamey Sharp <jamey@minilop.net> 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
+<keithp@keithp.com> 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 <cworth@isi.edu> wrote the first lines of Xr, after Keith
+Packard <keithp@keithp.com> proposed the plan for a stateful drawing
+library in C providing a PostScript-like rendering model.
+
+ LocalWords: mutex BeOS extraordinaire
diff --git a/gfx/cairo/cairo/README b/gfx/cairo/cairo/README
new file mode 100644
index 000000000..efca44cda
--- /dev/null
+++ b/gfx/cairo/cairo/README
@@ -0,0 +1,198 @@
+Cairo - Multi-platform 2D graphics library
+http://cairographics.org
+
+What is cairo
+=============
+Cairo is a 2D graphics library with support for multiple output
+devices. Currently supported output targets include the X Window
+System, win32, and image buffers, as well as PDF, PostScript, and SVG
+file output. Experimental backends include OpenGL (through glitz),
+Quartz, XCB, BeOS, OS/2, and DirectFB.
+
+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:
+
+ http://cairographics.org/
+
+The latest versions of cairo can always be found at:
+
+ http://cairographics.org/download
+
+Documentation on using cairo and frequently-asked questions:
+
+ http://cairographics.org/documentation
+ http://cairographics.org/FAQ
+
+Mailing lists for contacting cairo users and developers:
+
+ http://cairographics.org/lists
+
+Roadmap and unscheduled things to do, (please feel free to help out):
+
+ http://cairographics.org/roadmap
+ http://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, (with image, png, pdf,
+PostScript, svg, and xlib surface backends, and the freetype font
+backend), the following sample commands will install necessary
+dependencies:
+
+ Debian (and similar):
+
+ apt-get install libpng12-dev libz-dev libxrender-dev libfontconfig1-dev
+
+ Fedora (and similar):
+
+ yum install libpng-devel zlib-devel libXrender-devel fontconfig-devel
+
+(Those commands intentionally don't install pixman from a distribution
+package since if you're manually compiling cairo, then you likely want
+to grab pixman from the same place at the same time and compile it as
+well.)
+
+Supported, "standard" surface backends
+------------------------------------
+ image backend (required)
+ ------------------------
+ pixman >= 0.10.0 http://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 http://freedesktop.org/Software/xlibs
+
+ xlib-xrender backend
+ --------------------
+ Xrender >= 0.6 http://freedesktop.org/Software/xlibs
+
+ quartz backend
+ --------------
+ MacOS X >= 10.4 with Xcode >= 2.4
+
+ win32 backend
+ -------------
+ Microsoft Windows 2000 or newer[*].
+
+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
+-----------------------------
+ glitz
+ -------------
+ glitz >= 0.4.4 http://freedesktop.org/Software/glitz
+
+ xcb backend
+ -----------
+ XCB http://xcb.freedesktop.org
+
+ beos backend
+ ------------
+ No dependencies in itself other than an installed BeOS system, but cairo
+ 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 <cworth@cworth.org> and
+Keith Packard <keithp@keithp.com>. 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/src/cairo-analysis-surface-private.h b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h
new file mode 100644
index 000000000..c7dcb8291
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h
@@ -0,0 +1,75 @@
+/*
+ * 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 <keithp@keithp.com>
+ */
+
+#ifndef CAIRO_ANALYSIS_SURFACE_H
+#define CAIRO_ANALYSIS_SURFACE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+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_END_DECLS
+
+#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 000000000..96b43285d
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-analysis-surface.c
@@ -0,0 +1,925 @@
+/*
+ * 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 <keithp@keithp.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-analysis-surface-private.h"
+#include "cairo-error-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-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_status_is_error (status_a));
+ assert (! _cairo_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_STATUS_SUCCESS &&
+ status_b == CAIRO_STATUS_SUCCESS);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ const cairo_surface_pattern_t *surface_pattern;
+ cairo_bool_t old_has_ctm;
+ cairo_matrix_t old_ctm, p2d;
+ cairo_status_t status;
+ cairo_surface_t *source;
+
+ assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
+ surface_pattern = (const cairo_surface_pattern_t *) pattern;
+ assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
+
+ old_ctm = surface->ctm;
+ old_has_ctm = surface->has_ctm;
+
+ p2d = pattern->matrix;
+ status = cairo_matrix_invert (&p2d);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm);
+ surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
+
+ source = surface_pattern->surface;
+ if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+ source = sub->target;
+ }
+
+ status = _cairo_recording_surface_replay_and_create_regions (source, &surface->base);
+
+ surface->ctm = old_ctm;
+ surface->has_ctm = old_has_ctm;
+
+ return status;
+}
+
+static 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_STATUS_SUCCESS ||
+ backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
+ {
+ return CAIRO_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;
+ } 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_STATUS_SUCCESS ||
+ backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
+ {
+ return CAIRO_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 {
+ if (bbox.p1.x < surface->page_bbox.p1.x)
+ surface->page_bbox.p1.x = bbox.p1.x;
+ if (bbox.p1.y < surface->page_bbox.p1.y)
+ surface->page_bbox.p1.y = bbox.p1.y;
+ if (bbox.p2.x > surface->page_bbox.p2.x)
+ surface->page_bbox.p2.x = bbox.p2.x;
+ if (bbox.p2.y > surface->page_bbox.p2.y)
+ surface->page_bbox.p2.y = bbox.p2.y;
+ }
+
+ /* 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_STATUS_SUCCESS;
+ }
+
+ if (backend_status == CAIRO_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_STATUS_SUCCESS)
+ return CAIRO_INT_STATUS_IMAGE_FALLBACK;
+ else
+ return 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, cairo_clip_t *clip)
+{
+ const cairo_rectangle_int_t *clip_extents;
+ cairo_bool_t is_empty;
+
+ clip_extents = NULL;
+ if (clip != NULL)
+ clip_extents = _cairo_clip_get_extents (clip);
+
+ if (clip_extents != NULL)
+ is_empty = _cairo_rectangle_intersect (extents, clip_extents);
+}
+
+static void
+_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_bool_t is_empty;
+
+ is_empty = _cairo_surface_get_extents (&surface->base, extents);
+
+ if (_cairo_operator_bounded_by_source (op)) {
+ cairo_rectangle_int_t source_extents;
+
+ _cairo_pattern_get_extents (source, &source_extents);
+ is_empty = _cairo_rectangle_intersect (extents, &source_extents);
+ }
+
+ _rectangle_intersect_clip (extents, clip);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_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_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+ backend_status = _analyze_recording_surface_pattern (surface, source);
+
+ _cairo_analysis_surface_operation_extents (surface,
+ op, source, clip,
+ &extents);
+
+ 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,
+ cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_int_status_t backend_status;
+ cairo_rectangle_int_t extents;
+ cairo_bool_t is_empty;
+
+ if (surface->target->backend->mask == NULL) {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ backend_status =
+ surface->target->backend->mask (surface->target,
+ op, source, mask, clip);
+ if (_cairo_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ 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;
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source;
+ if (_cairo_surface_is_recording (surface_pattern->surface)) {
+ backend_source_status =
+ _analyze_recording_surface_pattern (surface, source);
+ if (_cairo_status_is_error (backend_source_status))
+ return backend_source_status;
+ }
+ }
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
+ if (_cairo_surface_is_recording (surface_pattern->surface)) {
+ backend_mask_status =
+ _analyze_recording_surface_pattern (surface, mask);
+ if (_cairo_status_is_error (backend_mask_status))
+ return backend_mask_status;
+ }
+ }
+
+ backend_status =
+ _cairo_analysis_surface_merge_status (backend_source_status,
+ backend_mask_status);
+ }
+
+ _cairo_analysis_surface_operation_extents (surface,
+ op, source, clip,
+ &extents);
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t mask_extents;
+
+ _cairo_pattern_get_extents (mask, &mask_extents);
+ is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
+
+ }
+
+ return _add_operation (surface, &extents, backend_status);
+}
+
+static cairo_int_status_t
+_cairo_analysis_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_status_t backend_status;
+ cairo_rectangle_int_t extents;
+ cairo_bool_t is_empty;
+
+ if (surface->target->backend->stroke == NULL) {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ backend_status =
+ surface->target->backend->stroke (surface->target, op,
+ source, path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ if (_cairo_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+ backend_status = _analyze_recording_surface_pattern (surface, source);
+
+ _cairo_analysis_surface_operation_extents (surface,
+ op, source, clip,
+ &extents);
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t mask_extents;
+
+ /* If the backend can handle the stroke, then mark the approximate
+ * extents of the operation. However, if we need to fallback in order
+ * to draw the stroke, then ensure that the fallback is as tight as
+ * possible -- both to minimise output file size and to ensure good
+ * quality printed output for neighbouring regions.
+ */
+ if (backend_status == CAIRO_STATUS_SUCCESS) {
+ _cairo_path_fixed_approximate_stroke_extents (path,
+ style, ctm,
+ &mask_extents);
+ } else {
+ cairo_status_t status;
+
+ status = _cairo_path_fixed_stroke_extents (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &mask_extents);
+ if (unlikely (status))
+ return status;
+ }
+
+ is_empty = _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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_status_t backend_status;
+ cairo_rectangle_int_t extents;
+ cairo_bool_t is_empty;
+
+ if (surface->target->backend->fill == NULL) {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ backend_status =
+ surface->target->backend->fill (surface->target, op,
+ source, path, fill_rule,
+ tolerance, antialias,
+ clip);
+ if (_cairo_status_is_error (backend_status))
+ return backend_status;
+ }
+
+ if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+ backend_status = _analyze_recording_surface_pattern (surface, source);
+
+ _cairo_analysis_surface_operation_extents (surface,
+ op, source, clip,
+ &extents);
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t mask_extents;
+
+ /* We want speed for the likely case where the operation can be
+ * performed natively, but accuracy if we have to resort to
+ * using images.
+ */
+ if (backend_status == CAIRO_STATUS_SUCCESS) {
+ _cairo_path_fixed_approximate_fill_extents (path, &mask_extents);
+ } else {
+ _cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
+ &mask_extents);
+ }
+ is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
+ }
+
+ 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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_status_t status, backend_status;
+ cairo_rectangle_int_t extents, glyph_extents;
+ cairo_bool_t is_empty;
+
+ /* Adapted from _cairo_surface_show_glyphs */
+ if (surface->target->backend->show_glyphs != NULL) {
+ backend_status =
+ surface->target->backend->show_glyphs (surface->target, op,
+ source,
+ glyphs, num_glyphs,
+ scaled_font,
+ clip,
+ remaining_glyphs);
+ if (_cairo_status_is_error (backend_status))
+ 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_status_is_error (backend_status))
+ return backend_status;
+ }
+ else
+ {
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+ backend_status = _analyze_recording_surface_pattern (surface, source);
+
+ _cairo_analysis_surface_operation_extents (surface,
+ op, source, clip,
+ &extents);
+
+ if (_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;
+
+ is_empty = _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,
+ cairo_clip_t *clip)
+{
+ cairo_analysis_surface_t *surface = abstract_surface;
+ cairo_status_t status, backend_status;
+ cairo_rectangle_int_t extents, glyph_extents;
+ cairo_bool_t is_empty;
+
+ /* Adapted from _cairo_surface_show_glyphs */
+ backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ 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_status_is_error (backend_status))
+ return backend_status;
+ }
+ if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED &&
+ surface->target->backend->show_glyphs != NULL)
+ {
+ int remaining_glyphs = num_glyphs;
+ backend_status =
+ surface->target->backend->show_glyphs (surface->target, op,
+ source,
+ glyphs, num_glyphs,
+ scaled_font,
+ clip,
+ &remaining_glyphs);
+ if (_cairo_status_is_error (backend_status))
+ return backend_status;
+
+ glyphs += num_glyphs - remaining_glyphs;
+ num_glyphs = remaining_glyphs;
+ if (remaining_glyphs == 0)
+ backend_status = CAIRO_STATUS_SUCCESS;
+ }
+
+ if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
+ backend_status = _analyze_recording_surface_pattern (surface, source);
+
+ _cairo_analysis_surface_operation_extents (surface,
+ op, source, clip,
+ &extents);
+
+ if (_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;
+
+ is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents);
+ }
+
+ return _add_operation (surface, &extents, backend_status);
+}
+
+static const cairo_surface_backend_t cairo_analysis_surface_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
+ NULL, /* create_similar */
+ _cairo_analysis_surface_finish,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_analysis_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+ _cairo_analysis_surface_paint,
+ _cairo_analysis_surface_mask,
+ _cairo_analysis_surface_stroke,
+ _cairo_analysis_surface_fill,
+ _cairo_analysis_surface_show_glyphs,
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ _cairo_analysis_surface_has_show_text_glyphs,
+ _cairo_analysis_surface_show_text_glyphs
+};
+
+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 = 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);
+
+ 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
+_return_success (void)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* These typedefs are just to silence the compiler... */
+typedef cairo_int_status_t
+(*_paint_func) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip);
+
+typedef cairo_int_status_t
+(*_mask_func) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip);
+
+typedef cairo_int_status_t
+(*_stroke_func) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+typedef cairo_int_status_t
+(*_fill_func) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+typedef cairo_int_status_t
+(*_show_glyphs_func) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs);
+
+static const cairo_surface_backend_t cairo_null_surface_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+
+ NULL, /* create_similar */
+ NULL, /* finish */
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ NULL, /* get_extents */
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+ (_paint_func) _return_success, /* paint */
+ (_mask_func) _return_success, /* mask */
+ (_stroke_func) _return_success, /* stroke */
+ (_fill_func) _return_success, /* fill */
+ (_show_glyphs_func) _return_success, /* show_glyphs */
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ NULL, /* has_show_text_glyphs */
+ NULL /* show_text_glyphs */
+};
+
+cairo_surface_t *
+cairo_null_surface_create (cairo_content_t content)
+{
+ cairo_surface_t *surface;
+
+ surface = 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);
+
+ 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 000000000..018a14b4a
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-arc-private.h
@@ -0,0 +1,57 @@
+/* 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 <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_ARC_PRIVATE_H
+#define CAIRO_ARC_PRIVATE_H
+
+#include "cairoint.h"
+
+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);
+
+#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 000000000..56d42f19e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-arc.c
@@ -0,0 +1,292 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-arc-private.h"
+
+/* 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 poynomials", Michael
+ Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
+ various values of h along with error analysis for each.
+
+ From that paper, a very practical value of h is:
+
+ h = 4/3 * tan(angle/4)
+
+ 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;
+
+ while (angle_max - angle_min > 4 * M_PI)
+ angle_max -= 2 * M_PI;
+
+ /* 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 angle, angle_step;
+
+ cairo_get_matrix (cr, &ctm);
+ segments = _arc_segments_needed (angle_max - angle_min,
+ radius, &ctm,
+ cairo_get_tolerance (cr));
+ angle_step = (angle_max - angle_min) / (double) segments;
+
+ if (dir == CAIRO_DIRECTION_FORWARD) {
+ angle = angle_min;
+ } else {
+ angle = angle_max;
+ angle_step = - angle_step;
+ }
+
+ for (i = 0; i < segments; i++, angle += angle_step) {
+ _cairo_arc_segment (cr, xc, yc,
+ radius,
+ angle,
+ angle + angle_step);
+ }
+ }
+}
+
+/**
+ * _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.c b/gfx/cairo/cairo/src/cairo-array.c
new file mode 100644
index 000000000..2d11402a3
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-array.c
@@ -0,0 +1,562 @@
+/* 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 <krh@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.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, int element_size)
+{
+ array->size = 0;
+ array->num_elements = 0;
+ array->element_size = element_size;
+ array->elements = NULL;
+
+ array->is_snapshot = FALSE;
+
+}
+
+/**
+ * _cairo_array_init_snapshot:
+ * @array: A #cairo_array_t to be initialized as a snapshot
+ * @other: The #cairo_array_t from which to create the snapshot
+ *
+ * Initialize @array as an immutable copy of @other. It is an error to
+ * call an array-modifying function (other than _cairo_array_fini) on
+ * @array after calling this function.
+ **/
+void
+_cairo_array_init_snapshot (cairo_array_t *array,
+ const cairo_array_t *other)
+{
+ array->size = other->size;
+ array->num_elements = other->num_elements;
+ array->element_size = other->element_size;
+ array->elements = other->elements;
+
+ array->is_snapshot = TRUE;
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+}
+
+/**
+ * _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)
+{
+ if (array->is_snapshot)
+ return;
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ if (array->elements) {
+ free (* array->elements);
+ 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;
+
+ assert (! array->is_snapshot);
+
+ /* check for integer overflow */
+ if (required_size > INT_MAX || required_size < array->num_elements)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ 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;
+
+ if (array->elements == NULL) {
+ array->elements = malloc (sizeof (char *));
+ if (unlikely (array->elements == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ *array->elements = NULL;
+ }
+
+ array->size = new_size;
+ new_elements = _cairo_realloc_ab (*array->elements,
+ 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;
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ 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)
+{
+ assert (! array->is_snapshot);
+
+ if (num_elements < array->num_elements)
+ array->num_elements = num_elements;
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+}
+
+/**
+ * _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:
+ *
+ * <informalexample><programlisting>
+ * 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 ...
+ * </programlisting></informalexample>
+ **/
+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, num_elements);
+ * 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);
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ return (void *) &(*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 (cairo_array_t *array, int index, void *dst)
+{
+ memcpy (dst, _cairo_array_index (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)
+{
+ assert (! array->is_snapshot);
+
+ 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,
+ int num_elements)
+{
+ cairo_status_t status;
+ void *dest;
+
+ assert (! array->is_snapshot);
+
+ status = _cairo_array_allocate (array, num_elements, &dest);
+ if (unlikely (status))
+ return status;
+
+ memcpy (dest, elements, num_elements * array->element_size);
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _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 unitialized, 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;
+
+ assert (! array->is_snapshot);
+
+ status = _cairo_array_grow_by (array, num_elements);
+ if (unlikely (status))
+ return status;
+
+ assert (array->num_elements + num_elements <= array->size);
+
+ *elements = &(*array->elements)[array->num_elements * array->element_size];
+
+ array->num_elements += num_elements;
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ 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.
+ **/
+int
+_cairo_array_num_elements (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.
+ **/
+int
+_cairo_array_size (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;
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ num_slots = array->num_elements;
+ if (num_slots) {
+ cairo_user_data_slot_t *slots;
+
+ slots = _cairo_array_index (array, 0);
+ do {
+ if (slots->user_data != NULL && slots->destroy != NULL)
+ slots->destroy (slots->user_data);
+ slots++;
+ } while (--num_slots);
+ }
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ _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;
+
+ if (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ num_slots = array->num_elements;
+ slots = _cairo_array_index (array, 0);
+ for (i = 0; i < num_slots; i++) {
+ 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 (array->num_elements != 0 && *array->elements == NULL)
+ abort();
+
+ if (slot) {
+ *slot = new_slot;
+ 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,
+ cairo_user_data_array_t *src)
+{
+ /* discard any existing user-data */
+ if (dst->num_elements != 0) {
+ _cairo_user_data_array_fini (dst);
+ _cairo_user_data_array_init (dst);
+ }
+
+ if (src->num_elements == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_array_append_multiple (dst,
+ _cairo_array_index (src, 0),
+ src->num_elements);
+}
+
+void
+_cairo_user_data_array_foreach (cairo_user_data_array_t *array,
+ void (*func) (const void *key,
+ void *elt,
+ void *closure),
+ void *closure)
+{
+ cairo_user_data_slot_t *slots;
+ int i, num_slots;
+
+ num_slots = array->num_elements;
+ slots = _cairo_array_index (array, 0);
+ for (i = 0; i < num_slots; i++) {
+ if (slots[i].user_data != NULL)
+ func (slots[i].key, slots[i].user_data, closure);
+ }
+}
diff --git a/gfx/cairo/cairo/src/cairo-atomic-private.h b/gfx/cairo/cairo/src/cairo-atomic-private.h
new file mode 100644
index 000000000..af462e370
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-atomic-private.h
@@ -0,0 +1,419 @@
+/* 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 <chris@chris-wilson.co.uk>
+ * Andrea Canciani <ranma42@gmail.com>
+ */
+
+#ifndef CAIRO_ATOMIC_PRIVATE_H
+#define CAIRO_ATOMIC_PRIVATE_H
+
+# include "cairo-compiler-private.h"
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_WIN32_ATOMIC_PRIMITIVES
+#include <Windows.h>
+#endif
+
+/* 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_WIN32_ATOMIC_PRIMITIVES
+
+#define HAS_ATOMIC_OPS 1
+
+typedef volatile long cairo_atomic_int_t;
+
+# define _cairo_atomic_int_get(x) ((int)*x)
+# define _cairo_atomic_int_get_relaxed(x) ((int)*(x))
+# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val))
+# define _cairo_atomic_ptr_get(x) ((void*)*x)
+
+# define _cairo_atomic_int_inc(x) ((void) InterlockedIncrement(x))
+# define _cairo_atomic_int_dec(x) ((void) InterlockedDecrement(x))
+# define _cairo_atomic_int_dec_and_test(x) (InterlockedDecrement(x) == 0)
+# define _cairo_atomic_int_cmpxchg(x, oldv, newv) (InterlockedCompareExchange(x, newv, oldv) == oldv)
+# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) InterlockedCompareExchange(x, newv, oldv)
+
+typedef volatile void* cairo_atomic_intptr_t;
+
+#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) (InterlockedCompareExchangePointer(x, newv, oldv) == oldv)
+#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) (InterlockedCompareExchangePointer(x, newv, oldv))
+
+#endif
+
+#if HAVE_INTEL_ATOMIC_PRIMITIVES
+
+#define HAS_ATOMIC_OPS 1
+
+typedef int cairo_atomic_int_t;
+
+#ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER
+static cairo_always_inline cairo_atomic_int_t
+_cairo_atomic_int_get (cairo_atomic_int_t *x)
+{
+ __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;
+}
+#else
+# define _cairo_atomic_int_get(x) (*x)
+# define _cairo_atomic_int_get_relaxed(x) (*(x))
+# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val))
+# define _cairo_atomic_ptr_get(x) (*x)
+#endif
+
+# define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1))
+# define _cairo_atomic_int_dec_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 <atomic_ops.h>
+
+#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_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 <libkern/OSAtomic.h>
+
+#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_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);
+
+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 { \
+ /* hide compiler warnings about cairo_status_t != int (gcc treats its as \
+ * an unsigned integer instead, and about ignoring the return value. */ \
+ int ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \
+ (void) ret__; \
+} while (0)
+
+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 000000000..2af50cd38
--- /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 <chris@chris-wilson.co.uk>
+ */
+
+#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-base64-stream.c b/gfx/cairo/cairo/src/cairo-base64-stream.c
new file mode 100644
index 000000000..636431372
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-base64-stream.c
@@ -0,0 +1,144 @@
+/* -*- 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 <emmanuel.pacaud@free.fr>
+ * 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 <krh@redhat.com>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#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] = '=';
+ 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 = 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 000000000..f81affb49
--- /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 <krh@redhat.com>
+ */
+
+#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 = 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 = 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 000000000..8c1d54f0c
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectangular.c
@@ -0,0 +1,838 @@
+/*
+ * 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-list-private.h"
+
+#include <setjmp.h>
+
+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 _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;
+
+ cairo_fill_rule_t fill_rule;
+
+ 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,
+ cairo_fill_rule_t fill_rule)
+{
+ _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;
+ /* we need to initialize prev so that we can check
+ * if this edge is the left most and make sure
+ * we always insert to the right of it, even if
+ * our x coordinate matches */
+ sweep_line->head.prev = NULL;
+
+ sweep_line->tail.x = INT32_MAX;
+ sweep_line->tail.right = NULL;
+ sweep_line->tail.dir = 0;
+ sweep_line->tail.prev = &sweep_line->head;
+ sweep_line->tail.next = NULL;
+
+ sweep_line->insert_left = &sweep_line->tail;
+ sweep_line->insert_right = &sweep_line->tail;
+
+ sweep_line->current_y = INT32_MIN;
+ sweep_line->last_y = INT32_MIN;
+
+ sweep_line->fill_rule = fill_rule;
+
+ pqueue_init (&sweep_line->pq);
+}
+
+static void
+sweep_line_fini (sweep_line_t *sweep_line)
+{
+ pqueue_fini (&sweep_line->pq);
+}
+
+static void
+edge_end_box (sweep_line_t *sweep_line,
+ edge_t *left,
+ int32_t bot,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ /* Only emit (trivial) non-degenerate trapezoids with positive height. */
+ if (likely (left->top < bot)) {
+ if (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 (container, left->top, bot, &_left, &_right);
+ status = _cairo_traps_status ((cairo_traps_t *) 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 (container, &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,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ 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;
+ }
+
+ edge_end_box (sweep_line,
+ left, top, do_traps, container);
+ }
+
+ if (right != NULL && left->x != right->x) {
+ left->top = top;
+ left->right = right;
+ }
+}
+
+static inline void
+active_edges_to_traps (sweep_line_t *sweep,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ int top = sweep->current_y;
+ edge_t *pos;
+
+ if (sweep->last_y == sweep->current_y)
+ return;
+
+ 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 */
+ if (left->right == NULL) {
+ while (unlikely (right->x == left->x)) {
+ winding += right->dir;
+ if (right->right != NULL) {
+ /* continuation on left */
+ left->top = right->top;
+ left->right = right->right;
+ right->right = NULL;
+ winding -= right->dir;
+ break;
+ }
+
+ right = right->next;
+ }
+
+ if (winding == 0) {
+ pos = right;
+ continue;
+ }
+ }
+
+ /* Greedily search for the closing edge, so that we generate the
+ * maximal span width with the minimal number of trapezoids.
+ */
+
+ do {
+ /* End all subsumed traps */
+ if (unlikely (right->right != NULL)) {
+ edge_end_box (sweep,
+ right, top, do_traps, container);
+ }
+
+ winding += right->dir;
+ if (winding == 0) {
+ /* skip co-linear edges */
+ if (likely (right->x != right->next->x))
+ break;
+ }
+
+ right = right->next;
+ } while (TRUE);
+
+ edge_start_or_continue_box (sweep,
+ left, right, top,
+ do_traps, container);
+
+ 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, do_traps, container);
+ }
+
+ if (++count & 1) {
+ /* skip co-linear edges */
+ if (likely (right->x != right->next->x))
+ break;
+ }
+
+ right = right->next;
+ } while (TRUE);
+
+ edge_start_or_continue_box (sweep,
+ pos, right, top,
+ do_traps, container);
+
+ pos = right->next;
+ } while (pos != &sweep->tail);
+ }
+
+ sweep->last_y = sweep->current_y;
+}
+
+static inline void
+sweep_line_delete_edge (sweep_line_t *sweep_line,
+ edge_t *edge,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ if (edge->right != NULL) {
+ edge_t *next = edge->next;
+ if (next->x == edge->x) {
+ next->top = edge->top;
+ next->right = edge->right;
+ } else {
+ edge_end_box (sweep_line,
+ edge,
+ sweep_line->current_y,
+ do_traps, container);
+ }
+ }
+
+ 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 cairo_bool_t
+sweep_line_delete (sweep_line_t *sweep,
+ rectangle_t *rectangle,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ 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,
+ do_traps, container);
+
+ sweep_line_delete_edge (sweep,
+ &rectangle->right,
+ do_traps, container);
+
+ pqueue_pop (&sweep->pq);
+ return update;
+}
+
+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);
+ }
+ }
+ if (pos->prev) {
+ pos->prev->next = edge;
+ edge->prev = pos->prev;
+ edge->next = pos;
+ pos->prev = edge;
+ } else {
+ /* we have edge that shares an x coordinate with the left most sentinal.
+ * instead of inserting before pos and ruining our sentinal we insert after pos. */
+ pos->next->prev = edge;
+ edge->next = pos->next;
+ edge->prev = pos;
+ pos->next = edge;
+ }
+}
+
+static inline cairo_bool_t
+sweep_line_insert (sweep_line_t *sweep,
+ rectangle_t *rectangle)
+{
+ edge_t *pos;
+
+ /* right edge */
+ pos = sweep->insert_right;
+ insert_edge (&rectangle->right, pos);
+ sweep->insert_right = &rectangle->right;
+
+ /* left edge */
+ pos = sweep->insert_left;
+ if (pos->x > sweep->insert_right->x)
+ pos = sweep->insert_right->prev;
+ insert_edge (&rectangle->left, pos);
+ sweep->insert_left = &rectangle->left;
+
+ pqueue_push (sweep, rectangle);
+
+ if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING &&
+ rectangle->left.prev->dir == rectangle->left.dir)
+ {
+ return rectangle->left.next != &rectangle->right;
+ }
+
+ return TRUE;
+}
+
+static cairo_status_t
+_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 = FALSE;
+
+ sweep_line_init (&sweep_line, rectangles, num_rectangles, fill_rule);
+ 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) {
+ if (update) {
+ active_edges_to_traps (&sweep_line,
+ do_traps, container);
+ update = FALSE;
+ }
+
+ sweep_line.current_y = stop->bottom;
+ }
+
+ update |= sweep_line_delete (&sweep_line, stop, do_traps, container);
+
+ stop = rectangle_peek_stop (&sweep_line);
+ }
+
+ if (update) {
+ active_edges_to_traps (&sweep_line, do_traps, container);
+ update = FALSE;
+ }
+
+ sweep_line.current_y = rectangle->top;
+ }
+
+ update |= 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) {
+ if (update) {
+ active_edges_to_traps (&sweep_line, do_traps, container);
+ update = FALSE;
+ }
+
+ sweep_line.current_y = rectangle->bottom;
+ }
+
+ update |= sweep_line_delete (&sweep_line, rectangle, do_traps, container);
+ }
+
+unwind:
+ sweep_line_fini (&sweep_line);
+ return status;
+}
+
+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 *rectangles;
+ rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1];
+ rectangle_t **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 *),
+ 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] = &rectangles[i];
+ }
+
+ _cairo_traps_clear (traps);
+ status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, 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 *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;
+
+ 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, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ rectangles = stack_rectangles;
+ rectangles_ptrs = stack_rectangles_ptrs;
+ if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) {
+ rectangles = _cairo_malloc_ab_plus_c (in->num_boxes,
+ sizeof (rectangle_t) +
+ sizeof (rectangle_t *),
+ sizeof (rectangle_t *));
+ if (unlikely (rectangles == NULL))
+ 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++) {
+ 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;
+
+ rectangles_ptrs[j] = &rectangles[j];
+ j++;
+ }
+ }
+
+ _cairo_boxes_clear (out);
+ status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, 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 000000000..1696d9367
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann-rectilinear.c
@@ -0,0 +1,667 @@
+/*
+ * 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-combsort-private.h"
+#include "cairo-error-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, &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 (cairo_traps_t *traps,
+ const cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_status_t status;
+ cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)];
+ cairo_bo_event_t *events;
+ cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
+ cairo_bo_event_t **event_ptrs;
+ cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)];
+ cairo_bo_edge_t *edges;
+ int num_events;
+ int i, j;
+
+ if (unlikely (polygon->num_edges == 0))
+ return CAIRO_STATUS_SUCCESS;
+
+ num_events = 2 * polygon->num_edges;
+
+ events = stack_events;
+ event_ptrs = stack_event_ptrs;
+ edges = stack_edges;
+ if (num_events > ARRAY_LENGTH (stack_events)) {
+ events = _cairo_malloc_ab_plus_c (num_events,
+ sizeof (cairo_bo_event_t) +
+ sizeof (cairo_bo_edge_t) +
+ sizeof (cairo_bo_event_t *),
+ sizeof (cairo_bo_event_t *));
+ if (unlikely (events == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ event_ptrs = (cairo_bo_event_t **) (events + num_events);
+ edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1);
+ }
+
+ for (i = j = 0; i < polygon->num_edges; i++) {
+ edges[i].edge = polygon->edges[i];
+ edges[i].deferred_trap.right = NULL;
+ edges[i].prev = NULL;
+ edges[i].next = NULL;
+
+ event_ptrs[j] = &events[j];
+ events[j].type = CAIRO_BO_EVENT_TYPE_START;
+ events[j].point.y = polygon->edges[i].top;
+ events[j].point.x = polygon->edges[i].line.p1.x;
+ events[j].edge = &edges[i];
+ j++;
+
+ event_ptrs[j] = &events[j];
+ events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
+ events[j].point.y = polygon->edges[i].bottom;
+ events[j].point.x = polygon->edges[i].line.p1.x;
+ events[j].edge = &edges[i];
+ j++;
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j,
+ fill_rule,
+ TRUE, traps);
+ if (events != stack_events)
+ free (events);
+
+ traps->is_rectilinear = TRUE;
+
+ return status;
+}
+
+cairo_status_t
+_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ 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 000000000..b3819f2f7
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c
@@ -0,0 +1,2133 @@
+/*
+ * 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-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_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-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, bx;
+
+ if (y == a->edge.line.p1.y)
+ ax = a->edge.line.p1.x;
+ else if (y == a->edge.line.p2.y)
+ ax = a->edge.line.p2.x;
+ else
+ have_ax_bx &= ~HAVE_AX;
+
+ if (y == b->edge.line.p1.y)
+ bx = b->edge.line.p1.x;
+ else if (y == b->edge.line.p2.y)
+ bx = b->edge.line.p2.x;
+ else
+ have_ax_bx &= ~HAVE_BX;
+
+ switch (have_ax_bx) {
+ default:
+ case HAVE_NEITHER:
+ return edges_compare_x_for_y_general (a, b, y);
+ case HAVE_AX:
+ return -edge_compare_for_y_against_x (b, y, ax);
+ case HAVE_BX:
+ return edge_compare_for_y_against_x (a, y, bx);
+ case HAVE_BOTH:
+ return ax - bx;
+ }
+}
+
+static inline int
+_line_equal (const cairo_line_t *a, const cairo_line_t *b)
+{
+ return a->p1.x == b->p1.x && a->p1.y == b->p1.y &&
+ a->p2.x == b->p2.x && a->p2.y == b->p2.y;
+}
+
+static int
+_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line,
+ 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<R => t > 1
+ *
+ * (where top/bottom must at least extend to the line endpoints).
+ *
+ * A similar substitution can be performed for s, yielding:
+ * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
+ */
+ R = det32_64 (dx2, dy2,
+ b->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->stopped = 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;
+}
+
+#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
+
+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;
+ }
+}
+
+/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */
+static cairo_status_t
+_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;
+
+ return _cairo_traps_status (traps);
+}
+
+
+/* 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_traps_t *traps)
+{
+ cairo_status_t status;
+
+ if (left->deferred_trap.right == right)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (left->deferred_trap.right != NULL) {
+ if (right != NULL && edges_colinear (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, traps);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (right != NULL && ! 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
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline cairo_status_t
+_active_edges_to_traps (cairo_bo_edge_t *left,
+ int32_t top,
+ cairo_fill_rule_t fill_rule,
+ cairo_traps_t *traps)
+{
+ cairo_bo_edge_t *right;
+ cairo_status_t status;
+
+#if DEBUG_PRINT_STATE
+ printf ("Processing active edges for %x\n", top);
+#endif
+
+ if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+ while (left != NULL) {
+ int in_out;
+
+ /* Greedily search for the closing edge, so that we generate the
+ * maximal span width with the minimal number of trapezoids.
+ */
+ in_out = left->edge.dir;
+
+ /* Check if there is a co-linear edge with an existing trap */
+ right = left->next;
+ if (left->deferred_trap.right == NULL) {
+ while (right != NULL && right->deferred_trap.right == NULL)
+ right = right->next;
+
+ if (right != NULL && edges_colinear (left, right)) {
+ /* continuation on left */
+ left->deferred_trap = right->deferred_trap;
+ right->deferred_trap.right = NULL;
+ }
+ }
+
+ /* End all subsumed traps */
+ right = left->next;
+ while (right != NULL) {
+ if (right->deferred_trap.right != NULL) {
+ status = _cairo_bo_edge_end_trap (right, top, traps);
+ if (unlikely (status))
+ return status;
+ }
+
+ in_out += right->edge.dir;
+ if (in_out == 0) {
+ cairo_bo_edge_t *next;
+ cairo_bool_t skip = FALSE;
+
+ /* skip co-linear edges */
+ next = right->next;
+ if (next != NULL)
+ skip = edges_colinear (right, next);
+
+ if (! skip)
+ break;
+ }
+
+ right = right->next;
+ }
+
+ status = _cairo_bo_edge_start_or_continue_trap (left, right,
+ top, traps);
+ if (unlikely (status))
+ return status;
+
+ left = right;
+ if (left != NULL)
+ left = left->next;
+ }
+ } else {
+ while (left != NULL) {
+ int in_out = 0;
+
+ right = left->next;
+ while (right != NULL) {
+ if (right->deferred_trap.right != NULL) {
+ status = _cairo_bo_edge_end_trap (right, top, traps);
+ if (unlikely (status))
+ return status;
+ }
+
+ if ((in_out++ & 1) == 0) {
+ cairo_bo_edge_t *next;
+ cairo_bool_t skip = FALSE;
+
+ /* skip co-linear edges */
+ next = right->next;
+ if (next != NULL)
+ skip = edges_colinear (right, next);
+
+ if (! skip)
+ break;
+ }
+
+ right = right->next;
+ }
+
+ status = _cairo_bo_edge_start_or_continue_trap (left, right,
+ top, traps);
+ if (unlikely (status))
+ return status;
+
+ left = right;
+ if (left != NULL)
+ left = left->next;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+/* Execute a single pass of the Bentley-Ottmann algorithm on edges,
+ * generating trapezoids according to the fill_rule and appending them
+ * to traps. */
+static cairo_status_t
+_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events,
+ int num_events,
+ cairo_fill_rule_t fill_rule,
+ cairo_traps_t *traps,
+ int *num_intersections)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
+ 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;
+
+#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) {
+ status = _cairo_bo_edge_end_trap (e1,
+ e1->edge.bottom,
+ traps);
+ if (unlikely (status))
+ goto unwind;
+ }
+ }
+ sweep_line.stopped = NULL;
+
+ status = _active_edges_to_traps (sweep_line.head,
+ sweep_line.current_y,
+ fill_rule, traps);
+ if (unlikely (status))
+ goto unwind;
+
+ 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;
+
+ 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;
+
+ /* 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) {
+ status = _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps);
+ if (unlikely (status))
+ break;
+ }
+ }
+ 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_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;
+
+ num_events = polygon->num_edges;
+ if (unlikely (0 == num_events))
+ return CAIRO_STATUS_SUCCESS;
+
+ 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_trap.right = NULL;
+ events[i].edge.prev = NULL;
+ events[i].edge.next = 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);
+ _cairo_polygon_limit (&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 000000000..e527272e6
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-beos-surface.cpp
@@ -0,0 +1,981 @@
+/* vim:set ts=8 sw=4 noet cin: */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Christian Biesinger <cbiesinger@web.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a 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 Christian Biesinger
+ * <cbiesinger@web.de>
+ *
+ * Contributor(s):
+ */
+
+// This is a C++ file in order to use the C++ BeOS API
+
+#include "cairoint.h"
+
+#include "cairo-beos.h"
+
+#include <new>
+
+#include <Bitmap.h>
+#include <Region.h>
+#if 0
+#include <DirectWindow.h>
+#endif
+#include <Screen.h>
+#include <Window.h>
+#include <Locker.h>
+
+#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)(CAIRO_STATUS_SUCCESS)
+
+struct cairo_beos_surface_t {
+ cairo_surface_t base;
+
+ 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 BRect
+_cairo_rect_to_brect (const cairo_rectangle_int16_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 cairo_rectangle_int16_t
+_brect_to_cairo_rect (const BRect& rect)
+{
+ cairo_rectangle_int16_t retval;
+ retval.x = int(rect.left + 0.5);
+ retval.y = int(rect.top + 0.5);
+ retval.width = rect.IntegerWidth() + 1;
+ retval.height = rect.IntegerHeight() + 1;
+ return retval;
+}
+
+static 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<BDirectWindow*>(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;
+}
+
+inline unsigned char
+unpremultiply (unsigned char color,
+ unsigned char alpha)
+{
+ if (alpha == 0)
+ return 0;
+ // plus alpha/2 to round instead of truncate
+ return (color * 255 + alpha / 2) / alpha;
+}
+
+inline unsigned char
+premultiply (unsigned char color,
+ unsigned char alpha)
+{
+ // + 127 to round, instead of truncate
+ return (color * alpha + 127) / 255;
+}
+
+/**
+ * unpremultiply_rgba:
+ *
+ * Takes an input in ABGR premultiplied image data and unmultiplies it.
+ * The result is stored in retdata.
+ **/
+static void
+unpremultiply_rgba (unsigned char* data,
+ 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) {
+ // XXX for a big-endian platform this'd have to change
+ int idx = 4 * i;
+ unsigned char alpha = in[idx + 3];
+ out[idx + 0] = unpremultiply(in[idx + 0], alpha); // B
+ out[idx + 1] = unpremultiply(in[idx + 1], alpha); // G
+ out[idx + 2] = unpremultiply(in[idx + 2], alpha); // R
+ out[idx + 3] = in[idx + 3]; // Alpha
+ }
+ }
+}
+
+/**
+ * premultiply_rgba:
+ *
+ * Takes an input in ABGR non-premultiplied image data and premultiplies it.
+ * The returned data must be freed with free().
+ **/
+static unsigned char*
+premultiply_rgba (unsigned char* data,
+ int width,
+ int height,
+ int stride)
+{
+ unsigned char* retdata = reinterpret_cast<unsigned char*>(_cairo_malloc_ab(height, stride));
+ if (!retdata)
+ return NULL;
+
+ 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) {
+ // XXX for a big-endian platform this'd have to change
+ int idx = 4 * i;
+ unsigned char alpha = in[idx + 3];
+ out[idx + 0] = premultiply(in[idx + 0], alpha); // B
+ out[idx + 1] = premultiply(in[idx + 1], alpha); // G
+ out[idx + 2] = premultiply(in[idx + 2], alpha); // R
+ out[idx + 3] = in[idx + 3]; // Alpha
+ }
+ }
+ return retdata;
+}
+
+/**
+ * _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<unsigned char*>(bitmap->Bits());
+ int width = bounds.IntegerWidth() + 1;
+ int height = bounds.IntegerHeight() + 1;
+ unsigned char* premultiplied;
+ if (cformat == CAIRO_FORMAT_ARGB32) {
+ premultiplied = premultiply_rgba(bits, width, height,
+ bitmap->BytesPerRow());
+ } else {
+ premultiplied = reinterpret_cast<unsigned char*>(
+ _cairo_malloc_ab(bitmap->BytesPerRow(), height));
+ if (premultiplied)
+ memcpy(premultiplied, bits, bitmap->BytesPerRow() * height);
+ }
+ if (!premultiplied)
+ return NULL;
+
+ cairo_image_surface_t* surf = reinterpret_cast<cairo_image_surface_t*>
+ (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_rgba(surface->data,
+ surface->width,
+ surface->height,
+ surface->stride,
+ reinterpret_cast<unsigned char*>(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
+#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)
+{
+ fprintf(stderr, "Creating similar\n");
+
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+
+ if (width <= 0)
+ width = 1;
+ if (height <= 0)
+ height = 1;
+
+ BRect rect(0.0, 0.0, width - 1, height - 1);
+ BBitmap* bmp;
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ // Can't support this natively
+ return _cairo_image_surface_create_with_content(content, width,
+ height);
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ bmp = new BBitmap(rect, B_RGBA32, true);
+ break;
+ case CAIRO_CONTENT_COLOR:
+ // Match the color depth
+ if (surface->bitmap) {
+ color_space space = surface->bitmap->ColorSpace();
+ // No alpha was requested -> make sure not to return
+ // a surface with alpha
+ if (space == B_RGBA32)
+ space = B_RGB32;
+ if (space == B_RGBA15)
+ space = B_RGB15;
+ bmp = new BBitmap(rect, space, true);
+ } else {
+ BScreen scr(surface->view->Window());
+ color_space space = B_RGB32;
+ if (scr.IsValid())
+ space = scr.ColorSpace();
+ bmp = new BBitmap(rect, space, true);
+ }
+ break;
+ default:
+ assert(0);
+ return NULL;
+
+ };
+ BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0);
+ bmp->AddChild(view);
+ return _cairo_beos_surface_create_internal(view, bmp, true);
+}
+
+static cairo_status_t
+_cairo_beos_surface_finish (void *abstract_surface)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+ if (surface->owns_bitmap_view) {
+ if (surface->bitmap)
+ surface->bitmap->RemoveChild(surface->view);
+
+ delete surface->view;
+ delete surface->bitmap;
+
+ surface->view = NULL;
+ surface->bitmap = NULL;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_beos_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ fprintf(stderr, "Getting source image\n");
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ 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 (!*image_out)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ *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 (!*image_out) {
+ delete bmp;
+ return CAIRO_STATUS_NO_MEMORY;
+ }
+ *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);
+
+ BBitmap* bmp = static_cast<BBitmap*>(image_extra);
+ delete bmp;
+}
+
+
+
+static cairo_status_t
+_cairo_beos_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int16_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int16_t *image_rect,
+ void **image_extra)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+
+ AutoLockView locker(surface->view);
+ if (!locker) {
+ *image_out = NULL;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (surface->bitmap) {
+ surface->view->Sync();
+ *image_out = _cairo_beos_bitmap_to_surface(surface->bitmap);
+ if (!*image_out)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ 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_rect_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_rect(rect);
+
+#if 0
+ fprintf(stderr, "Requested: (cairo rects) (%ix%i) dim (%u, %u) returning (%ix%i) dim (%u, %u)\n",
+ interest_rect->x, interest_rect->y, interest_rect->width, interest_rect->height,
+ image_rect->x, image_rect->y, image_rect->width, image_rect->height);
+#endif
+
+ *image_out = _cairo_beos_bitmap_to_surface(bitmap);
+ delete bitmap;
+ if (!*image_out)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+static void
+_cairo_beos_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int16_t *intersect_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int16_t *image_rect,
+ void *image_extra)
+{
+ fprintf(stderr, "Fallback drawing\n");
+
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+
+ AutoLockView locker(surface->view);
+ if (!locker)
+ return;
+
+ BBitmap* bitmap_to_draw = _cairo_image_surface_to_bitmap(image);
+ surface->view->PushState();
+
+ surface->view->SetDrawingMode(B_OP_COPY);
+ BRect rect(_cairo_rect_to_brect(image_rect));
+
+ surface->view->DrawBitmap(bitmap_to_draw, 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_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ dst);
+ 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;
+
+ 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<cairo_surface_pattern_t*>(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<cairo_image_surface_t*>(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<cairo_beos_surface_t*>(src_surface);
+ if (beos_surface->bitmap) {
+ AutoLockView locker(beos_surface->view);
+ if (locker)
+ beos_surface->view->Sync();
+ bmp = beos_surface->bitmap;
+ } else {
+ _cairo_beos_view_to_bitmap(surface->view, &bmp);
+ free_bmp = true;
+ }
+ }
+
+ if (!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;
+ }
+
+ fprintf(stderr, "Composite\n");
+
+ // Draw it on screen.
+ surface->view->PushState();
+
+ // If our image rect is only a subrect of the desired size, and we
+ // aren't using B_OP_ALPHA, then we need to fill the rect first.
+ if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) {
+ rgb_color black = { 0, 0, 0, 0 };
+
+ surface->view->SetDrawingMode(mode);
+ surface->view->SetHighColor(black);
+ surface->view->FillRect(dstRect);
+ }
+
+ if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) {
+ mode = B_OP_COPY;
+ }
+ surface->view->SetDrawingMode(mode);
+
+ if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
+ surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
+ else
+ surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
+
+ surface->view->DrawBitmap(bmp, srcRect, dstRect);
+
+ surface->view->PopState();
+
+ if (free_bmp)
+ delete bmp;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+
+static void
+_cairo_beos_surface_fill_rectangle (cairo_beos_surface_t *surface,
+ cairo_rectangle_int16_t *rect)
+{
+ BRect brect(_cairo_rect_to_brect(rect));
+ surface->view->FillRect(brect);
+}
+
+static cairo_int_status_t
+_cairo_beos_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int16_t *rects,
+ int num_rects)
+{
+ fprintf(stderr, "Drawing %i rectangles\n", num_rects);
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+
+ 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;
+
+ 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 = premultiply(be_color.red, be_color.alpha);
+ be_color.green = premultiply(be_color.green, be_color.alpha);
+ be_color.blue = premultiply(be_color.blue, be_color.alpha);
+ }
+
+ 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) {
+ _cairo_beos_surface_fill_rectangle(surface, &rects[i]);
+ }
+
+ surface->view->PopState();
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+
+
+static cairo_int_status_t
+_cairo_beos_surface_set_clip_region (void *abstract_surface,
+ pixman_region16_t *region)
+{
+ fprintf(stderr, "Setting clip region\n");
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+ AutoLockView locker(surface->view);
+ if (!locker)
+ return CAIRO_INT_STATUS_SUCCESS;
+
+ if (region == NULL) {
+ // No clipping
+ surface->view->ConstrainClippingRegion(NULL);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ int count = pixman_region_num_rects(region);
+ pixman_box16_t* rects = pixman_region_rects(region);
+ BRegion bregion;
+ for (int i = 0; i < count; ++i) {
+ // Have to substract one, because for pixman, the second coordinate
+ // lies outside the rectangle.
+ bregion.Include(BRect(rects[i].x1, rects[i].y1, rects[i].x2 - 1, rects[i].y2 - 1));
+ }
+ surface->view->ConstrainClippingRegion(&bregion);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_beos_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int16_t *rectangle)
+{
+ cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
+ abstract_surface);
+ AutoLockView locker(surface->view);
+ if (!locker)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ BRect size = surface->view->Bounds();
+
+ *rectangle = _brect_to_cairo_rect(size);
+
+ // Make sure to have our upperleft edge as (0,0)
+ rectangle->x = 0;
+ rectangle->y = 0;
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+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_set_clip_region,
+ NULL, /* intersect_clip_path */
+ _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<cairo_beos_surface_t*>(
+ malloc(sizeof(cairo_beos_surface_t)));
+ if (surface == NULL) {
+ _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return const_cast<cairo_surface_t*>(&_cairo_surface_nil);
+ }
+
+ cairo_content_t content = CAIRO_CONTENT_COLOR;
+ if (bmp && (bmp->ColorSpace() == B_RGBA32 || bmp->ColorSpace() == B_RGBA15))
+ content = CAIRO_CONTENT_COLOR_ALPHA;
+ _cairo_surface_init(&surface->base, &cairo_beos_surface_backend, content);
+
+ surface->view = view;
+ surface->bitmap = bmp;
+ surface->owns_bitmap_view = owns_bitmap_view;
+
+ return (cairo_surface_t *) surface;
+}
+
+/**
+ * cairo_beos_surface_create:
+ * @view: The view to draw on
+ *
+ * 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.
+ **/
+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.
+ **/
+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 000000000..fdb89a6c4
--- /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 <cbiesinger@web.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL 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
+ * <cbiesinger@web.de>
+ *
+ * Contributor(s):
+ */
+
+#ifndef CAIRO_BEOS_H
+#define CAIRO_BEOS_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_BEOS_SURFACE
+
+#include <View.h>
+
+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 000000000..0778a5dcd
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-botor-scan-converter.c
@@ -0,0 +1,2199 @@
+/*
+ * 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 <cworth@cworth.org>
+ * M Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* Provide definitions for standalone compilation */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-list-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-combsort-private.h"
+
+#include <setjmp.h>
+
+#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, bx;
+
+ /* 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<R => t > 1
+ *
+ * (where top/bottom must at least extend to the line endpoints).
+ *
+ * A similar substitution can be performed for s, yielding:
+ * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
+ */
+ R = det32_64 (dx2, dy2,
+ b->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);
+}
+
+inline 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].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].coverage = AREA_TO_ALPHA (cover);
+ ++num_spans;
+ }
+
+ if (cover && prev_x < self->xmax) {
+ spans[num_spans].x = self->xmax;
+ 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;
+}
+
+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);
+}
+
+static cairo_status_t
+_cairo_botor_scan_converter_add_polygon (void *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.add_edge = _cairo_botor_scan_converter_add_edge;
+ self->base.add_polygon = _cairo_botor_scan_converter_add_polygon;
+ self->base.generate = _cairo_botor_scan_converter_generate;
+
+ self->extents = *extents;
+ 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-boxes-private.h b/gfx/cairo/cairo/src/cairo-boxes-private.h
new file mode 100644
index 000000000..3af0fbdef
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-boxes-private.h
@@ -0,0 +1,84 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_BOXES_H
+#define CAIRO_BOXES_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+struct _cairo_boxes_t {
+ cairo_status_t status;
+ cairo_box_t limit;
+ const cairo_box_t *limits;
+ int num_limits;
+ int num_boxes;
+ unsigned int is_pixel_aligned : 1;
+
+ 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_for_array (cairo_boxes_t *boxes,
+ cairo_box_t *array,
+ int num_boxes);
+
+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,
+ const cairo_box_t *box);
+
+cairo_private void
+_cairo_boxes_extents (const cairo_boxes_t *boxes,
+ cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_boxes_clear (cairo_boxes_t *boxes);
+
+cairo_private void
+_cairo_boxes_fini (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 000000000..31bfc0e4e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-boxes.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.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.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_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;
+}
+
+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_fixed_is_integer (box->p1.x) &&
+ _cairo_fixed_is_integer (box->p1.y) &&
+ _cairo_fixed_is_integer (box->p2.x) &&
+ _cairo_fixed_is_integer (box->p2.y);
+ }
+}
+
+cairo_status_t
+_cairo_boxes_add (cairo_boxes_t *boxes,
+ const cairo_box_t *box)
+{
+ 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;
+}
+
+void
+_cairo_boxes_extents (const cairo_boxes_t *boxes,
+ cairo_rectangle_int_t *extents)
+{
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_box_t box;
+ int i;
+
+ box.p1.y = box.p1.x = INT_MAX;
+ box.p2.y = box.p2.x = INT_MIN;
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *b = chunk->base;
+ for (i = 0; i < chunk->count; i++) {
+ if (b[i].p1.x < box.p1.x)
+ box.p1.x = b[i].p1.x;
+
+ if (b[i].p1.y < box.p1.y)
+ box.p1.y = b[i].p1.y;
+
+ if (b[i].p2.x > box.p2.x)
+ box.p2.x = b[i].p2.x;
+
+ if (b[i].p2.y > box.p2.y)
+ box.p2.y = b[i].p2.y;
+ }
+ }
+
+ _cairo_box_round_to_rectangle (&box, extents);
+}
+
+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->num_boxes = 0;
+
+ boxes->is_pixel_aligned = TRUE;
+}
+
+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);
+ }
+}
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 000000000..927ff0c0b
--- /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 <keithp@keithp.com>
+ * Graydon Hoare <graydon@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_CACHE_PRIVATE_H
+#define CAIRO_CACHE_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+/**
+ * cairo_cache_entry_t:
+ *
+ * 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 {
+ uintptr_t 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 000000000..5c4e4caa3
--- /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 <keithp@keithp.com>
+ * Graydon Hoare <graydon@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#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 000000000..f9b036814
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-cff-subset.c
@@ -0,0 +1,2278 @@
+/* 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 <ajohnson@redneon.com>
+ * Eugeniy Meshcheryakov <eugen@debian.org>
+ */
+
+/*
+ * Useful links:
+ * http://www.adobe.com/devnet/font/pdfs/5176.CFF.pdf
+ */
+
+#define _BSD_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-truetype-subset-private.h"
+#include <string.h>
+
+/* CFF Dict Operators. If the high byte is 0 the command is encoded
+ * with a single byte. */
+#define BASEFONTNAME_OP 0x0c16
+#define CIDCOUNT_OP 0x0c22
+#define CHARSET_OP 0x000f
+#define CHARSTRINGS_OP 0x0011
+#define COPYRIGHT_OP 0x0c00
+#define ENCODING_OP 0x0010
+#define FAMILYNAME_OP 0x0003
+#define FDARRAY_OP 0x0c24
+#define FDSELECT_OP 0x0c25
+#define FONTBBOX_OP 0x0005
+#define FONTNAME_OP 0x0c26
+#define FULLNAME_OP 0x0002
+#define LOCAL_SUB_OP 0x0013
+#define NOTICE_OP 0x0001
+#define POSTSCRIPT_OP 0x0c15
+#define PRIVATE_OP 0x0012
+#define ROS_OP 0x0c1e
+#define UNIQUEID_OP 0x000d
+#define VERSION_OP 0x0000
+#define WEIGHT_OP 0x0004
+#define XUID_OP 0x000e
+
+#define NUM_STD_STRINGS 391
+
+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;
+ int num_glyphs;
+ cairo_bool_t is_cid;
+ int units_per_em;
+
+ /* 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;
+
+ /* Subsetted Font Data */
+ char *subset_font_name;
+ cairo_array_t charstrings_subset_index;
+ cairo_array_t strings_subset_index;
+ int *fdselect_subset;
+ unsigned int num_subset_fontdicts;
+ int *fd_subset_map;
+ int *private_dict_offset;
+ cairo_array_t output;
+
+ /* Subset Metrics */
+ int *widths;
+ int x_min, y_min, x_max, y_max;
+ int ascent, descent;
+
+} 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)((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 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 = be16_to_cpu( *((uint16_t *)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);
+ status = _cairo_array_append_multiple (output,
+ element->data,
+ element->length);
+ if (unlikely (status))
+ return status;
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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 = 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;
+ int i;
+
+ for (i = 0; i < _cairo_array_num_elements (index); i++) {
+ element = _cairo_array_index (index, i);
+ if (element->is_copy)
+ 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 = malloc (sizeof (cff_dict_operator_t));
+ if (unlikely (op == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_dict_init_key (op, operator);
+ op->operand = malloc (size);
+ if (unlikely (op->operand == NULL)) {
+ free (op);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ memcpy (op->operand, operand, size);
+ 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 = 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;
+
+ /* The original font name is not used in the subset. Read the name
+ * index to skip over it. */
+ cff_index_init (&index);
+ status = cff_index_read (&index, &font->current_ptr, font->data_end);
+ 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,
+ 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;
+
+ 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;
+ }
+
+ 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 = be16_to_cpu( *((uint16_t *)p) );
+ p += 2;
+ for (i = 0; i < num_ranges; i++)
+ {
+ first = be16_to_cpu( *((uint16_t *)p) );
+ p += 2;
+ fd = *p++;
+ last = be16_to_cpu( *((uint16_t *)p) );
+ 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;
+ }
+
+ 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->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;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail:
+ cff_index_fini (&index);
+
+ return status;
+}
+
+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, 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->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,
+ FDSELECT_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+
+ status = cff_dict_set_operands (font->top_dict,
+ FDARRAY_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+
+ status = cff_dict_set_operands (font->top_dict,
+ CHARSET_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ goto fail;
+
+ cff_dict_remove (font->top_dict, ENCODING_OP);
+ cff_dict_remove (font->top_dict, PRIVATE_OP);
+
+ /* 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)
+{
+ return cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
+}
+
+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 cairo_status_t
+cairo_cff_font_subset_charstrings (cairo_cff_font_t *font)
+{
+ cff_index_element_t *element;
+ unsigned int i;
+ cairo_status_t status;
+
+ for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
+ element = _cairo_array_index (&font->charstrings_index,
+ font->scaled_font_subset->glyphs[i]);
+ status = cff_index_append (&font->charstrings_subset_index,
+ element->data,
+ element->length);
+ 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;
+
+ 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++) {
+ fd = font->fdselect[font->scaled_font_subset->glyphs[i]];
+ 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 = 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 = malloc (sizeof (int));
+ if (unlikely (font->fd_subset_map == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->private_dict_offset = 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;
+}
+
+static cairo_status_t
+cairo_cff_font_subset_font (cairo_cff_font_t *font)
+{
+ cairo_status_t status;
+
+ status = cairo_cff_font_set_ros_strings (font);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_cff_font_subset_charstrings (font);
+ if (unlikely (status))
+ return status;
+
+ if (font->is_cid)
+ status = cairo_cff_font_subset_fontdict (font);
+ else
+ status = cairo_cff_font_create_cid_fontdict (font);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_cff_font_subset_strings (font);
+ if (unlikely (status))
+ return status;
+
+ 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->subset_font_name,
+ strlen(font->subset_font_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)
+{
+ return cff_index_write (&font->global_sub_index, &font->output);
+}
+
+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_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 (0);
+ status = _cairo_array_append_multiple (&font->output, &word, 2);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ byte = 0;
+ status = _cairo_array_append (&font->output, &byte);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ word = cpu_to_be16 (font->scaled_font_subset->num_glyphs);
+ status = _cairo_array_append_multiple (&font->output, &word, 2);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_cff_font_write_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;
+ uint32_t *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;
+ *offset_array_ptr = cpu_to_be32(1);
+ 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 = (uint32_t *) _cairo_array_index (&font->output, offset_array);
+ *offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base);
+ offset_array += 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)
+{
+ int offset;
+ int size;
+ unsigned char buf[10];
+ unsigned char *buf_end;
+ unsigned char *p;
+ cairo_status_t status;
+
+ 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);
+ 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]]);
+ 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);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef cairo_status_t
+(*font_write_t) (cairo_cff_font_t *font);
+
+static const font_write_t font_write_funcs[] = {
+ cairo_cff_font_write_header,
+ cairo_cff_font_write_name,
+ cairo_cff_font_write_top_dict,
+ cairo_cff_font_write_strings,
+ cairo_cff_font_write_global_subrs,
+ cairo_cff_font_write_charset,
+ cairo_cff_font_write_fdselect,
+ cairo_cff_font_write_charstrings,
+ cairo_cff_font_write_cid_fontdict,
+ cairo_cff_font_write_cid_private_dict_and_local_sub,
+};
+
+static cairo_status_t
+cairo_cff_font_write_subset (cairo_cff_font_t *font)
+{
+ cairo_int_status_t status;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_LENGTH (font_write_funcs); i++) {
+ status = font_write_funcs[i] (font);
+ if (unlikely (status))
+ return status;
+ }
+
+ 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;
+
+ 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;
+ unsigned char buf[10];
+ 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 = 1; 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,
+ buf, &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,
+ buf, &short_entry_size);
+ if (unlikely (status))
+ return status;
+ }
+ font->widths[i] = be16_to_cpu (*((int16_t*)buf));
+ }
+
+ 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_status_t status;
+ cairo_cff_font_t *font;
+ tt_head_t head;
+ tt_hhea_t hhea;
+ unsigned long size, data_length;
+
+ backend = scaled_font_subset->scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ data_length = 0;
+ status = backend->load_truetype_table( scaled_font_subset->scaled_font,
+ TT_TAG_CFF, 0, NULL, &data_length);
+ if (unlikely (status))
+ return status;
+
+ size = sizeof (tt_head_t);
+ status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+ TT_TAG_head, 0,
+ (unsigned char *) &head, &size);
+ if (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;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font_subset->scaled_font,
+ TT_TAG_hmtx, 0, NULL, &size);
+ if (unlikely (status))
+ return status;
+
+ font = malloc (sizeof (cairo_cff_font_t));
+ if (unlikely (font == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->backend = backend;
+ font->scaled_font_subset = scaled_font_subset;
+
+ _cairo_array_init (&font->output, sizeof (char));
+ status = _cairo_array_grow_by (&font->output, 4096);
+ if (unlikely (status))
+ goto fail2;
+
+ font->subset_font_name = strdup (subset_name);
+ if (unlikely (font->subset_font_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ font->x_min = (int16_t) be16_to_cpu (head.x_min);
+ font->y_min = (int16_t) be16_to_cpu (head.y_min);
+ font->x_max = (int16_t) be16_to_cpu (head.x_max);
+ 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 (scaled_font_subset->scaled_font,
+ &font->ps_name,
+ &font->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->ps_name == NULL) {
+ font->ps_name = malloc (30);
+ if (unlikely (font->ps_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+
+ snprintf(font->ps_name, 30, "CairoFont-%u-%u",
+ scaled_font_subset->font_id,
+ scaled_font_subset->subset_id);
+ }
+
+ font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
+ if (unlikely (font->widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail4;
+ }
+
+ status = cairo_cff_font_create_set_widths (font);
+ if (unlikely (status))
+ goto fail5;
+
+ font->data_length = data_length;
+ font->data = malloc (data_length);
+ if (unlikely (font->data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail5;
+ }
+ status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font,
+ TT_TAG_CFF, 0, font->data,
+ &font->data_length);
+ if (unlikely (status))
+ goto fail6;
+
+ font->data_end = font->data + font->data_length;
+
+ status = cff_dict_init (&font->top_dict);
+ if (unlikely (status))
+ goto fail6;
+
+ status = cff_dict_init (&font->private_dict);
+ if (unlikely (status))
+ goto fail7;
+
+ 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->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;
+
+fail7:
+ _cairo_hash_table_destroy (font->top_dict);
+fail6:
+ free (font->data);
+fail5:
+ free (font->widths);
+fail4:
+ if (font->font_name)
+ free (font->font_name);
+fail3:
+ free (font->subset_font_name);
+fail2:
+ _cairo_array_fini (&font->output);
+ free (font);
+
+ return status;
+}
+
+static void
+cairo_cff_font_destroy (cairo_cff_font_t *font)
+{
+ unsigned int i;
+
+ free (font->widths);
+ if (font->font_name)
+ free (font->font_name);
+ free (font->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);
+ }
+ if (font->fd_subset_map)
+ free (font->fd_subset_map);
+ if (font->private_dict_offset)
+ free (font->private_dict_offset);
+
+ if (font->is_cid) {
+ if (font->fdselect)
+ free (font->fdselect);
+ if (font->fdselect_subset)
+ 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);
+ }
+ }
+
+ if (font->data)
+ 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->font_name = strdup (font->font_name);
+ if (cff_subset->font_name == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ } else {
+ cff_subset->font_name = NULL;
+ }
+
+ cff_subset->widths = calloc (sizeof (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 = 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:
+ if (cff_subset->font_name)
+ free (cff_subset->font_name);
+ 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);
+ if (subset->font_name)
+ free (subset->font_name);
+ free (subset->widths);
+ free (subset->data);
+}
+
+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 = 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->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:
+ if (font->font_name)
+ 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;
+ unsigned int i;
+
+ /* 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;
+ 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;
+
+ status = cff_dict_set_operands (font->top_dict,
+ FDSELECT_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ status = cff_dict_set_operands (font->top_dict,
+ FDARRAY_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ status = cff_dict_set_operands (font->top_dict,
+ CHARSET_OP, buf, end_buf - buf);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_cff_font_set_ros_strings (font);
+ if (unlikely (status))
+ return status;
+
+ /* Create CID FD dictionary */
+ status = cairo_cff_font_create_cid_fontdict (font);
+ if (unlikely (status))
+ return status;
+
+ /* 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;
+ }
+
+ 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->font_name = NULL;
+ cff_subset->ps_name = strdup (font->ps_name);
+ if (unlikely (cff_subset->ps_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+
+ cff_subset->widths = calloc (sizeof (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 = 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;
+ 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-private.h b/gfx/cairo/cairo/src/cairo-clip-private.h
new file mode 100644
index 000000000..faf486409
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-clip-private.h
@@ -0,0 +1,151 @@
+/* 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 <krh@redhat.com>
+ */
+
+#ifndef CAIRO_CLIP_PRIVATE_H
+#define CAIRO_CLIP_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-reference-count-private.h"
+
+extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil;
+
+enum {
+ CAIRO_CLIP_PATH_HAS_REGION = 0x1,
+ CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED = 0x2,
+ CAIRO_CLIP_PATH_IS_BOX = 0x4
+};
+
+struct _cairo_clip_path {
+ cairo_reference_count_t ref_count;
+ cairo_path_fixed_t path;
+ cairo_fill_rule_t fill_rule;
+ double tolerance;
+ cairo_antialias_t antialias;
+ cairo_clip_path_t *prev;
+
+ cairo_rectangle_int_t extents;
+
+ /* partial caches */
+ unsigned int flags;
+ cairo_region_t *region;
+ cairo_surface_t *surface;
+};
+
+struct _cairo_clip {
+ /* can be used as a cairo_hash_entry_t for live clips */
+ cairo_clip_path_t *path;
+
+ cairo_bool_t all_clipped;
+
+};
+
+cairo_private void
+_cairo_clip_init (cairo_clip_t *clip);
+
+cairo_private_no_warn cairo_clip_t *
+_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other);
+
+cairo_private cairo_status_t
+_cairo_clip_init_copy_transformed (cairo_clip_t *clip,
+ cairo_clip_t *other,
+ const cairo_matrix_t *matrix);
+
+cairo_private void
+_cairo_clip_reset (cairo_clip_t *clip);
+
+cairo_private cairo_bool_t
+_cairo_clip_equal (const cairo_clip_t *clip_a,
+ const cairo_clip_t *clip_b);
+
+#define _cairo_clip_fini(clip) _cairo_clip_reset (clip)
+
+cairo_private cairo_status_t
+_cairo_clip_rectangle (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rectangle);
+
+cairo_private cairo_status_t
+_cairo_clip_clip (cairo_clip_t *clip,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias);
+
+cairo_private cairo_status_t
+_cairo_clip_apply_clip (cairo_clip_t *clip,
+ const cairo_clip_t *other);
+
+cairo_private const cairo_rectangle_int_t *
+_cairo_clip_get_extents (const cairo_clip_t *clip);
+
+cairo_private cairo_surface_t *
+_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty);
+
+cairo_private cairo_status_t
+_cairo_clip_combine_with_surface (cairo_clip_t *clip,
+ cairo_surface_t *dst,
+ int dst_x, int dst_y);
+
+cairo_private cairo_int_status_t
+_cairo_clip_get_region (cairo_clip_t *clip,
+ cairo_region_t **region);
+
+cairo_private cairo_int_status_t
+_cairo_clip_get_boxes (cairo_clip_t *clip,
+ cairo_box_t **boxes,
+ int *count);
+
+cairo_private cairo_status_t
+_cairo_clip_to_boxes (cairo_clip_t **clip,
+ cairo_composite_rectangles_t *extents,
+ cairo_box_t **boxes,
+ int *num_boxes);
+
+cairo_private cairo_bool_t
+_cairo_clip_contains_rectangle (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rect);
+
+cairo_private cairo_bool_t
+_cairo_clip_contains_extents (cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents);
+
+cairo_private void
+_cairo_clip_drop_cache (cairo_clip_t *clip);
+
+cairo_private cairo_rectangle_list_t*
+_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate);
+
+#endif /* CAIRO_CLIP_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-clip.c b/gfx/cairo/cairo/src/cairo-clip.c
new file mode 100644
index 000000000..0ebe9b207
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-clip.c
@@ -0,0 +1,1590 @@
+/* -*- 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 <cworth@cworth.org>
+ * Kristian Høgsberg <krh@redhat.com>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#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-composite-rectangles-private.h"
+#include "cairo-region-private.h"
+
+#if HAS_FREED_POOL
+static freed_pool_t clip_path_pool;
+#endif
+
+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 = malloc (sizeof (cairo_clip_path_t));
+ if (unlikely (clip_path == NULL))
+ return NULL;
+ }
+
+ CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
+
+ clip_path->flags = 0;
+ clip_path->region = NULL;
+ clip_path->surface = NULL;
+
+ clip_path->prev = clip->path;
+ clip->path = clip_path;
+
+ return clip_path;
+}
+
+static cairo_clip_path_t *
+_cairo_clip_path_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;
+}
+
+static 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->region != NULL)
+ cairo_region_destroy (clip_path->region);
+ if (clip_path->surface != NULL)
+ cairo_surface_destroy (clip_path->surface);
+
+ if (clip_path->prev != NULL)
+ _cairo_clip_path_destroy (clip_path->prev);
+
+ _freed_pool_put (&clip_path_pool, clip_path);
+}
+
+void
+_cairo_clip_init (cairo_clip_t *clip)
+{
+ clip->all_clipped = FALSE;
+ clip->path = NULL;
+}
+
+static void
+_cairo_clip_set_all_clipped (cairo_clip_t *clip)
+{
+ clip->all_clipped = TRUE;
+ if (clip->path != NULL) {
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ }
+}
+
+static cairo_status_t
+_cairo_clip_intersect_rectangle (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rect)
+{
+ cairo_clip_path_t *clip_path;
+ cairo_status_t status;
+
+ if (clip->path != NULL) {
+ if (rect->x <= clip->path->extents.x &&
+ rect->y <= clip->path->extents.y &&
+ rect->x + rect->width >= clip->path->extents.x + clip->path->extents.width &&
+ rect->y + rect->height >= clip->path->extents.y + clip->path->extents.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_path_fixed_init (&clip_path->path);
+
+ status = _cairo_path_fixed_move_to (&clip_path->path,
+ _cairo_fixed_from_int (rect->x),
+ _cairo_fixed_from_int (rect->y));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (&clip_path->path,
+ _cairo_fixed_from_int (rect->width),
+ _cairo_fixed_from_int (0));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (&clip_path->path,
+ _cairo_fixed_from_int (0),
+ _cairo_fixed_from_int (rect->height));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_rel_line_to (&clip_path->path,
+ _cairo_fixed_from_int (-rect->width),
+ _cairo_fixed_from_int (0));
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_path_fixed_close_path (&clip_path->path);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ clip_path->fill_rule = CAIRO_FILL_RULE_WINDING;
+ clip_path->tolerance = 1;
+ clip_path->antialias = CAIRO_ANTIALIAS_DEFAULT;
+ clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX;
+
+ clip_path->extents = *rect;
+ if (clip_path->prev != NULL) {
+ if (! _cairo_rectangle_intersect (&clip_path->extents,
+ &clip_path->prev->extents))
+ {
+ _cairo_clip_set_all_clipped (clip);
+ }
+ }
+
+ /* could preallocate the region if it proves worthwhile */
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_clip_t *
+_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
+{
+ if (other != NULL) {
+ clip->all_clipped = other->all_clipped;
+ if (other->path == NULL) {
+ clip->path = NULL;
+ if (! clip->all_clipped)
+ clip = NULL;
+ } else {
+ clip->path = _cairo_clip_path_reference (other->path);
+ }
+ } else {
+ _cairo_clip_init (clip);
+ clip = NULL;
+ }
+
+ return clip;
+}
+
+void
+_cairo_clip_reset (cairo_clip_t *clip)
+{
+ clip->all_clipped = FALSE;
+ if (clip->path != NULL) {
+ _cairo_clip_path_destroy (clip->path);
+ clip->path = NULL;
+ }
+}
+
+static cairo_status_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;
+ cairo_bool_t is_box = FALSE;
+
+ if (clip->path != NULL) {
+ if (clip->path->fill_rule == fill_rule &&
+ (path->is_rectilinear || tolerance == clip->path->tolerance) &&
+ antialias == clip->path->antialias &&
+ _cairo_path_fixed_equal (&clip->path->path, path))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ _cairo_path_fixed_approximate_clip_extents (path, &extents);
+ if (extents.width == 0 || extents.height == 0) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ is_box = _cairo_path_fixed_is_box (path, &box);
+ if (clip->path != NULL) {
+ if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* does this clip wholly subsume the others? */
+ if (is_box &&
+ box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) &&
+ box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) &&
+ box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) &&
+ box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_path_fixed_init_copy (&clip_path->path, path);
+ if (unlikely (status)) {
+ clip->path = clip->path->prev;
+ _cairo_clip_path_destroy (clip_path);
+ return status;
+ }
+
+ clip_path->extents = extents;
+ clip_path->fill_rule = fill_rule;
+ clip_path->tolerance = tolerance;
+ clip_path->antialias = antialias;
+ if (is_box)
+ clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_bool_t
+_cairo_clip_equal (const cairo_clip_t *clip_a,
+ const cairo_clip_t *clip_b)
+{
+ const cairo_clip_path_t *clip_path_a, *clip_path_b;
+
+ clip_path_a = clip_a->path;
+ clip_path_b = clip_b->path;
+
+ while (clip_path_a && clip_path_b) {
+ if (clip_path_a == clip_path_b)
+ return TRUE;
+
+ if (clip_path_a->fill_rule != clip_path_b->fill_rule)
+ return FALSE;
+
+ if (clip_path_a->tolerance != clip_path_b->tolerance)
+ return FALSE;
+
+ if (clip_path_a->antialias != clip_path_b->antialias)
+ return FALSE;
+
+ if (! _cairo_path_fixed_equal (&clip_path_a->path, &clip_path_b->path))
+ return FALSE;
+
+ clip_path_a = clip_path_a->prev;
+ clip_path_b = clip_path_b->prev;
+ }
+
+ return clip_path_a == clip_path_b; /* ie both NULL */
+}
+
+cairo_status_t
+_cairo_clip_clip (cairo_clip_t *clip,
+ const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ if (clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* catch the empty clip path */
+ if (_cairo_path_fixed_fill_is_empty (path)) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _cairo_clip_intersect_path (clip,
+ path, fill_rule, tolerance,
+ antialias);
+}
+
+cairo_status_t
+_cairo_clip_rectangle (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rectangle)
+{
+ if (clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (rectangle->width == 0 || rectangle->height == 0) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* if a smaller clip has already been set, ignore the new path */
+ if (clip->path != NULL) {
+ if (rectangle->x <= clip->path->extents.x &&
+ rectangle->y <= clip->path->extents.y &&
+ rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width &&
+ rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ return _cairo_clip_intersect_rectangle (clip, rectangle);
+}
+
+static cairo_status_t
+_cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip,
+ cairo_clip_path_t *other_path,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status;
+ cairo_clip_path_t *clip_path;
+ cairo_bool_t is_empty;
+
+ if (other_path->prev != NULL) {
+ status = _cairo_clip_path_reapply_clip_path_transform (clip,
+ other_path->prev,
+ matrix);
+ if (unlikely (status))
+ return status;
+ }
+
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_path_fixed_init_copy (&clip_path->path,
+ &other_path->path);
+ if (unlikely (status)) {
+ clip->path = clip->path->prev;
+ _cairo_clip_path_destroy (clip_path);
+ return status;
+ }
+
+ _cairo_path_fixed_transform (&clip_path->path, matrix);
+ _cairo_path_fixed_approximate_clip_extents (&clip_path->path,
+ &clip_path->extents);
+ if (clip_path->prev != NULL) {
+ is_empty = _cairo_rectangle_intersect (&clip_path->extents,
+ &clip_path->prev->extents);
+ }
+
+ clip_path->fill_rule = other_path->fill_rule;
+ clip_path->tolerance = other_path->tolerance;
+ clip_path->antialias = other_path->antialias;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip,
+ cairo_clip_path_t *other_path,
+ int tx, int ty)
+{
+ cairo_status_t status;
+ cairo_clip_path_t *clip_path;
+
+ if (other_path->prev != NULL) {
+ status = _cairo_clip_path_reapply_clip_path_translate (clip,
+ other_path->prev,
+ tx, ty);
+ if (unlikely (status))
+ return status;
+ }
+
+ clip_path = _cairo_clip_path_create (clip);
+ if (unlikely (clip_path == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_path_fixed_init_copy (&clip_path->path,
+ &other_path->path);
+ if (unlikely (status)) {
+ clip->path = clip->path->prev;
+ _cairo_clip_path_destroy (clip_path);
+ return status;
+ }
+
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (tx),
+ _cairo_fixed_from_int (ty));
+
+ clip_path->fill_rule = other_path->fill_rule;
+ clip_path->tolerance = other_path->tolerance;
+ clip_path->antialias = other_path->antialias;
+
+ clip_path->flags = other_path->flags;
+ if (other_path->region != NULL) {
+ clip_path->region = cairo_region_copy (other_path->region);
+ status = clip_path->region->status;
+ if (unlikely (status)) {
+ clip->path = clip->path->prev;
+ _cairo_clip_path_destroy (clip_path);
+ return status;
+ }
+
+ cairo_region_translate (clip_path->region, tx, ty);
+ }
+ clip_path->surface = cairo_surface_reference (other_path->surface);
+
+ clip_path->extents = other_path->extents;
+ clip_path->extents.x += tx;
+ clip_path->extents.y += ty;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_clip_init_copy_transformed (cairo_clip_t *clip,
+ cairo_clip_t *other,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ int tx, ty;
+
+ if (other == NULL) {
+ _cairo_clip_init (clip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (other->all_clipped) {
+ _cairo_clip_init (clip);
+ clip->all_clipped = TRUE;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (_cairo_matrix_is_identity (matrix)) {
+ _cairo_clip_init_copy (clip, other);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (other->path != NULL) {
+ _cairo_clip_init (clip);
+
+ /* if we only need to translate, so we can reuse the caches... */
+ /* XXX we still loose the benefit of constructs when the copy is
+ * deleted though. Indirect clip_paths?
+ */
+ if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) {
+ status = _cairo_clip_path_reapply_clip_path_translate (clip,
+ other->path,
+ tx, ty);
+ } else {
+ status = _cairo_clip_path_reapply_clip_path_transform (clip,
+ other->path,
+ matrix);
+ if (clip->path->extents.width == 0 &&
+ clip->path->extents.height == 0)
+ {
+ _cairo_clip_set_all_clipped (clip);
+ }
+ }
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_clip_apply_clip_path (cairo_clip_t *clip,
+ const cairo_clip_path_t *path)
+{
+ cairo_status_t status;
+
+ if (path->prev != NULL)
+ status = _cairo_clip_apply_clip_path (clip, path->prev);
+
+ return _cairo_clip_intersect_path (clip,
+ &path->path,
+ path->fill_rule,
+ path->tolerance,
+ path->antialias);
+}
+
+cairo_status_t
+_cairo_clip_apply_clip (cairo_clip_t *clip,
+ const cairo_clip_t *other)
+{
+ cairo_status_t status;
+
+ if (clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (other->all_clipped) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (other->path != NULL)
+ status = _cairo_clip_apply_clip_path (clip, other->path);
+
+ return status;
+}
+
+static inline cairo_bool_t
+_clip_paths_are_rectilinear (cairo_clip_path_t *clip_path)
+{
+ while (clip_path != NULL) {
+ if (! clip_path->path.is_rectilinear)
+ return FALSE;
+
+ clip_path = clip_path->prev;
+ }
+
+ return TRUE;
+}
+
+static cairo_int_status_t
+_cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
+{
+ cairo_traps_t traps;
+ cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)];
+ cairo_box_t *boxes = stack_boxes;
+ cairo_status_t status;
+ int n;
+
+ /* If we have nothing to intersect with this path, then it cannot
+ * magically be reduced into a region.
+ */
+ if (clip_path->prev == NULL)
+ goto UNSUPPORTED;
+
+ /* Start simple... Intersect some boxes with an arbitrary path. */
+ if (! clip_path->path.is_rectilinear)
+ goto UNSUPPORTED;
+ if (clip_path->prev->prev != NULL)
+ goto UNSUPPORTED;
+
+ _cairo_traps_init (&traps);
+ _cairo_box_from_rectangle (&boxes[0], &clip_path->extents);
+ _cairo_traps_limit (&traps, boxes, 1);
+
+ status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
+ clip_path->fill_rule,
+ &traps);
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ goto UNSUPPORTED;
+
+ if (unlikely (traps.num_traps == 0)) {
+ clip_path->region = cairo_region_create ();
+ clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
+ boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
+ if (unlikely (boxes == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (n = 0; n < traps.num_traps; n++) {
+ boxes[n].p1.x = traps.traps[n].left.p1.x;
+ boxes[n].p1.y = traps.traps[n].top;
+ boxes[n].p2.x = traps.traps[n].right.p1.x;
+ boxes[n].p2.y = traps.traps[n].bottom;
+ }
+
+ _cairo_traps_clear (&traps);
+ _cairo_traps_limit (&traps, boxes, n);
+ status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path,
+ clip_path->prev->fill_rule,
+ clip_path->prev->tolerance,
+ &traps);
+ if (boxes != stack_boxes)
+ free (boxes);
+
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_traps_extract_region (&traps, &clip_path->region);
+ _cairo_traps_fini (&traps);
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ goto UNSUPPORTED;
+ if (unlikely (status))
+ return status;
+
+ clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
+ return CAIRO_STATUS_SUCCESS;
+
+UNSUPPORTED:
+ clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
+{
+ cairo_int_status_t status;
+ cairo_region_t *prev = NULL;
+
+ if (clip_path->flags &
+ (CAIRO_CLIP_PATH_HAS_REGION |
+ CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED))
+ {
+ return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ?
+ CAIRO_INT_STATUS_UNSUPPORTED :
+ CAIRO_STATUS_SUCCESS;
+ }
+
+ if (! clip_path->path.maybe_fill_region)
+ return _cairo_clip_path_to_region_geometric (clip_path);
+
+ /* first retrieve the region for our antecedents */
+ if (clip_path->prev != NULL) {
+ status = _cairo_clip_path_to_region (clip_path->prev);
+ if (status) {
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ return _cairo_clip_path_to_region_geometric (clip_path);
+
+ return status;
+ }
+
+ prev = clip_path->prev->region;
+ }
+
+ /* now extract the region for ourselves */
+ clip_path->region =
+ _cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
+ clip_path->fill_rule,
+ &clip_path->extents);
+ assert (clip_path->region != NULL);
+
+ status = clip_path->region->status;
+ if (unlikely (status))
+ return status;
+
+ if (prev != NULL) {
+ status = cairo_region_intersect (clip_path->region, prev);
+ if (unlikely (status))
+ return status;
+ }
+
+ clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline int
+pot (int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+
+/* XXX there is likely a faster method! ;-) */
+static cairo_status_t
+_region_clip_to_boxes (const cairo_region_t *region,
+ cairo_box_t **boxes,
+ int *num_boxes,
+ int *size_boxes)
+{
+ cairo_traps_t traps;
+ cairo_status_t status;
+ int n, num_rects;
+
+ _cairo_traps_init (&traps);
+ _cairo_traps_limit (&traps, *boxes, *num_boxes);
+ traps.is_rectilinear = TRUE;
+ traps.is_rectangular = TRUE;
+
+ num_rects = cairo_region_num_rectangles (region);
+ for (n = 0; n < num_rects; n++) {
+ cairo_rectangle_int_t rect;
+ cairo_point_t p1, p2;
+
+ cairo_region_get_rectangle (region, n, &rect);
+
+ p1.x = _cairo_fixed_from_int (rect.x);
+ p1.y = _cairo_fixed_from_int (rect.y);
+ p2.x = _cairo_fixed_from_int (rect.x + rect.width);
+ p2.y = _cairo_fixed_from_int (rect.y + rect.height);
+
+ status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ n = *size_boxes;
+ if (n < 0)
+ n = -n;
+
+ if (traps.num_traps > n) {
+ cairo_box_t *new_boxes;
+
+ new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
+ if (unlikely (new_boxes == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ if (*size_boxes > 0)
+ free (*boxes);
+
+ *boxes = new_boxes;
+ *size_boxes = traps.num_traps;
+ }
+
+ for (n = 0; n < traps.num_traps; n++) {
+ (*boxes)[n].p1.x = traps.traps[n].left.p1.x;
+ (*boxes)[n].p1.y = traps.traps[n].top;
+ (*boxes)[n].p2.x = traps.traps[n].right.p1.x;
+ (*boxes)[n].p2.y = traps.traps[n].bottom;
+ }
+ *num_boxes = n;
+
+ CLEANUP:
+ _cairo_traps_fini (&traps);
+
+ return status;
+}
+
+static cairo_status_t
+_rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_box_t **boxes,
+ int *num_boxes,
+ int *size_boxes)
+{
+ cairo_polygon_t polygon;
+ cairo_traps_t traps;
+ cairo_status_t status;
+
+ _cairo_traps_init (&traps);
+ _cairo_traps_limit (&traps, *boxes, *num_boxes);
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, *boxes, *num_boxes);
+
+ status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+ fill_rule,
+ &traps);
+ if (unlikely (_cairo_status_is_error (status)))
+ goto CLEANUP;
+ if (status == CAIRO_STATUS_SUCCESS)
+ goto BOXES;
+
+ /* tolerance will be ignored as the path is rectilinear */
+ status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ if (polygon.num_edges == 0) {
+ *num_boxes = 0;
+ } else {
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
+ &polygon,
+ fill_rule);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ int i;
+
+ BOXES:
+ i = *size_boxes;
+ if (i < 0)
+ i = -i;
+
+ if (traps.num_traps > i) {
+ cairo_box_t *new_boxes;
+ int new_size;
+
+ new_size = pot (traps.num_traps);
+ new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t));
+ if (unlikely (new_boxes == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ if (*size_boxes > 0)
+ free (*boxes);
+
+ *boxes = new_boxes;
+ *size_boxes = new_size;
+ }
+
+ for (i = 0; i < traps.num_traps; i++) {
+ (*boxes)[i].p1.x = traps.traps[i].left.p1.x;
+ (*boxes)[i].p1.y = traps.traps[i].top;
+ (*boxes)[i].p2.x = traps.traps[i].right.p1.x;
+ (*boxes)[i].p2.y = traps.traps[i].bottom;
+ }
+ *num_boxes = i;
+ }
+ }
+
+ CLEANUP:
+ _cairo_polygon_fini (&polygon);
+ _cairo_traps_fini (&traps);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path,
+ cairo_box_t **boxes,
+ int *count)
+{
+ int size = -*count;
+ int num_boxes = 0;
+ cairo_status_t status;
+
+ if (clip_path->region != NULL) {
+ int num_rects, n;
+
+ num_rects = cairo_region_num_rectangles (clip_path->region);
+ if (num_rects > -size) {
+ cairo_box_t *new_boxes;
+
+ new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t));
+ if (unlikely (new_boxes == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ *boxes = new_boxes;
+ }
+
+ for (n = 0; n < num_rects; n++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_path->region, n, &rect);
+ (*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x);
+ (*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y);
+ (*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width);
+ (*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height);
+ }
+
+ *count = num_rects;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* keep it simple at first */
+ if (! _clip_paths_are_rectilinear (clip_path))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ assert (-size >= 1);
+ if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) {
+ num_boxes = 1;
+ } else {
+ status = _rectilinear_clip_to_boxes (&clip_path->path,
+ clip_path->fill_rule,
+ boxes, &num_boxes, &size);
+ if (unlikely (status))
+ return status;
+ }
+
+ while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) {
+ cairo_box_t box;
+
+ if (clip_path->region != NULL) {
+ status = _region_clip_to_boxes (clip_path->region,
+ boxes, &num_boxes, &size);
+ if (unlikely (status))
+ return status;
+
+ break;
+ } else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) {
+ int i, j;
+
+ for (i = j = 0; i < num_boxes; i++) {
+ if (j != i)
+ (*boxes)[j] = (*boxes)[i];
+
+ if (box.p1.x > (*boxes)[j].p1.x)
+ (*boxes)[j].p1.x = box.p1.x;
+ if (box.p2.x < (*boxes)[j].p2.x)
+ (*boxes)[j].p2.x = box.p2.x;
+
+ if (box.p1.y > (*boxes)[j].p1.y)
+ (*boxes)[j].p1.y = box.p1.y;
+ if (box.p2.y < (*boxes)[j].p2.y)
+ (*boxes)[j].p2.y = box.p2.y;
+
+ j += (*boxes)[j].p2.x > (*boxes)[j].p1.x &&
+ (*boxes)[j].p2.y > (*boxes)[j].p1.y;
+ }
+
+ num_boxes = j;
+ } else {
+ status = _rectilinear_clip_to_boxes (&clip_path->path,
+ clip_path->fill_rule,
+ boxes, &num_boxes, &size);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ *count = num_boxes;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
+ cairo_surface_t *target,
+ int *tx, int *ty)
+{
+ const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
+ cairo_bool_t need_translate;
+ cairo_surface_t *surface;
+ cairo_clip_path_t *prev;
+ cairo_status_t status;
+
+ while (clip_path->prev != NULL &&
+ clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
+ clip_path->path.maybe_fill_region)
+ {
+ clip_path = clip_path->prev;
+ }
+
+ clip_extents = &clip_path->extents;
+ if (clip_path->surface != NULL &&
+ clip_path->surface->backend == target->backend)
+ {
+ *tx = clip_extents->x;
+ *ty = clip_extents->y;
+ return clip_path->surface;
+ }
+
+ surface = _cairo_surface_create_similar_scratch (target,
+ CAIRO_CONTENT_ALPHA,
+ clip_extents->width,
+ clip_extents->height);
+ if (surface == NULL) {
+ surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
+ clip_extents->width,
+ clip_extents->height);
+ }
+ if (unlikely (surface->status))
+ return surface;
+
+ need_translate = clip_extents->x | clip_extents->y;
+ if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
+ clip_path->path.maybe_fill_region)
+ {
+ status = _cairo_surface_paint (surface,
+ CAIRO_OPERATOR_SOURCE,
+ &_cairo_pattern_white.base,
+ NULL);
+ if (unlikely (status))
+ goto BAIL;
+ }
+ else
+ {
+ status = _cairo_surface_paint (surface,
+ CAIRO_OPERATOR_CLEAR,
+ &_cairo_pattern_clear.base,
+ NULL);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (need_translate) {
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (-clip_extents->x),
+ _cairo_fixed_from_int (-clip_extents->y));
+ }
+ status = _cairo_surface_fill (surface,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL);
+ if (need_translate) {
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (clip_extents->x),
+ _cairo_fixed_from_int (clip_extents->y));
+ }
+
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ prev = clip_path->prev;
+ while (prev != NULL) {
+ if (prev->flags & CAIRO_CLIP_PATH_IS_BOX &&
+ prev->path.maybe_fill_region)
+ {
+ /* a simple box only affects the extents */
+ }
+ else if (prev->path.is_rectilinear ||
+ prev->surface == NULL ||
+ prev->surface->backend != target->backend)
+ {
+ if (need_translate) {
+ _cairo_path_fixed_translate (&prev->path,
+ _cairo_fixed_from_int (-clip_extents->x),
+ _cairo_fixed_from_int (-clip_extents->y));
+ }
+ status = _cairo_surface_fill (surface,
+ CAIRO_OPERATOR_IN,
+ &_cairo_pattern_white.base,
+ &prev->path,
+ prev->fill_rule,
+ prev->tolerance,
+ prev->antialias,
+ NULL);
+ if (need_translate) {
+ _cairo_path_fixed_translate (&prev->path,
+ _cairo_fixed_from_int (clip_extents->x),
+ _cairo_fixed_from_int (clip_extents->y));
+ }
+
+ if (unlikely (status))
+ goto BAIL;
+ }
+ else
+ {
+ cairo_surface_pattern_t pattern;
+ cairo_surface_t *prev_surface;
+ int prev_tx, prev_ty;
+
+ prev_surface = _cairo_clip_path_get_surface (prev, target, &prev_tx, &prev_ty);
+ status = prev_surface->status;
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_pattern_init_for_surface (&pattern, prev_surface);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ clip_extents->x - prev_tx,
+ clip_extents->y - prev_ty);
+ status = _cairo_surface_paint (surface,
+ CAIRO_OPERATOR_IN,
+ &pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ break;
+ }
+
+ prev = prev->prev;
+ }
+
+ *tx = clip_extents->x;
+ *ty = clip_extents->y;
+ cairo_surface_destroy (clip_path->surface);
+ return clip_path->surface = surface;
+
+ BAIL:
+ cairo_surface_destroy (surface);
+ return _cairo_surface_create_in_error (status);
+}
+
+cairo_bool_t
+_cairo_clip_contains_rectangle (cairo_clip_t *clip,
+ const cairo_rectangle_int_t *rect)
+{
+ cairo_clip_path_t *clip_path;
+
+ if (clip == NULL)
+ return FALSE;
+
+ clip_path = clip->path;
+ if (clip_path->extents.x > rect->x ||
+ clip_path->extents.y > rect->y ||
+ clip_path->extents.x + clip_path->extents.width < rect->x + rect->width ||
+ clip_path->extents.y + clip_path->extents.height < rect->y + rect->height)
+ {
+ return FALSE;
+ }
+
+ do {
+ cairo_box_t box;
+
+ if ((clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) == 0)
+ return FALSE;
+
+ if (! _cairo_path_fixed_is_box (&clip_path->path, &box))
+ return FALSE;
+
+ if (box.p1.x > _cairo_fixed_from_int (rect->x) ||
+ box.p1.y > _cairo_fixed_from_int (rect->y) ||
+ box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) ||
+ box.p2.y < _cairo_fixed_from_int (rect->y + rect->height))
+ {
+ return FALSE;
+ }
+ } while ((clip_path = clip_path->prev) != NULL);
+
+ return TRUE;
+}
+
+cairo_bool_t
+_cairo_clip_contains_extents (cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents)
+{
+ const cairo_rectangle_int_t *rect;
+
+ if (clip == NULL)
+ return FALSE;
+
+ rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
+ return _cairo_clip_contains_rectangle (clip, rect);
+}
+
+void
+_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip)
+{
+ cairo_clip_path_t *clip_path;
+
+ if (clip == NULL) {
+ fprintf (stream, "no clip\n");
+ return;
+ }
+
+ if (clip->all_clipped) {
+ fprintf (stream, "clip: all-clipped\n");
+ return;
+ }
+
+ if (clip->path == NULL) {
+ fprintf (stream, "clip: empty\n");
+ return;
+ }
+
+ fprintf (stream, "clip:\n");
+
+ clip_path = clip->path;
+ do {
+ fprintf (stream, "path: has region? %s, has surface? %s, aa=%d, tolerance=%f, rule=%d: ",
+ clip_path->region == NULL ? "no" : "yes",
+ clip_path->surface == NULL ? "no" : "yes",
+ clip_path->antialias,
+ clip_path->tolerance,
+ clip_path->fill_rule);
+ _cairo_debug_print_path (stream, &clip_path->path);
+ fprintf (stream, "\n");
+ } while ((clip_path = clip_path->prev) != NULL);
+}
+
+cairo_surface_t *
+_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty)
+{
+ /* XXX is_clear -> all_clipped */
+ assert (clip->path != NULL);
+ return _cairo_clip_path_get_surface (clip->path, target, tx, ty);
+}
+
+cairo_status_t
+_cairo_clip_combine_with_surface (cairo_clip_t *clip,
+ cairo_surface_t *dst,
+ int dst_x, int dst_y)
+{
+ cairo_clip_path_t *clip_path = clip->path;
+ cairo_bool_t need_translate;
+ cairo_status_t status;
+
+ assert (clip_path != NULL);
+
+ need_translate = dst_x | dst_y;
+ do {
+ if (clip_path->surface != NULL &&
+ clip_path->surface->backend == dst->backend)
+ {
+ cairo_surface_pattern_t pattern;
+
+ _cairo_pattern_init_for_surface (&pattern, clip_path->surface);
+ cairo_matrix_init_translate (&pattern.base.matrix,
+ dst_x - clip_path->extents.x,
+ dst_y - clip_path->extents.y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+ status = _cairo_surface_paint (dst,
+ CAIRO_OPERATOR_IN,
+ &pattern.base,
+ NULL);
+
+ _cairo_pattern_fini (&pattern.base);
+
+ return status;
+ }
+
+ if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
+ clip_path->path.maybe_fill_region)
+ {
+ continue;
+ }
+
+ if (need_translate) {
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (-dst_x),
+ _cairo_fixed_from_int (-dst_y));
+ }
+ status = _cairo_surface_fill (dst,
+ CAIRO_OPERATOR_IN,
+ &_cairo_pattern_white.base,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL);
+ if (need_translate) {
+ _cairo_path_fixed_translate (&clip_path->path,
+ _cairo_fixed_from_int (dst_x),
+ _cairo_fixed_from_int (dst_y));
+ }
+
+ if (unlikely (status))
+ return status;
+ } while ((clip_path = clip_path->prev) != NULL);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 };
+
+const cairo_rectangle_int_t *
+_cairo_clip_get_extents (const cairo_clip_t *clip)
+{
+ if (clip->all_clipped)
+ return &_cairo_empty_rectangle_int;
+
+ if (clip->path == NULL)
+ return NULL;
+
+ return &clip->path->extents;
+}
+
+void
+_cairo_clip_drop_cache (cairo_clip_t *clip)
+{
+ cairo_clip_path_t *clip_path;
+
+ if (clip->path == NULL)
+ return;
+
+ clip_path = clip->path;
+ do {
+ if (clip_path->region != NULL) {
+ cairo_region_destroy (clip_path->region);
+ clip_path->region = NULL;
+ }
+
+ if (clip_path->surface != NULL) {
+ cairo_surface_destroy (clip_path->surface);
+ clip_path->surface = NULL;
+ }
+
+ clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION;
+ } while ((clip_path = clip_path->prev) != NULL);
+}
+
+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_int_status_t
+_cairo_clip_get_region (cairo_clip_t *clip,
+ cairo_region_t **region)
+{
+ cairo_int_status_t status;
+
+ if (clip->all_clipped)
+ goto CLIPPED;
+
+ assert (clip->path != NULL);
+
+ status = _cairo_clip_path_to_region (clip->path);
+ if (status)
+ return status;
+
+ if (cairo_region_is_empty (clip->path->region)) {
+ _cairo_clip_set_all_clipped (clip);
+ goto CLIPPED;
+ }
+
+ if (region)
+ *region = clip->path->region;
+ return CAIRO_STATUS_SUCCESS;
+
+ CLIPPED:
+ if (region)
+ *region = NULL;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+cairo_int_status_t
+_cairo_clip_get_boxes (cairo_clip_t *clip,
+ cairo_box_t **boxes,
+ int *count)
+{
+ cairo_int_status_t status;
+
+ if (clip->all_clipped)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ assert (clip->path != NULL);
+
+ status = _cairo_clip_path_to_boxes (clip->path, boxes, count);
+ if (status)
+ return status;
+
+ if (*count == 0) {
+ _cairo_clip_set_all_clipped (clip);
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+box_is_aligned (const cairo_box_t *box)
+{
+ return
+ _cairo_fixed_is_integer (box->p1.x) &&
+ _cairo_fixed_is_integer (box->p1.y) &&
+ _cairo_fixed_is_integer (box->p2.x) &&
+ _cairo_fixed_is_integer (box->p2.y);
+}
+
+static void
+intersect_with_boxes (cairo_composite_rectangles_t *extents,
+ cairo_box_t *boxes,
+ int num_boxes)
+{
+ cairo_rectangle_int_t rect;
+ cairo_box_t box;
+ cairo_bool_t is_empty;
+
+ box.p1.x = box.p1.y = INT_MIN;
+ box.p2.x = box.p2.y = INT_MAX;
+ while (num_boxes--) {
+ if (boxes->p1.x < box.p1.x)
+ box.p1.x = boxes->p1.x;
+ if (boxes->p1.y < box.p1.y)
+ box.p1.y = boxes->p1.y;
+
+ if (boxes->p2.x > box.p2.x)
+ box.p2.x = boxes->p2.x;
+ if (boxes->p2.y > box.p2.y)
+ box.p2.y = boxes->p2.y;
+ }
+
+ _cairo_box_round_to_rectangle (&box, &rect);
+ is_empty = _cairo_rectangle_intersect (&extents->bounded, &rect);
+ is_empty = _cairo_rectangle_intersect (&extents->unbounded, &rect);
+}
+
+cairo_status_t
+_cairo_clip_to_boxes (cairo_clip_t **clip,
+ cairo_composite_rectangles_t *extents,
+ cairo_box_t **boxes,
+ int *num_boxes)
+{
+ cairo_status_t status;
+ const cairo_rectangle_int_t *rect;
+
+ rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
+
+ if (*clip == NULL)
+ goto EXTENTS;
+
+ status = _cairo_clip_rectangle (*clip, rect);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
+ switch ((int) status) {
+ case CAIRO_STATUS_SUCCESS:
+ intersect_with_boxes (extents, *boxes, *num_boxes);
+ if (rect->width == 0 || rect->height == 0 ||
+ extents->is_bounded ||
+ (*num_boxes == 1 && box_is_aligned (*boxes)))
+ {
+ *clip = NULL;
+ }
+ goto DONE;
+
+ case CAIRO_INT_STATUS_UNSUPPORTED:
+ goto EXTENTS;
+
+ default:
+ return status;
+ }
+
+ EXTENTS:
+ status = CAIRO_STATUS_SUCCESS;
+ _cairo_box_from_rectangle (&(*boxes)[0], rect);
+ *num_boxes = 1;
+ DONE:
+ return status;
+}
+
+
+static cairo_rectangle_list_t *
+_cairo_rectangle_list_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 = malloc (sizeof (*list));
+ if (unlikely (list == NULL)) {
+ _cairo_error_throw (status);
+ 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;
+ cairo_int_status_t status;
+ int n_rects = 0;
+ int i;
+
+ if (clip->all_clipped)
+ goto DONE;
+
+ if (!clip->path)
+ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+
+ status = _cairo_clip_get_region (clip, &region);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ goto DONE;
+ } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+ } else if (unlikely (status)) {
+ return ERROR_LIST (status);
+ }
+
+ 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 = 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_rectangles()
+ *
+ * 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);
+}
diff --git a/gfx/cairo/cairo/src/cairo-color.c b/gfx/cairo/cairo/src/cairo-color.c
new file mode 100644
index 000000000..d20fea4d2
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-color.c
@@ -0,0 +1,211 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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;
+ }
+}
+
+void
+_cairo_color_init (cairo_color_t *color)
+{
+ *color = cairo_color_white;
+}
+
+void
+_cairo_color_init_rgb (cairo_color_t *color,
+ double red, double green, double blue)
+{
+ _cairo_color_init_rgba (color, red, green, blue, 1.0);
+}
+
+/* Convert a double in [0.0, 1.0] to an integer in [0, 65535]
+ * The conversion is designed to divide the input range into 65536
+ * equally-sized regions. This is achieved by multiplying by 65536 and
+ * then special-casing the result of an input value of 1.0 so that it
+ * maps to 65535 instead of 65536.
+ */
+uint16_t
+_cairo_color_double_to_short (double d)
+{
+ uint32_t i;
+ i = (uint32_t) (d * 65536);
+ i -= (i >> 16);
+ return i;
+}
+
+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-private.h b/gfx/cairo/cairo/src/cairo-combsort-private.h
new file mode 100644
index 000000000..bb7abb477
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-combsort-private.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This fragment implements a comb sort (specifically combsort11) */
+#ifndef _HAVE_CAIRO_COMBSORT_NEWGAP
+#define _HAVE_CAIRO_COMBSORT_NEWGAP
+static inline unsigned int
+_cairo_combsort_newgap (unsigned int gap)
+{
+ gap = 10 * gap / 13;
+ if (gap == 9 || gap == 10)
+ gap = 11;
+ if (gap < 1)
+ gap = 1;
+ return gap;
+}
+#endif
+
+#define CAIRO_COMBSORT_DECLARE(NAME, TYPE, CMP) \
+static void \
+NAME (TYPE *base, unsigned int nmemb) \
+{ \
+ unsigned int gap = nmemb; \
+ unsigned int i, j; \
+ int swapped; \
+ do { \
+ gap = _cairo_combsort_newgap (gap); \
+ swapped = 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); \
+}
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 000000000..18dc661bd
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-compiler-private.h
@@ -0,0 +1,277 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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 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(__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 _CAIRO_BOOLEAN_EXPR(expr) \
+ __extension__ ({ \
+ int _cairo_boolean_var_; \
+ if (expr) \
+ _cairo_boolean_var_ = 1; \
+ else \
+ _cairo_boolean_var_ = 0; \
+ _cairo_boolean_var_; \
+})
+#define likely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 1))
+#define unlikely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 0))
+#else
+#define likely(expr) (expr)
+#define unlikely(expr) (expr)
+#endif
+
+/*
+ * clang-cl supports __attribute__, but MSVC doesn't, so we need to make sure
+ * we do this if not GNUC but also if not clang either.
+ */
+#if !defined(__GNUC__) && !defined(__clang__)
+#undef __attribute__
+#define __attribute__(x)
+#endif
+
+#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
+#define snprintf _snprintf
+#define popen _popen
+#define pclose _pclose
+#define hypot _hypot
+#endif
+
+#ifdef _MSC_VER
+
+#define HAVE_WIN32_ATOMIC_PRIMITIVES 1
+
+#ifndef __cplusplus
+#undef inline
+#define inline __inline
+#endif
+
+/* there are currently linkage problems that arise when trying to include intrin.h in c++:
+ * D:\sdks\v7.0\include\winnt.h(3674) : error C2733: second C linkage of overloaded function '_interlockedbittestandset' not allowed
+ * so avoid defining ffs in c++ code for now */
+#ifndef __cplusplus
+/* Add a definition of ffs */
+#include <intrin.h>
+#pragma intrinsic(_BitScanForward)
+static __forceinline int
+ffs (int x)
+{
+ unsigned long i;
+
+ if (_BitScanForward(&i, x) != 0)
+ return i + 1;
+
+ return 0;
+}
+#endif
+
+#elif defined(__WIN32__) && defined(__GNUC__)
+
+#define ffs(x) __builtin_ffs(x)
+
+#endif
+
+#if defined(_MSC_VER) && defined(_M_IX86)
+/* 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 000000000..8c3c5abcc
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-composite-rectangles-private.h
@@ -0,0 +1,105 @@
+/* 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 <chris@chris-wilson.co.u>
+ */
+
+#ifndef CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H
+#define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H
+
+#include "cairo-types-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_rectangle_int_t source;
+ cairo_rectangle_int_t mask;
+ cairo_rectangle_int_t bounded; /* dst */
+ cairo_rectangle_int_t unbounded; /* clip */
+ uint32_t is_bounded;
+};
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ 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,
+ cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_clip_t *clip);
+
+cairo_private cairo_int_status_t
+_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_clip_t *clip,
+ cairo_bool_t *overlap);
+
+#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 000000000..7f6484339
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-composite-rectangles.c
@@ -0,0 +1,195 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-composite-rectangles-private.h"
+
+/* A collection of routines to facilitate writing compositors. */
+
+static inline cairo_bool_t
+_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ extents->unbounded = *surface_extents;
+
+ if (clip != NULL) {
+ const cairo_rectangle_int_t *clip_extents;
+
+ clip_extents = _cairo_clip_get_extents (clip);
+ if (clip_extents == NULL)
+ return FALSE;
+
+ if (! _cairo_rectangle_intersect (&extents->unbounded, clip_extents))
+ return FALSE;
+ }
+
+ extents->bounded = extents->unbounded;
+ extents->is_bounded = _cairo_operator_bounded_by_either (op);
+
+ _cairo_pattern_get_extents (source, &extents->source);
+ if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
+ if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ if (! _cairo_composite_rectangles_init (extents,
+ surface_extents,
+ op, source, clip))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ extents->mask = extents->bounded;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents)
+{
+ cairo_bool_t ret;
+
+ ret = _cairo_rectangle_intersect (&extents->bounded, &extents->mask);
+ if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ if (! _cairo_composite_rectangles_init (extents,
+ surface_extents,
+ op, source, clip))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ _cairo_pattern_get_extents (mask, &extents->mask);
+
+ return _cairo_composite_rectangles_intersect (extents);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ 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,
+ cairo_clip_t *clip)
+{
+ if (! _cairo_composite_rectangles_init (extents,
+ surface_extents,
+ op, source, clip))
+ {
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask);
+
+ return _cairo_composite_rectangles_intersect (extents);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_clip_t *clip)
+{
+ if (! _cairo_composite_rectangles_init (extents,
+ surface_extents,
+ 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);
+}
+
+cairo_int_status_t
+_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_clip_t *clip,
+ cairo_bool_t *overlap)
+{
+ cairo_status_t status;
+
+ if (! _cairo_composite_rectangles_init (extents,
+ surface_extents,
+ op, source, clip))
+ {
+ 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;
+
+ return _cairo_composite_rectangles_intersect (extents);
+}
diff --git a/gfx/cairo/cairo/src/cairo-d2d-private-fx.h b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h
new file mode 100644
index 000000000..1756c57f6
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h
@@ -0,0 +1,1164 @@
+#if 0
+//
+// FX Version: fx_4_0
+// Child effect (requires effect pool): false
+//
+// 1 local buffer(s)
+//
+cbuffer cb0
+{
+ float4 QuadDesc; // Offset: 0, size: 16
+ float4 TexCoords; // Offset: 16, size: 16
+ float4 TextColor; // Offset: 32, size: 16
+}
+
+//
+// 3 local object(s)
+//
+Texture2D tex;
+BlendState bTextBlend
+{
+ AlphaToCoverageEnable = bool(FALSE /* 0 */);
+ BlendEnable[0] = bool(TRUE /* 1 */);
+ SrcBlend[0] = uint(SRC1_COLOR /* 16 */);
+ DestBlend[0] = uint(INV_SRC1_COLOR /* 17 */);
+ BlendOp[0] = uint(ADD /* 1 */);
+ SrcBlendAlpha[0] = uint(SRC1_ALPHA /* 18 */);
+ DestBlendAlpha[0] = uint(INV_SRC1_ALPHA /* 19 */);
+ BlendOpAlpha[0] = uint(ADD /* 1 */);
+ RenderTargetWriteMask[0] = byte(0x0f);
+};
+SamplerState sSampler
+{
+ Texture = tex;
+ AddressU = uint(CLAMP /* 3 */);
+ AddressV = uint(CLAMP /* 3 */);
+};
+
+//
+// 2 technique(s)
+//
+technique10 SampleTexture
+{
+ pass P0
+ {
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 TextColor; // Offset: 32 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------ ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------ ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c3, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c3.xyxy
+
+ // approximately 4 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[2], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ ret
+ // Approximately 4 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------ ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------ ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0.xy
+ dcl_2d s0
+ texld r0, t0, s0
+ mov oC0, r0
+
+ // approximately 2 instruction slots used (1 texture, 1 arithmetic)
+ ps_4_0
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ sample o0.xyzw, v1.xyxx, t0.xyzw, s0
+ ret
+ // Approximately 2 instruction slots used
+
+ };
+ }
+
+}
+
+technique10 SampleTextTexture
+{
+ pass P0
+ {
+ AB_BlendFactor = float4(0, 0, 0, 0);
+ AB_SampleMask = uint(0xffffffff);
+ BlendState = bTextBlend;
+ VertexShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16
+ // float4 TexCoords; // Offset: 16 Size: 16
+ // float4 TextColor; // Offset: 32 Size: 16 [unused]
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------ ------
+ // POSITION 0 xyz 0 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------ ------
+ // SV_Position 0 xyzw 0 POS float xyzw
+ // TEXCOORD 0 xy 1 NONE float xy
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c1 cb0 0 2 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Runtime generated constant mappings:
+ //
+ // Target Reg Constant Description
+ // ---------- --------------------------------------------------
+ // c0 Vertex Shader position offset
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ vs_2_x
+ def c3, 0, 1, 0, 0
+ dcl_texcoord v0
+ mad oT0.xy, v0, c2.zwzw, c2
+ mad r0.xy, v0, c1.zwzw, c1
+ add oPos.xy, r0, c0
+ mov oPos.zw, c3.xyxy
+
+ // approximately 4 instruction slots used
+ vs_4_0
+ dcl_constantbuffer cb0[2], immediateIndexed
+ dcl_input v0.xy
+ dcl_output_siv o0.xyzw, position
+ dcl_output o1.xy
+ mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx
+ mov o0.zw, l(0,0,0,1.000000)
+ mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx
+ ret
+ // Approximately 4 instruction slots used
+
+ };
+ GeometryShader = NULL;
+ PixelShader = asm {
+ //
+ // Generated by Microsoft (R) HLSL Shader Compiler 9.29.952.3111
+ //
+ //
+ // Buffer Definitions:
+ //
+ // cbuffer cb0
+ // {
+ //
+ // float4 QuadDesc; // Offset: 0 Size: 16 [unused]
+ // float4 TexCoords; // Offset: 16 Size: 16 [unused]
+ // float4 TextColor; // Offset: 32 Size: 16
+ //
+ // }
+ //
+ //
+ // Resource Bindings:
+ //
+ // Name Type Format Dim Slot Elements
+ // ------------------------------ ---------- ------- ----------- ---- --------
+ // sSampler sampler NA NA 0 1
+ // tex texture float4 2d 0 1
+ // cb0 cbuffer NA NA 0 1
+ //
+ //
+ //
+ // Input signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------ ------
+ // SV_Position 0 xyzw 0 POS float
+ // TEXCOORD 0 xy 1 NONE float xy
+ //
+ //
+ // Output signature:
+ //
+ // Name Index Mask Register SysValue Format Used
+ // -------------------- ----- ------ -------- -------- ------ ------
+ // SV_Target 0 xyzw 0 TARGET float xyzw
+ // SV_Target 1 xyzw 1 TARGET float xyzw
+ //
+ //
+ // Constant buffer to DX9 shader constant mappings:
+ //
+ // Target Reg Buffer Start Reg # of Regs Data Conversion
+ // ---------- ------- --------- --------- ----------------------
+ // c0 cb0 2 1 ( FLT, FLT, FLT, FLT)
+ //
+ //
+ // Sampler/Resource to DX9 shader sampler mappings:
+ //
+ // Target Sampler Source Sampler Source Resource
+ // -------------- --------------- ----------------
+ // s0 s0 t0
+ //
+ //
+ // Level9 shader bytecode:
+ //
+ ps_2_x
+ dcl t0.xy
+ dcl_2d s0
+ mov oC0, c0
+ texld r0, t0, s0
+ mul r0, r0.zyxy, c0.w
+ mov oC1, r0
+
+ // approximately 4 instruction slots used (1 texture, 3 arithmetic)
+ ps_4_0
+ dcl_constantbuffer cb0[3], immediateIndexed
+ dcl_sampler s0, mode_default
+ dcl_resource_texture2d (float,float,float,float) t0
+ dcl_input_ps linear v1.xy
+ dcl_output o0.xyzw
+ dcl_output o1.xyzw
+ dcl_temps 1
+ mov o0.xyzw, cb0[2].xyzw
+ sample r0.xyzw, v1.xyxx, t0.xyzw, s0
+ mul o1.xyzw, r0.zyxy, cb0[2].wwww
+ ret
+ // Approximately 4 instruction slots used
+
+ };
+ }
+
+}
+
+#endif
+
+const BYTE g_main[] =
+{
+ 68, 88, 66, 67, 53, 137,
+ 246, 90, 48, 255, 136, 62,
+ 98, 150, 163, 150, 147, 186,
+ 203, 53, 1, 0, 0, 0,
+ 225, 18, 0, 0, 1, 0,
+ 0, 0, 36, 0, 0, 0,
+ 70, 88, 49, 48, 181, 18,
+ 0, 0, 1, 16, 255, 254,
+ 1, 0, 0, 0, 3, 0,
+ 0, 0, 3, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 57, 16,
+ 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4, 0,
+ 0, 0, 4, 0, 0, 0,
+ 0, 0, 0, 0, 99, 98,
+ 48, 0, 102, 108, 111, 97,
+ 116, 52, 0, 8, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 16, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 10, 33, 0,
+ 0, 81, 117, 97, 100, 68,
+ 101, 115, 99, 0, 84, 101,
+ 120, 67, 111, 111, 114, 100,
+ 115, 0, 84, 101, 120, 116,
+ 67, 111, 108, 111, 114, 0,
+ 84, 101, 120, 116, 117, 114,
+ 101, 50, 68, 0, 72, 0,
+ 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 12, 0,
+ 0, 0, 116, 101, 120, 0,
+ 66, 108, 101, 110, 100, 83,
+ 116, 97, 116, 101, 0, 114,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 98, 84, 101,
+ 120, 116, 66, 108, 101, 110,
+ 100, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 16, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 17, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 18, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 19, 0,
+ 0, 0, 1, 0, 0, 0,
+ 2, 0, 0, 0, 1, 0,
+ 0, 0, 1, 0, 0, 0,
+ 3, 0, 0, 0, 15, 0,
+ 0, 0, 83, 97, 109, 112,
+ 108, 101, 114, 83, 116, 97,
+ 116, 101, 0, 16, 1, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 21, 0, 0,
+ 0, 115, 83, 97, 109, 112,
+ 108, 101, 114, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 3, 0, 0, 0, 1, 0,
+ 0, 0, 2, 0, 0, 0,
+ 3, 0, 0, 0, 83, 97,
+ 109, 112, 108, 101, 84, 101,
+ 120, 116, 117, 114, 101, 0,
+ 80, 48, 0, 188, 3, 0,
+ 0, 68, 88, 66, 67, 211,
+ 96, 210, 105, 17, 130, 48,
+ 194, 178, 234, 96, 122, 215,
+ 146, 217, 132, 1, 0, 0,
+ 0, 188, 3, 0, 0, 6,
+ 0, 0, 0, 56, 0, 0,
+ 0, 228, 0, 0, 0, 168,
+ 1, 0, 0, 36, 2, 0,
+ 0, 48, 3, 0, 0, 100,
+ 3, 0, 0, 65, 111, 110,
+ 57, 164, 0, 0, 0, 164,
+ 0, 0, 0, 0, 2, 254,
+ 255, 112, 0, 0, 0, 52,
+ 0, 0, 0, 1, 0, 36,
+ 0, 0, 0, 48, 0, 0,
+ 0, 48, 0, 0, 0, 36,
+ 0, 1, 0, 48, 0, 0,
+ 0, 0, 0, 2, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 254,
+ 255, 81, 0, 0, 5, 3,
+ 0, 15, 160, 0, 0, 0,
+ 0, 0, 0, 128, 63, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 31, 0, 0, 2, 5,
+ 0, 0, 128, 0, 0, 15,
+ 144, 4, 0, 0, 4, 0,
+ 0, 3, 224, 0, 0, 228,
+ 144, 2, 0, 238, 160, 2,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 3, 128, 0,
+ 0, 228, 144, 1, 0, 238,
+ 160, 1, 0, 228, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 1, 0, 0,
+ 2, 0, 0, 12, 192, 3,
+ 0, 68, 160, 255, 255, 0,
+ 0, 83, 72, 68, 82, 188,
+ 0, 0, 0, 64, 0, 1,
+ 0, 47, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 95, 0, 0,
+ 3, 50, 16, 16, 0, 0,
+ 0, 0, 0, 103, 0, 0,
+ 4, 242, 32, 16, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 50,
+ 32, 16, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 50,
+ 32, 16, 0, 0, 0, 0,
+ 0, 70, 16, 16, 0, 0,
+ 0, 0, 0, 230, 138, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 70, 128, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 194, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 128, 63, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 82, 68, 69,
+ 70, 4, 1, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0, 1, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 208,
+ 0, 0, 0, 60, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 99,
+ 98, 48, 0, 60, 0, 0,
+ 0, 3, 0, 0, 0, 88,
+ 0, 0, 0, 48, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 160, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 172, 0, 0, 0, 0,
+ 0, 0, 0, 188, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 172, 0, 0, 0, 0,
+ 0, 0, 0, 198, 0, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 172, 0, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 84,
+ 101, 120, 116, 67, 111, 108,
+ 111, 114, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 57, 46, 50, 57,
+ 46, 57, 53, 50, 46, 51,
+ 49, 49, 49, 0, 171, 171,
+ 171, 73, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 3, 0, 0, 80,
+ 79, 83, 73, 84, 73, 79,
+ 78, 0, 171, 171, 171, 79,
+ 83, 71, 78, 80, 0, 0,
+ 0, 2, 0, 0, 0, 8,
+ 0, 0, 0, 56, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 68, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 12, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 107, 1, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 188,
+ 2, 0, 0, 68, 88, 66,
+ 67, 57, 173, 135, 37, 0,
+ 15, 237, 50, 142, 80, 59,
+ 160, 81, 240, 60, 171, 1,
+ 0, 0, 0, 188, 2, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 164, 0, 0,
+ 0, 16, 1, 0, 0, 140,
+ 1, 0, 0, 48, 2, 0,
+ 0, 136, 2, 0, 0, 65,
+ 111, 110, 57, 100, 0, 0,
+ 0, 100, 0, 0, 0, 0,
+ 2, 255, 255, 60, 0, 0,
+ 0, 40, 0, 0, 0, 0,
+ 0, 40, 0, 0, 0, 40,
+ 0, 0, 0, 40, 0, 1,
+ 0, 36, 0, 0, 0, 40,
+ 0, 0, 0, 0, 0, 1,
+ 2, 255, 255, 31, 0, 0,
+ 2, 0, 0, 0, 128, 0,
+ 0, 3, 176, 31, 0, 0,
+ 2, 0, 0, 0, 144, 0,
+ 8, 15, 160, 66, 0, 0,
+ 3, 0, 0, 15, 128, 0,
+ 0, 228, 176, 0, 8, 228,
+ 160, 1, 0, 0, 2, 0,
+ 8, 15, 128, 0, 0, 228,
+ 128, 255, 255, 0, 0, 83,
+ 72, 68, 82, 100, 0, 0,
+ 0, 64, 0, 0, 0, 25,
+ 0, 0, 0, 90, 0, 0,
+ 3, 0, 96, 16, 0, 0,
+ 0, 0, 0, 88, 24, 0,
+ 4, 0, 112, 16, 0, 0,
+ 0, 0, 0, 85, 85, 0,
+ 0, 98, 16, 0, 3, 50,
+ 16, 16, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 69, 0, 0, 9, 242,
+ 32, 16, 0, 0, 0, 0,
+ 0, 70, 16, 16, 0, 1,
+ 0, 0, 0, 70, 126, 16,
+ 0, 0, 0, 0, 0, 0,
+ 96, 16, 0, 0, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 82, 68, 69,
+ 70, 156, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 255,
+ 255, 0, 1, 0, 0, 105,
+ 0, 0, 0, 92, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 101,
+ 0, 0, 0, 2, 0, 0,
+ 0, 5, 0, 0, 0, 4,
+ 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 1,
+ 0, 0, 0, 12, 0, 0,
+ 0, 115, 83, 97, 109, 112,
+ 108, 101, 114, 0, 116, 101,
+ 120, 0, 77, 105, 99, 114,
+ 111, 115, 111, 102, 116, 32,
+ 40, 82, 41, 32, 72, 76,
+ 83, 76, 32, 83, 104, 97,
+ 100, 101, 114, 32, 67, 111,
+ 109, 112, 105, 108, 101, 114,
+ 32, 57, 46, 50, 57, 46,
+ 57, 53, 50, 46, 51, 49,
+ 49, 49, 0, 171, 171, 73,
+ 83, 71, 78, 80, 0, 0,
+ 0, 2, 0, 0, 0, 8,
+ 0, 0, 0, 56, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 68, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 3, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 79, 83, 71,
+ 78, 44, 0, 0, 0, 1,
+ 0, 0, 0, 8, 0, 0,
+ 0, 32, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 15, 0, 0,
+ 0, 83, 86, 95, 84, 97,
+ 114, 103, 101, 116, 0, 171,
+ 171, 63, 5, 0, 0, 0,
+ 0, 0, 0, 83, 97, 109,
+ 112, 108, 101, 84, 101, 120,
+ 116, 84, 101, 120, 116, 117,
+ 114, 101, 0, 4, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 255,
+ 255, 255, 255, 188, 3, 0,
+ 0, 68, 88, 66, 67, 211,
+ 96, 210, 105, 17, 130, 48,
+ 194, 178, 234, 96, 122, 215,
+ 146, 217, 132, 1, 0, 0,
+ 0, 188, 3, 0, 0, 6,
+ 0, 0, 0, 56, 0, 0,
+ 0, 228, 0, 0, 0, 168,
+ 1, 0, 0, 36, 2, 0,
+ 0, 48, 3, 0, 0, 100,
+ 3, 0, 0, 65, 111, 110,
+ 57, 164, 0, 0, 0, 164,
+ 0, 0, 0, 0, 2, 254,
+ 255, 112, 0, 0, 0, 52,
+ 0, 0, 0, 1, 0, 36,
+ 0, 0, 0, 48, 0, 0,
+ 0, 48, 0, 0, 0, 36,
+ 0, 1, 0, 48, 0, 0,
+ 0, 0, 0, 2, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 254,
+ 255, 81, 0, 0, 5, 3,
+ 0, 15, 160, 0, 0, 0,
+ 0, 0, 0, 128, 63, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 31, 0, 0, 2, 5,
+ 0, 0, 128, 0, 0, 15,
+ 144, 4, 0, 0, 4, 0,
+ 0, 3, 224, 0, 0, 228,
+ 144, 2, 0, 238, 160, 2,
+ 0, 228, 160, 4, 0, 0,
+ 4, 0, 0, 3, 128, 0,
+ 0, 228, 144, 1, 0, 238,
+ 160, 1, 0, 228, 160, 2,
+ 0, 0, 3, 0, 0, 3,
+ 192, 0, 0, 228, 128, 0,
+ 0, 228, 160, 1, 0, 0,
+ 2, 0, 0, 12, 192, 3,
+ 0, 68, 160, 255, 255, 0,
+ 0, 83, 72, 68, 82, 188,
+ 0, 0, 0, 64, 0, 1,
+ 0, 47, 0, 0, 0, 89,
+ 0, 0, 4, 70, 142, 32,
+ 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 95, 0, 0,
+ 3, 50, 16, 16, 0, 0,
+ 0, 0, 0, 103, 0, 0,
+ 4, 242, 32, 16, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 101, 0, 0, 3, 50,
+ 32, 16, 0, 1, 0, 0,
+ 0, 50, 0, 0, 11, 50,
+ 32, 16, 0, 0, 0, 0,
+ 0, 70, 16, 16, 0, 0,
+ 0, 0, 0, 230, 138, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 70, 128, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 0, 0,
+ 8, 194, 32, 16, 0, 0,
+ 0, 0, 0, 2, 64, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 128, 63, 50,
+ 0, 0, 11, 50, 32, 16,
+ 0, 1, 0, 0, 0, 70,
+ 16, 16, 0, 0, 0, 0,
+ 0, 230, 138, 32, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 70, 128, 32, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 82, 68, 69,
+ 70, 4, 1, 0, 0, 1,
+ 0, 0, 0, 64, 0, 0,
+ 0, 1, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 254,
+ 255, 0, 1, 0, 0, 208,
+ 0, 0, 0, 60, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 99,
+ 98, 48, 0, 60, 0, 0,
+ 0, 3, 0, 0, 0, 88,
+ 0, 0, 0, 48, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 160, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 172, 0, 0, 0, 0,
+ 0, 0, 0, 188, 0, 0,
+ 0, 16, 0, 0, 0, 16,
+ 0, 0, 0, 2, 0, 0,
+ 0, 172, 0, 0, 0, 0,
+ 0, 0, 0, 198, 0, 0,
+ 0, 32, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 172, 0, 0, 0, 0,
+ 0, 0, 0, 81, 117, 97,
+ 100, 68, 101, 115, 99, 0,
+ 171, 171, 171, 1, 0, 3,
+ 0, 1, 0, 4, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 84, 101, 120, 67, 111,
+ 111, 114, 100, 115, 0, 84,
+ 101, 120, 116, 67, 111, 108,
+ 111, 114, 0, 77, 105, 99,
+ 114, 111, 115, 111, 102, 116,
+ 32, 40, 82, 41, 32, 72,
+ 76, 83, 76, 32, 83, 104,
+ 97, 100, 101, 114, 32, 67,
+ 111, 109, 112, 105, 108, 101,
+ 114, 32, 57, 46, 50, 57,
+ 46, 57, 53, 50, 46, 51,
+ 49, 49, 49, 0, 171, 171,
+ 171, 73, 83, 71, 78, 44,
+ 0, 0, 0, 1, 0, 0,
+ 0, 8, 0, 0, 0, 32,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 3, 0, 0, 80,
+ 79, 83, 73, 84, 73, 79,
+ 78, 0, 171, 171, 171, 79,
+ 83, 71, 78, 80, 0, 0,
+ 0, 2, 0, 0, 0, 8,
+ 0, 0, 0, 56, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 15,
+ 0, 0, 0, 68, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 3, 0, 0,
+ 0, 1, 0, 0, 0, 3,
+ 12, 0, 0, 83, 86, 95,
+ 80, 111, 115, 105, 116, 105,
+ 111, 110, 0, 84, 69, 88,
+ 67, 79, 79, 82, 68, 0,
+ 171, 171, 171, 73, 8, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 4, 0, 0, 68, 88, 66,
+ 67, 175, 129, 196, 242, 129,
+ 85, 126, 90, 106, 179, 87,
+ 12, 194, 2, 170, 102, 1,
+ 0, 0, 0, 16, 4, 0,
+ 0, 6, 0, 0, 0, 56,
+ 0, 0, 0, 204, 0, 0,
+ 0, 148, 1, 0, 0, 16,
+ 2, 0, 0, 108, 3, 0,
+ 0, 196, 3, 0, 0, 65,
+ 111, 110, 57, 140, 0, 0,
+ 0, 140, 0, 0, 0, 0,
+ 2, 255, 255, 88, 0, 0,
+ 0, 52, 0, 0, 0, 1,
+ 0, 40, 0, 0, 0, 52,
+ 0, 0, 0, 52, 0, 1,
+ 0, 36, 0, 0, 0, 52,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 2, 255, 255, 31, 0, 0,
+ 2, 0, 0, 0, 128, 0,
+ 0, 3, 176, 31, 0, 0,
+ 2, 0, 0, 0, 144, 0,
+ 8, 15, 160, 1, 0, 0,
+ 2, 0, 8, 15, 128, 0,
+ 0, 228, 160, 66, 0, 0,
+ 3, 0, 0, 15, 128, 0,
+ 0, 228, 176, 0, 8, 228,
+ 160, 5, 0, 0, 3, 0,
+ 0, 15, 128, 0, 0, 70,
+ 128, 0, 0, 255, 160, 1,
+ 0, 0, 2, 1, 8, 15,
+ 128, 0, 0, 228, 128, 255,
+ 255, 0, 0, 83, 72, 68,
+ 82, 192, 0, 0, 0, 64,
+ 0, 0, 0, 48, 0, 0,
+ 0, 89, 0, 0, 4, 70,
+ 142, 32, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 90,
+ 0, 0, 3, 0, 96, 16,
+ 0, 0, 0, 0, 0, 88,
+ 24, 0, 4, 0, 112, 16,
+ 0, 0, 0, 0, 0, 85,
+ 85, 0, 0, 98, 16, 0,
+ 3, 50, 16, 16, 0, 1,
+ 0, 0, 0, 101, 0, 0,
+ 3, 242, 32, 16, 0, 0,
+ 0, 0, 0, 101, 0, 0,
+ 3, 242, 32, 16, 0, 1,
+ 0, 0, 0, 104, 0, 0,
+ 2, 1, 0, 0, 0, 54,
+ 0, 0, 6, 242, 32, 16,
+ 0, 0, 0, 0, 0, 70,
+ 142, 32, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 69,
+ 0, 0, 9, 242, 0, 16,
+ 0, 0, 0, 0, 0, 70,
+ 16, 16, 0, 1, 0, 0,
+ 0, 70, 126, 16, 0, 0,
+ 0, 0, 0, 0, 96, 16,
+ 0, 0, 0, 0, 0, 56,
+ 0, 0, 8, 242, 32, 16,
+ 0, 1, 0, 0, 0, 102,
+ 4, 16, 0, 0, 0, 0,
+ 0, 246, 143, 32, 0, 0,
+ 0, 0, 0, 2, 0, 0,
+ 0, 62, 0, 0, 1, 83,
+ 84, 65, 84, 116, 0, 0,
+ 0, 4, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 82, 68, 69,
+ 70, 84, 1, 0, 0, 1,
+ 0, 0, 0, 144, 0, 0,
+ 0, 3, 0, 0, 0, 28,
+ 0, 0, 0, 0, 4, 255,
+ 255, 0, 1, 0, 0, 32,
+ 1, 0, 0, 124, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 133,
+ 0, 0, 0, 2, 0, 0,
+ 0, 5, 0, 0, 0, 4,
+ 0, 0, 0, 255, 255, 255,
+ 255, 0, 0, 0, 0, 1,
+ 0, 0, 0, 12, 0, 0,
+ 0, 137, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 115, 83, 97,
+ 109, 112, 108, 101, 114, 0,
+ 116, 101, 120, 0, 99, 98,
+ 48, 0, 171, 171, 171, 137,
+ 0, 0, 0, 3, 0, 0,
+ 0, 168, 0, 0, 0, 48,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 240,
+ 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 252, 0, 0,
+ 0, 0, 0, 0, 0, 12,
+ 1, 0, 0, 16, 0, 0,
+ 0, 16, 0, 0, 0, 0,
+ 0, 0, 0, 252, 0, 0,
+ 0, 0, 0, 0, 0, 22,
+ 1, 0, 0, 32, 0, 0,
+ 0, 16, 0, 0, 0, 2,
+ 0, 0, 0, 252, 0, 0,
+ 0, 0, 0, 0, 0, 81,
+ 117, 97, 100, 68, 101, 115,
+ 99, 0, 171, 171, 171, 1,
+ 0, 3, 0, 1, 0, 4,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 84, 101, 120,
+ 67, 111, 111, 114, 100, 115,
+ 0, 84, 101, 120, 116, 67,
+ 111, 108, 111, 114, 0, 77,
+ 105, 99, 114, 111, 115, 111,
+ 102, 116, 32, 40, 82, 41,
+ 32, 72, 76, 83, 76, 32,
+ 83, 104, 97, 100, 101, 114,
+ 32, 67, 111, 109, 112, 105,
+ 108, 101, 114, 32, 57, 46,
+ 50, 57, 46, 57, 53, 50,
+ 46, 51, 49, 49, 49, 0,
+ 171, 171, 171, 73, 83, 71,
+ 78, 80, 0, 0, 0, 2,
+ 0, 0, 0, 8, 0, 0,
+ 0, 56, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 15, 0, 0,
+ 0, 68, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 1,
+ 0, 0, 0, 3, 3, 0,
+ 0, 83, 86, 95, 80, 111,
+ 115, 105, 116, 105, 111, 110,
+ 0, 84, 69, 88, 67, 79,
+ 79, 82, 68, 0, 171, 171,
+ 171, 79, 83, 71, 78, 68,
+ 0, 0, 0, 2, 0, 0,
+ 0, 8, 0, 0, 0, 56,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0,
+ 0, 15, 0, 0, 0, 56,
+ 0, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 3,
+ 0, 0, 0, 1, 0, 0,
+ 0, 15, 0, 0, 0, 83,
+ 86, 95, 84, 97, 114, 103,
+ 101, 116, 0, 171, 171, 29,
+ 12, 0, 0, 0, 0, 0,
+ 0, 4, 0, 0, 0, 48,
+ 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 255,
+ 255, 255, 255, 0, 0, 0,
+ 0, 43, 0, 0, 0, 15,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 52,
+ 0, 0, 0, 15, 0, 0,
+ 0, 0, 0, 0, 0, 16,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 62, 0, 0,
+ 0, 15, 0, 0, 0, 0,
+ 0, 0, 0, 32, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 110, 0, 0, 0, 82,
+ 0, 0, 0, 0, 0, 0,
+ 0, 255, 255, 255, 255, 0,
+ 0, 0, 0, 153, 0, 0,
+ 0, 125, 0, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255,
+ 255, 9, 0, 0, 0, 36,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 164,
+ 0, 0, 0, 37, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 176, 0, 0,
+ 0, 38, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 188, 0, 0, 0, 39,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 200,
+ 0, 0, 0, 40, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 212, 0, 0,
+ 0, 41, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 224, 0, 0, 0, 42,
+ 0, 0, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0, 236,
+ 0, 0, 0, 43, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 248, 0, 0,
+ 0, 44, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 4, 1, 0, 0, 0,
+ 0, 0, 0, 57, 1, 0,
+ 0, 29, 1, 0, 0, 0,
+ 0, 0, 0, 255, 255, 255,
+ 255, 3, 0, 0, 0, 55,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 110,
+ 0, 0, 0, 46, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 66, 1, 0,
+ 0, 47, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 78, 1, 0, 0, 0,
+ 0, 0, 0, 90, 1, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 104, 1, 0,
+ 0, 3, 0, 0, 0, 0,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 43, 5, 0,
+ 0, 8, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 51, 5, 0, 0, 7,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 0, 0, 0, 255,
+ 7, 0, 0, 7, 8, 0,
+ 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 104, 1, 0,
+ 0, 6, 0, 0, 0, 0,
+ 0, 0, 0, 10, 0, 0,
+ 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 25, 8, 0,
+ 0, 11, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 61, 8, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 153,
+ 0, 0, 0, 6, 0, 0,
+ 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 9, 12, 0,
+ 0, 8, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0,
+ 0, 17, 12, 0, 0, 7,
+ 0, 0, 0, 0, 0, 0,
+ 0, 7, 0, 0, 0, 49,
+ 16, 0, 0
+};
diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.fx b/gfx/cairo/cairo/src/cairo-d2d-private.fx
new file mode 100644
index 000000000..8f05d693b
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-d2d-private.fx
@@ -0,0 +1,96 @@
+// We store vertex coordinates and the quad shape in a constant buffer, this is
+// easy to update and allows us to use a single call to set the x, y, w, h of
+// the quad.
+// The QuadDesc and TexCoords both work as follows:
+// The x component is the quad left point, the y component is the top point
+// the z component is the width, and the w component is the height. The quad
+// are specified in viewport coordinates, i.e. { -1.0f, 1.0f, 2.0f, -2.0f }
+// would cover the entire viewport (which runs from <-1.0f, 1.0f> left to right
+// and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture
+// space <0, 1.0f> left to right and top to bottom. The input vertices of the
+// shader stage always form a rectangle from {0, 0} - {1, 1}
+cbuffer cb0
+{
+ float4 QuadDesc;
+ float4 TexCoords;
+ float4 TextColor;
+}
+
+struct VS_OUTPUT
+{
+ float4 Position : SV_Position;
+ float2 TexCoord : TEXCOORD0;
+};
+
+struct PS_OUTPUT
+{
+ float4 color;
+ float4 alpha;
+};
+
+Texture2D tex;
+
+BlendState bTextBlend
+{
+ AlphaToCoverageEnable = FALSE;
+ BlendEnable[0] = TRUE;
+ SrcBlend = Src1_Color;
+ DestBlend = Inv_Src1_Color;
+ BlendOp = Add;
+ SrcBlendAlpha = Src1_Alpha;
+ DestBlendAlpha = Inv_Src1_Alpha;
+ BlendOpAlpha = Add;
+ RenderTargetWriteMask[0] = 0x0F; // All
+};
+
+sampler sSampler = sampler_state {
+ Texture = tex;
+ AddressU = Clamp;
+ AddressV = Clamp;
+};
+
+VS_OUTPUT SampleTextureVS(float3 pos : POSITION)
+{
+ VS_OUTPUT Output;
+ Output.Position.w = 1.0f;
+ Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x;
+ Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y;
+ Output.Position.z = 0;
+ Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x;
+ Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y;
+ return Output;
+}
+
+float4 SampleTexturePS( VS_OUTPUT In) : SV_Target
+{
+ return tex.Sample(sSampler, In.TexCoord);
+};
+
+PS_OUTPUT SampleTextTexturePS( VS_OUTPUT In) : SV_Target
+{
+ PS_OUTPUT output;
+ output.color = TextColor;
+ output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a;
+ return output;
+};
+
+technique10 SampleTexture
+{
+ pass P0
+ {
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS()));
+ }
+}
+
+technique10 SampleTextTexture
+{
+ pass P0
+ {
+ SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
+ SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+ SetGeometryShader(NULL);
+ SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePS()));
+ }
+}
diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.h b/gfx/cairo/cairo/src/cairo-d2d-private.h
new file mode 100644
index 000000000..15810eb6d
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-d2d-private.h
@@ -0,0 +1,191 @@
+/* -*- 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 <bschouten@mozilla.com>
+ */
+#ifndef CAIRO_D2D_PRIVATE_H
+#define CAIRO_D2D_PRIVATE_H
+
+#ifdef CAIRO_HAS_D2D_SURFACE
+
+#include <windows.h>
+#include <d2d1.h>
+#include <d3d10.h>
+#include <dxgi.h>
+
+#include "cairoint.h"
+#include "cairo-surface-clipper-private.h"
+
+#include "cairo-win32-refptr.h"
+#include "cairo-d2d-private-fx.h"
+#include "cairo-win32.h"
+#include "cairo-list-private.h"
+
+/* describes the type of the currently applied clip so that we can pop it */
+struct d2d_clip_t;
+
+#define MAX_OPERATORS CAIRO_OPERATOR_HSL_LUMINOSITY + 1
+
+struct _cairo_d2d_device
+{
+ cairo_device_t base;
+
+ HMODULE mD3D10_1;
+ RefPtr<ID3D10Device1> mD3D10Device;
+ RefPtr<ID3D10Effect> mSampleEffect;
+ RefPtr<ID3D10InputLayout> mInputLayout;
+ RefPtr<ID3D10Buffer> mQuadBuffer;
+ RefPtr<ID3D10RasterizerState> mRasterizerState;
+ RefPtr<ID3D10BlendState> mBlendStates[MAX_OPERATORS];
+ /** Texture used for manual glyph rendering */
+ RefPtr<ID3D10Texture2D> mTextTexture;
+ RefPtr<ID3D10ShaderResourceView> mTextTextureView;
+ int mVRAMUsage;
+};
+
+const unsigned int TEXT_TEXTURE_WIDTH = 2048;
+const unsigned int TEXT_TEXTURE_HEIGHT = 512;
+typedef struct _cairo_d2d_device cairo_d2d_device_t;
+
+struct _cairo_d2d_surface {
+ _cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false),
+ textRenderingState(TEXT_RENDERING_UNINITIALIZED)
+ {
+ _cairo_clip_init (&this->clip);
+ cairo_list_init(&this->dependent_surfaces);
+ }
+
+ ~_cairo_d2d_surface();
+
+
+ cairo_surface_t base;
+ /* Device used by this surface
+ * NOTE: In upstream cairo this is in the surface base class */
+ cairo_d2d_device_t *device;
+
+ /** Render target of the texture we render to */
+ RefPtr<ID2D1RenderTarget> rt;
+ /** Surface containing our backstore */
+ RefPtr<ID3D10Resource> surface;
+ /**
+ * Surface used to temporarily store our surface if a bitmap isn't available
+ * straight from our render target surface.
+ */
+ RefPtr<ID3D10Texture2D> bufferTexture;
+ /** Backbuffer surface hwndrt renders to (NULL if not a window surface) */
+ RefPtr<IDXGISurface> backBuf;
+ /** Bitmap shared with texture and rendered to by rt */
+ RefPtr<ID2D1Bitmap> surfaceBitmap;
+ /** Swap chain holding our backbuffer (NULL if not a window surface) */
+ RefPtr<IDXGISwapChain> dxgiChain;
+ /** Window handle of the window we belong to */
+ HWND hwnd;
+ /** Format of the surface */
+ cairo_format_t format;
+
+ cairo_clip_t clip;
+ d2d_clip_t *d2d_clip;
+
+
+ /** Mask layer used by surface_mask to push opacity masks */
+ RefPtr<ID2D1Layer> maskLayer;
+ /**
+ * Layer used for clipping when tiling, and also for clearing out geometries
+ * - lazily initialized
+ */
+ RefPtr<ID2D1Layer> helperLayer;
+ /** If this layer currently is clipping, used to prevent excessive push/pops */
+ bool clipping;
+ /** Brush used for bitmaps */
+ RefPtr<ID2D1BitmapBrush> bitmapBrush;
+ /** Brush used for solid colors */
+ RefPtr<ID2D1SolidColorBrush> solidColorBrush;
+ /** Indicates if our render target is currently in drawing mode */
+ bool isDrawing;
+ /** Indicates if text rendering is initialized */
+ enum TextRenderingState {
+ TEXT_RENDERING_UNINITIALIZED,
+ TEXT_RENDERING_NO_CLEARTYPE,
+ TEXT_RENDERING_NORMAL,
+ TEXT_RENDERING_GDI_CLASSIC
+ };
+ TextRenderingState textRenderingState;
+
+ RefPtr<ID3D10RenderTargetView> buffer_rt_view;
+ RefPtr<ID3D10ShaderResourceView> buffer_sr_view;
+
+ // Other d2d surfaces which depend on this one and need to be flushed if
+ // it is drawn to. This is required for situations where this surface is
+ // drawn to another surface, but may be modified before the other surface
+ // has flushed. When the flush of the other surface then happens and the
+ // drawing command is actually executed, the contents of this surface will
+ // no longer be what it was when the drawing command was issued.
+ cairo_list_t dependent_surfaces;
+ //cairo_surface_clipper_t clipper;
+};
+typedef struct _cairo_d2d_surface cairo_d2d_surface_t;
+
+struct _cairo_d2d_surface_entry
+{
+ cairo_list_t link;
+ cairo_d2d_surface_t *surface;
+};
+
+typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
+ D2D1_FACTORY_TYPE factoryType,
+ REFIID iid,
+ CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
+ void **factory
+);
+
+typedef HRESULT (WINAPI*D3D10CreateDevice1Func)(
+ IDXGIAdapter *pAdapter,
+ D3D10_DRIVER_TYPE DriverType,
+ HMODULE Software,
+ UINT Flags,
+ D3D10_FEATURE_LEVEL1 HardwareLevel,
+ UINT SDKVersion,
+ ID3D10Device1 **ppDevice
+);
+
+typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)(
+ void *pData,
+ SIZE_T DataLength,
+ UINT FXFlags,
+ ID3D10Device *pDevice,
+ ID3D10EffectPool *pEffectPool,
+ ID3D10Effect **ppEffect
+);
+
+#endif /* CAIRO_HAS_D2D_SURFACE */
+#endif /* CAIRO_D2D_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
new file mode 100644
index 000000000..cb90245dc
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -0,0 +1,4866 @@
+/* -*- 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 <bschouten@mozilla.com>
+ */
+#define INITGUID
+
+#include "cairoint.h"
+#include "cairo-d2d-private.h"
+#include "cairo-dwrite-private.h"
+
+#include "cairo-win32.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-error-private.h"
+
+// Required for using placement new.
+#include <new>
+
+#include "d2d1_1.h"
+
+#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS
+
+struct Vertex
+{
+ float position[2];
+};
+
+// This factory is not device dependent, we can store it. But will clear it
+// if there are no devices left needing it.
+static ID2D1Factory *sD2DFactory = NULL;
+static HMODULE sD2DModule;
+
+static void
+_cairo_d2d_release_factory()
+{
+ int refcnt = sD2DFactory->Release();
+ if (!refcnt) {
+ // Once the last reference goes, free the library.
+ sD2DFactory = NULL;
+ FreeLibrary(sD2DModule);
+ }
+}
+
+/**
+ * Set a blending mode for an operator. This will also return a boolean that
+ * reports if for this blend mode the entire surface needs to be blended. This
+ * is true whenever the DEST blend is not ONE when src alpha is 0.
+ */
+static cairo_int_status_t
+_cairo_d2d_set_operator(cairo_d2d_device_t *device,
+ cairo_operator_t op)
+{
+ assert(op < MAX_OPERATORS);
+ if (op >= MAX_OPERATORS) {
+ // Eep! Someone forgot to update MAX_OPERATORS probably.
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (device->mBlendStates[static_cast<size_t>(op)]) {
+ device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ D3D10_BLEND_DESC desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.BlendEnable[0] = TRUE;
+ desc.AlphaToCoverageEnable = FALSE;
+ desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
+
+ switch (op) {
+ case CAIRO_OPERATOR_OVER:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE;
+ break;
+ case CAIRO_OPERATOR_ADD:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE;
+ break;
+ case CAIRO_OPERATOR_IN:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA;
+ break;
+ case CAIRO_OPERATOR_OUT:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
+ break;
+ case CAIRO_OPERATOR_ATOP:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA;
+ break;
+ case CAIRO_OPERATOR_DEST:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO;
+ break;
+ case CAIRO_OPERATOR_DEST_OVER:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
+ break;
+ case CAIRO_OPERATOR_DEST_IN:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO;
+ break;
+ case CAIRO_OPERATOR_DEST_OUT:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO;
+ break;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
+ break;
+ case CAIRO_OPERATOR_XOR:
+ desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
+ desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
+ desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
+ break;
+ default:
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ };
+ device->mD3D10Device->CreateBlendState(&desc, &device->mBlendStates[op]);
+
+ device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff);
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_device_t *
+cairo_d2d_create_device_from_d3d10device(ID3D10Device1 *d3d10device)
+{
+ HRESULT hr;
+ D3D10_RASTERIZER_DESC rastDesc;
+ D3D10_INPUT_ELEMENT_DESC layout[] =
+ {
+ { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
+ };
+ D3D10_PASS_DESC passDesc;
+ ID3D10EffectTechnique *technique;
+ Vertex vertices[] = { {{0.0, 0.0}}, {{1.0, 0.0}}, {{0.0, 1.0}}, {{1.0, 1.0}} };
+ CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER);
+ D3D10_SUBRESOURCE_DATA data;
+ CD3D10_TEXTURE2D_DESC textDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ TEXT_TEXTURE_WIDTH,
+ TEXT_TEXTURE_HEIGHT,
+ 1, 1);
+
+ cairo_d2d_device_t *device = new cairo_d2d_device_t;
+
+ device->mD3D10Device = d3d10device;
+
+ device->mD3D10_1 = LoadLibraryA("d3d10_1.dll");
+ D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc)
+ GetProcAddress(device->mD3D10_1, "D3D10CreateEffectFromMemory");
+ D2D1CreateFactoryFunc createD2DFactory;
+
+ if (!createEffect) {
+ goto FAILED;
+ }
+
+ if (!sD2DFactory) {
+ sD2DModule = LoadLibraryW(L"d2d1.dll");
+ createD2DFactory = (D2D1CreateFactoryFunc)
+ GetProcAddress(sD2DModule, "D2D1CreateFactory");
+ if (!createD2DFactory) {
+ goto FAILED;
+ }
+ D2D1_FACTORY_OPTIONS options;
+#ifdef DEBUG
+ options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
+#else
+ options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
+#endif
+ hr = createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
+ __uuidof(ID2D1Factory),
+ &options,
+ (void**)&sD2DFactory);
+ if (FAILED(hr)) {
+ goto FAILED;
+ }
+ } else {
+ sD2DFactory->AddRef();
+ }
+
+ device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP);
+ createEffect((void*)g_main, sizeof(g_main), 0, device->mD3D10Device, NULL, &device->mSampleEffect);
+
+ technique = device->mSampleEffect->GetTechniqueByName("SampleTexture");
+ technique->GetPassByIndex(0)->GetDesc(&passDesc);
+
+
+ hr = device->mD3D10Device->CreateInputLayout(layout,
+ sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC),
+ passDesc.pIAInputSignature,
+ passDesc.IAInputSignatureSize,
+ &device->mInputLayout);
+ if (FAILED(hr)) {
+ goto FAILED;
+ }
+
+ data.pSysMem = (void*)vertices;
+ hr = device->mD3D10Device->CreateBuffer(&bufferDesc, &data, &device->mQuadBuffer);
+ if (FAILED(hr)) {
+ goto FAILED;
+ }
+
+ memset(&rastDesc, 0, sizeof(rastDesc));
+ rastDesc.CullMode = D3D10_CULL_NONE;
+ rastDesc.FillMode = D3D10_FILL_SOLID;
+ rastDesc.DepthClipEnable = TRUE;
+ hr = device->mD3D10Device->CreateRasterizerState(&rastDesc, &device->mRasterizerState);
+ if (FAILED(hr)) {
+ goto FAILED;
+ }
+ device->base.refcount = 1;
+
+ // We start out with TEXT_TEXTURE roughly in VRAM usage.
+ device->mVRAMUsage = TEXT_TEXTURE_WIDTH * TEXT_TEXTURE_HEIGHT * 4;
+
+ // We create this with USAGE_DEFAULT, our intention is to have VRAM reserved
+ // for text usage. We actually store glyph data in STAGING textures for the
+ // rendering pipeline to read and copy it to this VRAM texture.
+ textDesc.Usage = D3D10_USAGE_DEFAULT;
+ hr = device->mD3D10Device->CreateTexture2D(&textDesc, NULL, &device->mTextTexture);
+ if (FAILED(hr)) {
+ goto FAILED;
+ }
+
+ hr = device->mD3D10Device->CreateShaderResourceView(device->mTextTexture,
+ NULL,
+ &device->mTextTextureView);
+
+ return &device->base;
+FAILED:
+ delete &device->base;
+ return NULL;
+}
+
+cairo_device_t *
+cairo_d2d_create_device()
+{
+ HMODULE d3d10module = LoadLibraryA("d3d10_1.dll");
+ D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func)
+ GetProcAddress(d3d10module, "D3D10CreateDevice1");
+
+ if (!createD3DDevice) {
+ return NULL;
+ }
+
+ RefPtr<ID3D10Device1> d3ddevice;
+
+ /**
+ * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS:
+ * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
+ * can be misleading. In fact, that flag gives no such indication. I pointed this
+ * out to Bas in my email. However, Microsoft is in fact using this flag to
+ * indicate "light weight" DX applications. By light weight they are essentially
+ * referring to applications that are not games. The idea is that when you create
+ * a DX game, the driver assumes that you will pretty much have a single instance
+ * and therefore it doesn't try to hold back when it comes to GPU resource
+ * allocation as long as it can crank out performance. In other words, the
+ * priority in regular DX applications is to make that one application run as fast
+ * as you can. For "light weight" applications, including D2D applications, the
+ * priorities are a bit different. Now you are no longer going to have a single
+ * (or very few) instances. You can have a lot of them (say, for example, a
+ * separate DX context/device per browser tab). In such cases, the GPU resource
+ * allocation scheme changes.
+ */
+ HRESULT hr = createD3DDevice(
+ NULL,
+ D3D10_DRIVER_TYPE_HARDWARE,
+ NULL,
+ D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+ D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+ D3D10_FEATURE_LEVEL_10_1,
+ D3D10_1_SDK_VERSION,
+ &d3ddevice);
+ if (FAILED(hr)) {
+ hr = createD3DDevice(
+ NULL,
+ D3D10_DRIVER_TYPE_HARDWARE,
+ NULL,
+ D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+ D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+ D3D10_FEATURE_LEVEL_10_0,
+ D3D10_1_SDK_VERSION,
+ &d3ddevice);
+ if (FAILED(hr)) {
+ /* This is not guaranteed to be too fast! */
+ hr = createD3DDevice(
+ NULL,
+ D3D10_DRIVER_TYPE_HARDWARE,
+ NULL,
+ D3D10_CREATE_DEVICE_BGRA_SUPPORT |
+ D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
+ D3D10_FEATURE_LEVEL_9_3,
+ D3D10_1_SDK_VERSION,
+ &d3ddevice);
+
+ }
+ }
+ if (FAILED(hr)) {
+ return NULL;
+ }
+
+ cairo_device_t *device = cairo_d2d_create_device_from_d3d10device(d3ddevice);
+
+ // Free our reference to the modules. The created device should have its own.
+ FreeLibrary(d3d10module);
+ return device;
+}
+
+int
+cairo_release_device(cairo_device_t *device)
+{
+ int newrefcnt = --device->refcount;
+ if (!newrefcnt) {
+ // Call the correct destructor
+ cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device);
+ HMODULE d3d10_1 = d2d_device->mD3D10_1;
+ delete d2d_device;
+ _cairo_d2d_release_factory();
+ FreeLibrary(d3d10_1);
+ }
+ return newrefcnt;
+}
+
+int
+cairo_addref_device(cairo_device_t *device)
+{
+ return ++device->refcount;
+}
+
+void
+cairo_d2d_finish_device(cairo_device_t *device)
+{
+ cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device);
+ // Here it becomes interesting, this flush method is generally called when
+ // interop is going on between our device and another device. The
+ // synchronisation between these devices is not always that great. The
+ // device flush method may flush the device's command queue, but it gives
+ // no guarantee that the device will actually be done with those commands,
+ // and so the surface may still not be complete when the external device
+ // chooses to use it. The EVENT query will actually tell us when the GPU
+ // is completely done with our commands.
+ D3D10_QUERY_DESC queryDesc;
+ queryDesc.MiscFlags = 0;
+ queryDesc.Query = D3D10_QUERY_EVENT;
+ RefPtr<ID3D10Query> query;
+
+ d2d_device->mD3D10Device->CreateQuery(&queryDesc, &query);
+
+ // QUERY_EVENT does not use Begin(). It's disabled.
+ query->End();
+
+ BOOL done = FALSE;
+ while (!done) {
+ // This will return S_OK and done = FALSE when the GPU is not done, and
+ // S_OK and done = TRUE when the GPU is done. Any other return value
+ // means we need to break out or risk an infinite loop.
+ if (FAILED(query->GetData(&done, sizeof(BOOL), 0))) {
+ break;
+ }
+ if (FAILED(d2d_device->mD3D10Device->GetDeviceRemovedReason())) {
+ break;
+ }
+ }
+}
+
+ID3D10Device1*
+cairo_d2d_device_get_device(cairo_device_t *device)
+{
+ cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device);
+ return d2d_device->mD3D10Device;
+}
+
+static void
+_cairo_d2d_setup_for_blend(cairo_d2d_device_t *device)
+{
+ device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ device->mD3D10Device->IASetInputLayout(device->mInputLayout);
+
+ UINT stride = sizeof(Vertex);
+ UINT offset = 0;
+ ID3D10Buffer *buff = device->mQuadBuffer;
+ device->mD3D10Device->IASetVertexBuffers(0, 1, &buff, &stride, &offset);
+
+ device->mD3D10Device->RSSetState(device->mRasterizerState);
+}
+
+// Contains our cache usage - perhaps this should be made threadsafe.
+static int cache_usage = 0;
+
+/**
+ * Create a similar surface which will blend effectively to
+ * another surface. For D2D, this will create another texture.
+ * Within the types we use blending is always easy.
+ *
+ * \param surface Surface this needs to be similar to
+ * \param content Content type of the new surface
+ * \param width Width of the new surface
+ * \param height Height of the new surface
+ * \return New surface
+ */
+static cairo_surface_t*
+_cairo_d2d_create_similar(void *surface,
+ cairo_content_t content,
+ int width,
+ int height);
+
+/**
+ * Release all the data held by a surface, the surface structure
+ * itsself will be freed by cairo.
+ *
+ * \param surface Surface to clean up
+ */
+static cairo_status_t
+_cairo_d2d_finish(void *surface);
+
+/**
+ * Get a read-only image surface that contains the pixel data
+ * of a D2D surface.
+ *
+ * \param abstract_surface D2D surface to acquire the image from
+ * \param image_out Pointer to where we should store the image surface pointer
+ * \param image_extra Pointer where to store extra data we want to know about
+ * at the point of release.
+ * \return CAIRO_STATUS_SUCCESS for success
+ */
+static cairo_status_t
+_cairo_d2d_acquire_source_image(void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra);
+
+/**
+ * Release a read-only image surface that was obtained using acquire_source_image
+ *
+ * \param abstract_surface D2D surface to acquire the image from
+ * \param image_out Pointer to where we should store the image surface pointer
+ * \param image_extra Pointer where to store extra data we want to know about
+ * at the point of release.
+ * \return CAIRO_STATUS_SUCCESS for success
+ */
+static void
+_cairo_d2d_release_source_image(void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra);
+
+/**
+ * Get a read-write image surface that contains the pixel data
+ * of a D2D surface.
+ *
+ * \param abstract_surface D2D surface to acquire the image from
+ * \param image_out Pointer to where we should store the image surface pointer
+ * \param image_extra Pointer where to store extra data we want to know about
+ * at the point of release.
+ * \return CAIRO_STATUS_SUCCESS for success
+ */
+static cairo_status_t
+_cairo_d2d_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);
+
+/**
+ * Release a read-write image surface that was obtained using acquire_source_image
+ *
+ * \param abstract_surface D2D surface to acquire the image from
+ * \param image_out Pointer to where we should store the image surface pointer
+ * \param image_extra Pointer where to store extra data we want to know about
+ * at the point of release.
+ * \return CAIRO_STATUS_SUCCESS for success
+ */
+static void
+_cairo_d2d_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);
+
+/**
+ * Flush this surface, only after this operation is the related hardware texture
+ * guaranteed to contain all the results of the executed drawing operations.
+ *
+ * \param surface D2D surface to flush
+ * \return CAIRO_STATUS_SUCCESS or CAIRO_SURFACE_TYPE_MISMATCH
+ */
+static cairo_status_t
+_cairo_d2d_flush(void *surface);
+
+/**
+ * Fill a path on this D2D surface.
+ *
+ * \param surface The surface to apply this operation to, must be
+ * a D2D surface
+ * \param op The operator to use
+ * \param source The source pattern to fill this path with
+ * \param path The path to fill
+ * \param fill_rule The fill rule to uses on the path
+ * \param tolerance The tolerance applied to the filling
+ * \param antialias The anti-alias mode to use
+ * \param clip The clip of this operation
+ * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH,
+ * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS
+ */
+static cairo_int_status_t
+_cairo_d2d_fill(void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+/**
+ * Paint this surface, applying the operation to the entire surface
+ *
+ * \param surface The surface to apply this operation to, must be
+ * a D2D surface
+ * \param op Operator to use when painting
+ * \param source The pattern to fill this surface with, source of the op
+ * \param clip The clip of this operation
+ * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH,
+ * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS
+ */
+static cairo_int_status_t
+_cairo_d2d_paint(void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip);
+
+/**
+ * Paint something on the surface applying a certain mask to that
+ * source.
+ *
+ * \param surface The surface to apply this oepration to, must be
+ * a D2D surface
+ * \param op Operator to use
+ * \param source Source for this operation
+ * \param mask Pattern to mask source with
+ * \param clip The clip of this operation
+ * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH,
+ * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS
+ */
+static cairo_int_status_t
+_cairo_d2d_mask(void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip);
+
+/**
+ * Show a glyph run on the target D2D surface.
+ *
+ * \param surface The surface to apply this oepration to, must be
+ * a D2D surface
+ * \param op Operator to use
+ * \param source Source for this operation
+ * \param glyphs Glyphs to draw
+ * \param num_gluphs Amount of glyphs stored at glyphs
+ * \param scaled_font Scaled font to draw
+ * \param remaining_glyphs Pointer to store amount of glyphs still
+ * requiring drawing.
+ * \param clip The clip of this operation
+ * \return CAIRO_ERROR_SURFACE_TYPE_MISMATCH, CAIRO_ERROR_FONT_TYPE_MISMATCH,
+ * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS
+ */
+static cairo_int_status_t
+_cairo_d2d_show_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs);
+
+/**
+ * Get the extents of this surface.
+ *
+ * \param surface D2D surface to get the extents for
+ * \param extents Pointer to where to store the extents
+ * \param CAIRO_ERROR_SURFACE_TYPE_MISTMATCH or CAIRO_STATUS_SUCCESS
+ */
+static cairo_bool_t
+_cairo_d2d_getextents(void *surface,
+ cairo_rectangle_int_t *extents);
+
+
+/**
+ * Stroke a path on this D2D surface.
+ *
+ * \param surface The surface to apply this operation to, must be
+ * a D2D surface
+ * \param op The operator to use
+ * \param source The source pattern to fill this path with
+ * \param path The path to stroke
+ * \param style The style of the stroke
+ * \param ctm A logical to device matrix, since the path might be in
+ * device space the miter angle and such are not, hence we need to
+ * be aware of the transformation to apply correct stroking.
+ * \param ctm_inverse Inverse of ctm, used to transform the path back
+ * to logical space.
+ * \param tolerance Tolerance to stroke with
+ * \param antialias Antialias mode to use
+ * \param clip The clip of this operation
+ * \return Return code, this can be CAIRO_ERROR_SURFACE_TYPE_MISMATCH,
+ * CAIRO_INT_STATUS_UNSUPPORTED or CAIRO_STATUS_SUCCESS
+ */
+static cairo_int_status_t
+_cairo_d2d_stroke(void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+static const cairo_surface_backend_t cairo_d2d_surface_backend = {
+ CAIRO_SURFACE_TYPE_D2D,
+ _cairo_d2d_create_similar, /* create_similar */
+ _cairo_d2d_finish, /* finish */
+ _cairo_d2d_acquire_source_image, /* acquire_source_image */
+ _cairo_d2d_release_source_image, /* release_source_image */
+ _cairo_d2d_acquire_dest_image, /* acquire_dest_image */
+ _cairo_d2d_release_dest_image, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_d2d_getextents, /* get_extents */
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ _cairo_d2d_flush, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+ _cairo_d2d_paint, /* paint */
+ _cairo_d2d_mask, /* mask */
+ _cairo_d2d_stroke, /* stroke */
+ _cairo_d2d_fill, /* fill */
+ _cairo_d2d_show_glyphs, /* show_glyphs */
+ NULL, /* snapshot */
+ NULL
+};
+
+/*
+ * Helper functions.
+ */
+
+/* Stack-based helper to manage region destruction. */
+struct cairo_region_auto_ptr
+{
+ cairo_region_auto_ptr() : region(NULL)
+ { }
+ cairo_region_auto_ptr(cairo_region_t *in_region) : region(in_region)
+ { }
+
+ void set(cairo_region_t *in_region) { region = in_region; }
+
+ ~cairo_region_auto_ptr() { if (region) cairo_region_destroy (region); }
+
+ cairo_region_t *region;
+};
+
+/* This clears a new D2D surface in case the VRAM was reused from an existing surface
+ * and is therefor not empty, this must be called outside of drawing state! */
+static void
+_d2d_clear_surface(cairo_d2d_surface_t *surf)
+{
+ surf->rt->BeginDraw();
+ surf->rt->Clear(D2D1::ColorF(0, 0));
+ surf->rt->EndDraw();
+}
+
+static cairo_rectangle_int_t
+_cairo_rect_from_windows_rect(const RECT *rect)
+{
+ cairo_rectangle_int_t new_rect;
+
+ new_rect.x = rect->left;
+ new_rect.y = rect->top;
+ new_rect.width = rect->right - rect->left;
+ new_rect.height = rect->bottom - rect->top;
+
+ return new_rect;
+}
+
+static D2D1_POINT_2F
+_d2d_point_from_cairo_point(const cairo_point_t *point)
+{
+ return D2D1::Point2F(_cairo_fixed_to_float(point->x),
+ _cairo_fixed_to_float(point->y));
+}
+
+static D2D1_COLOR_F
+_cairo_d2d_color_from_cairo_color(const cairo_color_t &color)
+{
+ return D2D1::ColorF((FLOAT)color.red,
+ (FLOAT)color.green,
+ (FLOAT)color.blue,
+ (FLOAT)color.alpha);
+}
+
+static void
+_cairo_d2d_round_out_to_int_rect(cairo_rectangle_int_t *rect, double x1, double y1, double x2, double y2)
+{
+ rect->x = (int)floor(x1);
+ rect->y = (int)floor(y1);
+ rect->width = (int)ceil(x2) - rect->x;
+ rect->height = (int)ceil(y2) - rect->y;
+}
+
+static int
+_cairo_d2d_compute_surface_mem_size(cairo_d2d_surface_t *surface)
+{
+ int size = surface->rt->GetPixelSize().width * surface->rt->GetPixelSize().height;
+ size *= surface->rt->GetPixelFormat().format == DXGI_FORMAT_A8_UNORM ? 1 : 4;
+ return size;
+}
+
+static D2D1_COLOR_F
+_cairo_d2d_color_from_cairo_color_stop(const cairo_color_stop_t &color)
+{
+ return D2D1::ColorF((FLOAT)color.red,
+ (FLOAT)color.green,
+ (FLOAT)color.blue,
+ (FLOAT)color.alpha);
+}
+
+
+/**
+ * Gets the surface buffer texture for window surfaces whose backbuffer
+ * is not directly usable as a bitmap.
+ *
+ * \param surface D2D surface.
+ * \return Buffer texture
+ */
+static ID3D10Texture2D*
+_cairo_d2d_get_buffer_texture(cairo_d2d_surface_t *surface)
+{
+ if (!surface->bufferTexture) {
+ RefPtr<IDXGISurface> surf;
+ DXGI_SURFACE_DESC surfDesc;
+ surface->surface->QueryInterface(&surf);
+ surf->GetDesc(&surfDesc);
+ CD3D10_TEXTURE2D_DESC softDesc(surfDesc.Format, surfDesc.Width, surfDesc.Height);
+ softDesc.MipLevels = 1;
+ softDesc.Usage = D3D10_USAGE_DEFAULT;
+ softDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+ surface->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture);
+ surface->device->mVRAMUsage += _cairo_d2d_compute_surface_mem_size(surface);
+ }
+ return surface->bufferTexture;
+}
+
+/**
+ * Ensure that the surface has an up-to-date surface bitmap. Used for
+ * window surfaces which cannot have a surface bitmap directly related
+ * to their backbuffer for some reason.
+ * You cannot create a bitmap around a backbuffer surface for reason (it will
+ * fail with an E_INVALIDARG). Meaning they need a special texture to store
+ * their graphical data which is wrapped by a D2D bitmap if a window surface
+ * is ever used in a surface pattern. All other D2D surfaces use a texture as
+ * their backing store so can have a bitmap directly.
+ *
+ * \param surface D2D surface.
+ */
+static void _cairo_d2d_update_surface_bitmap(cairo_d2d_surface_t *d2dsurf)
+{
+ if (!d2dsurf->backBuf && d2dsurf->rt->GetPixelFormat().format != DXGI_FORMAT_A8_UNORM) {
+ return;
+ }
+
+ if (!d2dsurf->surfaceBitmap) {
+ d2dsurf->rt->CreateBitmap(d2dsurf->rt->GetPixelSize(),
+ D2D1::BitmapProperties(d2dsurf->rt->GetPixelFormat()),
+ &d2dsurf->surfaceBitmap);
+ }
+
+ d2dsurf->surfaceBitmap->CopyFromRenderTarget(NULL, d2dsurf->rt, NULL);
+}
+
+/**
+ * Present the backbuffer for a surface create for an HWND. This needs
+ * to be called when the owner of the original window surface wants to
+ * actually present the executed drawing operations to the screen.
+ *
+ * \param surface D2D surface.
+ */
+void cairo_d2d_present_backbuffer(cairo_surface_t *surface)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_D2D) {
+ return;
+ }
+ cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+ _cairo_d2d_flush(d2dsurf);
+ if (d2dsurf->dxgiChain) {
+ d2dsurf->dxgiChain->Present(0, 0);
+ d2dsurf->device->mD3D10Device->Flush();
+ }
+}
+
+struct d2d_clip_t
+{
+ enum clip_type {LAYER, AXIS_ALIGNED_CLIP};
+ d2d_clip_t * const prev;
+ const enum clip_type type;
+ d2d_clip_t(d2d_clip_t *prev, clip_type type) : prev(prev), type(type) { }
+};
+
+static RefPtr<ID2D1PathGeometry>
+_cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ D2D1_FIGURE_BEGIN type);
+
+
+static cairo_bool_t
+box_is_integer (cairo_box_t *box)
+{
+ return _cairo_fixed_is_integer(box->p1.x) &&
+ _cairo_fixed_is_integer(box->p1.y) &&
+ _cairo_fixed_is_integer(box->p2.x) &&
+ _cairo_fixed_is_integer(box->p2.y);
+}
+
+static cairo_status_t
+push_clip (cairo_d2d_surface_t *d2dsurf, cairo_clip_path_t *clip_path)
+{
+ cairo_box_t box;
+ if (_cairo_path_fixed_is_box(&clip_path->path, &box)) {
+
+ assert(box.p1.y < box.p2.y);
+
+ D2D1_ANTIALIAS_MODE mode;
+ if (box_is_integer (&box)) {
+ mode = D2D1_ANTIALIAS_MODE_ALIASED;
+ } else {
+ mode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
+ }
+ d2dsurf->rt->PushAxisAlignedClip (
+ D2D1::RectF(
+ _cairo_fixed_to_float(box.p1.x),
+ _cairo_fixed_to_float(box.p1.y),
+ _cairo_fixed_to_float(box.p2.x),
+ _cairo_fixed_to_float(box.p2.y)),
+ mode);
+
+ d2dsurf->d2d_clip = new d2d_clip_t (d2dsurf->d2d_clip, d2d_clip_t::AXIS_ALIGNED_CLIP);
+ } else {
+ HRESULT hr;
+ RefPtr<ID2D1PathGeometry> geom = _cairo_d2d_create_path_geometry_for_path (&clip_path->path,
+ clip_path->fill_rule,
+ D2D1_FIGURE_BEGIN_FILLED);
+ RefPtr<ID2D1Layer> layer;
+
+ hr = d2dsurf->rt->CreateLayer (&layer);
+
+ D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
+ D2D1_LAYER_OPTIONS1 options1 = D2D1_LAYER_OPTIONS1_NONE;
+
+ if (d2dsurf->base.content == CAIRO_CONTENT_COLOR) {
+ options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
+ options1 = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
+ options1 = D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
+ }
+
+ RefPtr<ID2D1DeviceContext> dc;
+ hr = d2dsurf->rt->QueryInterface(IID_ID2D1DeviceContext, (void**)&dc);
+
+ if (FAILED(hr)) {
+ d2dsurf->rt->PushLayer(D2D1::LayerParameters(
+ D2D1::InfiniteRect(),
+ geom,
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+ D2D1::IdentityMatrix(),
+ 1.0,
+ 0,
+ options),
+ layer);
+ } else {
+ dc->PushLayer(D2D1::LayerParameters1(
+ D2D1::InfiniteRect(),
+ geom,
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+ D2D1::IdentityMatrix(),
+ 1.0,
+ 0,
+ options1),
+ layer);
+ }
+
+ d2dsurf->d2d_clip = new d2d_clip_t(d2dsurf->d2d_clip, d2d_clip_t::LAYER);
+ }
+ if (!d2dsurf->d2d_clip)
+ return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+pop_clip (cairo_d2d_surface_t *d2dsurf)
+{
+ d2d_clip_t *current_clip = d2dsurf->d2d_clip;
+
+ /* pop the clip from the render target */
+ if (current_clip->type == d2d_clip_t::LAYER) {
+ d2dsurf->rt->PopLayer();
+ } else if (current_clip->type == d2d_clip_t::AXIS_ALIGNED_CLIP) {
+ d2dsurf->rt->PopAxisAlignedClip();
+ }
+
+ /* pop it from our own stack */
+ d2dsurf->d2d_clip = current_clip->prev;
+ delete current_clip;
+}
+
+/* intersect clip_paths until we reach head */
+static cairo_status_t
+clipper_intersect_clip_path_recursive (cairo_d2d_surface_t *d2dsurf,
+ cairo_clip_path_t *head,
+ cairo_clip_path_t *clip_path)
+{
+ cairo_status_t status;
+
+ if (clip_path->prev != head) {
+ status =
+ clipper_intersect_clip_path_recursive (d2dsurf,
+ head,
+ clip_path->prev);
+ if (unlikely (status))
+ return status;
+ }
+ return push_clip(d2dsurf, clip_path);
+
+}
+
+/* pop all of the clipping layers and reset the clip */
+static void
+reset_clip (cairo_d2d_surface_t *d2dsurf)
+{
+ cairo_clip_path_t *current_clip_path = d2dsurf->clip.path;
+ while (current_clip_path != NULL) {
+ pop_clip (d2dsurf);
+ current_clip_path = current_clip_path->prev;
+ }
+
+ _cairo_clip_reset (&d2dsurf->clip);
+}
+
+/* finds the lowest common ancestor of a and b */
+static cairo_clip_path_t *
+find_common_ancestor(cairo_clip_path_t *a, cairo_clip_path_t *b)
+{
+ int a_depth = 0, b_depth = 0;
+
+ cairo_clip_path_t *x;
+
+ /* find the depths of the clip_paths */
+ x = a;
+ while (x) {
+ a_depth++;
+ x = x->prev;
+ }
+
+ x = b;
+ while (x) {
+ b_depth++;
+ x = x->prev;
+ }
+
+ /* rewind the deeper chain to the depth of the shallowest chain */
+ while (b_depth < a_depth && a) {
+ a = a->prev;
+ a_depth--;
+ }
+
+ while (a_depth < b_depth && b) {
+ b = b->prev;
+ b_depth--;
+ }
+
+ /* walk back until we find a common ancesstor */
+
+ /* b will be null if and only if a is null because the depths
+ * must match at this point */
+ while (a) {
+ if (a == b)
+ return a;
+
+ a = a->prev;
+ b = b->prev;
+ }
+
+ /* a will be NULL */
+ return a;
+}
+
+static cairo_status_t
+_cairo_d2d_set_clip (cairo_d2d_surface_t *d2dsurf, cairo_clip_t *clip)
+{
+ if (clip == NULL) {
+ reset_clip (d2dsurf);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (clip != NULL && clip->path == d2dsurf->clip.path)
+ return CAIRO_STATUS_SUCCESS;
+
+ cairo_clip_path_t *current_clip_path = d2dsurf->clip.path;
+ cairo_clip_path_t *new_clip_path = clip->path;
+ cairo_clip_path_t *ancestor = find_common_ancestor (current_clip_path, new_clip_path);
+
+ /* adjust the clip to the common ancestor */
+ while (current_clip_path != ancestor) {
+ pop_clip (d2dsurf);
+ current_clip_path = current_clip_path->prev;
+ }
+
+ /* we now have a common parent (current_clip_path) for the clip */
+
+ /* replace the old clip */
+ _cairo_clip_reset (&d2dsurf->clip);
+ _cairo_clip_init_copy (&d2dsurf->clip, clip);
+
+ /* push the new clip paths up to current_clip_path */
+ if (current_clip_path != clip->path)
+ return clipper_intersect_clip_path_recursive (d2dsurf, current_clip_path, clip->path);
+ else
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void _cairo_d2d_add_dependent_surface(cairo_d2d_surface_t *surf, cairo_d2d_surface_t *user)
+{
+ _cairo_d2d_surface_entry *entry = new _cairo_d2d_surface_entry;
+ entry->surface = user;
+ cairo_surface_reference(&user->base);
+ cairo_list_add(&entry->link, &surf->dependent_surfaces);
+};
+
+static void _cairo_d2d_flush_dependent_surfaces(cairo_d2d_surface_t *surf)
+{
+ _cairo_d2d_surface_entry *entry, *next;
+ cairo_list_foreach_entry_safe(entry, next, _cairo_d2d_surface_entry, &surf->dependent_surfaces, link) {
+ _cairo_d2d_flush(entry->surface);
+ cairo_surface_destroy(&entry->surface->base);
+ delete entry;
+ }
+ cairo_list_init(&surf->dependent_surfaces);
+}
+
+/**
+ * Enter the state where the surface is ready for drawing. This will guarantee
+ * the surface is in the correct state, and the correct clipping area is pushed.
+ *
+ * \param surface D2D surface
+ */
+static void _begin_draw_state(cairo_d2d_surface_t* surface)
+{
+ if (!surface->isDrawing) {
+ _cairo_d2d_flush_dependent_surfaces(surface);
+ surface->rt->BeginDraw();
+ surface->isDrawing = true;
+ }
+}
+
+/**
+ * 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);
+}
+
+/**
+ * Returns the inverse matrix for a D2D1 matrix. We cannot use the Invert function
+ * on the Matrix3x2F function class since it's statically linked and we'd have to
+ * lookup the symbol in the library. Doing this ourselves is easier.
+ *
+ * \param mat matrix
+ * \return inverse of matrix mat
+ */
+static D2D1::Matrix3x2F
+_cairo_d2d_invert_matrix(const D2D1::Matrix3x2F &mat)
+{
+ float inv_det = (1 / mat.Determinant());
+
+ return D2D1::Matrix3x2F(mat._22 * inv_det,
+ -mat._12 * inv_det,
+ -mat._21 * inv_det,
+ mat._11 * inv_det,
+ (mat._21 * mat._32 - mat._22 * mat._31) * inv_det,
+ -(mat._11 * mat._32 - mat._12 * mat._31) * inv_det);
+}
+
+/**
+ * Create a D2D stroke style interface for a cairo stroke style object. Must be
+ * released when the calling function is finished with it.
+ *
+ * \param style Cairo stroke style object
+ * \return D2D StrokeStyle interface
+ */
+static RefPtr<ID2D1StrokeStyle>
+_cairo_d2d_create_strokestyle_for_stroke_style(const cairo_stroke_style_t *style)
+{
+ D2D1_CAP_STYLE line_cap = D2D1_CAP_STYLE_FLAT;
+ switch (style->line_cap) {
+ case CAIRO_LINE_CAP_BUTT:
+ line_cap = D2D1_CAP_STYLE_FLAT;
+ break;
+ case CAIRO_LINE_CAP_SQUARE:
+ line_cap = D2D1_CAP_STYLE_SQUARE;
+ break;
+ case CAIRO_LINE_CAP_ROUND:
+ line_cap = D2D1_CAP_STYLE_ROUND;
+ break;
+ }
+
+ D2D1_LINE_JOIN line_join = D2D1_LINE_JOIN_MITER;
+ switch (style->line_join) {
+ case CAIRO_LINE_JOIN_MITER:
+ line_join = D2D1_LINE_JOIN_MITER_OR_BEVEL;
+ break;
+ case CAIRO_LINE_JOIN_ROUND:
+ line_join = D2D1_LINE_JOIN_ROUND;
+ break;
+ case CAIRO_LINE_JOIN_BEVEL:
+ line_join = D2D1_LINE_JOIN_BEVEL;
+ break;
+ }
+
+ FLOAT *dashes = NULL;
+ if (style->num_dashes) {
+ dashes = new FLOAT[style->num_dashes];
+ for (unsigned int i = 0; i < style->num_dashes; i++) {
+ /* D2D seems to specify dash lengths in units of
+ * line width instead of the more traditional approach
+ * that cairo and many other APIs use where the unit
+ * is in pixels or someother constant unit. */
+ dashes[i] = (FLOAT) (style->dash[i] / style->line_width);
+ }
+ }
+
+ D2D1_DASH_STYLE dashStyle = D2D1_DASH_STYLE_SOLID;
+ if (dashes) {
+ dashStyle = D2D1_DASH_STYLE_CUSTOM;
+ }
+
+ RefPtr<ID2D1StrokeStyle> strokeStyle;
+ sD2DFactory->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap,
+ line_cap,
+ line_cap,
+ line_join,
+ (FLOAT)style->miter_limit,
+ dashStyle,
+ (FLOAT)style->dash_offset),
+ dashes,
+ style->num_dashes,
+ &strokeStyle);
+ delete [] dashes;
+ return strokeStyle;
+}
+
+static int _d2d_compute_bitmap_mem_size(ID2D1Bitmap *bitmap)
+{
+ D2D1_SIZE_U size = bitmap->GetPixelSize();
+ int bytes_per_pixel = bitmap->GetPixelFormat().format == DXGI_FORMAT_A8_UNORM ? 1 : 4;
+ return size.width * size.height * bytes_per_pixel;
+}
+
+cairo_user_data_key_t bitmap_key_nonextend;
+cairo_user_data_key_t bitmap_key_extend;
+cairo_user_data_key_t bitmap_key_snapshot;
+
+struct cached_bitmap {
+ cached_bitmap()
+ {
+ sD2DFactory->AddRef();
+ }
+
+ ~cached_bitmap()
+ {
+ // Clear bitmap out first because it depends on the factory.
+ bitmap = NULL;
+ _cairo_d2d_release_factory();
+ }
+
+ /* Device this cached bitmap was created with, we should really have a per
+ * device cache, see bug 607408 */
+ cairo_d2d_device_t *device;
+ /** The cached bitmap */
+ RefPtr<ID2D1Bitmap> bitmap;
+ /** The cached bitmap is dirty and needs its data refreshed */
+ bool dirty;
+ /** Order of snapshot detach/release bitmap called not guaranteed, single threaded refcount for now */
+ int refs;
+};
+
+/**
+ * This is called when user data on a surface is replaced or the surface is
+ * destroyed.
+ */
+static void _d2d_release_bitmap(void *bitmap)
+{
+ cached_bitmap *existingBitmap = (cached_bitmap*)bitmap;
+ if (!--existingBitmap->refs) {
+ cache_usage -= _d2d_compute_bitmap_mem_size(existingBitmap->bitmap);
+ delete existingBitmap;
+ }
+}
+
+/**
+ * Via a little trick this is just used to determine when a surface has been
+ * modified.
+ */
+static void _d2d_snapshot_detached(cairo_surface_t *surface)
+{
+ cached_bitmap *existingBitmap = (cached_bitmap*)cairo_surface_get_user_data(surface, &bitmap_key_snapshot);
+ if (existingBitmap) {
+ existingBitmap->dirty = true;
+ }
+ if (!--existingBitmap->refs) {
+ cache_usage -= _d2d_compute_bitmap_mem_size(existingBitmap->bitmap);
+ delete existingBitmap;
+ }
+ cairo_surface_destroy(surface);
+}
+
+/**
+ * This function will calculate the part of srcSurf which will possibly be used within
+ * the boundaries of d2dsurf given the current transformation mat. This is used to
+ * determine what the minimal part of a surface is that needs to be uploaded.
+ *
+ * \param d2dsurf D2D surface
+ * \param srcSurf Source surface for operation
+ * \param mat Transformation matrix applied to source
+ */
+static void
+_cairo_d2d_calculate_visible_rect(cairo_d2d_surface_t *d2dsurf, cairo_image_surface_t *srcSurf,
+ cairo_matrix_t *mat,
+ int *x, int *y, unsigned int *width, unsigned int *height)
+{
+ /** Leave room for extend_none space, 2 pixels */
+ UINT32 maxSize = d2dsurf->rt->GetMaximumBitmapSize() - 2;
+
+ /* Transform this surface to image surface space */
+ cairo_matrix_t invMat = *mat;
+ if (_cairo_matrix_is_invertible(mat)) {
+ /* If this is not invertible it will be rank zero, and invMat = mat is fine */
+ cairo_matrix_invert(&invMat);
+ }
+
+ RefPtr<IDXGISurface> surf;
+ d2dsurf->surface->QueryInterface(&surf);
+ DXGI_SURFACE_DESC desc;
+ surf->GetDesc(&desc);
+
+ double leftMost = 0;
+ double rightMost = desc.Width;
+ double topMost = 0;
+ double bottomMost = desc.Height;
+
+ _cairo_matrix_transform_bounding_box(&invMat, &leftMost, &topMost, &rightMost, &bottomMost, NULL);
+
+ leftMost -= 1;
+ topMost -= 1;
+ rightMost += 1;
+ bottomMost += 1;
+
+ /* Calculate the offsets into the source image and the width of the part required */
+ if ((UINT32)srcSurf->width > maxSize) {
+ *x = (int)MAX(0, floor(leftMost));
+ /* Ensure that we get atleast 1 column of pixels as source, this will make EXTEND_PAD work */
+ if (*x < srcSurf->width) {
+ *width = (unsigned int)MIN(MAX(1, ceil(rightMost - *x)), srcSurf->width - *x);
+ } else {
+ *x = srcSurf->width - 1;
+ *width = 1;
+ }
+ } else {
+ *x = 0;
+ *width = srcSurf->width;
+ }
+
+ if ((UINT32)srcSurf->height > maxSize) {
+ *y = (int)MAX(0, floor(topMost));
+ /* Ensure that we get atleast 1 row of pixels as source, this will make EXTEND_PAD work */
+ if (*y < srcSurf->height) {
+ *height = (unsigned int)MIN(MAX(1, ceil(bottomMost - *y)), srcSurf->height - *y);
+ } else {
+ *y = srcSurf->height - 1;
+ *height = 1;
+ }
+ } else {
+ *y = 0;
+ *height = srcSurf->height;
+ }
+}
+
+static double
+_cairo_d2d_point_dist(const cairo_point_double_t &p1, const cairo_point_double_t &p2)
+{
+ return hypot(p2.x - p1.x, p2.y - p1.y);
+}
+
+static void
+_cairo_d2d_normalize_point(cairo_point_double_t *p)
+{
+ double length = hypot(p->x, p->y);
+ p->x /= length;
+ p->y /= length;
+}
+
+static cairo_point_double_t
+_cairo_d2d_subtract_point(const cairo_point_double_t &p1, const cairo_point_double_t &p2)
+{
+ cairo_point_double_t p = {p1.x - p2.x, p1.y - p2.y};
+ return p;
+}
+
+static double
+_cairo_d2d_dot_product(const cairo_point_double_t &p1, const cairo_point_double_t &p2)
+{
+ return p1.x * p2.x + p1.y * p2.y;
+}
+
+static RefPtr<ID2D1Brush>
+_cairo_d2d_create_radial_gradient_brush(cairo_d2d_surface_t *d2dsurf,
+ cairo_radial_pattern_t *source_pattern)
+{
+ cairo_matrix_t inv_mat = source_pattern->base.base.matrix;
+ if (_cairo_matrix_is_invertible(&inv_mat)) {
+ /* If this is not invertible it will be rank zero, and invMat = mat is fine */
+ cairo_matrix_invert(&inv_mat);
+ }
+
+ D2D1_BRUSH_PROPERTIES brushProps =
+ D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&inv_mat));
+
+ if ((source_pattern->c1.x != source_pattern->c2.x ||
+ source_pattern->c1.y != source_pattern->c2.y) &&
+ source_pattern->r1 != 0) {
+ /**
+ * In this particular case there's no way to deal with this!
+ * \todo Create an image surface with the gradient and use that.
+ */
+ return NULL;
+ }
+
+ D2D_POINT_2F center =
+ _d2d_point_from_cairo_point(&source_pattern->c2);
+ D2D_POINT_2F origin =
+ _d2d_point_from_cairo_point(&source_pattern->c1);
+ origin.x -= center.x;
+ origin.y -= center.y;
+
+ float outer_radius = _cairo_fixed_to_float(source_pattern->r2);
+ float inner_radius = _cairo_fixed_to_float(source_pattern->r1);
+ int num_stops = source_pattern->base.n_stops;
+ int repeat_count = 1;
+ D2D1_GRADIENT_STOP *stops;
+
+ if (source_pattern->base.base.extend == CAIRO_EXTEND_REPEAT || source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+ bool reflected = false;
+ bool reflect = source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT;
+
+ RefPtr<IDXGISurface> surf;
+ d2dsurf->surface->QueryInterface(&surf);
+ DXGI_SURFACE_DESC desc;
+ surf->GetDesc(&desc);
+
+ // Calculate the largest distance the origin could be from the edge after inverse
+ // transforming by the pattern transformation.
+ cairo_point_double_t top_left, top_right, bottom_left, bottom_right, gradient_center;
+ top_left.x = bottom_left.x = top_left.y = top_right.y = 0;
+ top_right.x = bottom_right.x = desc.Width;
+ bottom_right.y = bottom_left.y = desc.Height;
+
+ gradient_center.x = _cairo_fixed_to_float(source_pattern->c1.x);
+ gradient_center.y = _cairo_fixed_to_float(source_pattern->c1.y);
+
+ // Transform surface corners into pattern coordinates.
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_left.x, &top_left.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_right.x, &top_right.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_left.x, &bottom_left.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &bottom_right.y);
+
+ // Find the corner furthest away from the gradient center in pattern space.
+ double largest = MAX(_cairo_d2d_point_dist(top_left, gradient_center), _cairo_d2d_point_dist(top_right, gradient_center));
+ largest = MAX(largest, _cairo_d2d_point_dist(bottom_left, gradient_center));
+ largest = MAX(largest, _cairo_d2d_point_dist(bottom_right, gradient_center));
+
+ unsigned int minSize = (unsigned int)ceil(largest);
+
+ // Calculate how often we need to repeat on the inside (for filling the inner radius)
+ // and on the outside (for covering the entire surface) and create the appropriate number
+ // of stops.
+ float gradient_length = outer_radius - inner_radius;
+ int inner_repeat = (int)ceil(inner_radius / gradient_length);
+ int outer_repeat = (int)MAX(1, ceil((minSize - inner_radius) / gradient_length));
+ num_stops *= (inner_repeat + outer_repeat);
+ stops = new D2D1_GRADIENT_STOP[num_stops];
+
+ // Change outer_radius to the true outer radius after taking into account the needed
+ // repeats.
+ outer_radius = (inner_repeat + outer_repeat) * gradient_length;
+
+ float stop_scale = 1.0f / (inner_repeat + outer_repeat);
+
+ float inner_position = (inner_repeat * gradient_length) / outer_radius;
+ if (reflect) {
+ // We start out reflected (meaning reflected starts as false since it will
+ // immediately be inverted) if the inner_repeats are uneven.
+ reflected = !(inner_repeat & 0x1);
+
+ for (int i = 0; i < num_stops; i++) {
+ if (!(i % source_pattern->base.n_stops)) {
+ // Reflect here
+ reflected = !reflected;
+ }
+ // Calculate the repeat count.
+ int repeat = i / source_pattern->base.n_stops;
+ // Take the stop that we're using in the pattern.
+ int stop = i % source_pattern->base.n_stops;
+ if (reflected) {
+ // Take the stop from the opposite side when reflected.
+ stop = source_pattern->base.n_stops - stop - 1;
+ // When reflected take 1 - offset as the offset.
+ stops[i].position = (FLOAT)((repeat + 1.0f - source_pattern->base.stops[stop].offset) * stop_scale);
+ } else {
+ stops[i].position = (FLOAT)((repeat + source_pattern->base.stops[stop].offset) * stop_scale);
+ }
+ stops[i].color =
+ _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[stop].color);
+ }
+ } else {
+ // Simple case, we don't need to reflect.
+ for (int i = 0; i < num_stops; i++) {
+ // Calculate which repeat this is.
+ int repeat = i / source_pattern->base.n_stops;
+ // Calculate which stop this would be in the original pattern
+ cairo_gradient_stop_t *stop = &source_pattern->base.stops[i % source_pattern->base.n_stops];
+ stops[i].position = (FLOAT)((repeat + stop->offset) * stop_scale);
+ stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color);
+ }
+ }
+ } else if (source_pattern->base.base.extend == CAIRO_EXTEND_PAD) {
+ float offset_factor = (outer_radius - inner_radius) / outer_radius;
+ float global_offset = inner_radius / outer_radius;
+
+ stops = new D2D1_GRADIENT_STOP[num_stops];
+
+ // If the inner radius is not 0 we need to scale and offset the stops.
+ for (unsigned int i = 0; i < source_pattern->base.n_stops; i++) {
+ cairo_gradient_stop_t *stop = &source_pattern->base.stops[i];
+ stops[i].position = (FLOAT)(global_offset + stop->offset * offset_factor);
+ stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color);
+ }
+ } else if (source_pattern->base.base.extend == CAIRO_EXTEND_NONE) {
+ float offset_factor = (outer_radius - inner_radius) / outer_radius;
+ float global_offset = inner_radius / outer_radius;
+
+ num_stops++; // Add a stop on the outer radius.
+ if (inner_radius != 0) {
+ num_stops++; // Add a stop on the inner radius.
+ }
+
+ stops = new D2D1_GRADIENT_STOP[num_stops];
+
+ // If the inner radius is not 0 we need to scale and offset the stops and put a stop before the inner_radius
+ // of a transparent color.
+ int i = 0;
+ if (inner_radius != 0) {
+ stops[i].position = global_offset;
+ stops[i].color = D2D1::ColorF(0, 0);
+ i++;
+ }
+ for (unsigned int j = 0; j < source_pattern->base.n_stops; j++, i++) {
+ cairo_gradient_stop_t *stop = &source_pattern->base.stops[j];
+ stops[i].position = (FLOAT)(global_offset + stop->offset * offset_factor);
+ stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color);
+ }
+ stops[i].position = 1.0f;
+ stops[i].color = D2D1::ColorF(0, 0);
+ } else {
+ return NULL;
+ }
+
+ RefPtr<ID2D1GradientStopCollection> stopCollection;
+ d2dsurf->rt->CreateGradientStopCollection(stops, num_stops, &stopCollection);
+ RefPtr<ID2D1RadialGradientBrush> brush;
+
+ d2dsurf->rt->CreateRadialGradientBrush(D2D1::RadialGradientBrushProperties(center,
+ origin,
+ outer_radius,
+ outer_radius),
+ brushProps,
+ stopCollection,
+ &brush);
+ delete [] stops;
+ return brush;
+}
+
+static RefPtr<ID2D1Brush>
+_cairo_d2d_create_linear_gradient_brush(cairo_d2d_surface_t *d2dsurf,
+ cairo_path_fixed_t *fill_path,
+ cairo_linear_pattern_t *source_pattern)
+{
+ if (source_pattern->p1.x == source_pattern->p2.x &&
+ source_pattern->p1.y == source_pattern->p2.y) {
+ // Cairo behavior in this situation is to draw a solid color the size of the last stop.
+ RefPtr<ID2D1SolidColorBrush> brush;
+ d2dsurf->rt->CreateSolidColorBrush(
+ _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[source_pattern->base.n_stops - 1].color),
+ &brush);
+ return brush;
+ }
+
+ cairo_matrix_t inv_mat = source_pattern->base.base.matrix;
+ /**
+ * Cairo views this matrix as the transformation of the destination
+ * when the pattern is imposed. We see this differently, D2D transformation
+ * moves the pattern over the destination.
+ */
+ if (_cairo_matrix_is_invertible(&inv_mat)) {
+ /* If this is not invertible it will be rank zero, and invMat = mat is fine */
+ cairo_matrix_invert(&inv_mat);
+ }
+ D2D1_BRUSH_PROPERTIES brushProps =
+ D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&inv_mat));
+ cairo_point_double_t p1, p2;
+ p1.x = _cairo_fixed_to_float(source_pattern->p1.x);
+ p1.y = _cairo_fixed_to_float(source_pattern->p1.y);
+ p2.x = _cairo_fixed_to_float(source_pattern->p2.x);
+ p2.y = _cairo_fixed_to_float(source_pattern->p2.y);
+
+ D2D1_GRADIENT_STOP *stops;
+ int num_stops = source_pattern->base.n_stops;
+ if (source_pattern->base.base.extend == CAIRO_EXTEND_REPEAT || source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+ // Get this when the points are not transformed yet.
+ double gradient_length = _cairo_d2d_point_dist(p1, p2);
+ cairo_point_double_t top_left, top_right, bottom_left, bottom_right;
+
+ if (fill_path) {
+ // Calculate the repeat count needed;
+ cairo_box_t fill_extents;
+ _cairo_path_fixed_extents (fill_path, &fill_extents);
+
+ top_left.x = bottom_left.x = _cairo_fixed_to_double (fill_extents.p1.x);
+ top_left.y = top_right.y = _cairo_fixed_to_double (fill_extents.p1.y);
+ top_right.x = bottom_right.x = _cairo_fixed_to_double (fill_extents.p2.x);
+ bottom_right.y = bottom_left.y = _cairo_fixed_to_double (fill_extents.p2.y);
+ } else {
+ RefPtr<IDXGISurface> surf;
+ d2dsurf->surface->QueryInterface(&surf);
+ DXGI_SURFACE_DESC desc;
+ surf->GetDesc(&desc);
+
+ top_left.x = bottom_left.x = 0;
+ top_left.y = top_right.y = 0;
+ top_right.x = bottom_right.x = desc.Width;
+ bottom_right.y = bottom_left.y = desc.Height;
+ }
+
+ // Transform the corners of our surface to pattern space.
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_left.x, &top_left.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_right.x, &top_right.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_left.x, &bottom_left.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &bottom_right.y);
+
+ cairo_point_double_t u;
+ // Unit vector of the gradient direction.
+ u = _cairo_d2d_subtract_point(p2, p1);
+ _cairo_d2d_normalize_point(&u);
+
+ // (corner - p1) . u = |corner - p1| cos(a) where a is the angle between the two vectors.
+ // Coincidentally |corner - p1| cos(a) is actually also the distance our gradient needs to cover since
+ // at this point on the gradient line it will be perpendicular to the line running from the gradient
+ // line through the corner.
+
+ double max_dist, min_dist;
+ max_dist = MAX(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)),
+ _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1)));
+ max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1)));
+ max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1)));
+ min_dist = MIN(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)),
+ _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1)));
+ min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1)));
+ min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1)));
+
+ min_dist = MAX(-min_dist, 0);
+
+ // Repeats after gradient start.
+ // It's possible for max_dist and min_dist to both be zero, in which case
+ // we'll set num_stops to 0 and crash D2D. Let's just ensure after_repeat
+ // is at least 1.
+ int after_repeat = MAX((int)ceil(max_dist / gradient_length), 1);
+ int before_repeat = (int)ceil(min_dist / gradient_length);
+ num_stops *= (after_repeat + before_repeat);
+
+ p2.x = p1.x + u.x * after_repeat * gradient_length;
+ p2.y = p1.y + u.y * after_repeat * gradient_length;
+ p1.x = p1.x - u.x * before_repeat * gradient_length;
+ p1.y = p1.y - u.y * before_repeat * gradient_length;
+
+ float stop_scale = 1.0f / (float)(after_repeat + before_repeat);
+ float begin_position = (float)before_repeat / (float)(after_repeat + before_repeat);
+
+ stops = new D2D1_GRADIENT_STOP[num_stops];
+ if (source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+ // We start out reflected (meaning reflected starts as false since it will
+ // immediately be inverted) if the inner_repeats are uneven.
+ bool reflected = !(before_repeat & 0x1);
+
+ for (int i = 0; i < num_stops; i++) {
+ if (!(i % source_pattern->base.n_stops)) {
+ // Reflect here
+ reflected = !reflected;
+ }
+ // Calculate the repeat count.
+ int repeat = i / source_pattern->base.n_stops;
+ // Take the stop that we're using in the pattern.
+ int stop = i % source_pattern->base.n_stops;
+ if (reflected) {
+ // Take the stop from the opposite side when reflected.
+ stop = source_pattern->base.n_stops - stop - 1;
+ // When reflected take 1 - offset as the offset.
+ stops[i].position = (FLOAT)((repeat + 1.0f - source_pattern->base.stops[stop].offset) * stop_scale);
+ } else {
+ stops[i].position = (FLOAT)((repeat + source_pattern->base.stops[stop].offset) * stop_scale);
+ }
+ stops[i].color =
+ _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[stop].color);
+ }
+ } else {
+ // Simple case, we don't need to reflect.
+ for (int i = 0; i < num_stops; i++) {
+ // Calculate which repeat this is.
+ int repeat = i / source_pattern->base.n_stops;
+ // Calculate which stop this would be in the original pattern
+ cairo_gradient_stop_t *stop = &source_pattern->base.stops[i % source_pattern->base.n_stops];
+ stops[i].position = (FLOAT)((repeat + stop->offset) * stop_scale);
+ stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color);
+ }
+ }
+ } else if (source_pattern->base.base.extend == CAIRO_EXTEND_PAD) {
+ stops = new D2D1_GRADIENT_STOP[source_pattern->base.n_stops];
+ for (unsigned int i = 0; i < source_pattern->base.n_stops; i++) {
+ cairo_gradient_stop_t *stop = &source_pattern->base.stops[i];
+ stops[i].position = (FLOAT)stop->offset;
+ stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color);
+ }
+ } else if (source_pattern->base.base.extend == CAIRO_EXTEND_NONE) {
+ num_stops += 2;
+ stops = new D2D1_GRADIENT_STOP[num_stops];
+ stops[0].position = 0;
+ stops[0].color = D2D1::ColorF(0, 0);
+ for (unsigned int i = 1; i < source_pattern->base.n_stops + 1; i++) {
+ cairo_gradient_stop_t *stop = &source_pattern->base.stops[i - 1];
+ stops[i].position = (FLOAT)stop->offset;
+ stops[i].color = _cairo_d2d_color_from_cairo_color_stop(stop->color);
+ }
+ stops[source_pattern->base.n_stops + 1].position = 1.0f;
+ stops[source_pattern->base.n_stops + 1].color = D2D1::ColorF(0, 0);
+ }
+ RefPtr<ID2D1GradientStopCollection> stopCollection;
+ d2dsurf->rt->CreateGradientStopCollection(stops, num_stops, &stopCollection);
+ RefPtr<ID2D1LinearGradientBrush> brush;
+ d2dsurf->rt->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2D1::Point2F((FLOAT)p1.x, (FLOAT)p1.y),
+ D2D1::Point2F((FLOAT)p2.x, (FLOAT)p2.y)),
+ brushProps,
+ stopCollection,
+ &brush);
+ delete [] stops;
+ return brush;
+}
+
+/**
+ * This creates an ID2D1Brush that will fill with the correct pattern.
+ * This function passes a -strong- reference to the caller, the brush
+ * needs to be released, even if it is not unique.
+ *
+ * \param d2dsurf Surface to create a brush for
+ * \param pattern The pattern to create a brush for
+ * \param unique We cache the bitmap/color brush for speed. If this
+ * needs a brush that is unique (i.e. when more than one is needed),
+ * this will make the function return a seperate brush.
+ * \return A brush object
+ */
+static RefPtr<ID2D1Brush>
+_cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
+ cairo_path_fixed_t *fill_path,
+ const cairo_pattern_t *pattern,
+ bool unique = false)
+{
+ HRESULT hr;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *sourcePattern =
+ (cairo_solid_pattern_t*)pattern;
+ D2D1_COLOR_F color = _cairo_d2d_color_from_cairo_color(sourcePattern->color);
+ if (unique) {
+ RefPtr<ID2D1SolidColorBrush> brush;
+ d2dsurf->rt->CreateSolidColorBrush(color,
+ &brush);
+ return brush;
+ } else {
+ if (d2dsurf->solidColorBrush->GetColor().a != color.a ||
+ d2dsurf->solidColorBrush->GetColor().r != color.r ||
+ d2dsurf->solidColorBrush->GetColor().g != color.g ||
+ d2dsurf->solidColorBrush->GetColor().b != color.b) {
+ d2dsurf->solidColorBrush->SetColor(color);
+ }
+ return d2dsurf->solidColorBrush;
+ }
+
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *source_pattern =
+ (cairo_linear_pattern_t*)pattern;
+ return _cairo_d2d_create_linear_gradient_brush(d2dsurf, fill_path, source_pattern);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ cairo_radial_pattern_t *source_pattern =
+ (cairo_radial_pattern_t*)pattern;
+ return _cairo_d2d_create_radial_gradient_brush(d2dsurf, source_pattern);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_matrix_t mat = pattern->matrix;
+ cairo_matrix_invert(&mat);
+
+ cairo_surface_pattern_t *surfacePattern =
+ (cairo_surface_pattern_t*)pattern;
+ D2D1_EXTEND_MODE extendMode;
+
+ cairo_user_data_key_t *key = &bitmap_key_extend;
+
+ if (pattern->extend == CAIRO_EXTEND_NONE) {
+ extendMode = D2D1_EXTEND_MODE_CLAMP;
+ key = &bitmap_key_nonextend;
+ /**
+ * We create a slightly larger bitmap with a transparent border
+ * around it for this case. Need to translate for that.
+ */
+ cairo_matrix_translate(&mat, -1.0, -1.0);
+ } else if (pattern->extend == CAIRO_EXTEND_REPEAT) {
+ extendMode = D2D1_EXTEND_MODE_WRAP;
+ } else if (pattern->extend == CAIRO_EXTEND_REFLECT) {
+ extendMode = D2D1_EXTEND_MODE_MIRROR;
+ } else {
+ extendMode = D2D1_EXTEND_MODE_CLAMP;
+ }
+
+ RefPtr<ID2D1Bitmap> sourceBitmap;
+ bool partial = false;
+ int xoffset = 0;
+ int yoffset = 0;
+ unsigned int width;
+ unsigned int height;
+ unsigned char *data = NULL;
+ unsigned int stride = 0;
+
+ if (surfacePattern->surface->type == CAIRO_SURFACE_TYPE_D2D) {
+ /**
+ * \todo We need to somehow get a rectangular transparent
+ * border here too!!
+ */
+ cairo_d2d_surface_t *srcSurf =
+ reinterpret_cast<cairo_d2d_surface_t*>(surfacePattern->surface);
+
+ if (srcSurf == d2dsurf) {
+ /* D2D cannot deal with self-copy. We should add an optimized
+ * codepath for self-copy in the easy cases that does ping-pong like
+ * scroll does. See bug 579215. For now fallback.
+ */
+ return NULL;
+ }
+ if (srcSurf->device != d2dsurf->device) {
+ /* This code does not work if the source surface does not use
+ * the same device. Some work could be done to do something
+ * fairly efficient here, for now, fallback.
+ */
+ return NULL;
+ }
+
+ _cairo_d2d_update_surface_bitmap(srcSurf);
+ _cairo_d2d_flush(srcSurf);
+
+ // Mark a dependency on the source surface.
+ _cairo_d2d_add_dependent_surface(srcSurf, d2dsurf);
+
+ if (pattern->extend == CAIRO_EXTEND_NONE) {
+ ID2D1Bitmap *srcSurfBitmap = srcSurf->surfaceBitmap;
+ d2dsurf->rt->CreateBitmap(
+ D2D1::SizeU(srcSurfBitmap->GetPixelSize().width + 2,
+ srcSurfBitmap->GetPixelSize().height + 2),
+ D2D1::BitmapProperties(srcSurfBitmap->GetPixelFormat()),
+ &sourceBitmap);
+ D2D1_POINT_2U point = D2D1::Point2U(1, 1);
+ sourceBitmap->CopyFromBitmap(&point, srcSurfBitmap, NULL);
+ } else {
+ sourceBitmap = srcSurf->surfaceBitmap;
+ }
+
+ } else if (surfacePattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ cairo_image_surface_t *srcSurf =
+ reinterpret_cast<cairo_image_surface_t*>(surfacePattern->surface);
+ D2D1_ALPHA_MODE alpha;
+ if (srcSurf->format == CAIRO_FORMAT_ARGB32 ||
+ srcSurf->format == CAIRO_FORMAT_A8) {
+ alpha = D2D1_ALPHA_MODE_PREMULTIPLIED;
+ } else {
+ alpha = D2D1_ALPHA_MODE_IGNORE;
+ }
+
+ data = srcSurf->data;
+ stride = srcSurf->stride;
+
+ /* This is used as a temporary surface for resampling surfaces larget than maxSize. */
+ pixman_image_t *pix_image = NULL;
+
+ DXGI_FORMAT format;
+ unsigned int Bpp;
+ if (srcSurf->format == CAIRO_FORMAT_ARGB32) {
+ format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ Bpp = 4;
+ } else if (srcSurf->format == CAIRO_FORMAT_RGB24) {
+ format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ Bpp = 4;
+ } else if (srcSurf->format == CAIRO_FORMAT_A8) {
+ format = DXGI_FORMAT_A8_UNORM;
+ Bpp = 1;
+ } else {
+ return NULL;
+ }
+
+ /** Leave room for extend_none space, 2 pixels */
+ UINT32 maxSize = d2dsurf->rt->GetMaximumBitmapSize() - 2;
+
+ if ((UINT32)srcSurf->width > maxSize || (UINT32)srcSurf->height > maxSize) {
+ if (pattern->extend == CAIRO_EXTEND_REPEAT ||
+ pattern->extend == CAIRO_EXTEND_REFLECT) {
+ // XXX - we don't have code to deal with these yet.
+ return NULL;
+ }
+
+ /* We cannot fit this image directly into a texture, start doing tricks to draw correctly anyway. */
+ partial = true;
+
+ /* First we check which part of the image is inside the viewable area. */
+ _cairo_d2d_calculate_visible_rect(d2dsurf, srcSurf, &mat, &xoffset, &yoffset, &width, &height);
+
+ cairo_matrix_translate(&mat, xoffset, yoffset);
+
+ if (width > maxSize || height > maxSize) {
+ /*
+ * We cannot upload the required part of the surface directly, we're going to create
+ * a version which is downsampled to a smaller size by pixman and then uploaded.
+ *
+ * We need to size it to at least the diagonal size of this surface, in order to prevent ever
+ * upsampling this again when drawing it to the surface. We want the resized surface
+ * to be as small as possible to limit pixman required fill rate.
+ *
+ * Note this isn't necessarily perfect. Imagine having a 5x5 pixel destination and
+ * a 10x5 image containing a line of blackpixels, white pixels, black pixels, if you rotate
+ * this by 45 degrees and scale it to a size of 5x5 pixels and composite it to the destination,
+ * the composition will require all 10 original columns to do the best possible sampling.
+ */
+ RefPtr<IDXGISurface> surf;
+ d2dsurf->surface->QueryInterface(&surf);
+ DXGI_SURFACE_DESC desc;
+ surf->GetDesc(&desc);
+
+ unsigned int minSize = (unsigned int)ceil(sqrt(pow((float)desc.Width, 2) + pow((float)desc.Height, 2)));
+
+ unsigned int newWidth = MIN(minSize, MIN(width, maxSize));
+ unsigned int newHeight = MIN(minSize, MIN(height, maxSize));
+ double xRatio = (double)width / newWidth;
+ double yRatio = (double)height / newHeight;
+
+ if (newWidth > maxSize || newHeight > maxSize) {
+ /*
+ * Okay, the diagonal of our surface is big enough to require a sampling larger
+ * than the maximum texture size. This is where we give up.
+ */
+ return NULL;
+ }
+
+ /* Create a temporary surface to hold the downsampled image */
+ pix_image = pixman_image_create_bits(srcSurf->pixman_format,
+ newWidth,
+ newHeight,
+ NULL,
+ -1);
+
+ /* Set the transformation to downsample and call pixman_image_composite to downsample */
+ pixman_transform_t transform;
+ pixman_transform_init_scale(&transform, pixman_double_to_fixed(xRatio), pixman_double_to_fixed(yRatio));
+ pixman_transform_translate(&transform, NULL, pixman_int_to_fixed(xoffset), pixman_int_to_fixed(yoffset));
+
+ pixman_image_set_transform(srcSurf->pixman_image, &transform);
+ pixman_image_composite(PIXMAN_OP_SRC, srcSurf->pixman_image, NULL, pix_image, 0, 0, 0, 0, 0, 0, newWidth, newHeight);
+
+ /* Adjust the pattern transform to the used temporary surface */
+ cairo_matrix_scale(&mat, xRatio, yRatio);
+
+ data = (unsigned char*)pixman_image_get_data(pix_image);
+ stride = pixman_image_get_stride(pix_image);
+
+ /* Into this image we actually have no offset */
+ xoffset = 0;
+ yoffset = 0;
+ width = newWidth;
+ height = newHeight;
+ }
+ } else {
+ width = srcSurf->width;
+ height = srcSurf->height;
+ }
+
+ cached_bitmap *cachebitmap = NULL;
+
+ if (!partial) {
+ cachebitmap =
+ (cached_bitmap*)cairo_surface_get_user_data(
+ surfacePattern->surface,
+ key);
+ if (cachebitmap && cachebitmap->device != d2dsurf->device) {
+ cachebitmap = NULL;
+ }
+ }
+
+ if (cachebitmap) {
+ sourceBitmap = cachebitmap->bitmap;
+ if (cachebitmap->dirty) {
+ D2D1_RECT_U rect;
+ /* No need to take partial uploading into account - partially uploaded surfaces are never cached. */
+ if (pattern->extend == CAIRO_EXTEND_NONE) {
+ rect = D2D1::RectU(1, 1, srcSurf->width + 1, srcSurf->height + 1);
+ } else {
+ rect = D2D1::RectU(0, 0, srcSurf->width, srcSurf->height);
+ }
+ sourceBitmap->CopyFromMemory(&rect,
+ srcSurf->data,
+ srcSurf->stride);
+ cairo_surface_t *nullSurf =
+ cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
+ cachebitmap->refs++;
+ cachebitmap->dirty = false;
+ cairo_surface_set_user_data(nullSurf,
+ &bitmap_key_snapshot,
+ cachebitmap,
+ NULL);
+ cairo_surface_attach_snapshot(surfacePattern->surface,
+ nullSurf,
+ _d2d_snapshot_detached);
+ }
+ } else {
+ if (pattern->extend != CAIRO_EXTEND_NONE) {
+ hr = d2dsurf->rt->CreateBitmap(D2D1::SizeU(width, height),
+ data + yoffset * stride + xoffset * Bpp,
+ stride,
+ D2D1::BitmapProperties(D2D1::PixelFormat(format,
+ alpha)),
+ &sourceBitmap);
+
+ if (FAILED(hr)) {
+ return NULL;
+ }
+ } else {
+ /**
+ * Trick here, we create a temporary rectangular
+ * surface with 1 pixel margin on each side. This
+ * provides a rectangular transparent border, that
+ * will ensure CLAMP acts as EXTEND_NONE. Perhaps
+ * this could be further optimized by not memsetting
+ * the whole array.
+ */
+ unsigned int tmpWidth = width + 2;
+ unsigned int tmpHeight = height + 2;
+ unsigned char *tmp = new unsigned char[tmpWidth * tmpHeight * Bpp];
+ memset(tmp, 0, tmpWidth * tmpHeight * Bpp);
+ for (unsigned int y = 0; y < height; y++) {
+ memcpy(
+ tmp + tmpWidth * Bpp * y + tmpWidth * Bpp + Bpp,
+ data + yoffset * stride + y * stride + xoffset * Bpp,
+ width * Bpp);
+ }
+
+ hr = d2dsurf->rt->CreateBitmap(D2D1::SizeU(tmpWidth, tmpHeight),
+ tmp,
+ tmpWidth * Bpp,
+ D2D1::BitmapProperties(D2D1::PixelFormat(format,
+ D2D1_ALPHA_MODE_PREMULTIPLIED)),
+ &sourceBitmap);
+
+ delete [] tmp;
+ if (FAILED(hr)) {
+ return NULL;
+ }
+ }
+
+ if (!partial) {
+ cached_bitmap *cachebitmap = new cached_bitmap;
+ /* We can cache it if it isn't a partial bitmap */
+ cachebitmap->dirty = false;
+ cachebitmap->bitmap = sourceBitmap;
+ cachebitmap->device = d2dsurf->device;
+ /*
+ * This will start out with two references, one on the snapshot
+ * and one more in the user data structure.
+ */
+ cachebitmap->refs = 2;
+ cairo_surface_set_user_data(surfacePattern->surface,
+ key,
+ cachebitmap,
+ _d2d_release_bitmap);
+ cairo_surface_t *nullSurf =
+ cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
+ cairo_surface_set_user_data(nullSurf,
+ &bitmap_key_snapshot,
+ cachebitmap,
+ NULL);
+ cairo_surface_attach_snapshot(surfacePattern->surface,
+ nullSurf,
+ _d2d_snapshot_detached);
+ cache_usage += _d2d_compute_bitmap_mem_size(sourceBitmap);
+ }
+ if (pix_image) {
+ pixman_image_unref(pix_image);
+ }
+ }
+ } else {
+ return NULL;
+ }
+ D2D1_BITMAP_BRUSH_PROPERTIES bitProps;
+
+ if (surfacePattern->base.filter == CAIRO_FILTER_NEAREST) {
+ bitProps = D2D1::BitmapBrushProperties(extendMode,
+ extendMode,
+ D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
+ } else {
+ bitProps = D2D1::BitmapBrushProperties(extendMode,
+ extendMode,
+ D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
+ }
+ if (unique) {
+ RefPtr<ID2D1BitmapBrush> bitBrush;
+ D2D1_BRUSH_PROPERTIES brushProps =
+ D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&mat));
+ d2dsurf->rt->CreateBitmapBrush(sourceBitmap,
+ &bitProps,
+ &brushProps,
+ &bitBrush);
+ return bitBrush;
+ } else {
+ D2D1_MATRIX_3X2_F matrix = _cairo_d2d_matrix_from_matrix(&mat);
+
+ if (d2dsurf->bitmapBrush) {
+ d2dsurf->bitmapBrush->SetTransform(matrix);
+
+ if (surfacePattern->base.filter == CAIRO_FILTER_NEAREST) {
+ d2dsurf->bitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
+ } else {
+ d2dsurf->bitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
+ }
+
+ d2dsurf->bitmapBrush->SetBitmap(sourceBitmap);
+ d2dsurf->bitmapBrush->SetExtendModeX(extendMode);
+ d2dsurf->bitmapBrush->SetExtendModeY(extendMode);
+ } else {
+ D2D1_BRUSH_PROPERTIES brushProps =
+ D2D1::BrushProperties(1.0, _cairo_d2d_matrix_from_matrix(&mat));
+ d2dsurf->rt->CreateBitmapBrush(sourceBitmap,
+ &bitProps,
+ &brushProps,
+ &d2dsurf->bitmapBrush);
+ }
+ return d2dsurf->bitmapBrush;
+ }
+ } else {
+ return NULL;
+ }
+}
+
+
+/** Path Conversion */
+
+/**
+ * Structure to use for the closure, containing all needed data.
+ */
+struct path_conversion {
+ /** Geometry sink that we need to write to */
+ ID2D1GeometrySink *sink;
+ /**
+ * If this figure is active, cairo doesn't always send us a close. But
+ * we do need to end this figure if it didn't.
+ */
+ bool figureActive;
+ /**
+ * Current point, D2D has no explicit move so we need to track moved for
+ * the next begin.
+ */
+ cairo_point_t current_point;
+ /** The type of figure begin for this geometry instance */
+ D2D1_FIGURE_BEGIN type;
+};
+
+static cairo_status_t
+_cairo_d2d_path_move_to(void *closure,
+ const cairo_point_t *point)
+{
+ path_conversion *pathConvert =
+ static_cast<path_conversion*>(closure);
+ if (pathConvert->figureActive) {
+ pathConvert->sink->EndFigure(D2D1_FIGURE_END_OPEN);
+ pathConvert->figureActive = false;
+ }
+
+ pathConvert->current_point = *point;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_d2d_path_line_to(void *closure,
+ const cairo_point_t *point)
+{
+ path_conversion *pathConvert =
+ static_cast<path_conversion*>(closure);
+ if (!pathConvert->figureActive) {
+ pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point),
+ pathConvert->type);
+ pathConvert->figureActive = true;
+ }
+
+ D2D1_POINT_2F d2dpoint = _d2d_point_from_cairo_point(point);
+
+ pathConvert->sink->AddLine(d2dpoint);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_d2d_path_curve_to(void *closure,
+ const cairo_point_t *p0,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2)
+{
+ path_conversion *pathConvert =
+ static_cast<path_conversion*>(closure);
+ if (!pathConvert->figureActive) {
+ pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point),
+ D2D1_FIGURE_BEGIN_FILLED);
+ pathConvert->figureActive = true;
+ }
+
+ pathConvert->sink->AddBezier(D2D1::BezierSegment(_d2d_point_from_cairo_point(p0),
+ _d2d_point_from_cairo_point(p1),
+ _d2d_point_from_cairo_point(p2)));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_d2d_path_close(void *closure)
+{
+ path_conversion *pathConvert =
+ static_cast<path_conversion*>(closure);
+
+ if (!pathConvert->figureActive) {
+ pathConvert->sink->BeginFigure(_d2d_point_from_cairo_point(&pathConvert->current_point),
+ pathConvert->type);
+ /**
+ * In this case we mean a single point. For D2D this means we need to add an infinitely
+ * small line here to get that effect.
+ */
+ pathConvert->sink->AddLine(_d2d_point_from_cairo_point(&pathConvert->current_point));
+ }
+
+ pathConvert->sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+ pathConvert->figureActive = false;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * Create an ID2D1PathGeometry for a cairo_path_fixed_t
+ *
+ * \param path Path to create a geometry for
+ * \param fill_rule Fill rule to use
+ * \param type Figure begin type to use
+ * \return A D2D geometry
+ */
+static RefPtr<ID2D1PathGeometry>
+_cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ D2D1_FIGURE_BEGIN type)
+{
+ RefPtr<ID2D1PathGeometry> d2dpath;
+ sD2DFactory->CreatePathGeometry(&d2dpath);
+ RefPtr<ID2D1GeometrySink> sink;
+ d2dpath->Open(&sink);
+ D2D1_FILL_MODE fillMode = D2D1_FILL_MODE_WINDING;
+ if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+ fillMode = D2D1_FILL_MODE_WINDING;
+ } else if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD) {
+ fillMode = D2D1_FILL_MODE_ALTERNATE;
+ }
+ sink->SetFillMode(fillMode);
+
+ path_conversion pathConvert;
+ pathConvert.type = type;
+ pathConvert.sink = sink;
+ pathConvert.figureActive = false;
+ _cairo_path_fixed_interpret(path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_d2d_path_move_to,
+ _cairo_d2d_path_line_to,
+ _cairo_d2d_path_curve_to,
+ _cairo_d2d_path_close,
+ &pathConvert);
+ if (pathConvert.figureActive) {
+ sink->EndFigure(D2D1_FIGURE_END_OPEN);
+ }
+ sink->Close();
+ return d2dpath;
+}
+
+static cairo_bool_t
+clip_contains_only_boxes (cairo_clip_t *clip)
+{
+ cairo_bool_t is_boxes = TRUE;
+
+ if (clip) {
+ cairo_box_t clip_box;
+ cairo_clip_path_t *path = clip->path;
+
+ while (path) {
+ is_boxes &= _cairo_path_fixed_is_box(&path->path, &clip_box);
+ path = path->prev;
+ }
+ }
+ return is_boxes;
+}
+
+static cairo_int_status_t
+_cairo_d2d_clear_box (cairo_d2d_surface_t *d2dsurf,
+ cairo_clip_t *clip,
+ cairo_box_t *box)
+{
+ if (clip_contains_only_boxes (clip)) {
+ /* clear the box using axis aligned clips */
+ d2dsurf->rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(box->p1.x),
+ _cairo_fixed_to_float(box->p1.y),
+ _cairo_fixed_to_float(box->p2.x),
+ _cairo_fixed_to_float(box->p2.y)),
+ D2D1_ANTIALIAS_MODE_ALIASED);
+ d2dsurf->rt->Clear(D2D1::ColorF(0, 0));
+ d2dsurf->rt->PopAxisAlignedClip();
+
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_d2d_clear (cairo_d2d_surface_t *d2dsurf,
+ cairo_clip_t *clip)
+{
+ cairo_region_t *region;
+ cairo_int_status_t status;
+
+ if (!clip) {
+ /* no clip so clear everything */
+ _begin_draw_state(d2dsurf);
+ reset_clip(d2dsurf);
+ d2dsurf->rt->Clear(D2D1::ColorF(0, 0));
+
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ status = _cairo_clip_get_region (clip, &region);
+ if (status)
+ return status;
+
+ /* We now have a region, we'll clear it one rectangle at a time */
+ _begin_draw_state(d2dsurf);
+
+ reset_clip(d2dsurf);
+
+ if (region) {
+ int num_rects;
+ int i;
+
+ 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);
+
+ d2dsurf->rt->PushAxisAlignedClip(
+ D2D1::RectF((FLOAT)rect.x,
+ (FLOAT)rect.y,
+ (FLOAT)rect.x + rect.width,
+ (FLOAT)rect.y + rect.height),
+ D2D1_ANTIALIAS_MODE_ALIASED);
+
+ d2dsurf->rt->Clear(D2D1::ColorF(0, 0));
+
+ d2dsurf->rt->PopAxisAlignedClip();
+ }
+
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op,
+ const cairo_pattern_t *source)
+{
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ /** Operator over is easier for D2D! If the source if opaque, change */
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ const cairo_surface_pattern_t *surfpattern =
+ reinterpret_cast<const cairo_surface_pattern_t*>(source);
+ if (surfpattern->surface->content == CAIRO_CONTENT_COLOR) {
+ return CAIRO_OPERATOR_OVER;
+ }
+ } else if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ const cairo_solid_pattern_t *solidpattern =
+ reinterpret_cast<const cairo_solid_pattern_t*>(source);
+ if (solidpattern->color.alpha == 1.0) {
+ return CAIRO_OPERATOR_OVER;
+ }
+ }
+ }
+ return op;
+}
+
+void
+_cairo_d2d_surface_init(cairo_d2d_surface_t *newSurf, cairo_d2d_device_t *d2d_device, cairo_format_t format)
+{
+ newSurf->format = format;
+
+ newSurf->device = d2d_device;
+ cairo_addref_device(&d2d_device->base);
+ d2d_device->mVRAMUsage += _cairo_d2d_compute_surface_mem_size(newSurf);
+}
+
+_cairo_d2d_surface::~_cairo_d2d_surface()
+{
+ _cairo_d2d_surface_entry *entry, *next;
+ cairo_list_foreach_entry_safe(entry, next, _cairo_d2d_surface_entry, &dependent_surfaces, link) {
+ // We do not need to flush, the contents of our texture has not changed,
+ // our users have their own reference and can just use it later.
+ cairo_surface_destroy(&entry->surface->base);
+ delete entry;
+ }
+
+}
+
+// Implementation
+static cairo_surface_t*
+_cairo_d2d_create_similar(void *surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+ cairo_d2d_surface_t *newSurf = static_cast<cairo_d2d_surface_t*>(malloc(sizeof(cairo_d2d_surface_t)));
+
+ new (newSurf) cairo_d2d_surface_t();
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content);
+
+
+ D2D1_SIZE_U sizePixels;
+ D2D1_SIZE_F size;
+ HRESULT hr;
+
+ sizePixels.width = width;
+ sizePixels.height = height;
+ FLOAT dpiX;
+ FLOAT dpiY;
+
+ d2dsurf->rt->GetDpi(&dpiX, &dpiY);
+
+ D2D1_ALPHA_MODE alpha;
+
+ if (content == CAIRO_CONTENT_COLOR) {
+ alpha = D2D1_ALPHA_MODE_IGNORE;
+ } else {
+ alpha = D2D1_ALPHA_MODE_PREMULTIPLIED;
+ }
+
+ size.width = sizePixels.width * dpiX;
+ size.height = sizePixels.height * dpiY;
+ D2D1_BITMAP_PROPERTIES bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN,
+ alpha));
+
+ if (sizePixels.width < 1) {
+ sizePixels.width = 1;
+ }
+ if (sizePixels.height < 1) {
+ sizePixels.height = 1;
+ }
+ RefPtr<IDXGISurface> oldDxgiSurface;
+ d2dsurf->surface->QueryInterface(&oldDxgiSurface);
+ DXGI_SURFACE_DESC origDesc;
+
+ oldDxgiSurface->GetDesc(&origDesc);
+
+ CD3D10_TEXTURE2D_DESC desc(origDesc.Format,
+ sizePixels.width,
+ sizePixels.height);
+
+ if (content == CAIRO_CONTENT_ALPHA) {
+ desc.Format = DXGI_FORMAT_A8_UNORM;
+ }
+
+ desc.MipLevels = 1;
+ desc.Usage = D3D10_USAGE_DEFAULT;
+ desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+
+ /* CreateTexture2D does not support D3D10_RESOURCE_MISC_GDI_COMPATIBLE with DXGI_FORMAT_A8_UNORM */
+ if (desc.Format != DXGI_FORMAT_A8_UNORM)
+ desc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE;
+
+ RefPtr<ID3D10Texture2D> texture;
+ RefPtr<IDXGISurface> dxgiSurface;
+
+ D2D1_RENDER_TARGET_USAGE usage = (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) ?
+ D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE
+ : D2D1_RENDER_TARGET_USAGE_NONE;
+
+ hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture);
+ if (FAILED(hr)) {
+ goto FAIL_CREATESIMILAR;
+ }
+
+ newSurf->surface = texture;
+
+ // Create the DXGI surface.
+ hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface);
+ if (FAILED(hr)) {
+ goto FAIL_CREATESIMILAR;
+ }
+
+ hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface,
+ D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN,
+ alpha),
+ dpiX,
+ dpiY,
+ usage),
+ &newSurf->rt);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATESIMILAR;
+ }
+
+ if (desc.Format != DXGI_FORMAT_A8_UNORM) {
+ /* For some reason creation of shared bitmaps for A8 UNORM surfaces
+ * doesn't work even though the documentation suggests it does. The
+ * function will return an error if we try */
+ hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface,
+ dxgiSurface,
+ &bitProps,
+ &newSurf->surfaceBitmap);
+ if (FAILED(hr)) {
+ goto FAIL_CREATESIMILAR;
+ }
+ }
+
+ newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush);
+
+ _d2d_clear_surface(newSurf);
+
+ _cairo_d2d_surface_init(newSurf, d2dsurf->device, _cairo_format_from_content(content));
+
+ return reinterpret_cast<cairo_surface_t*>(newSurf);
+
+FAIL_CREATESIMILAR:
+ /** Ensure we call our surfaces desctructor */
+ newSurf->~cairo_d2d_surface_t();
+ free(newSurf);
+ return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY));
+}
+
+static cairo_status_t
+_cairo_d2d_finish(void *surface)
+{
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+
+ d2dsurf->device->mVRAMUsage -= _cairo_d2d_compute_surface_mem_size(d2dsurf);
+ if (d2dsurf->bufferTexture) {
+ d2dsurf->device->mVRAMUsage -= _cairo_d2d_compute_surface_mem_size(d2dsurf);
+ }
+
+ reset_clip(d2dsurf);
+
+ // We need to release the device after calling the constructor, since the
+ // device destruction may release the D3D/D2D libraries.
+ cairo_device_t *device = &d2dsurf->device->base;
+ d2dsurf->~cairo_d2d_surface_t();
+ cairo_release_device(device);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* The input types for src and dst don't match because in our particular use case, copying from a texture,
+ * those types don't match. */
+static void
+_copy_data_to_different_stride(unsigned char *dst, int dst_stride, void *src, UINT src_stride, int height)
+{
+
+ unsigned char *src_p = (unsigned char *)src;
+ int min_stride = MIN(dst_stride, src_stride);
+ while (height) {
+ memcpy(dst, src_p, min_stride);
+ height--;
+ dst += dst_stride;
+ src_p += src_stride;
+ }
+}
+
+static cairo_status_t
+_cairo_d2d_acquire_source_image(void *abstract_surface,
+ cairo_image_surface_t **image_out_ret,
+ void **image_extra)
+{
+ cairo_surface_t *image_out;
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(abstract_surface);
+ _cairo_d2d_flush(d2dsurf);
+
+ HRESULT hr;
+ D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize();
+
+ RefPtr<ID3D10Texture2D> softTexture;
+
+ RefPtr<IDXGISurface> dxgiSurface;
+ d2dsurf->surface->QueryInterface(&dxgiSurface);
+ DXGI_SURFACE_DESC desc;
+
+ dxgiSurface->GetDesc(&desc);
+
+ CD3D10_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height);
+
+ /**
+ * We can't actually map our backing store texture, so we create one in CPU memory, and then
+ * tell D3D to copy the data from our surface there, readback is expensive, we -never-
+ * -ever- want this to happen.
+ */
+ softDesc.MipLevels = 1;
+ softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ;
+ softDesc.Usage = D3D10_USAGE_STAGING;
+ softDesc.BindFlags = 0;
+ hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture);
+ if (FAILED(hr)) {
+ return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+ }
+
+ d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface);
+
+ D3D10_MAPPED_TEXTURE2D data;
+ hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data);
+ if (FAILED(hr)) {
+ return _cairo_error(CAIRO_STATUS_NO_DEVICE);
+ }
+
+ if (_cairo_valid_stride_alignment(data.RowPitch)) {
+ image_out = cairo_image_surface_create_for_data((unsigned char*)data.pData,
+ d2dsurf->format,
+ size.width,
+ size.height,
+ data.RowPitch);
+ } else {
+ /* Slow path used when the stride doesn't match our requirements.
+ * This is possible on at least the Intel driver 8.15.10.2302.
+ *
+ * Create a new image surface and copy our data into it */
+ image_out = cairo_image_surface_create(d2dsurf->format,
+ size.width,
+ size.height);
+ _copy_data_to_different_stride(cairo_image_surface_get_data(image_out),
+ cairo_image_surface_get_stride(image_out),
+ data.pData,
+ data.RowPitch,
+ size.height);
+
+ }
+ /* these are the only surface statuses we expect */
+ assert(cairo_surface_status(image_out) == CAIRO_STATUS_SUCCESS ||
+ cairo_surface_status(image_out) == CAIRO_STATUS_NO_MEMORY);
+
+ *image_extra = softTexture.forget().drop();
+ *image_out_ret = (cairo_image_surface_t*)image_out;
+
+ return cairo_surface_status(image_out);
+}
+
+static void
+_cairo_d2d_release_source_image(void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ if (((cairo_surface_t*)abstract_surface)->type != CAIRO_SURFACE_TYPE_D2D) {
+ return;
+ }
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(abstract_surface);
+
+ cairo_surface_destroy(&image->base);
+ ID3D10Texture2D *softTexture = (ID3D10Texture2D*)image_extra;
+
+ softTexture->Unmap(0);
+ softTexture->Release();
+ softTexture = NULL;
+}
+
+static cairo_status_t
+_cairo_d2d_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_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(abstract_surface);
+ _cairo_d2d_flush(d2dsurf);
+
+ HRESULT hr;
+ D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize();
+
+ RefPtr<ID3D10Texture2D> softTexture;
+
+
+ RefPtr<IDXGISurface> dxgiSurface;
+ d2dsurf->surface->QueryInterface(&dxgiSurface);
+ DXGI_SURFACE_DESC desc;
+
+ dxgiSurface->GetDesc(&desc);
+
+ CD3D10_TEXTURE2D_DESC softDesc(desc.Format, desc.Width, desc.Height);
+
+ image_rect->width = desc.Width;
+ image_rect->height = desc.Height;
+ image_rect->x = image_rect->y = 0;
+
+ softDesc.MipLevels = 1;
+ softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ;
+ softDesc.Usage = D3D10_USAGE_STAGING;
+ softDesc.BindFlags = 0;
+ hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture);
+ if (FAILED(hr)) {
+ return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+ }
+ d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface);
+
+ D3D10_MAPPED_TEXTURE2D data;
+ hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data);
+ if (FAILED(hr)) {
+ return _cairo_error(CAIRO_STATUS_NO_DEVICE);
+ }
+ *image_out =
+ (cairo_image_surface_t*)cairo_image_surface_create_for_data((unsigned char*)data.pData,
+ _cairo_format_from_content(d2dsurf->base.content),
+ size.width,
+ size.height,
+ data.RowPitch);
+ *image_extra = softTexture.forget().drop();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_d2d_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_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(abstract_surface);
+
+ ID3D10Texture2D *softTexture = (ID3D10Texture2D*)image_extra;
+ D2D1_POINT_2U point;
+ point.x = 0;
+ point.y = 0;
+ D2D1_RECT_U rect;
+ D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize();
+ rect.left = rect.top = 0;
+ rect.right = size.width;
+ rect.bottom = size.height;
+
+ cairo_surface_destroy(&image->base);
+
+ softTexture->Unmap(0);
+ d2dsurf->device->mD3D10Device->CopyResource(d2dsurf->surface, softTexture);
+ softTexture->Release();
+}
+
+
+static cairo_status_t
+_cairo_d2d_flush(void *surface)
+{
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+
+ if (d2dsurf->isDrawing) {
+ reset_clip(d2dsurf);
+ HRESULT hr = d2dsurf->rt->EndDraw();
+ d2dsurf->isDrawing = false;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_d2d_copy_surface(cairo_d2d_surface_t *dst,
+ cairo_d2d_surface_t *src,
+ cairo_point_int_t *translation,
+ cairo_region_t *region)
+{
+ RefPtr<IDXGISurface> dstSurface;
+ dst->surface->QueryInterface(&dstSurface);
+ RefPtr<IDXGISurface> srcSurface;
+ src->surface->QueryInterface(&srcSurface);
+ DXGI_SURFACE_DESC srcDesc, dstDesc;
+
+ srcSurface->GetDesc(&srcDesc);
+ dstSurface->GetDesc(&dstDesc);
+
+ cairo_rectangle_int_t clip_rect;
+ clip_rect.x = 0;
+ clip_rect.y = 0;
+ clip_rect.width = dstDesc.Width;
+ clip_rect.height = dstDesc.Height;
+
+ cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS;
+
+ _cairo_d2d_flush(dst);
+ ID3D10Resource *srcResource = src->surface;
+ if (src->surface.get() == dst->surface.get()) {
+ // Self-copy
+ srcResource = _cairo_d2d_get_buffer_texture(dst);
+ src->device->mD3D10Device->CopyResource(srcResource, src->surface);
+ } else {
+ // Need to flush the source too if it's a different surface.
+ _cairo_d2d_flush(src);
+ }
+
+ // One copy for each rectangle in the final clipping region.
+ for (int i = 0; i < cairo_region_num_rectangles(region); i++) {
+ D3D10_BOX rect;
+ cairo_rectangle_int_t area_to_copy;
+
+ cairo_region_get_rectangle(region, i, &area_to_copy);
+
+ cairo_rectangle_int_t transformed_rect = { area_to_copy.x + translation->x,
+ area_to_copy.y + translation->y,
+ area_to_copy.width, area_to_copy.height };
+ cairo_rectangle_int_t surface_rect = { 0, 0, srcDesc.Width, srcDesc.Height };
+
+
+ if (!_cairo_rectangle_contains(&surface_rect, &transformed_rect)) {
+ /* We cannot do any sort of extend, in the future a little bit of extra code could
+ * allow us to support EXTEND_NONE.
+ */
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ break;
+ }
+
+ rect.front = 0;
+ rect.back = 1;
+ rect.left = transformed_rect.x;
+ rect.top = transformed_rect.y;
+ rect.right = transformed_rect.x + transformed_rect.width;
+ rect.bottom = transformed_rect.y + transformed_rect.height;
+
+ src->device->mD3D10Device->CopySubresourceRegion(dst->surface,
+ 0,
+ area_to_copy.x,
+ area_to_copy.y,
+ 0,
+ srcResource,
+ 0,
+ &rect);
+ }
+
+ return rv;
+}
+
+static cairo_int_status_t
+_cairo_d2d_blend_surface(cairo_d2d_surface_t *dst,
+ cairo_d2d_surface_t *src,
+ const cairo_matrix_t *transform,
+ cairo_box_t *box,
+ cairo_clip_t *clip,
+ cairo_filter_t filter,
+ float opacity)
+{
+ if (dst == src) {
+ // We cannot do self-blend.
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS;
+
+ _begin_draw_state(dst);
+ _cairo_d2d_set_clip(dst, clip);
+ _cairo_d2d_flush(src);
+ D2D1_SIZE_U sourceSize = src->surfaceBitmap->GetPixelSize();
+
+
+ double x1, x2, y1, y2;
+ if (box) {
+ _cairo_box_to_doubles(box, &x1, &y1, &x2, &y2);
+ } else {
+ x1 = y1 = 0;
+ x2 = dst->rt->GetSize().width;
+ y2 = dst->rt->GetSize().height;
+ }
+
+ if (clip) {
+ const cairo_rectangle_int_t *clipExtent = _cairo_clip_get_extents(clip);
+ x1 = MAX(x1, clipExtent->x);
+ x2 = MIN(x2, clipExtent->x + clipExtent->width);
+ y1 = MAX(y1, clipExtent->y);
+ y2 = MIN(y2, clipExtent->y + clipExtent->height);
+ }
+
+ // We should be in drawing state for this.
+ _begin_draw_state(dst);
+ _cairo_d2d_set_clip (dst, clip);
+ D2D1_RECT_F rectSrc;
+ rectSrc.left = (float)(x1 * transform->xx + transform->x0);
+ rectSrc.top = (float)(y1 * transform->yy + transform->y0);
+ rectSrc.right = (float)(x2 * transform->xx + transform->x0);
+ rectSrc.bottom = (float)(y2 * transform->yy + transform->y0);
+
+ if (rectSrc.left < 0 || rectSrc.top < 0 || rectSrc.right < 0 || rectSrc.bottom < 0 ||
+ rectSrc.right > sourceSize.width || rectSrc.bottom > sourceSize.height ||
+ rectSrc.left > sourceSize.width || rectSrc.top > sourceSize.height) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ D2D1_RECT_F rectDst;
+ rectDst.left = (float)x1;
+ rectDst.top = (float)y1;
+ rectDst.right = (float)x2;
+ rectDst.bottom = (float)y2;
+
+ // Bug 599658 - if the src rect is inverted in either axis D2D is fine with
+ // this but it does not actually invert the bitmap. This is an easy way
+ // of doing that.
+ D2D1_MATRIX_3X2_F matrix = D2D1::IdentityMatrix();
+ bool needsTransform = false;
+ if (rectSrc.left > rectSrc.right) {
+ rectDst.left = -rectDst.left;
+ rectDst.right = -rectDst.right;
+ matrix._11 = -1.0;
+ needsTransform = true;
+ }
+ if (rectSrc.top > rectSrc.bottom) {
+ rectDst.top = -rectDst.top;
+ rectDst.bottom = -rectDst.bottom;
+ matrix._22 = -1.0;
+ needsTransform = true;
+ }
+
+ _cairo_d2d_add_dependent_surface(src, dst);
+
+ D2D1_BITMAP_INTERPOLATION_MODE interpMode =
+ D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
+
+ if (needsTransform) {
+ dst->rt->SetTransform(matrix);
+ }
+
+ if (filter == CAIRO_FILTER_NEAREST) {
+ interpMode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
+ }
+
+ dst->rt->DrawBitmap(src->surfaceBitmap,
+ rectDst,
+ opacity,
+ interpMode,
+ rectSrc);
+ if (needsTransform) {
+ dst->rt->SetTransform(D2D1::IdentityMatrix());
+ }
+
+ return rv;
+}
+/**
+ * This function will text if we can use GPU mem cpy to execute an operation with
+ * a surface pattern. If box is NULL it will operate on the entire dst surface.
+ */
+static cairo_int_status_t
+_cairo_d2d_try_fastblit(cairo_d2d_surface_t *dst,
+ cairo_surface_t *src,
+ cairo_box_t *box,
+ const cairo_matrix_t *matrix,
+ cairo_clip_t *clip,
+ cairo_operator_t op,
+ cairo_filter_t filter,
+ float opacity = 1.0f)
+{
+ if (op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR) {
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+ if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* For now we do only D2D sources */
+ if (src->type != CAIRO_SURFACE_TYPE_D2D) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ cairo_d2d_surface_t *d2dsrc = reinterpret_cast<cairo_d2d_surface_t*>(src);
+ if (op == CAIRO_OPERATOR_OVER && matrix->xy == 0 && matrix->yx == 0) {
+ return _cairo_d2d_blend_surface(dst, d2dsrc, matrix, box, clip, filter, opacity);
+ }
+
+ if (op == CAIRO_OPERATOR_OVER || opacity != 1.0f) {
+ // Past this point we will never get into a situation where we can
+ // support OVER.
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ cairo_point_int_t translation;
+ if ((box && !box_is_integer(box)) ||
+ !_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ cairo_rectangle_int_t rect;
+ if (box) {
+ _cairo_box_round_to_rectangle(box, &rect);
+ } else {
+ rect.x = rect.y = 0;
+ rect.width = dst->rt->GetPixelSize().width;
+ rect.height = dst->rt->GetPixelSize().height;
+ }
+
+ if (d2dsrc->device != dst->device) {
+ // This doesn't work between different devices.
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* Region we need to clip this operation to */
+ cairo_region_t *clipping_region = NULL;
+ cairo_region_t *region;
+ cairo_region_auto_ptr region_ptr;
+
+ if (clip) {
+ _cairo_clip_get_region(clip, &clipping_region);
+
+ if (!clipping_region) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ region = cairo_region_copy(clipping_region);
+ region_ptr.set(region);
+
+ cairo_region_intersect_rectangle(region, &rect);
+
+ if (cairo_region_is_empty(region)) {
+ // Nothing to do.
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+ } else {
+ region = cairo_region_create_rectangle(&rect);
+ region_ptr.set(region);
+
+ // Areas outside of the surface do not matter.
+ cairo_rectangle_int_t surface_rect = { 0, 0,
+ dst->rt->GetPixelSize().width,
+ dst->rt->GetPixelSize().height };
+ cairo_region_intersect_rectangle(region, &surface_rect);
+ }
+
+ cairo_int_status_t rv = _cairo_d2d_copy_surface(dst, d2dsrc, &translation, region);
+
+ return rv;
+}
+
+static RefPtr<ID2D1RenderTarget>
+_cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip)
+{
+ RefPtr<ID3D10Texture2D> texture = _cairo_d2d_get_buffer_texture(surf);
+ RefPtr<ID2D1RenderTarget> new_rt;
+ RefPtr<IDXGISurface> dxgiSurface;
+ texture->QueryInterface(&dxgiSurface);
+ HRESULT hr;
+
+ _cairo_d2d_flush(surf);
+
+ if (!surf) {
+ return NULL;
+ }
+
+ D2D1_RENDER_TARGET_PROPERTIES props =
+ D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
+ hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface,
+ props,
+ &new_rt);
+
+ if (FAILED(hr)) {
+ return NULL;
+ }
+
+ new_rt->BeginDraw();
+ new_rt->Clear(D2D1::ColorF(0, 0));
+
+ // Since this is a fresh surface there's no point in doing clever things to
+ // keep the clip path around until a certain depth. So we just do a straight-
+ // forward push of all clip paths in the tree, similar to what the normal
+ // clip code does, but a little less clever.
+ if (clip) {
+ cairo_clip_path_t *path = clip->path;
+ while (path) {
+ cairo_box_t clip_box;
+ if (_cairo_path_fixed_is_box(&path->path, &clip_box)) {
+ // If this does not have a region it could be none-pixel aligned.
+ D2D1_ANTIALIAS_MODE aaMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
+ if (box_is_integer(&clip_box)) {
+ aaMode = D2D1_ANTIALIAS_MODE_ALIASED;
+ }
+ new_rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(clip_box.p1.x),
+ _cairo_fixed_to_float(clip_box.p1.y),
+ _cairo_fixed_to_float(clip_box.p2.x),
+ _cairo_fixed_to_float(clip_box.p2.y)),
+ aaMode);
+ } else {
+ HRESULT hr;
+ RefPtr<ID2D1PathGeometry> geom = _cairo_d2d_create_path_geometry_for_path (&path->path,
+ path->fill_rule,
+ D2D1_FIGURE_BEGIN_FILLED);
+ RefPtr<ID2D1Layer> layer;
+
+ hr = new_rt->CreateLayer (&layer);
+
+ D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
+
+ new_rt->PushLayer(D2D1::LayerParameters(
+ D2D1::InfiniteRect(),
+ geom,
+ D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+ D2D1::IdentityMatrix(),
+ 1.0,
+ 0,
+ options),
+ layer);
+ }
+ path = path->prev;
+ }
+ }
+ return new_rt;
+}
+
+static cairo_int_status_t
+_cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds = NULL)
+{
+ _cairo_d2d_flush_dependent_surfaces(surf);
+
+ int numPaths = 0;
+ if (clip) {
+ cairo_clip_path_t *path = clip->path;
+ while (path) {
+ numPaths++;
+ path = path->prev;
+ }
+
+ cairo_clip_path_t **paths = new cairo_clip_path_t*[numPaths];
+
+ numPaths = 0;
+ path = clip->path;
+ while (path) {
+ paths[numPaths++] = path;
+ path = path->prev;
+ }
+
+ for (int i = numPaths - 1; i >= 0; i--) {
+ if (paths[i]->flags & CAIRO_CLIP_PATH_IS_BOX) {
+ rt->PopAxisAlignedClip();
+ } else {
+ rt->PopLayer();
+ }
+ }
+ delete [] paths;
+ }
+ rt->EndDraw();
+ HRESULT hr;
+
+ RefPtr<ID3D10Texture2D> srcTexture = _cairo_d2d_get_buffer_texture(surf);
+ RefPtr<ID3D10Texture2D> dstTexture;
+
+ surf->surface->QueryInterface(&dstTexture);
+ ID3D10Device *device = surf->device->mD3D10Device;
+
+ if (!surf->buffer_rt_view) {
+ hr = device->CreateRenderTargetView(dstTexture, NULL, &surf->buffer_rt_view);
+ if (FAILED(hr)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ if (!surf->buffer_sr_view) {
+ hr = device->CreateShaderResourceView(srcTexture, NULL, &surf->buffer_sr_view);
+ if (FAILED(hr)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ cairo_int_status_t status;
+
+ status = _cairo_d2d_set_operator(surf->device, op);
+
+ if (unlikely(status)) {
+ return status;
+ }
+
+ D3D10_TEXTURE2D_DESC tDesc;
+ dstTexture->GetDesc(&tDesc);
+ D3D10_VIEWPORT vp;
+ vp.Height = tDesc.Height;
+ vp.MinDepth = 0;
+ vp.MaxDepth = 1.0;
+ vp.TopLeftX = 0;
+ vp.TopLeftY = 0;
+ vp.Width = tDesc.Width;
+ device->RSSetViewports(1, &vp);
+
+ ID3D10Effect *effect = surf->device->mSampleEffect;
+
+ ID3D10RenderTargetView *rtViewPtr = surf->buffer_rt_view;
+ device->OMSetRenderTargets(1, &rtViewPtr, 0);
+ ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector();
+ ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector();
+
+ float quadDescVal[] = { -1.0f, 1.0f, 2.0f, -2.0f };
+ float texCoordsVal[] = { 0.0, 0.0, 1.0f, 1.0f };
+ if (bounds && _cairo_operator_bounded_by_mask(op)) {
+ quadDescVal[0] = -1.0f + ((float)bounds->x / (float)tDesc.Width) * 2.0f;
+ quadDescVal[1] = 1.0f - ((float)bounds->y / (float)tDesc.Height) * 2.0f;
+ quadDescVal[2] = ((float)bounds->width / (float)tDesc.Width) * 2.0f;
+ quadDescVal[3] = -((float)bounds->height / (float)tDesc.Height) * 2.0f;
+ texCoordsVal[0] = (float)bounds->x / (float)tDesc.Width;
+ texCoordsVal[1] = (float)bounds->y / (float)tDesc.Height;
+ texCoordsVal[2] = (float)bounds->width / (float)tDesc.Width;
+ texCoordsVal[3] = (float)bounds->height / (float)tDesc.Height;
+ }
+ quadDesc->SetFloatVector(quadDescVal);
+ texCoords->SetFloatVector(texCoordsVal);
+
+ _cairo_d2d_setup_for_blend(surf->device);
+ ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTexture");
+ technique->GetPassByIndex(0)->Apply(0);
+
+ ID3D10ShaderResourceView *srViewPtr = surf->buffer_sr_view;
+ device->PSSetShaderResources(0, 1, &srViewPtr);
+
+ device->Draw(4, 0);
+
+#ifdef DEBUG
+ // Quiet down some info messages from D3D10 debug layer
+ srViewPtr = NULL;
+ device->PSSetShaderResources(0, 1, &srViewPtr);
+ rtViewPtr = NULL;
+ device->OMSetRenderTargets(1, &rtViewPtr, 0);
+#endif
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_d2d_paint(void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+ cairo_int_status_t status;
+
+ op = _cairo_d2d_simplify_operator(op, source);
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ if (!clip) {
+ _cairo_d2d_clear(d2dsurf, NULL);
+ op = CAIRO_OPERATOR_OVER;
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ return _cairo_d2d_clear(d2dsurf, clip);
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ const cairo_surface_pattern_t *surf_pattern =
+ reinterpret_cast<const cairo_surface_pattern_t*>(source);
+
+ status = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface,
+ NULL, &source->matrix, clip,
+ op, source->filter);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ return status;
+ }
+ }
+ RefPtr<ID2D1RenderTarget> target_rt = d2dsurf->rt;
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ if (op != CAIRO_OPERATOR_OVER) {
+#endif
+ target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip);
+ if (!target_rt) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ } else {
+ _begin_draw_state(d2dsurf);
+ status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip);
+
+ if (unlikely(status))
+ return status;
+ }
+#endif
+
+ target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+
+ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL,
+ source);
+
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ D2D1_SIZE_F size = target_rt->GetSize();
+ target_rt->FillRectangle(D2D1::RectF((FLOAT)0,
+ (FLOAT)0,
+ (FLOAT)size.width,
+ (FLOAT)size.height),
+ brush);
+
+ if (target_rt.get() != d2dsurf->rt.get()) {
+ return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_d2d_mask(void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+ cairo_rectangle_int_t extents;
+
+ cairo_clip_t *actual_clip = clip;
+ cairo_clip_t temporary_clip;
+
+ cairo_int_status_t status;
+
+ status = (cairo_int_status_t)_cairo_surface_mask_extents (&d2dsurf->base,
+ op, source,
+ mask,
+ clip, &extents);
+ if (unlikely (status))
+ return status;
+
+ bool isSolidAlphaMask = false;
+ float solidAlphaValue = 1.0f;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solidPattern =
+ (cairo_solid_pattern_t*)mask;
+ if (_cairo_color_get_content (&solidPattern->color) == CAIRO_CONTENT_ALPHA) {
+ isSolidAlphaMask = true;
+ solidAlphaValue = solidPattern->color.alpha;
+ }
+ }
+
+ cairo_box_t box;
+ _cairo_box_from_rectangle(&box, &extents);
+
+ if (clip && isSolidAlphaMask) {
+ // We do some work here to try and avoid pushing and popping clips for rectangular areas,
+ // if we do this fill rects will occur without rectangular clips being pushed and popped.
+ // This is faster for non-axis aligned clips in general and allows more efficient batching
+ // of the pop-clip calls.
+ int num_boxes = 1;
+ cairo_box_t box_stack;
+ cairo_box_t *boxes;
+ boxes = &box_stack;
+
+ // This function assumes atleast a single box resides at 'boxes' and the
+ // amount of boxes that reside there are passed in under num_boxes.
+ status = _cairo_clip_get_boxes(clip, &boxes, &num_boxes);
+
+ if (!status && num_boxes == 1) {
+ box.p1.x = MAX(box.p1.x, boxes->p1.x);
+ box.p2.x = MIN(box.p2.x, boxes->p2.x);
+ box.p1.y = MAX(box.p1.y, boxes->p1.y);
+ box.p2.y = MIN(box.p2.y, boxes->p2.y);
+
+ if (clip->path != d2dsurf->clip.path) {
+ // If we have a clip set, but it's not the right one. We want to
+ // pop as much as we need to, to be sure the area affected by
+ // the operation is not clipped. To do this we set the clip path
+ // to the common ancestor of the currently set clip path and the
+ // clip path for this operation. This will cause
+ // _cairo_d2d_set_clip to pop to that common ancestor, but not
+ // needlessly push the additional clips we're trying to avoid.
+ temporary_clip.path = find_common_ancestor(clip->path, d2dsurf->clip.path);
+
+ // We're not going to be using this down the line so it doesn't
+ // really matter what the value is. If all -was- clipped this
+ // call shouldn't even have reached the surface backend.
+ temporary_clip.all_clipped = FALSE;
+
+ actual_clip = &temporary_clip;
+ }
+ }
+
+ if (boxes != &box_stack) {
+ // If the function changed the boxes pointer, we need to free it.
+ free(boxes);
+ }
+ }
+
+ if (isSolidAlphaMask) {
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ const cairo_surface_pattern_t *surf_pattern =
+ reinterpret_cast<const cairo_surface_pattern_t*>(source);
+ cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf,
+ surf_pattern->surface,
+ &box,
+ &source->matrix,
+ clip,
+ op,
+ source->filter,
+ solidAlphaValue);
+ if (rv != CAIRO_INT_STATUS_UNSUPPORTED) {
+ return rv;
+ }
+ }
+ }
+
+ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, source);
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ RefPtr<ID2D1RenderTarget> target_rt = d2dsurf->rt;
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ if (op != CAIRO_OPERATOR_OVER) {
+#endif
+ target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip);
+ if (!target_rt) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ } else {
+ _begin_draw_state(d2dsurf);
+
+ status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, actual_clip);
+ if (unlikely(status))
+ return status;
+ }
+#endif
+
+ D2D1_RECT_F rect = D2D1::RectF(_cairo_fixed_to_float(box.p1.x),
+ _cairo_fixed_to_float(box.p1.y),
+ _cairo_fixed_to_float(box.p2.x),
+ _cairo_fixed_to_float(box.p2.y));
+
+ if (isSolidAlphaMask) {
+ brush->SetOpacity(solidAlphaValue);
+ target_rt->FillRectangle(rect,
+ brush);
+ brush->SetOpacity(1.0);
+
+ if (target_rt.get() != d2dsurf->rt.get()) {
+ return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip);
+ }
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ RefPtr<ID2D1Brush> opacityBrush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, mask, true);
+ if (!opacityBrush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (!d2dsurf->maskLayer) {
+ d2dsurf->rt->CreateLayer(&d2dsurf->maskLayer);
+ }
+ target_rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(),
+ 0,
+ D2D1_ANTIALIAS_MODE_ALIASED,
+ D2D1::IdentityMatrix(),
+ 1.0,
+ opacityBrush),
+ d2dsurf->maskLayer);
+
+ target_rt->FillRectangle(rect,
+ brush);
+ target_rt->PopLayer();
+
+ if (target_rt.get() != d2dsurf->rt.get()) {
+ return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip);
+ }
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_d2d_stroke(void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+
+ op = _cairo_d2d_simplify_operator(op, source);
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ RefPtr<ID2D1RenderTarget> target_rt = d2dsurf->rt;
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ if (op != CAIRO_OPERATOR_OVER) {
+#endif
+ target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip);
+ if (!target_rt) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ } else {
+ _begin_draw_state(d2dsurf);
+ status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip);
+
+ if (unlikely(status))
+ return status;
+ }
+#endif
+
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+ } else {
+ target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ }
+ RefPtr<ID2D1StrokeStyle> strokeStyle = _cairo_d2d_create_strokestyle_for_stroke_style(style);
+
+ if (!strokeStyle) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ RefPtr<ID2D1Geometry> d2dpath = _cairo_d2d_create_path_geometry_for_path(path,
+ CAIRO_FILL_RULE_WINDING,
+ D2D1_FIGURE_BEGIN_FILLED);
+
+ bool transformed = true;
+
+ if (_cairo_matrix_is_identity(ctm)) {
+ transformed = false;
+ }
+
+ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL,
+ source);
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ D2D1::Matrix3x2F mat;
+ if (transformed) {
+ // If we are transformed we will draw the geometry multiplied by the
+ // inverse transformation and apply the transform to our render target.
+ // This way the transformation will also be applied to the strokestyle.
+ mat = _cairo_d2d_matrix_from_matrix(ctm);
+ D2D1::Matrix3x2F inverse_mat = _cairo_d2d_invert_matrix(mat);
+
+ RefPtr<ID2D1TransformedGeometry> trans_geom;
+ sD2DFactory->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom);
+
+ // If we are setting a transform on the render target, we've multiplied
+ // the geometry by the inverse transform, we should also multiply the
+ // brush matrix by this inverse transform then to map the brush to the
+ // correct place.
+ D2D1_MATRIX_3X2_F brushMatrix;
+ brush->GetTransform(&brushMatrix);
+ brushMatrix = brushMatrix * inverse_mat;
+ brush->SetTransform(brushMatrix);
+ target_rt->SetTransform(mat);
+ d2dpath = trans_geom;
+ } else {
+ mat = D2D1::Matrix3x2F::Identity();
+ }
+
+ target_rt->DrawGeometry(d2dpath, brush, (FLOAT)style->line_width, strokeStyle);
+
+ if (transformed) {
+ target_rt->SetTransform(D2D1::Matrix3x2F::Identity());
+ }
+
+ if (target_rt.get() != d2dsurf->rt.get()) {
+ D2D1_RECT_F bounds;
+ d2dpath->GetWidenedBounds((FLOAT)style->line_width, strokeStyle, mat, &bounds);
+ cairo_rectangle_int_t bound_rect;
+ _cairo_d2d_round_out_to_int_rect(&bound_rect, bounds.left, bounds.top, bounds.right, bounds.bottom);
+ return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bound_rect);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_d2d_fill(void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+ cairo_box_t box;
+ bool is_box = _cairo_path_fixed_is_box(path, &box);
+
+ if (is_box && source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ const cairo_surface_pattern_t *surf_pattern =
+ reinterpret_cast<const cairo_surface_pattern_t*>(source);
+ cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface,
+ &box, &source->matrix, clip, op,
+ source->filter);
+
+ if (rv != CAIRO_INT_STATUS_UNSUPPORTED) {
+ return rv;
+ }
+ }
+
+ op = _cairo_d2d_simplify_operator(op, source);
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ if (_cairo_path_fixed_is_box(path, &box)) {
+ _begin_draw_state(d2dsurf);
+ status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip);
+
+ if (unlikely(status))
+ return status;
+
+ return _cairo_d2d_clear_box (d2dsurf, clip, &box);
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ RefPtr<ID2D1RenderTarget> target_rt = d2dsurf->rt;
+
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ if (op != CAIRO_OPERATOR_OVER) {
+#endif
+ target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip);
+ if (!target_rt) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ } else {
+ _begin_draw_state(d2dsurf);
+ status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip);
+
+ if (unlikely(status))
+ return status;
+ }
+#endif
+
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+ } else {
+ target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+ }
+
+ if (is_box) {
+ float x1 = _cairo_fixed_to_float(box.p1.x);
+ float y1 = _cairo_fixed_to_float(box.p1.y);
+ float x2 = _cairo_fixed_to_float(box.p2.x);
+ float y2 = _cairo_fixed_to_float(box.p2.y);
+ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf,
+ path, source);
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ target_rt->FillRectangle(D2D1::RectF(x1,
+ y1,
+ x2,
+ y2),
+ brush);
+ } else {
+ RefPtr<ID2D1Geometry> d2dpath = _cairo_d2d_create_path_geometry_for_path(path, fill_rule, D2D1_FIGURE_BEGIN_FILLED);
+
+ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf,
+ path, source);
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ target_rt->FillGeometry(d2dpath, brush);
+ }
+
+ if (target_rt.get() != d2dsurf->rt.get()) {
+ double x1, y1, x2, y2;
+ cairo_box_t box;
+ _cairo_path_fixed_extents (path, &box);
+ 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);
+ cairo_rectangle_int_t bounds;
+ _cairo_d2d_round_out_to_int_rect(&bounds, x1, y1, x2, y2);
+ return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bounds);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_dwrite_manual_show_glyphs_on_d2d_surface(void *surface,
+ cairo_operator_t op,
+ const cairo_solid_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_dwrite_scaled_font_t *scaled_font,
+ cairo_clip_t *clip)
+{
+ cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast<cairo_dwrite_scaled_font_t*>(scaled_font);
+ if (!dwritesf->manual_show_glyphs_allowed) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ cairo_dwrite_font_face_t *dwriteff = reinterpret_cast<cairo_dwrite_font_face_t*>(scaled_font->base.font_face);
+ cairo_d2d_surface_t *dst = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+
+ BOOL transform = FALSE;
+ HRESULT hr;
+
+ cairo_region_t *clip_region = NULL;
+
+ // We can only draw axis and pixel aligned rectangular quads, this means we
+ // can only support clips which form regions, since the intersection with
+ // our text area will then always be a set of rectangular axis and pixel
+ // aligned quads.
+ if (clip) {
+ _cairo_clip_get_region(clip, &clip_region);
+ if (!clip_region) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ if (!dst->isDrawing) {
+ _cairo_d2d_flush_dependent_surfaces(dst);
+ }
+
+ _cairo_d2d_set_clip(dst, NULL);
+ dst->rt->Flush();
+
+ AutoDWriteGlyphRun run;
+ _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, scaled_font, &run, &transform);
+
+ RefPtr<IDWriteGlyphRunAnalysis> analysis;
+ DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat);
+
+ RefPtr<IDWriteRenderingParams> params;
+ dst->rt->GetTextRenderingParams(&params);
+
+ DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT;
+ if (params) {
+ hr = dwriteff->dwriteface->GetRecommendedRenderingMode(
+ (FLOAT)scaled_font->base.font_matrix.yy,
+ 1.0f,
+ DWRITE_MEASURING_MODE_NATURAL,
+ params,
+ &renderMode);
+ if (FAILED(hr)) {
+ // this probably never happens, but let's play it safe
+ renderMode = DWRITE_RENDERING_MODE_DEFAULT;
+ }
+ }
+
+ // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept.
+ switch (renderMode) {
+ case DWRITE_RENDERING_MODE_ALIASED:
+ // ClearType texture creation will fail in this mode, so bail out
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ case DWRITE_RENDERING_MODE_DEFAULT:
+ // As per DWRITE_RENDERING_MODE documentation, pick Natural for font
+ // sizes under 16 ppem
+ if (scaled_font->base.font_matrix.yy < 16.0f) {
+ renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
+ } else {
+ renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+ }
+ break;
+ case DWRITE_RENDERING_MODE_OUTLINE:
+ renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+ break;
+ default:
+ break;
+ }
+
+ DWRITE_MEASURING_MODE measureMode =
+ renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC :
+ renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL :
+ DWRITE_MEASURING_MODE_NATURAL;
+
+ hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run,
+ 1.0f,
+ transform ? &dwmat : 0,
+ renderMode,
+ measureMode,
+ 0,
+ 0,
+ &analysis);
+ if (FAILED(hr)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ RECT bounds;
+ hr = analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1,
+ &bounds);
+ if (FAILED(hr) ||
+ // with bitmap sizes of asian fonts, GetAlphaTextureBounds returns
+ // an empty rect, so we need to detect that and fall back
+ (bounds.top == 0 && bounds.bottom == 0 && num_glyphs > 0))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ cairo_rectangle_int_t cairo_bounds =
+ _cairo_rect_from_windows_rect(&bounds);
+
+ cairo_region_t *region;
+ if (clip) {
+ region = cairo_region_copy(clip_region);
+
+ cairo_region_intersect_rectangle(region, &cairo_bounds);
+ } else {
+ region = cairo_region_create_rectangle(&cairo_bounds);
+ }
+
+ cairo_region_auto_ptr region_ptr(region);
+
+ if (cairo_region_is_empty(region)) {
+ // Nothing to do.
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ int bufferSize = cairo_bounds.width * cairo_bounds.height * 3;
+
+ if (!bufferSize) {
+ // width == 0 || height == 0
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ // We add one byte so we can safely read an entire 32-bit int when copying
+ // the last 3 bytes of the alpha texture.
+ BYTE *texture = new BYTE[bufferSize + 1];
+ hr = analysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1,
+ &bounds, texture, bufferSize);
+ if (FAILED(hr)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ RefPtr<ID3D10ShaderResourceView> srView;
+ ID3D10Device1 *device = dst->device->mD3D10Device;
+
+ int textureWidth, textureHeight;
+
+ if (cairo_bounds.width < TEXT_TEXTURE_WIDTH &&
+ cairo_bounds.height < TEXT_TEXTURE_HEIGHT)
+ {
+ // Use our cached TextTexture when it is big enough.
+ RefPtr<ID3D10Texture2D> tmpTexture;
+ CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ cairo_bounds.width, cairo_bounds.height,
+ 1, 1, 0);
+
+ desc.Usage = D3D10_USAGE_STAGING;
+ desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
+
+ hr = device->CreateTexture2D(&desc, NULL, &tmpTexture);
+
+ D3D10_MAPPED_TEXTURE2D texMap;
+ hr = tmpTexture->Map(0, D3D10_MAP_WRITE, 0, &texMap);
+
+ if (FAILED(hr)) {
+ delete [] texture;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ BYTE *alignedTextureData = (BYTE*)texMap.pData;
+ for (int y = 0; y < cairo_bounds.height; y++) {
+ for (int x = 0; x < cairo_bounds.width; x++) {
+ // Copy 3 Bpp source to 4 Bpp texture.
+ //
+ // Since we don't care what ends up in the alpha pixel of the
+ // destination, therefor we can simply copy a normal 32 bit
+ // integer each time, filling the alpha pixel of the destination
+ // with the first subpixel of the next pixel from the source.
+ *((int*)(alignedTextureData + (y * texMap.RowPitch) + x * 4)) =
+ *((int*)(texture + (y * cairo_bounds.width + x) * 3));
+ }
+ }
+
+ tmpTexture->Unmap(0);
+
+ delete [] texture;
+
+ D3D10_BOX box;
+ box.front = box.top = box.left = 0;
+ box.back = 1;
+ box.right = cairo_bounds.width;
+ box.bottom = cairo_bounds.height;
+
+ device->CopySubresourceRegion(dst->device->mTextTexture, 0, 0, 0, 0, tmpTexture, 0, &box);
+
+ srView = dst->device->mTextTextureView;
+
+ textureWidth = TEXT_TEXTURE_WIDTH;
+ textureHeight = TEXT_TEXTURE_HEIGHT;
+ } else {
+ int alignedBufferSize = cairo_bounds.width * cairo_bounds.height * 4;
+
+ // Create a one-off immutable texture from system memory.
+ BYTE *alignedTextureData = new BYTE[alignedBufferSize];
+ for (int y = 0; y < cairo_bounds.height; y++) {
+ for (int x = 0; x < cairo_bounds.width; x++) {
+ // Copy 3 Bpp source to 4 Bpp destination memory used for
+ // texture creation. D3D10 has no 3 Bpp texture format we can
+ // use.
+ //
+ // Since we don't care what ends up in the alpha pixel of the
+ // destination, therefor we can simply copy a normal 32 bit
+ // integer each time, filling the alpha pixel of the destination
+ // with the first subpixel of the next pixel from the source.
+ *((int*)(alignedTextureData + (y * cairo_bounds.width + x) * 4)) =
+ *((int*)(texture + (y * cairo_bounds.width + x) * 3));
+ }
+ }
+
+ D3D10_SUBRESOURCE_DATA data;
+
+ CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+ cairo_bounds.width, cairo_bounds.height,
+ 1, 1);
+ desc.Usage = D3D10_USAGE_IMMUTABLE;
+
+ data.SysMemPitch = cairo_bounds.width * 4;
+ data.pSysMem = alignedTextureData;
+
+ RefPtr<ID3D10Texture2D> tex;
+ hr = device->CreateTexture2D(&desc, &data, &tex);
+
+ delete [] alignedTextureData;
+ delete [] texture;
+
+ if (FAILED(hr)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ hr = device->CreateShaderResourceView(tex, NULL, &srView);
+
+ if (FAILED(hr)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ textureWidth = cairo_bounds.width;
+ textureHeight = cairo_bounds.height;
+ }
+
+ // Prepare destination surface for rendering.
+ RefPtr<ID3D10Texture2D> dstTexture;
+
+ dst->surface->QueryInterface(&dstTexture);
+
+ if (!dst->buffer_rt_view) {
+ hr = device->CreateRenderTargetView(dstTexture, NULL, &dst->buffer_rt_view);
+ if (FAILED(hr)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ D3D10_TEXTURE2D_DESC tDesc;
+ dstTexture->GetDesc(&tDesc);
+ D3D10_VIEWPORT vp;
+ vp.Height = tDesc.Height;
+ vp.MinDepth = 0;
+ vp.MaxDepth = 1.0;
+ vp.TopLeftX = 0;
+ vp.TopLeftY = 0;
+ vp.Width = tDesc.Width;
+ device->RSSetViewports(1, &vp);
+
+ ID3D10Effect *effect = dst->device->mSampleEffect;
+
+ ID3D10RenderTargetView *rtViewPtr = dst->buffer_rt_view;
+
+ device->OMSetRenderTargets(1, &rtViewPtr, 0);
+
+ ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTextTexture");
+
+ ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector();
+ ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector();
+ ID3D10EffectVectorVariable *textColor = effect->GetVariableByName("TextColor")->AsVector();
+
+ float colorVal[] = { float(source->color.red * source->color.alpha),
+ float(source->color.green * source->color.alpha),
+ float(source->color.blue * source->color.alpha),
+ float(source->color.alpha) };
+ textColor->SetFloatVector(colorVal);
+
+ float quadDescVal[4];
+ float texCoordsVal[4];
+
+ // Draw a quad for each rectangle in the intersection of the clip and the
+ // text area.
+ for (int i = 0; i < cairo_region_num_rectangles(region); i++) {
+ cairo_rectangle_int_t quad;
+ cairo_region_get_rectangle(region, i, &quad);
+
+ quadDescVal[0] = -1.0f + ((float)quad.x / (float)tDesc.Width) * 2.0f;
+ quadDescVal[1] = 1.0f - ((float)quad.y / (float)tDesc.Height) * 2.0f;
+ quadDescVal[2] = ((float)quad.width / (float)tDesc.Width) * 2.0f;
+ quadDescVal[3] = -((float)quad.height / (float)tDesc.Height) * 2.0f;
+
+ texCoordsVal[0] = (float)(quad.x - cairo_bounds.x) / textureWidth;
+ texCoordsVal[1] = (float)(quad.y - cairo_bounds.y) / textureHeight;
+ texCoordsVal[2] = (float)quad.width / textureWidth;
+ texCoordsVal[3] = (float)quad.height / textureHeight;
+
+ quadDesc->SetFloatVector(quadDescVal);
+ texCoords->SetFloatVector(texCoordsVal);
+
+ _cairo_d2d_setup_for_blend(dst->device);
+ technique->GetPassByIndex(0)->Apply(0);
+
+ ID3D10ShaderResourceView *srViewPtr = srView;
+ device->PSSetShaderResources(0, 1, &srViewPtr);
+
+ device->Draw(4, 0);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_dwrite_show_glyphs_on_d2d_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 status;
+
+ // TODO: Check font & surface for types.
+ cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast<cairo_dwrite_scaled_font_t*>(scaled_font);
+ cairo_dwrite_font_face_t *dwriteff = reinterpret_cast<cairo_dwrite_font_face_t*>(scaled_font->font_face);
+ cairo_d2d_surface_t *dst = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+
+ /* We can only handle dwrite fonts */
+ //XXX: this is checked by at least one caller
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ op = _cairo_d2d_simplify_operator(op, source);
+
+ /* We cannot handle operator SOURCE or CLEAR */
+ if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (op == CAIRO_OPERATOR_OVER && source->type == CAIRO_PATTERN_TYPE_SOLID &&
+ dst->base.content != CAIRO_CONTENT_COLOR &&
+ dst->base.permit_subpixel_antialiasing &&
+ dwritesf->antialias_mode == CAIRO_ANTIALIAS_SUBPIXEL)
+ {
+ // The D2D/DWrite drawing API's will not allow drawing subpixel AA to
+ // an RGBA surface. We do however want to do this if we know all text
+ // on a surface will be over opaque pixels, when this is the case
+ // we set the permit_subpixel_antialiasing flag on a surface. We then
+ // proceed to manually composite the glyphs to the surface.
+
+ const cairo_solid_pattern_t *solid_src =
+ reinterpret_cast<const cairo_solid_pattern_t*>(source);
+ status = _cairo_dwrite_manual_show_glyphs_on_d2d_surface(surface,
+ op,
+ solid_src,
+ glyphs,
+ num_glyphs,
+ dwritesf,
+ clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ return status;
+ }
+ }
+
+ RefPtr<ID2D1RenderTarget> target_rt = dst->rt;
+ cairo_rectangle_int_t fontArea;
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ if (op != CAIRO_OPERATOR_OVER) {
+#endif
+ target_rt = _cairo_d2d_get_temp_rt(dst, clip);
+
+ if (!target_rt) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+#ifndef ALWAYS_MANUAL_COMPOSITE
+ } else {
+ _begin_draw_state(dst);
+ status = (cairo_int_status_t)_cairo_d2d_set_clip (dst, clip);
+
+ if (unlikely(status))
+ return status;
+ }
+#endif
+
+ D2D1_TEXT_ANTIALIAS_MODE cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
+
+ // If we're rendering to a temporary surface we cannot do sub-pixel AA.
+ if (dst->base.content != CAIRO_CONTENT_COLOR || dst->rt.get() != target_rt.get()) {
+ cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
+ }
+
+ RefPtr<IDWriteRenderingParams> params;
+ target_rt->GetTextRenderingParams(&params);
+
+ DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT;
+ if (params) {
+ HRESULT hr = dwriteff->dwriteface->GetRecommendedRenderingMode(
+ (FLOAT)dwritesf->base.font_matrix.yy,
+ 1.0f,
+ DWRITE_MEASURING_MODE_NATURAL,
+ params,
+ &renderMode);
+ if (FAILED(hr)) {
+ // this probably never happens, but let's play it safe
+ renderMode = DWRITE_RENDERING_MODE_DEFAULT;
+ }
+ }
+
+ // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept
+ switch (renderMode) {
+ case DWRITE_RENDERING_MODE_DEFAULT:
+ // As per DWRITE_RENDERING_MODE documentation, pick Natural for font
+ // sizes under 16 ppem
+ if (dwritesf->base.font_matrix.yy < 16.0f) {
+ renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
+ } else {
+ renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+ }
+ break;
+ case DWRITE_RENDERING_MODE_OUTLINE:
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ default:
+ break;
+ }
+
+ switch (dwritesf->antialias_mode) {
+ case CAIRO_ANTIALIAS_NONE:
+ cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+ cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
+ break;
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ break;
+ }
+
+ if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
+ cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
+ }
+
+ target_rt->SetTextAntialiasMode(cleartype_quality);
+
+ DWRITE_MEASURING_MODE measureMode =
+ renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC :
+ renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL :
+ DWRITE_MEASURING_MODE_NATURAL;
+
+ cairo_bool_t transform = FALSE;
+
+ AutoDWriteGlyphRun run;
+ _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, dwritesf, &run, &transform);
+
+ D2D1::Matrix3x2F mat = _cairo_d2d_matrix_from_matrix(&dwritesf->mat);
+
+ if (transform) {
+ target_rt->SetTransform(mat);
+ }
+
+ if (dst->rt.get() != target_rt.get()) {
+ RefPtr<IDWriteGlyphRunAnalysis> analysis;
+ DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat);
+ DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run,
+ 1.0f,
+ transform ? &dwmat : 0,
+ renderMode,
+ measureMode,
+ 0,
+ 0,
+ &analysis);
+
+ RECT bounds;
+ analysis->GetAlphaTextureBounds(scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE ?
+ DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1,
+ &bounds);
+ fontArea.x = bounds.left;
+ fontArea.y = bounds.top;
+ fontArea.width = bounds.right - bounds.left;
+ fontArea.height = bounds.bottom - bounds.top;
+ }
+
+ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(dst, NULL,
+ source);
+
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (transform) {
+ D2D1::Matrix3x2F mat_inverse = _cairo_d2d_matrix_from_matrix(&dwritesf->mat_inverse);
+ D2D1::Matrix3x2F mat_brush;
+
+ // The brush matrix needs to be multiplied with the inverted matrix
+ // as well, to move the brush into the space of the glyphs. Before
+ // the render target transformation.
+ brush->GetTransform(&mat_brush);
+ mat_brush = mat_brush * mat_inverse;
+ brush->SetTransform(&mat_brush);
+ }
+
+ target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush, measureMode);
+
+ if (transform) {
+ target_rt->SetTransform(D2D1::Matrix3x2F::Identity());
+ }
+
+ if (target_rt.get() != dst->rt.get()) {
+ return _cairo_d2d_blend_temp_surface(dst, op, target_rt, clip, &fontArea);
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_d2d_show_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ if (((cairo_surface_t*)surface)->type != CAIRO_SURFACE_TYPE_D2D ||
+ scaled_font->backend->type != CAIRO_FONT_TYPE_DWRITE)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+ cairo_d2d_surface_t::TextRenderingState textRenderingState =
+ reinterpret_cast<cairo_dwrite_scaled_font_t*>(scaled_font)->rendering_mode;
+ if (d2dsurf->textRenderingState != textRenderingState) {
+ RefPtr<IDWriteRenderingParams> params =
+ DWriteFactory::RenderingParams(textRenderingState);
+ d2dsurf->rt->SetTextRenderingParams(params);
+ d2dsurf->textRenderingState = textRenderingState;
+ }
+ cairo_int_status_t status = (cairo_int_status_t)
+ _cairo_dwrite_show_glyphs_on_d2d_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip);
+
+ return status;
+}
+
+
+static cairo_bool_t
+_cairo_d2d_getextents(void *surface,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
+ extents->x = 0;
+ extents->y = 0;
+ D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize();
+ extents->width = size.width;
+ extents->height = size.height;
+ return TRUE;
+}
+
+
+/** Helper functions. */
+
+
+
+cairo_surface_t*
+cairo_d2d_surface_create_for_hwnd(cairo_device_t *cairo_device,
+ HWND wnd,
+ cairo_content_t content)
+{
+ cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(cairo_device);
+ cairo_d2d_surface_t *newSurf = static_cast<cairo_d2d_surface_t*>(malloc(sizeof(cairo_d2d_surface_t)));
+ new (newSurf) cairo_d2d_surface_t();
+
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content);
+
+ RECT rc;
+ HRESULT hr;
+
+ newSurf->isDrawing = false;
+ ::GetClientRect(wnd, &rc);
+
+ FLOAT dpiX;
+ FLOAT dpiY;
+ D2D1_SIZE_U sizePixels;
+ D2D1_SIZE_F size;
+
+ dpiX = 96;
+ dpiY = 96;
+
+
+ sizePixels.width = rc.right - rc.left;
+ sizePixels.height = rc.bottom - rc.top;
+
+ if (!sizePixels.width) {
+ sizePixels.width = 1;
+ }
+ if (!sizePixels.height) {
+ sizePixels.height = 1;
+ }
+ ID3D10Device1 *device = d2d_device->mD3D10Device;
+ RefPtr<IDXGIDevice> dxgiDevice;
+ RefPtr<IDXGIAdapter> dxgiAdapter;
+ RefPtr<IDXGIFactory> dxgiFactory;
+ D2D1_RENDER_TARGET_PROPERTIES props;
+ D2D1_BITMAP_PROPERTIES bitProps;
+
+ device->QueryInterface(&dxgiDevice);
+ dxgiDevice->GetAdapter(&dxgiAdapter);
+ dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory));
+
+ DXGI_SWAP_CHAIN_DESC swapDesc;
+ ::ZeroMemory(&swapDesc, sizeof(swapDesc));
+
+ swapDesc.BufferDesc.Width = sizePixels.width;
+ swapDesc.BufferDesc.Height = sizePixels.height;
+ swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ swapDesc.BufferDesc.RefreshRate.Numerator = 60;
+ swapDesc.BufferDesc.RefreshRate.Denominator = 1;
+ swapDesc.SampleDesc.Count = 1;
+ swapDesc.SampleDesc.Quality = 0;
+ swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ swapDesc.BufferCount = 1;
+ swapDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;
+ swapDesc.OutputWindow = wnd;
+ swapDesc.Windowed = TRUE;
+
+ /**
+ * Create a swap chain, this swap chain will contain the backbuffer for
+ * the window we draw to. The front buffer is the full screen front
+ * buffer.
+ */
+ hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc, &newSurf->dxgiChain);
+
+ /**
+ * We do not want DXGI to intercept alt-enter events and make the window go
+ * fullscreen! This shouldn't be in the cairo backend but controlled through
+ * the device. See comments on mozilla bug 553603.
+ */
+ dxgiFactory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES);
+
+ if (FAILED(hr)) {
+ goto FAIL_HWND;
+ }
+ /** Get the backbuffer surface from the swap chain */
+ hr = newSurf->dxgiChain->GetBuffer(0,
+ IID_PPV_ARGS(&newSurf->surface));
+
+ if (FAILED(hr)) {
+ goto FAIL_HWND;
+ }
+
+ newSurf->surface->QueryInterface(&newSurf->backBuf);
+
+ size.width = sizePixels.width * dpiX;
+ size.height = sizePixels.height * dpiY;
+
+ props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
+ dpiX,
+ dpiY,
+ D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE);
+ hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(newSurf->backBuf,
+ props,
+ &newSurf->rt);
+ if (FAILED(hr)) {
+ goto FAIL_HWND;
+ }
+
+ bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN,
+ D2D1_ALPHA_MODE_PREMULTIPLIED));
+
+ newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush);
+
+ _d2d_clear_surface(newSurf);
+
+ _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content));
+
+ return reinterpret_cast<cairo_surface_t*>(newSurf);
+
+FAIL_HWND:
+ newSurf->~cairo_d2d_surface_t();
+ free(newSurf);
+ return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY));
+}
+
+
+
+cairo_surface_t *
+cairo_d2d_surface_create(cairo_device_t *device,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ if (width == 0 || height == 0) {
+ return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE));
+ }
+
+ cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device);
+ cairo_d2d_surface_t *newSurf = static_cast<cairo_d2d_surface_t*>(malloc(sizeof(cairo_d2d_surface_t)));
+ new (newSurf) cairo_d2d_surface_t();
+
+ DXGI_FORMAT dxgiformat = DXGI_FORMAT_B8G8R8A8_UNORM;
+ D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED;
+ if (format == CAIRO_FORMAT_ARGB32) {
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR_ALPHA);
+ } else if (format == CAIRO_FORMAT_RGB24) {
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR);
+ alpha = D2D1_ALPHA_MODE_IGNORE;
+ } else {
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_ALPHA);
+ dxgiformat = DXGI_FORMAT_A8_UNORM;
+ }
+
+
+ newSurf->format = format;
+
+ D2D1_SIZE_U sizePixels;
+ HRESULT hr;
+
+ sizePixels.width = width;
+ sizePixels.height = height;
+
+ CD3D10_TEXTURE2D_DESC desc(
+ dxgiformat,
+ sizePixels.width,
+ sizePixels.height
+ );
+ desc.MipLevels = 1;
+ desc.Usage = D3D10_USAGE_DEFAULT;
+ desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+
+ /* CreateTexture2D does not support D3D10_RESOURCE_MISC_GDI_COMPATIBLE with DXGI_FORMAT_A8_UNORM */
+ if (desc.Format != DXGI_FORMAT_A8_UNORM)
+ desc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE;
+
+ RefPtr<ID3D10Texture2D> texture;
+ RefPtr<IDXGISurface> dxgiSurface;
+ D2D1_BITMAP_PROPERTIES bitProps;
+ D2D1_RENDER_TARGET_PROPERTIES props;
+
+ hr = d2d_device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATE;
+ }
+
+ newSurf->surface = texture;
+
+ /** Create the DXGI surface. */
+ hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface);
+ if (FAILED(hr)) {
+ goto FAIL_CREATE;
+ }
+
+ props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha));
+
+ if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE)
+ props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
+
+ hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface,
+ props,
+ &newSurf->rt);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATE;
+ }
+
+ bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN,
+ alpha));
+
+ if (dxgiformat != DXGI_FORMAT_A8_UNORM) {
+ /* For some reason creation of shared bitmaps for A8 UNORM surfaces
+ * doesn't work even though the documentation suggests it does. The
+ * function will return an error if we try */
+ hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface,
+ dxgiSurface,
+ &bitProps,
+ &newSurf->surfaceBitmap);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATE;
+ }
+ }
+
+ newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush);
+
+ _d2d_clear_surface(newSurf);
+
+ _cairo_d2d_surface_init(newSurf, d2d_device, format);
+
+ return reinterpret_cast<cairo_surface_t*>(newSurf);
+
+FAIL_CREATE:
+ newSurf->~cairo_d2d_surface_t();
+ free(newSurf);
+ return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY));
+}
+
+cairo_surface_t *
+cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content)
+{
+ if (!device) {
+ return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE));
+ }
+
+ cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device);
+ cairo_d2d_surface_t *newSurf = static_cast<cairo_d2d_surface_t*>(malloc(sizeof(cairo_d2d_surface_t)));
+ new (newSurf) cairo_d2d_surface_t();
+
+ cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
+ HRESULT hr;
+ RefPtr<ID3D10Texture2D> texture;
+ RefPtr<IDXGISurface> dxgiSurface;
+ D2D1_BITMAP_PROPERTIES bitProps;
+ D2D1_RENDER_TARGET_PROPERTIES props;
+ DXGI_FORMAT format;
+ DXGI_SURFACE_DESC desc;
+ D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED;
+
+ hr = d2d_device->mD3D10Device->OpenSharedResource(handle,
+ __uuidof(ID3D10Resource),
+ (void**)&newSurf->surface);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATEHANDLE;
+ }
+
+ hr = newSurf->surface->QueryInterface(&dxgiSurface);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATEHANDLE;
+ }
+
+ dxgiSurface->GetDesc(&desc);
+ format = desc.Format;
+
+ if (format == DXGI_FORMAT_B8G8R8A8_UNORM) {
+ if (content == CAIRO_CONTENT_ALPHA) {
+ status = CAIRO_STATUS_INVALID_CONTENT;
+ goto FAIL_CREATEHANDLE;
+ }
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content);
+ if (content == CAIRO_CONTENT_COLOR) {
+ alpha = D2D1_ALPHA_MODE_IGNORE;
+ }
+ } else if (format == DXGI_FORMAT_A8_UNORM) {
+ if (content != CAIRO_CONTENT_ALPHA) {
+ status = CAIRO_STATUS_INVALID_CONTENT;
+ goto FAIL_CREATEHANDLE;
+ }
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_ALPHA);
+ } else {
+ status = CAIRO_STATUS_INVALID_FORMAT;
+ // We don't know how to support this format!
+ goto FAIL_CREATEHANDLE;
+ }
+
+ props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha));
+
+ hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface,
+ props,
+ &newSurf->rt);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATEHANDLE;
+ }
+
+ bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN,
+ alpha));
+
+ if (format != DXGI_FORMAT_A8_UNORM) {
+ /* For some reason creation of shared bitmaps for A8 UNORM surfaces
+ * doesn't work even though the documentation suggests it does. The
+ * function will return an error if we try */
+ hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface,
+ dxgiSurface,
+ &bitProps,
+ &newSurf->surfaceBitmap);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATEHANDLE;
+ }
+ }
+
+ newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush);
+
+ _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content));
+
+ return &newSurf->base;
+
+FAIL_CREATEHANDLE:
+ newSurf->~cairo_d2d_surface_t();
+ free(newSurf);
+ return _cairo_surface_create_in_error(_cairo_error(status));
+}
+
+cairo_surface_t *
+cairo_d2d_surface_create_for_texture(cairo_device_t *device,
+ ID3D10Texture2D *texture,
+ cairo_content_t content)
+{
+ cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device);
+ cairo_d2d_surface_t *newSurf = static_cast<cairo_d2d_surface_t*>(malloc(sizeof(cairo_d2d_surface_t)));
+ new (newSurf) cairo_d2d_surface_t();
+
+ D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED;
+ if (content == CAIRO_CONTENT_COLOR) {
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, CAIRO_CONTENT_COLOR);
+ alpha = D2D1_ALPHA_MODE_IGNORE;
+ } else {
+ _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, NULL, content);
+ }
+
+ D2D1_SIZE_U sizePixels;
+ HRESULT hr;
+
+ D3D10_TEXTURE2D_DESC desc;
+ RefPtr<IDXGISurface> dxgiSurface;
+ D2D1_BITMAP_PROPERTIES bitProps;
+ D2D1_RENDER_TARGET_PROPERTIES props;
+
+ texture->GetDesc(&desc);
+
+ sizePixels.width = desc.Width;
+ sizePixels.height = desc.Height;
+
+ newSurf->surface = texture;
+
+ /** Create the DXGI surface. */
+ hr = newSurf->surface->QueryInterface(IID_IDXGISurface, (void**)&dxgiSurface);
+ if (FAILED(hr)) {
+ goto FAIL_CREATE;
+ }
+
+ props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha));
+
+ if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE)
+ props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
+
+ hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface,
+ props,
+ &newSurf->rt);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATE;
+ }
+
+ bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN,
+ alpha));
+
+ if (content != CAIRO_CONTENT_ALPHA) {
+ /* For some reason creation of shared bitmaps for A8 UNORM surfaces
+ * doesn't work even though the documentation suggests it does. The
+ * function will return an error if we try */
+ hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface,
+ dxgiSurface,
+ &bitProps,
+ &newSurf->surfaceBitmap);
+
+ if (FAILED(hr)) {
+ goto FAIL_CREATE;
+ }
+ }
+
+ newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush);
+
+ _cairo_d2d_surface_init(newSurf, d2d_device, _cairo_format_from_content(content));
+
+ return reinterpret_cast<cairo_surface_t*>(newSurf);
+
+FAIL_CREATE:
+ newSurf->~cairo_d2d_surface_t();
+ free(newSurf);
+ return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY));
+}
+
+ID3D10Texture2D*
+cairo_d2d_surface_get_texture(cairo_surface_t *surface)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_D2D) {
+ return NULL;
+ }
+
+ cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+
+ RefPtr<ID3D10Texture2D> texture;
+ d2dsurf->surface->QueryInterface(&texture);
+
+ return texture;
+}
+
+void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_D2D) {
+ return;
+ }
+ cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+
+ /** For now, we invalidate our storing texture with this operation. */
+ D2D1_POINT_2U point;
+ D3D10_BOX rect;
+ rect.front = 0;
+ rect.back = 1;
+
+ RefPtr<IDXGISurface> dxgiSurface;
+ d2dsurf->surface->QueryInterface(&dxgiSurface);
+ DXGI_SURFACE_DESC desc;
+
+ dxgiSurface->GetDesc(&desc);
+
+ /**
+ * It's important we constrain the size of the clip region to the area of
+ * the surface. If we don't we might get a box that goes outside the
+ * surface, and CopySubresourceRegion will become very angry with us.
+ * It will cause a device failure and subsequent drawing will break.
+ */
+ clip->x = MAX(clip->x, 0);
+ clip->y = MAX(clip->y, 0);
+ clip->width = MIN(clip->width, desc.Width - clip->x);
+ clip->height = MIN(clip->height, desc.Height - clip->y);
+
+ if (x < 0) {
+ point.x = (UINT32)clip->x;
+ rect.left = (UINT)(clip->x - x);
+ rect.right = (UINT)(clip->x + clip->width);
+ } else {
+ point.x = (UINT32)(clip->x + x);
+ rect.left = (UINT)clip->x;
+ rect.right = (UINT32)(clip->x + clip->width - x);
+ }
+ if (y < 0) {
+ point.y = (UINT32)clip->y;
+ rect.top = (UINT)(clip->y - y);
+ rect.bottom = (UINT)(clip->y + clip->height);
+ } else {
+ point.y = (UINT32)(clip->y + y);
+ rect.top = (UINT)clip->y;
+ rect.bottom = (UINT)(clip->y + clip->height - y);
+ }
+ ID3D10Texture2D *texture = _cairo_d2d_get_buffer_texture(d2dsurf);
+
+ d2dsurf->device->mD3D10Device->CopyResource(texture, d2dsurf->surface);
+ d2dsurf->device->mD3D10Device->CopySubresourceRegion(d2dsurf->surface,
+ 0,
+ point.x,
+ point.y,
+ 0,
+ texture,
+ 0,
+ &rect);
+
+}
+
+HDC
+cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_D2D) {
+ return NULL;
+ }
+ cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+
+ /* We'll pop the clip here manually so that we'll stay in drawing state if we
+ * already are, we need to ensure d2dsurf->isDrawing manually then though
+ */
+
+ /* Clips aren't allowed as per MSDN docs */
+ reset_clip(d2dsurf);
+
+ if (!d2dsurf->isDrawing) {
+ /* GetDC must be called between BeginDraw/EndDraw */
+ d2dsurf->rt->BeginDraw();
+ d2dsurf->isDrawing = true;
+ }
+
+ RefPtr<ID2D1GdiInteropRenderTarget> interopRT;
+
+ /* This QueryInterface call will always succeed even if the
+ * the render target doesn't support the ID2D1GdiInteropRenderTarget
+ * interface */
+ d2dsurf->rt->QueryInterface(&interopRT);
+
+ HDC dc;
+ HRESULT rv;
+
+ rv = interopRT->GetDC(retain_contents ? D2D1_DC_INITIALIZE_MODE_COPY :
+ D2D1_DC_INITIALIZE_MODE_CLEAR, &dc);
+
+ if (FAILED(rv)) {
+ return NULL;
+ }
+
+ return dc;
+}
+
+void
+cairo_d2d_release_dc(cairo_surface_t *surface, const cairo_rectangle_int_t *updated_rect)
+{
+ if (surface->type != CAIRO_SURFACE_TYPE_D2D) {
+ return;
+ }
+ cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+
+ RefPtr<ID2D1GdiInteropRenderTarget> interopRT;
+
+ d2dsurf->rt->QueryInterface(&interopRT);
+
+ if (!updated_rect) {
+ interopRT->ReleaseDC(NULL);
+ return;
+ }
+
+ RECT r;
+ r.left = updated_rect->x;
+ r.top = updated_rect->y;
+ r.right = r.left + updated_rect->width;
+ r.bottom = r.top + updated_rect->height;
+
+ interopRT->ReleaseDC(&r);
+}
+
+int
+cairo_d2d_get_image_surface_cache_usage()
+{
+ return _cairo_atomic_int_get(&cache_usage);
+}
+
+int
+cairo_d2d_get_surface_vram_usage(cairo_device_t *device)
+{
+ cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device);
+ return d2d_device->mVRAMUsage;
+}
+
+int
+cairo_d2d_surface_get_width(cairo_surface_t *surface)
+{
+ if (surface->backend != &cairo_d2d_surface_backend) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+ D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize();
+ return size.width;
+}
+
+int
+cairo_d2d_surface_get_height(cairo_surface_t *surface)
+{
+ if (surface->backend != &cairo_d2d_surface_backend) {
+ _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ return 0;
+ }
+
+ cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
+ D2D1_SIZE_U size = d2dsurf->rt->GetPixelSize();
+ return size.height;
+}
diff --git a/gfx/cairo/cairo/src/cairo-debug.c b/gfx/cairo/cairo/src/cairo-debug.c
new file mode 100644
index 000000000..e9e72b6aa
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-debug.c
@@ -0,0 +1,252 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.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).
+ **/
+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 ();
+
+#if CAIRO_HAS_DRM_SURFACE
+ _cairo_drm_device_reset_static_data ();
+#endif
+
+ _cairo_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_ARGB32:
+ width = image->width*4;
+ 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, cairo_path_fixed_t *path)
+{
+ cairo_status_t status;
+
+ printf ("path: extents=(%f, %f), (%f, %f)\n",
+ _cairo_fixed_to_double (path->extents.p1.x),
+ _cairo_fixed_to_double (path->extents.p1.y),
+ _cairo_fixed_to_double (path->extents.p2.x),
+ _cairo_fixed_to_double (path->extents.p2.y));
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _print_move_to,
+ _print_line_to,
+ _print_curve_to,
+ _print_close,
+ stream);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ printf ("\n");
+}
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 000000000..ba5f18392
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-deflate-stream.c
@@ -0,0 +1,151 @@
+/* -*- 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 <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+#include <zlib.h>
+
+#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 = 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;
+}
diff --git a/gfx/cairo/cairo/src/cairo-deprecated.h b/gfx/cairo/cairo/src/cairo-deprecated.h
new file mode 100644
index 000000000..7a56aadbf
--- /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 <cworth@cworth.org>
+ */
+
+#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 000000000..6eb44f3b6
--- /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 <chris@chris-wilson.co.uk>
+ */
+
+#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 000000000..15b048477
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-device.c
@@ -0,0 +1,533 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#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:
+ * <informalexample><programlisting>
+ * 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);
+ * }
+ * </programlisting></informalexample>
+ *
+ * <note><para>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.
+ * </para></note>
+ */
+
+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:
+ 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.
+ *
+ * The number of references to a #cairo_device_t can be get using
+ * cairo_device_get_reference_count().
+ *
+ * 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->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);
+
+ device->finished = TRUE;
+
+ if (device->backend->finish != NULL)
+ device->backend->finish (device);
+}
+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_t) -1;
+ }
+
+ 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.
+ *
+ * <note><para>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.
+ * </para></note>
+ *
+ * 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_SURFACE_FINISHED); /* XXX */
+
+ 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 || status >= CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ /* Don't overwrite an existing error. This preserves the first
+ * error, which is the most significant. */
+ _cairo_status_set_error (&device->status, status);
+
+ return _cairo_error (status);
+}
+
+/**
+ * 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 000000000..fc7509c1a
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-directfb-surface.c
@@ -0,0 +1,1964 @@
+/* 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):
+ * Michael Emmel <mike.emmel@gmail.com>
+ * Claudio Ciccani <klan@users.sf.net>
+ */
+
+#include "cairoint.h"
+#include "cairo-directfb.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+
+#include <pixman.h>
+
+#include <directfb.h>
+#include <direct/types.h>
+#include <direct/debug.h>
+#include <direct/memcpy.h>
+#include <direct/util.h>
+
+/*
+ * Rectangle works fine.
+ * Bugs 361377, 359553, 359243 in Gnome BTS are caused
+ * by GDK/DirectFB, not by Cairo/DirectFB.
+ */
+#define DFB_RECTANGLES 1
+
+/*
+ * Composite works fine.
+ */
+#define DFB_COMPOSITE 1
+
+/*
+ * CompositeTrapezoids works (without antialiasing).
+ */
+#define DFB_COMPOSITE_TRAPEZOIDS 1
+
+/*
+ * ShowGlyphs works fine.
+ */
+#define DFB_SHOW_GLYPHS 1
+
+#define PIXMAN_invalid (pixman_format_code_t) 0
+
+
+D_DEBUG_DOMAIN (CairoDFB_Acquire, "CairoDFB/Acquire", "Cairo DirectFB Acquire");
+D_DEBUG_DOMAIN (CairoDFB_Clip, "CairoDFB/Clip", "Cairo DirectFB Clipping");
+D_DEBUG_DOMAIN (CairoDFB_Font, "CairoDFB/Font", "Cairo DirectFB Font Rendering");
+D_DEBUG_DOMAIN (CairoDFB_Render, "CairoDFB/Render", "Cairo DirectFB Rendering");
+D_DEBUG_DOMAIN (CairoDFB_Surface, "CairoDFB/Surface", "Cairo DirectFB Surface");
+
+/*****************************************************************************/
+
+typedef struct _cairo_directfb_surface {
+ cairo_surface_t base;
+
+ pixman_format_code_t pixman_format;
+ cairo_bool_t supported_destination;
+
+ IDirectFB *dfb;
+ IDirectFBSurface *dfbsurface;
+ IDirectFBSurface *tmpsurface;
+ pixman_format_code_t tmpformat;
+
+ int width;
+ int height;
+
+ unsigned local : 1;
+ unsigned blit_premultiplied : 1;
+} cairo_directfb_surface_t;
+
+
+typedef struct _cairo_directfb_font_cache {
+ IDirectFB *dfb;
+ IDirectFBSurface *dfbsurface;
+
+ int width;
+ int height;
+
+ /* coordinates within the surface
+ * of the last loaded glyph */
+ int x;
+ int y;
+} cairo_directfb_font_cache_t;
+
+static cairo_surface_backend_t _cairo_directfb_surface_backend;
+
+/*****************************************************************************/
+
+static int _directfb_argb_font = 0;
+
+/*****************************************************************************/
+
+#define RUN_CLIPPED(surface, clip_region, clip, func) {\
+ if ((clip_region) != NULL) {\
+ int n_clips = cairo_region_num_rectangles (clip_region), n; \
+ for (n = 0; n < n_clips; n++) {\
+ if (clip) {\
+ DFBRegion reg, *cli = (clip); \
+ cairo_rectangle_int_t rect; \
+ cairo_region_get_rectangle (clip_region, n, &rect); \
+ reg.x1 = rect.x; \
+ reg.y1 = rect.y; \
+ reg.x2 = rect.x + rect.width - 1; \
+ reg.y2 = rect.y + rect.height - 1; \
+ if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\
+ reg.x1 > cli->x2 || reg.y1 > cli->y2)\
+ continue;\
+ if (reg.x1 < cli->x1)\
+ reg.x1 = cli->x1;\
+ if (reg.y1 < cli->y1)\
+ reg.y1 = cli->y1;\
+ if (reg.x2 > cli->x2)\
+ reg.x2 = cli->x2;\
+ if (reg.y2 > cli->y2)\
+ reg.y2 = cli->y2;\
+ (surface)->dfbsurface->SetClip ((surface)->dfbsurface, &reg);\
+ } 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, &reg); \
+ }\
+ func;\
+ }\
+ } else {\
+ (surface)->dfbsurface->SetClip ((surface)->dfbsurface, clip);\
+ func;\
+ }\
+}
+
+#define TRANSFORM_POINT2X(m, x, y, ret_x, ret_y) do { \
+ double _x = (x); \
+ double _y = (y); \
+ (ret_x) = (_x * (m).xx + (m).x0); \
+ (ret_y) = (_y * (m).yy + (m).y0); \
+} while (0)
+
+#define TRANSFORM_POINT3X(m, x, y, ret_x, ret_y) do { \
+ double _x = (x); \
+ double _y = (y); \
+ (ret_x) = (_x * (m).xx + _y * (m).xy + (m).x0); \
+ (ret_y) = (_x * (m).yx + _y * (m).yy + (m).y0); \
+} while (0)
+
+/* XXX: A1 has a different bits ordering in cairo.
+ * Probably we should drop it.
+ */
+
+static cairo_content_t
+_directfb_format_to_content (DFBSurfacePixelFormat format)
+{
+ if (DFB_PIXELFORMAT_HAS_ALPHA (format)) {
+ if (DFB_COLOR_BITS_PER_PIXEL (format))
+ return CAIRO_CONTENT_COLOR_ALPHA;
+
+ return CAIRO_CONTENT_ALPHA;
+ }
+
+ return CAIRO_CONTENT_COLOR;
+}
+
+static inline DFBSurfacePixelFormat
+_cairo_to_directfb_format (cairo_format_t format)
+{
+ switch (format) {
+ case CAIRO_FORMAT_RGB24:
+ return DSPF_RGB32;
+ case CAIRO_FORMAT_ARGB32:
+ return DSPF_ARGB;
+ case CAIRO_FORMAT_A8:
+ return DSPF_A8;
+ case CAIRO_FORMAT_A1:
+ return DSPF_A1;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static inline pixman_format_code_t
+_directfb_to_pixman_format (DFBSurfacePixelFormat format)
+{
+ switch (format) {
+ case DSPF_UNKNOWN: return PIXMAN_invalid;
+ 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 PIXMAN_invalid;
+ case DSPF_I420: return PIXMAN_invalid;
+ case DSPF_YV12: return PIXMAN_yv12;
+ case DSPF_LUT8: return PIXMAN_invalid;
+ case DSPF_ALUT44: return PIXMAN_invalid;
+ case DSPF_AiRGB: return PIXMAN_invalid;
+ case DSPF_A1: return PIXMAN_a1; /* bit reversed, oops */
+ case DSPF_NV12: return PIXMAN_invalid;
+ case DSPF_NV16: return PIXMAN_invalid;
+ case DSPF_ARGB2554: return PIXMAN_invalid;
+ case DSPF_ARGB4444: return PIXMAN_a4r4g4b4;
+ case DSPF_NV21: return PIXMAN_invalid;
+ case DSPF_AYUV: return PIXMAN_invalid;
+ case DSPF_A4: return PIXMAN_a4;
+ case DSPF_ARGB1666: return PIXMAN_invalid;
+ case DSPF_ARGB6666: return PIXMAN_invalid;
+ case DSPF_RGB18: return PIXMAN_invalid;
+ case DSPF_LUT2: return PIXMAN_invalid;
+ case DSPF_RGB444: return PIXMAN_x4r4g4b4;
+ case DSPF_RGB555: return PIXMAN_x1r5g5b5;
+#if DFB_NUM_PIXELFORMATS >= 29
+ case DSPF_BGR555: return PIXMAN_x1b5g5r5;
+#endif
+ }
+ return PIXMAN_invalid;
+}
+
+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;
+}
+
+static cairo_status_t
+_directfb_buffer_surface_create (IDirectFB *dfb,
+ DFBSurfacePixelFormat format,
+ int width,
+ int height,
+ IDirectFBSurface **out)
+{
+ IDirectFBSurface *buffer;
+ DFBSurfaceDescription dsc;
+ DFBResult ret;
+
+ dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
+ dsc.caps = DSCAPS_PREMULTIPLIED;
+ dsc.width = width;
+ dsc.height = height;
+ dsc.pixelformat = format;
+
+ ret = dfb->CreateSurface (dfb, &dsc, &buffer);
+ if (ret) {
+ DirectFBError ("IDirectFB::CreateSurface()", ret);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ *out = buffer;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_directfb_acquire_surface (cairo_directfb_surface_t *surface,
+ cairo_rectangle_int_t *intrest_rec,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra,
+ DFBSurfaceLockFlags lock_flags)
+{
+ IDirectFBSurface *buffer = NULL;
+ DFBRectangle source_rect;
+ cairo_surface_t *image;
+ pixman_image_t *pixman_image;
+ pixman_format_code_t pixman_format;
+ cairo_status_t status;
+ void *data;
+ int pitch;
+
+ if (surface->pixman_format == PIXMAN_invalid) {
+ if (intrest_rec != NULL) {
+ source_rect.x = intrest_rec->x;
+ source_rect.y = intrest_rec->y;
+ source_rect.w = intrest_rec->width;
+ source_rect.h = intrest_rec->height;
+ } else {
+ source_rect.x = 0;
+ source_rect.y = 0;
+ surface->dfbsurface->GetSize (surface->dfbsurface,
+ &source_rect.w, &source_rect.h);
+ }
+
+ if (surface->tmpsurface != NULL) {
+ int w, h;
+
+ surface->tmpsurface->GetSize (surface->tmpsurface, &w, &h);
+ if (w < source_rect.w || h < source_rect.h) {
+ surface->tmpsurface->Release (surface->tmpsurface);
+ surface->tmpsurface = NULL;
+ surface->tmpformat = PIXMAN_invalid;
+ }
+ }
+
+ if (surface->tmpsurface == NULL) {
+ DFBSurfacePixelFormat format;
+
+ D_DEBUG_AT (CairoDFB_Acquire, "Allocating buffer for surface %p.\n", surface);
+
+ format = _cairo_to_directfb_format (_cairo_format_from_content (surface->base.content));
+ status =
+ _directfb_buffer_surface_create (surface->dfb, format,
+ source_rect.w, source_rect.h,
+ &surface->tmpsurface);
+ if (unlikely (status))
+ goto ERROR;
+
+ surface->tmpformat = _directfb_to_pixman_format (format);
+ }
+ buffer = surface->tmpsurface;
+ pixman_format = surface->tmpformat;
+
+
+/* surface->dfbsurface->GetCapabilities (surface->dfbsurface, &caps);
+ DFBSurfaceCapabilities caps;
+ if (caps & DSCAPS_FLIPPING) {
+ DFBRegion region = { .x1 = source_rect.x, .y1 = source_rect.y,
+ .x2 = source_rect.x + source_rect.w - 1,
+ .y2 = source_rect.y + source_rect.h - 1 };
+ surface->dfbsurface->Flip (surface->dfbsurface, &region, DSFLIP_BLIT);
+ } */
+ buffer->Blit (buffer, surface->dfbsurface, &source_rect, 0, 0);
+ } else {
+ /*might be a subsurface get the offset*/
+ surface->dfbsurface->GetVisibleRectangle (surface->dfbsurface, &source_rect);
+ pixman_format = surface->pixman_format;
+ buffer = surface->dfbsurface;
+ }
+
+ if (buffer->Lock (buffer, lock_flags, &data, &pitch)) {
+ D_DEBUG_AT (CairoDFB_Acquire, "Couldn't lock surface!\n");
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto ERROR;
+ }
+
+ pixman_image = pixman_image_create_bits (pixman_format,
+ source_rect.w, source_rect.h,
+ data, pitch);
+ if (pixman_image == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto ERROR;
+ }
+
+ image = _cairo_image_surface_create_for_pixman_image (pixman_image,
+ pixman_format);
+ status = image->status;
+ if (status)
+ goto ERROR;
+
+ if (image_rect_out) {
+ image_rect_out->x = source_rect.x;
+ image_rect_out->y = source_rect.y;
+ image_rect_out->width = source_rect.w;
+ image_rect_out->height = source_rect.h;
+ } else {
+ /* lock for read */
+ /* might be a subsurface */
+ if (buffer == surface->dfbsurface) {
+ cairo_surface_set_device_offset (image,
+ source_rect.x, source_rect.y);
+ }
+ }
+
+ *image_extra = buffer;
+ *image_out = (cairo_image_surface_t *) image;
+ return CAIRO_STATUS_SUCCESS;
+
+ERROR:
+ if (buffer) {
+ buffer->Unlock (buffer);
+ if (buffer != surface->dfbsurface)
+ buffer->Release (buffer);
+ }
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_directfb_surface_create_internal (IDirectFB *dfb,
+ DFBSurfacePixelFormat format,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_directfb_surface_t *surface;
+ cairo_status_t status;
+
+ surface = calloc (1, sizeof (cairo_directfb_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface->dfb = dfb;
+
+ if (width < 8 || height < 8) {
+ IDirectFBSurface *tmp;
+ DFBRectangle rect = { .x=0, .y=0, .w=width, .h=height };
+
+ /* Some cards (e.g. Matrox) don't support surfaces smaller than 8x8 */
+ status = _directfb_buffer_surface_create (dfb, format,
+ MAX (width, 8), MAX (height, 8),
+ &tmp);
+ if (status) {
+ free (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ tmp->GetSubSurface (tmp, &rect, &surface->dfbsurface);
+ tmp->Release (tmp);
+ } else {
+ status = _directfb_buffer_surface_create (dfb, format,
+ width, height,
+ &surface->dfbsurface);
+ if (status) {
+ free (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+ }
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_directfb_surface_backend,
+ NULL, /* device */
+ content);
+ surface->pixman_format = _directfb_to_pixman_format (format);
+ surface->supported_destination = pixman_format_supported_destination (surface->pixman_format);
+
+ surface->width = width;
+ surface->height = height;
+ surface->local = TRUE;
+ surface->blit_premultiplied = TRUE;
+
+ return &surface->base;
+}
+
+static cairo_surface_t *
+_cairo_directfb_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_directfb_surface_t *other = abstract_src;
+ DFBSurfacePixelFormat format;
+
+ D_DEBUG_AT (CairoDFB_Surface,
+ "%s( src=%p, content=0x%x, width=%d, height=%d).\n",
+ __FUNCTION__, other, content, width, height);
+
+ width = (width <= 0) ? 1 : width;
+ height = (height<= 0) ? 1 : height;
+
+ format = _cairo_to_directfb_format (_cairo_format_from_content (content));
+ return _cairo_directfb_surface_create_internal (other->dfb, format,
+ content, width, height);
+}
+
+static cairo_status_t
+_cairo_directfb_surface_finish (void *data)
+{
+ cairo_directfb_surface_t *surface = (cairo_directfb_surface_t *)data;
+
+ D_DEBUG_AT (CairoDFB_Surface, "%s( surface=%p ).\n", __FUNCTION__, surface);
+
+ if (surface->tmpsurface) {
+ surface->tmpsurface->Release (surface->tmpsurface);
+ surface->tmpsurface = NULL;
+ }
+
+ if (surface->dfbsurface) {
+ surface->dfbsurface->Release (surface->dfbsurface);
+ surface->dfbsurface = NULL;
+ }
+
+ if (surface->dfb)
+ surface->dfb = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_directfb_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_directfb_surface_t *surface = abstract_surface;
+
+ D_DEBUG_AT (CairoDFB_Acquire,
+ "%s( surface=%p ).\n", __FUNCTION__, surface);
+
+ return _directfb_acquire_surface (surface, NULL, image_out,
+ NULL, image_extra, DSLF_READ);
+}
+
+static void
+_cairo_directfb_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ IDirectFBSurface *buffer = image_extra;
+
+ D_DEBUG_AT (CairoDFB_Acquire,
+ "%s( release=%p ).\n", __FUNCTION__, abstract_surface);
+
+ buffer->Unlock (buffer);
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_directfb_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ cairo_directfb_surface_t *surface = abstract_surface;
+
+ D_DEBUG_AT (CairoDFB_Acquire,
+ "%s( surface=%p (%dx%d), interest_rect={ %u %u %u %u } ).\n",
+ __FUNCTION__, surface, surface->width, surface->height,
+ interest_rect ? interest_rect->x : 0,
+ interest_rect ? interest_rect->y : 0,
+ interest_rect ? interest_rect->width : (unsigned) surface->width,
+ interest_rect ? interest_rect->height : (unsigned) surface->height);
+
+ return _directfb_acquire_surface (surface, interest_rect, image_out,
+ image_rect_out, image_extra,
+ DSLF_READ | DSLF_WRITE);
+}
+
+static void
+_cairo_directfb_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_directfb_surface_t *surface = abstract_surface;
+ IDirectFBSurface *buffer = image_extra;
+
+ D_DEBUG_AT (CairoDFB_Acquire,
+ "%s( surface=%p ).\n", __FUNCTION__, surface);
+
+ buffer->Unlock (buffer);
+
+ if (surface->dfbsurface != buffer) {
+ DFBRegion region = {
+ .x1 = interest_rect->x,
+ .y1 = interest_rect->y,
+ .x2 = interest_rect->x + interest_rect->width - 1,
+ .y2 = interest_rect->y + interest_rect->height - 1
+ };
+ surface->dfbsurface->SetBlittingFlags (surface->dfbsurface, DSBLIT_NOFX);
+ surface->dfbsurface->SetClip (surface->dfbsurface, &region);
+ surface->dfbsurface->Blit (surface->dfbsurface,
+ buffer, NULL,
+ image_rect->x, image_rect->y);
+ }
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_directfb_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_directfb_surface_t *surface = abstract_surface;
+ cairo_directfb_surface_t *clone;
+
+ D_DEBUG_AT (CairoDFB_Surface,
+ "%s( surface=%p, src=%p ).\n", __FUNCTION__, surface, src);
+
+ if (src->backend == surface->base.backend) {
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ } else if (_cairo_surface_is_image (src)) {
+ cairo_image_surface_t *image_src = (cairo_image_surface_t *) src;
+ DFBSurfacePixelFormat format;
+ DFBResult ret;
+ pixman_image_t *pixman_image;
+ void *data;
+ int pitch;
+
+ format = _directfb_from_pixman_format (image_src->pixman_format);
+ if (format == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ clone = (cairo_directfb_surface_t *)
+ _cairo_directfb_surface_create_internal (surface->dfb, format,
+ image_src->base.content,
+ width, height);
+ if (unlikely (clone->base.status))
+ return clone->base.status;
+
+ ret = clone->dfbsurface->Lock (clone->dfbsurface,
+ DSLF_WRITE, (void *)&data, &pitch);
+ if (ret) {
+ DirectFBError ("IDirectFBSurface::Lock()", ret);
+ cairo_surface_destroy (&clone->base);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image = pixman_image_create_bits (clone->pixman_format,
+ width, height,
+ data, pitch);
+ if (unlikely (pixman_image == NULL)) {
+ DirectFBError ("IDirectFBSurface::Lock()", ret);
+ cairo_surface_destroy (&clone->base);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image_src->pixman_image,
+ NULL,
+ pixman_image,
+ src_x, src_y,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ pixman_image_unref (pixman_image);
+
+ clone->dfbsurface->Unlock (clone->dfbsurface);
+
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+ *clone_out = &clone->base;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+#if DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS
+static cairo_int_status_t
+_directfb_prepare_composite (cairo_directfb_surface_t *dst,
+ const cairo_pattern_t *src_pattern,
+ const cairo_pattern_t *mask_pattern,
+ cairo_operator_t op,
+ int *src_x, int *src_y,
+ int *mask_x, int *mask_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_directfb_surface_t **ret_src,
+ cairo_surface_attributes_t *ret_src_attr)
+{
+ cairo_directfb_surface_t *src;
+ cairo_surface_attributes_t src_attr;
+ cairo_status_t status;
+ DFBSurfaceBlittingFlags flags;
+ DFBSurfaceBlendFunction sblend;
+ DFBSurfaceBlendFunction dblend;
+ const cairo_color_t *color;
+
+ /* XXX Unbounded operators are not handled correctly */
+ if (! _cairo_operator_bounded_by_source (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _directfb_get_operator (op, &sblend, &dblend))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (mask_pattern) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
+ const cairo_pattern_t *tmp;
+ int tmp_x, tmp_y;
+
+ if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID ||
+ sblend == DSBF_INVDESTALPHA) /* Doesn't work correctly */
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ D_DEBUG_AT (CairoDFB_Render, "Replacing src pattern by mask pattern.\n");
+
+ tmp = src_pattern;
+ tmp_x = *src_x; tmp_y = *src_y;
+
+ src_pattern = mask_pattern;
+ *src_x = *mask_x; *src_y = *mask_y;
+
+ mask_pattern = tmp;
+ *mask_x = tmp_x; *mask_y = tmp_y;
+
+ if (sblend == DSBF_ONE) {
+ sblend = DSBF_SRCALPHA;
+ /*dblend = DSBF_INVSRCALPHA;*/
+ }
+ }
+
+ color = &((cairo_solid_pattern_t *) mask_pattern)->color;
+ } else {
+ color = _cairo_stock_color (CAIRO_STOCK_WHITE);
+ }
+
+ status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
+ *src_x, *src_y, width, height,
+ CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
+ (cairo_surface_t **) &src,
+ &src_attr);
+ if (status)
+ return status;
+
+ if (src->base.backend != &_cairo_directfb_surface_backend ||
+ src->dfb != dst->dfb)
+ {
+ _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if ((src->base.content & CAIRO_CONTENT_ALPHA) == 0) {
+ if (sblend == DSBF_SRCALPHA)
+ sblend = DSBF_ONE;
+ else if (sblend == DSBF_INVSRCALPHA)
+ sblend = DSBF_ZERO;
+
+ if (dblend == DSBF_SRCALPHA)
+ dblend = DSBF_ONE;
+ else if (dblend == DSBF_INVSRCALPHA)
+ dblend = DSBF_ZERO;
+ }
+
+ if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) {
+ if (sblend == DSBF_DESTALPHA)
+ sblend = DSBF_ONE;
+ else if (sblend == DSBF_INVDESTALPHA)
+ sblend = DSBF_ZERO;
+
+ if (dblend == DSBF_DESTALPHA)
+ dblend = DSBF_ONE;
+ else if (dblend == DSBF_INVDESTALPHA)
+ dblend = DSBF_ZERO;
+ }
+
+ flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO)
+ ? DSBLIT_NOFX : DSBLIT_BLEND_ALPHACHANNEL;
+ if (! CAIRO_COLOR_IS_OPAQUE (color))
+ flags |= DSBLIT_BLEND_COLORALPHA;
+ if (! _cairo_color_equal (color, _cairo_stock_color (CAIRO_STOCK_WHITE)))
+ flags |= DSBLIT_COLORIZE;
+
+ dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags);
+
+ if (flags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA)) {
+ dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend);
+ dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend);
+ }
+
+ if (flags & (DSBLIT_BLEND_COLORALPHA | DSBLIT_COLORIZE)) {
+ if (dst->blit_premultiplied) {
+ dst->dfbsurface->SetColor (dst->dfbsurface,
+ color->red_short >> 8,
+ color->green_short >> 8,
+ color->blue_short >> 8,
+ color->alpha_short >> 8);
+ } else {
+ dst->dfbsurface->SetColor (dst->dfbsurface,
+ color->red * 0xff,
+ color->green * 0xff,
+ color->blue * 0xff,
+ color->alpha * 0xff);
+ }
+ }
+
+ *ret_src = src;
+ *ret_src_attr = src_attr;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_directfb_finish_composite (cairo_directfb_surface_t *dst,
+ const cairo_pattern_t *src_pattern,
+ cairo_surface_t *src,
+ cairo_surface_attributes_t *src_attr)
+{
+ _cairo_pattern_release_surface (src_pattern, src, src_attr);
+}
+#endif /* DFB_COMPOSITE || DFB_COMPOSITE_TRAPEZOIDS */
+
+#if DFB_COMPOSITE
+static DFBAccelerationMask
+_directfb_categorize_operation (cairo_surface_attributes_t *src_attr)
+{
+ cairo_matrix_t *m = &src_attr->matrix;
+
+ if (m->xy != 0 || m->yx != 0 || m->xx < 0 || m->yy < 0) {
+ if (src_attr->extend != CAIRO_EXTEND_NONE)
+ return DFXL_NONE;
+
+ return DFXL_TEXTRIANGLES;
+ }
+
+ if (m->xx != 1 || m->yy != 1) {
+ if (src_attr->extend != CAIRO_EXTEND_NONE)
+ return DFXL_NONE;
+
+ return DFXL_STRETCHBLIT;
+ }
+
+ switch (src_attr->extend) {
+ case CAIRO_EXTEND_NONE:
+ case CAIRO_EXTEND_REPEAT:
+ if (_cairo_matrix_is_integer_translation (&src_attr->matrix,
+ NULL, NULL))
+ {
+ return DFXL_BLIT;
+ }
+ else
+ {
+ return DFXL_STRETCHBLIT;
+ }
+
+ default:
+ case CAIRO_EXTEND_REFLECT:
+ case CAIRO_EXTEND_PAD:
+ return DFXL_NONE;
+ }
+}
+
+static cairo_int_status_t
+_cairo_directfb_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ const cairo_pattern_t *mask_pattern,
+ void *abstract_dst,
+ int src_x, int src_y,
+ int mask_x, int mask_y,
+ int dst_x, int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_directfb_surface_t *dst = abstract_dst;
+ cairo_directfb_surface_t *src;
+ cairo_surface_attributes_t src_attr;
+ cairo_bool_t is_integer_translation;
+ DFBAccelerationMask accel, mask;
+ cairo_int_status_t status;
+ int tx, ty;
+
+ D_DEBUG_AT (CairoDFB_Render,
+ "%s( op=%d, src_pattern=%p, mask_pattern=%p, dst=%p,"
+ " src_x=%d, src_y=%d, mask_x=%d, mask_y=%d, dst_x=%d,"
+ " dst_y=%d, width=%u, height=%u ).\n",
+ __FUNCTION__, op, src_pattern, mask_pattern, dst,
+ src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height);
+
+ if (! dst->supported_destination)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _directfb_prepare_composite (dst, src_pattern, mask_pattern, op,
+ &src_x, &src_y, &mask_x, &mask_y,
+ width, height, &src, &src_attr);
+ if (status)
+ return status;
+
+ accel = _directfb_categorize_operation (&src_attr);
+ if (accel == DFXL_NONE) {
+ _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ dst->dfbsurface->GetAccelerationMask (dst->dfbsurface,
+ src->dfbsurface,
+ &mask);
+ if ((mask & accel) == 0) {
+ D_DEBUG_AT (CairoDFB_Render, "No acceleration (%08x)!\n", accel);
+ if (accel != DFXL_BLIT) {
+ _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ src_x += src_attr.x_offset;
+ src_y += src_attr.y_offset;
+
+ switch ((int) accel) {
+ case DFXL_BLIT:
+ {
+ DFBRectangle sr;
+
+ is_integer_translation =
+ _cairo_matrix_is_integer_translation (&src_attr.matrix,
+ &tx, &ty);
+ assert (is_integer_translation);
+
+ sr.x = src_x + tx;
+ sr.y = src_y + ty;
+ sr.w = width;
+ sr.h = height;
+
+ if (src_attr.extend == CAIRO_EXTEND_NONE) {
+ D_DEBUG_AT (CairoDFB_Render, "Running Blit().\n");
+
+ RUN_CLIPPED (dst, clip_region, NULL,
+ dst->dfbsurface->Blit (dst->dfbsurface,
+ src->dfbsurface,
+ &sr, dst_x, dst_y));
+ } else if (src_attr.extend == CAIRO_EXTEND_REPEAT) {
+ DFBRegion clip;
+
+ clip.x1 = dst_x;
+ clip.y1 = dst_y;
+ clip.x2 = dst_x + width - 1;
+ clip.y2 = dst_y + height - 1;
+
+ D_DEBUG_AT (CairoDFB_Render, "Running TileBlit().\n");
+
+ RUN_CLIPPED (dst, clip_region, &clip,
+ dst->dfbsurface->TileBlit (dst->dfbsurface,
+ src->dfbsurface,
+ &sr, dst_x, dst_y));
+ }
+ break;
+ }
+
+ case DFXL_STRETCHBLIT:
+ {
+ DFBRectangle sr, dr;
+ double x1, y1, x2, y2;
+
+ TRANSFORM_POINT2X (src_attr.matrix,
+ src_x, src_y, x1, y1);
+ TRANSFORM_POINT2X (src_attr.matrix,
+ src_x+width, src_y+height, x2, y2);
+
+ sr.x = floor (x1);
+ sr.y = floor (y1);
+ sr.w = ceil (x2) - sr.x;
+ sr.h = ceil (y2) - sr.y;
+
+ dr.x = dst_x;
+ dr.y = dst_y;
+ dr.w = width;
+ dr.h = height;
+
+ D_DEBUG_AT (CairoDFB_Render, "Running StretchBlit().\n");
+
+ RUN_CLIPPED (dst, clip_region, NULL,
+ dst->dfbsurface->StretchBlit (dst->dfbsurface,
+ src->dfbsurface,
+ &sr, &dr));
+ break;
+ }
+
+ case DFXL_TEXTRIANGLES:
+ {
+ DFBRegion clip;
+ DFBVertex v[4];
+ float x1, y1, x2, y2;
+ int w, h;
+
+ status = cairo_matrix_invert (&src_attr.matrix);
+ /* guaranteed by cairo_pattern_set_matrix (); */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ x1 = src_x;
+ y1 = src_y;
+ x2 = width + x1;
+ y2 = height + y1;
+
+ src->dfbsurface->GetSize (src->dfbsurface, &w, &h);
+
+ TRANSFORM_POINT3X (src_attr.matrix, x1, y1, v[0].x, v[0].y);
+ v[0].z = 0;
+ v[0].w = 1;
+ v[0].s = x1 / w;
+ v[0].t = y1 / h;
+
+ TRANSFORM_POINT3X (src_attr.matrix, x2, y1, v[1].x, v[1].y);
+ v[1].z = 0;
+ v[1].w = 1;
+ v[1].s = x2 / w;
+ v[1].t = y1 / h;
+
+ TRANSFORM_POINT3X (src_attr.matrix, x2, y2, v[2].x, v[2].y);
+ v[2].z = 0;
+ v[2].w = 1;
+ v[2].s = x2 / w;
+ v[2].t = y2 / h;
+
+ TRANSFORM_POINT3X (src_attr.matrix, x1, y2, v[3].x, v[3].y);
+ v[3].z = 0;
+ v[3].w = 1;
+ v[3].s = x1 / w;
+ v[3].t = y2 / h;
+
+ clip.x1 = dst_x;
+ clip.y1 = dst_y;
+ clip.x2 = dst_x + width - 1;
+ clip.y2 = dst_y + height - 1;
+
+ D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n");
+
+ RUN_CLIPPED (dst, clip_region, &clip,
+ dst->dfbsurface->TextureTriangles (dst->dfbsurface,
+ src->dfbsurface,
+ v, NULL,
+ 4, DTTF_FAN));
+ break;
+ }
+
+ default:
+ D_BUG ("Unexpected operation");
+ break;
+ }
+
+ _directfb_finish_composite (dst, src_pattern, &src->base, &src_attr);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif /* DFB_COMPOSITE */
+
+#if DFB_RECTANGLES
+static cairo_int_status_t
+_cairo_directfb_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int n_rects)
+{
+ cairo_directfb_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
+
+#if DFB_COMPOSITE_TRAPEZOIDS
+static cairo_int_status_t
+_cairo_directfb_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region)
+{
+ cairo_directfb_surface_t *dst = abstract_dst;
+ cairo_directfb_surface_t *src;
+ cairo_surface_attributes_t src_attr;
+ cairo_status_t status;
+ DFBAccelerationMask accel;
+
+ D_DEBUG_AT (CairoDFB_Render,
+ "%s( op=%d, pattern=%p, dst=%p, antialias=%d,"
+ " src_x=%d, src_y=%d, dst_x=%d, dst_y=%d,"
+ " width=%u, height=%u, traps=%p, num_traps=%d ).\n",
+ __FUNCTION__, op, pattern, dst, antialias,
+ src_x, src_y, dst_x, dst_y, width, height, traps, num_traps);
+
+ if (! dst->supported_destination)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (antialias != CAIRO_ANTIALIAS_NONE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Textures are not supported yet. */
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _directfb_prepare_composite (dst, pattern, NULL, op,
+ &src_x, &src_y, NULL, NULL,
+ width, height, &src, &src_attr);
+ if (status)
+ return status;
+
+ dst->dfbsurface->GetAccelerationMask (dst->dfbsurface,
+ src->dfbsurface,
+ &accel);
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (accel & DFXL_TEXTRIANGLES) {
+ DFBVertex vertex[6*num_traps];
+ DFBVertex *v = &vertex[0];
+ int n = 0;
+
+#define ADD_TRI_V(V, X, Y) do { \
+ (V)->x = (X); (V)->y = (Y); (V)->w = 1; (V)->z = (V)->s = (V)->t = 0; \
+} while (0)
+#define ADD_TRI(id, x1, y1, x2, y2, x3, y3) do {\
+ const int p = (id)*3;\
+ ADD_TRI_V (v+p+0, x1, y1); \
+ ADD_TRI_V (v+p+1, x2, y2); \
+ ADD_TRI_V (v+p+2, x3, y3); \
+} while (0)
+ while (num_traps--) {
+ double lx1, ly1, lx2, ly2;
+ double rx1, ry1, rx2, ry2;
+
+ lx1 = _cairo_fixed_to_double (traps->left.p1.x);
+ ly1 = _cairo_fixed_to_double (traps->left.p1.y);
+ lx2 = _cairo_fixed_to_double (traps->left.p2.x);
+ ly2 = _cairo_fixed_to_double (traps->left.p2.y);
+ rx1 = _cairo_fixed_to_double (traps->right.p1.x);
+ ry1 = _cairo_fixed_to_double (traps->right.p1.y);
+ rx2 = _cairo_fixed_to_double (traps->right.p2.x);
+ ry2 = _cairo_fixed_to_double (traps->right.p2.y);
+
+ if (traps->left.p1.y < traps->top) {
+ double y = _cairo_fixed_to_double (traps->top);
+ if (lx2 != lx1)
+ lx1 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1;
+ ly1 = y;
+ }
+ if (traps->left.p2.y > traps->bottom) {
+ double y = _cairo_fixed_to_double (traps->bottom);
+ if (lx2 != lx1)
+ lx2 = (y - ly1) * (lx2 - lx1) / (ly2 - ly1) + lx1;
+ ly2 = y;
+ }
+
+ if (traps->right.p1.y < traps->top) {
+ double y = _cairo_fixed_to_double (traps->top);
+ if (rx2 != rx1)
+ rx1 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1;
+ ry1 = y;
+ }
+ if (traps->right.p2.y > traps->bottom) {
+ double y = _cairo_fixed_to_double (traps->bottom);
+ if (rx2 != rx1)
+ rx2 = (y - ry1) * (rx2 - rx1) / (ry2 - ry1) + rx1;
+ ry2 = y;
+ }
+
+ if (lx1 == rx1 && ly1 == ry1) {
+ ADD_TRI (0, lx2, ly2, lx1, ly1, rx2, ry2);
+ v += 3;
+ n += 3;
+ } else if (lx2 == rx2 && ly2 == ry2) {
+ ADD_TRI (0, lx1, ly1, lx2, ly2, rx1, ry1);
+ v += 3;
+ n += 3;
+ } else {
+ ADD_TRI (0, lx1, ly1, rx1, ry1, lx2, ly2);
+ ADD_TRI (1, lx2, ly2, rx1, ry1, rx2, ry2);
+ v += 6;
+ n += 6;
+ }
+
+ traps++;
+ }
+#undef ADD_TRI
+#undef ADD_TRI_V
+
+ D_DEBUG_AT (CairoDFB_Render, "Running TextureTriangles().\n");
+
+ RUN_CLIPPED (dst, clip_region, NULL,
+ dst->dfbsurface->TextureTriangles (dst->dfbsurface,
+ src->dfbsurface,
+ vertex, NULL, n,
+ DTTF_LIST));
+
+ status = CAIRO_STATUS_SUCCESS;
+ }
+
+ _directfb_finish_composite (dst, pattern, &src->base, &src_attr);
+
+ return status;
+}
+#endif /* DFB_COMPOSITE_TRAPEZOIDS */
+
+static cairo_bool_t
+_cairo_directfb_abstract_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_directfb_surface_t *surface = abstract_surface;
+
+ D_DEBUG_AT (CairoDFB_Surface,
+ "%s( surface=%p, rectangle=%p ).\n",
+ __FUNCTION__, surface, rectangle);
+
+ if (!surface->local) {
+ surface->dfbsurface->GetSize (surface->dfbsurface,
+ &surface->width, &surface->height);
+ }
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+#if DFB_SHOW_GLYPHS
+static cairo_status_t
+_directfb_allocate_font_cache (IDirectFB *dfb,
+ int width, int height,
+ cairo_directfb_font_cache_t **out)
+{
+ cairo_directfb_font_cache_t *cache;
+ cairo_status_t status;
+
+ cache = calloc (1, sizeof (cairo_directfb_font_cache_t));
+ if (cache == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ cache->dfb = dfb;
+ status = _directfb_buffer_surface_create (dfb,
+ _directfb_argb_font ? DSPF_ARGB : DSPF_A8,
+ width, height,
+ &cache->dfbsurface);
+ if (status) {
+ free (cache);
+ return status;
+ }
+
+ cache->width = width;
+ cache->height = height;
+ *out = cache;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_directfb_destroy_font_cache (cairo_directfb_font_cache_t *cache)
+{
+ cache->dfbsurface->Release (cache->dfbsurface);
+ free (cache);
+}
+
+/* XXX hook into rtree font cache from drm */
+static cairo_status_t
+_directfb_acquire_font_cache (cairo_directfb_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_directfb_font_cache_t **ret_cache,
+ DFBRectangle *rects,
+ DFBPoint *points,
+ int *ret_num)
+{
+ cairo_status_t status;
+ cairo_scaled_glyph_t *chars[num_glyphs];
+ int num_chars = 0;
+ cairo_directfb_font_cache_t *cache = NULL;
+ int n = 0;
+ int x = 0;
+ int y = 0;
+ int w = 8;
+ int h = 8;
+ int i;
+
+ D_DEBUG_AT (CairoDFB_Font, "%s( %p [%d] )\n", __FUNCTION__, glyphs, num_glyphs );
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+
+ if (scaled_font->surface_private) {
+ cache = scaled_font->surface_private;
+ x = cache->x;
+ y = cache->y;
+ }
+
+ for (i = 0; i < num_glyphs; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_image_surface_t *img;
+
+ D_DEBUG_AT (CairoDFB_Font, " -> [%2d] = %4lu\n", i, glyphs[i].index );
+
+ status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+ if (status) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return status;
+ }
+
+ img = scaled_glyph->surface;
+ switch (img->format) {
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_A8:
+ case CAIRO_FORMAT_ARGB32:
+ break;
+ case CAIRO_FORMAT_RGB24:
+ default:
+ D_DEBUG_AT (CairoDFB_Font,
+ " -> Unsupported font format %d!\n", img->format);
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ points[n].x = _cairo_lround (glyphs[i].x - img->base.device_transform.x0);
+ points[n].y = _cairo_lround (glyphs[i].y - img->base.device_transform.y0);
+
+ // D_DEBUG_AT (CairoDFB_Font, " (%4d,%4d) [%2d]\n", points[n].x, points[n].y, n );
+
+ if (points[n].x >= surface->width ||
+ points[n].y >= surface->height ||
+ points[n].x+img->width <= 0 ||
+ points[n].y+img->height <= 0)
+ {
+ continue;
+ }
+
+ if (scaled_glyph->surface_private == NULL) {
+ DFBRectangle *rect;
+
+ if (x+img->width > 2048) {
+ x = 0;
+ y = h;
+ h = 0;
+ }
+
+ rects[n].x = x;
+ rects[n].y = y;
+ rects[n].w = img->width;
+ rects[n].h = img->height;
+
+ x += img->width;
+ h = MAX (h, img->height);
+ w = MAX (w, x);
+
+ /* Remember glyph location */
+ rect = malloc (sizeof (DFBRectangle));
+ if (rect == NULL) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ *rect = rects[n];
+
+ scaled_glyph->surface_private = rect;
+ chars[num_chars++] = scaled_glyph;
+
+ D_DEBUG_AT (CairoDFB_Font, " -> loading at %4d,%2d <- rect %p, img %p, entry %p\n",
+ rects[n].x, rects[n].y, rect, scaled_glyph->surface, scaled_glyph);
+ } else {
+ rects[n] = *(DFBRectangle *) scaled_glyph->surface_private;
+
+ D_DEBUG_AT (CairoDFB_Font, " -> exists at %4d,%2d\n", rects[n].x, rects[n].y);
+ }
+
+ n++;
+ }
+
+ if (n == 0) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ }
+
+ h += y;
+ w = MAX (w, 8);
+ h = MAX (h, 8);
+
+ /* XXX query maximum surface size */
+ if (w > 2048 || h > 2048) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (cache) {
+ if (cache->width < w || cache->height < h) {
+ cairo_directfb_font_cache_t *new_cache;
+
+ w = MAX (w, cache->width);
+ h = MAX (h, cache->height);
+
+ D_DEBUG_AT (CairoDFB_Font, " -> Reallocating font cache (%dx%d).\n", w, h);
+
+ status = _directfb_allocate_font_cache (surface->dfb,
+ w, h,
+ &new_cache);
+ if (status) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return status;
+ }
+
+ new_cache->dfbsurface->Blit (new_cache->dfbsurface,
+ cache->dfbsurface, NULL, 0, 0);
+
+ _directfb_destroy_font_cache (cache);
+ scaled_font->surface_private = cache = new_cache;
+ }
+ } else {
+ D_DEBUG_AT (CairoDFB_Font, " -> Allocating font cache (%dx%d).\n", w, h);
+
+ status = _directfb_allocate_font_cache (surface->dfb, w, h, &cache);
+ if (status) {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return status;
+ }
+
+ scaled_font->surface_backend = &_cairo_directfb_surface_backend;
+ scaled_font->surface_private = cache;
+ }
+
+ if (num_chars) {
+ unsigned char *data;
+ int pitch;
+
+ if (cache->dfbsurface->Lock (cache->dfbsurface,
+ DSLF_WRITE, (void *)&data, &pitch))
+ {
+ _cairo_scaled_font_thaw_cache (scaled_font);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ D_DEBUG_AT (CairoDFB_Font, " => %d chars to load, cache %dx%d\n", num_chars, cache->width, cache->height);
+
+ for (i = 0; i < num_chars; i++) {
+ cairo_image_surface_t *img = chars[i]->surface;
+ DFBRectangle *rect = chars[i]->surface_private;
+ unsigned char *dst = data;
+ unsigned char *src;
+ int j;
+
+ D_DEBUG_AT (CairoDFB_Font, " -> loading [%2d] <- rect %p, img %p, entry %p\n", i, rect, img, chars[i]);
+
+ src = img->data;
+
+ D_DEBUG_AT (CairoDFB_Font, " from %p\n", src);
+
+ dst += rect->y * pitch + (_directfb_argb_font ? (rect->x<<2) : rect->x);
+
+ D_DEBUG_AT (CairoDFB_Font, " to %4d,%2d (%p)\n", rect->x, rect->y, dst);
+
+ if (img->format == CAIRO_FORMAT_A1) {
+ for (h = rect->h; h; h--) {
+ if (_directfb_argb_font) {
+ for (j = 0; j < rect->w; j++)
+ ((uint32_t *) dst)[j] = (src[j>>3] & (1 << (j&7))) ? 0xffffffff : 0;
+ } else {
+ for (j = 0; j < rect->w; j++)
+ dst[j] = (src[j>>3] & (1 << (j&7))) ? 0xff : 0;
+ }
+
+ dst += pitch;
+ src += img->stride;
+ }
+ } else if (img->format == CAIRO_FORMAT_A8) {
+ for (h = rect->h; h; h--) {
+ if (_directfb_argb_font) {
+ for (j = 0; j < rect->w; j++)
+ ((uint32_t *) dst)[j] = src[j] * 0x01010101;
+ } else {
+ direct_memcpy (dst, src, rect->w);
+ }
+
+ dst += pitch;
+ src += img->stride;
+ }
+ } else { /* ARGB32 */
+ for (h = rect->h; h; h--) {
+ if (_directfb_argb_font) {
+ direct_memcpy (dst, src, rect->w<<2);
+ } else {
+ for (j = 0; j < rect->w; j++)
+ dst[j] = ((uint32_t *) src)[j] >> 24;
+ }
+
+ dst += pitch;
+ src += img->stride;
+ }
+ }
+ }
+
+ cache->dfbsurface->Unlock (cache->dfbsurface);
+ }
+
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ cache->x = x;
+ cache->y = y;
+
+ D_DEBUG_AT (CairoDFB_Font, " => cache %d,%d, %p [%d]\n", x, y, cache, n);
+
+ *ret_cache = cache;
+ *ret_num = n;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_directfb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+ cairo_directfb_font_cache_t *cache = scaled_font->surface_private;
+
+ D_DEBUG_AT (CairoDFB_Font,
+ "%s( scaled_font=%p ).\n", __FUNCTION__, scaled_font);
+
+ if (cache != NULL) {
+ _directfb_destroy_font_cache (cache);
+ scaled_font->surface_private = NULL;
+ }
+}
+
+static void
+_cairo_directfb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font)
+{
+ D_DEBUG_AT (CairoDFB_Font,
+ "%s( scaled_glyph=%p, scaled_font=%p ).\n",
+ __FUNCTION__, scaled_glyph, scaled_font);
+
+ if (scaled_glyph->surface_private != NULL) {
+ free (scaled_glyph->surface_private);
+ scaled_glyph->surface_private = NULL;
+ }
+}
+
+static cairo_int_status_t
+_cairo_directfb_surface_show_glyphs (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_directfb_surface_t *dst = abstract_dst;
+ cairo_directfb_font_cache_t *cache;
+ cairo_status_t status;
+ DFBSurfaceBlittingFlags flags;
+ DFBSurfaceBlendFunction sblend;
+ DFBSurfaceBlendFunction dblend;
+ DFBRectangle rects[num_glyphs];
+ DFBPoint points[num_glyphs];
+ int num;
+ const cairo_color_t *color;
+ cairo_region_t *clip_region = NULL;
+
+ D_DEBUG_AT (CairoDFB_Font,
+ "%s( dst=%p, op=%d, pattern=%p, glyphs=%p, num_glyphs=%d, scaled_font=%p ).\n",
+ __FUNCTION__, dst, op, pattern, glyphs, num_glyphs, scaled_font);
+
+ if (! dst->supported_destination)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Fallback if we need to emulate clip regions */
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+ if (status)
+ return status;
+ }
+
+ /* XXX Unbounded operators are not handled correctly */
+ if (! _cairo_operator_bounded_by_mask (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _directfb_get_operator (op, &sblend, &dblend) ||
+ sblend == DSBF_DESTALPHA || sblend == DSBF_INVDESTALPHA)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = _directfb_acquire_font_cache (dst, scaled_font, glyphs, num_glyphs,
+ &cache, &rects[0], &points[0], &num);
+ if (status) {
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ status = CAIRO_STATUS_SUCCESS;
+ return status;
+ }
+
+ color = &((cairo_solid_pattern_t *) pattern)->color;
+
+ flags = DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_COLORIZE;
+ if (! CAIRO_COLOR_IS_OPAQUE (color))
+ flags |= DSBLIT_BLEND_COLORALPHA;
+
+ if (!_directfb_argb_font) {
+ if (sblend == DSBF_ONE) {
+ sblend = DSBF_SRCALPHA;
+ if (dblend == DSBF_ZERO)
+ dblend = DSBF_INVSRCALPHA;
+ }
+ }
+
+ dst->dfbsurface->SetBlittingFlags (dst->dfbsurface, flags);
+ dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend);
+ dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend);
+ if (dst->blit_premultiplied) {
+ dst->dfbsurface->SetColor (dst->dfbsurface,
+ color->red_short >> 8,
+ color->green_short >> 8,
+ color->blue_short >> 8,
+ color->alpha_short >> 8);
+ } else {
+ dst->dfbsurface->SetColor (dst->dfbsurface,
+ color->red * 0xff,
+ color->green * 0xff,
+ color->blue * 0xff,
+ color->alpha * 0xff);
+ }
+
+ D_DEBUG_AT (CairoDFB_Font, "Running BatchBlit().\n");
+
+ RUN_CLIPPED (dst, clip_region, NULL,
+ dst->dfbsurface->BatchBlit (dst->dfbsurface,
+ cache->dfbsurface, rects, points, num));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif /* DFB_SHOW_GLYPHS */
+
+
+static cairo_bool_t
+_cairo_directfb_surface_is_similar (void *surface_a, void *surface_b)
+{
+ cairo_directfb_surface_t *a = (cairo_directfb_surface_t *) surface_a;
+ cairo_directfb_surface_t *b = (cairo_directfb_surface_t *) surface_b;
+
+ return a->dfb == b->dfb;
+}
+
+static cairo_surface_backend_t
+_cairo_directfb_surface_backend = {
+ CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/
+ _cairo_directfb_surface_create_similar,/*create_similar*/
+ _cairo_directfb_surface_finish, /*finish*/
+ _cairo_directfb_surface_acquire_source_image,/*acquire_source_image*/
+ _cairo_directfb_surface_release_source_image,/*release_source_image*/
+ _cairo_directfb_surface_acquire_dest_image,/*acquire_dest_image*/
+ _cairo_directfb_surface_release_dest_image,/*release_dest_image*/
+ _cairo_directfb_surface_clone_similar,/*clone_similar*/
+#if DFB_COMPOSITE
+ _cairo_directfb_surface_composite,/*composite*/
+#else
+ NULL,/*composite*/
+#endif
+#if DFB_RECTANGLES
+ _cairo_directfb_surface_fill_rectangles,/*fill_rectangles*/
+#else
+ NULL,/*fill_rectangles*/
+#endif
+#if DFB_COMPOSITE_TRAPEZOIDS
+ _cairo_directfb_surface_composite_trapezoids,/*composite_trapezoids*/
+#else
+ NULL,/*composite_trapezoids*/
+#endif
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_directfb_abstract_surface_get_extents,/* get_extents */
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+#if DFB_SHOW_GLYPHS
+ _cairo_directfb_surface_scaled_font_fini,/* scaled_font_fini */
+ _cairo_directfb_surface_scaled_glyph_fini,/* scaled_glyph_fini */
+#else
+ NULL,
+ NULL,
+#endif
+ NULL, /* paint */
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+#if DFB_SHOW_GLYPHS
+ _cairo_directfb_surface_show_glyphs,/* show_glyphs */
+#else
+ NULL, /* show_glyphs */
+#endif
+ NULL, /* snapshot */
+ _cairo_directfb_surface_is_similar,
+};
+
+
+static void
+cairo_directfb_surface_backend_init (IDirectFB *dfb)
+{
+ static int done = 0;
+
+ if (done)
+ return;
+
+ if (getenv ("CAIRO_DIRECTFB_NO_ACCEL")) {
+#if DFB_RECTANGLES
+ _cairo_directfb_surface_backend.fill_rectangles = NULL;
+#endif
+#if DFB_COMPOSITE
+ _cairo_directfb_surface_backend.composite = NULL;
+#endif
+#if DFB_COMPOSITE_TRAPEZOIDS
+ _cairo_directfb_surface_backend.composite_trapezoids = NULL;
+#endif
+#if DFB_SHOW_GLYPHS
+ _cairo_directfb_surface_backend.scaled_font_fini = NULL;
+ _cairo_directfb_surface_backend.scaled_glyph_fini = NULL;
+ _cairo_directfb_surface_backend.show_glyphs = NULL;
+#endif
+ D_DEBUG_AT (CairoDFB_Surface, "Acceleration disabled.\n");
+ } else {
+ DFBGraphicsDeviceDescription dsc;
+
+ dfb->GetDeviceDescription (dfb, &dsc);
+
+#if DFB_COMPOSITE
+ // if (!(dsc.acceleration_mask & DFXL_BLIT))
+ // _cairo_directfb_surface_backend.composite = NULL;
+#endif
+
+#if DFB_COMPOSITE_TRAPEZOIDS
+ // if (!(dsc.acceleration_mask & DFXL_TEXTRIANGLES))
+ // _cairo_directfb_surface_backend.composite_trapezoids = NULL;
+#endif
+ }
+
+ if (getenv ("CAIRO_DIRECTFB_ARGB_FONT")) {
+ _directfb_argb_font = 1;
+ D_DEBUG_AT (CairoDFB_Surface, "Using ARGB fonts.\n");
+ }
+
+ done = 1;
+}
+
+cairo_surface_t *
+cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface)
+{
+ cairo_directfb_surface_t *surface;
+ DFBSurfacePixelFormat format;
+ DFBSurfaceCapabilities caps;
+
+ D_ASSERT (dfb != NULL);
+ D_ASSERT (dfbsurface != NULL);
+
+ cairo_directfb_surface_backend_init (dfb);
+
+ surface = calloc (1, sizeof (cairo_directfb_surface_t));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ dfbsurface->AddRef (dfbsurface);
+ dfbsurface->GetPixelFormat (dfbsurface, &format);
+ dfbsurface->GetSize (dfbsurface, &surface->width, &surface->height);
+ surface->dfb = dfb;
+ surface->dfbsurface = dfbsurface;
+ surface->pixman_format = _directfb_to_pixman_format (format);
+ surface->supported_destination = pixman_format_supported_destination (surface->pixman_format);
+
+ dfbsurface->GetCapabilities (dfbsurface, &caps);
+ if (caps & DSCAPS_PREMULTIPLIED)
+ surface->blit_premultiplied = TRUE;
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_directfb_surface_backend,
+ NULL, /* device */
+ _directfb_format_to_content (format));
+
+ return &surface->base;
+}
diff --git a/gfx/cairo/cairo/src/cairo-directfb.h b/gfx/cairo/cairo/src/cairo-directfb.h
new file mode 100644
index 000000000..e3d818c66
--- /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 <cworth@isi.edu>
+ */
+
+/*
+ * 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 <directfb.h>
+
+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 000000000..907610dcd
--- /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-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
new file mode 100644
index 000000000..f3d45da1e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
@@ -0,0 +1,1622 @@
+/* -*- 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 <bschouten@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-win32-private.h"
+#include "cairo-surface-private.h"
+#include "cairo-clip-private.h"
+
+#include "cairo-d2d-private.h"
+#include "cairo-dwrite-private.h"
+#include "cairo-truetype-subset-private.h"
+#include <float.h>
+
+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 void
+_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_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,
+ 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,
+ _cairo_dwrite_ucs4_to_index,
+ _cairo_dwrite_scaled_show_glyphs,
+ _cairo_dwrite_load_truetype_table,
+ NULL,
+};
+
+/* 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
+ * 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 void
+_cairo_dwrite_font_face_destroy (void *font_face)
+{
+ cairo_dwrite_font_face_t *dwrite_font_face = static_cast<cairo_dwrite_font_face_t*>(font_face);
+ if (dwrite_font_face->dwriteface)
+ dwrite_font_face->dwriteface->Release();
+ if (dwrite_font_face->font)
+ dwrite_font_face->font->Release();
+}
+
+
+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<UINT16*>(run->glyphIndices);
+ FLOAT *advances = const_cast<FLOAT*>(run->glyphAdvances);
+ DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run->glyphOffsets);
+
+ cairo_dwrite_font_face_t *dwriteff = reinterpret_cast<cairo_dwrite_font_face_t*>(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<cairo_dwrite_font_face_t*>(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<cairo_scaled_font_t*>(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->manual_show_glyphs_allowed = TRUE;
+ dwriteFont->rendering_mode =
+ default_quality == CAIRO_ANTIALIAS_SUBPIXEL ?
+ cairo_d2d_surface_t::TEXT_RENDERING_NORMAL : cairo_d2d_surface_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<cairo_dwrite_scaled_font_t*>(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<cairo_dwrite_scaled_font_t*>(scaled_font);
+ cairo_dwrite_font_face_t *face = reinterpret_cast<cairo_dwrite_font_face_t*>(dwritesf->base.font_face);
+
+ UINT16 index;
+ face->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index);
+ 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<cairo_dwrite_scaled_font_t*>(scaled_font);
+ BOOL transform = FALSE;
+
+ AutoDWriteGlyphRun run;
+ run.allocate(num_glyphs);
+ UINT16 *indices = const_cast<UINT16*>(run.glyphIndices);
+ FLOAT *advances = const_cast<FLOAT*>(run.glyphAdvances);
+ DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(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,
+ 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<IDWriteGeometrySink*>(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 also stolen from 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)
+{
+ cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image;
+ cairo_image_surface_t *image8;
+ int i, j;
+
+ if (image24->base.status)
+ return cairo_surface_reference (&image24->base);
+
+ image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8,
+ image24->width, image24->height);
+ if (image8->base.status)
+ return &image8->base;
+
+ for (i = 0; i < image24->height; i++) {
+ uint32_t *p = (uint32_t *) (image24->data + i * image24->stride);
+ unsigned char *q = (unsigned char *) (image8->data + i * image8->stride);
+
+ for (j = 0; j < image24->width; j++) {
+ *q = 255 - ((*p & 0x0000ff00) >> 8);
+ p++;
+ q++;
+ }
+ }
+
+ return &image8->base;
+}
+
+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);
+ 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<cairo_dwrite_scaled_font_t*>(scaled_font);
+ cairo_dwrite_font_face_t *face = reinterpret_cast<cairo_dwrite_font_face_t*>(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<IDWriteFont*>(dwrite_font);
+ IDWriteFontFace *dwriteface = static_cast<IDWriteFontFace*>(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_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed)
+{
+ cairo_dwrite_scaled_font_t *font = reinterpret_cast<cairo_dwrite_scaled_font_t*>(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<cairo_dwrite_scaled_font_t*>(dwrite_scaled_font);
+ if (force && font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_NORMAL) {
+ font->rendering_mode = cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC;
+ } else if (!force && font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) {
+ font->rendering_mode = cairo_d2d_surface_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<cairo_dwrite_scaled_font_t*>(dwrite_scaled_font);
+ return font->rendering_mode == cairo_d2d_surface_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_d2d_surface_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_d2d_surface_t::TEXT_RENDERING_NORMAL ||
+ renderingState == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) &&
+ !surface->base.permit_subpixel_antialiasing) {
+ renderingState = cairo_d2d_surface_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_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC:
+ case cairo_d2d_surface_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<cairo_dwrite_scaled_font_t*>(scaled_font);
+ cairo_dwrite_font_face_t *dwriteff = reinterpret_cast<cairo_dwrite_font_face_t*>(scaled_font->font_face);
+ cairo_win32_surface_t *dst = reinterpret_cast<cairo_win32_surface_t*>(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 (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);
+ }
+
+ /* 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<UINT16*>(run.glyphIndices);
+ FLOAT *advances = const_cast<FLOAT*>(run.glyphAdvances);
+ DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run.glyphOffsets);
+
+ BOOL transform = FALSE;
+ /* Needed to calculate bounding box for efficient blitting */
+ INT32 smallestX = INT_MAX;
+ INT32 largestX = 0;
+ INT32 smallestY = INT_MAX;
+ INT32 largestY = 0;
+ for (int i = 0; i < num_glyphs; i++) {
+ if (glyphs[i].x < smallestX) {
+ smallestX = (INT32)glyphs[i].x;
+ }
+ if (glyphs[i].x > largestX) {
+ largestX = (INT32)glyphs[i].x;
+ }
+ if (glyphs[i].y < smallestY) {
+ smallestY = (INT32)glyphs[i].y;
+ }
+ if (glyphs[i].y > largestY) {
+ largestY = (INT32)glyphs[i].y;
+ }
+ }
+ /**
+ * Here we try to get a rough estimate of the area that this glyph run will
+ * cover on the surface. Since we use GDI interop to draw we will be copying
+ * data around the size of the area of the surface that we map. We will want
+ * to map an area as small as possible to prevent large surfaces to be
+ * copied around. We take the X/Y-size of the font as margin on the left/top
+ * twice the X/Y-size of the font as margin on the right/bottom.
+ * This should always cover the entire area where the glyphs are.
+ */
+ RECT fontArea;
+ fontArea.left = (INT32)(smallestX - scaled_font->font_matrix.xx);
+ fontArea.right = (INT32)(largestX + scaled_font->font_matrix.xx * 2);
+ fontArea.top = (INT32)(smallestY - scaled_font->font_matrix.yy);
+ fontArea.bottom = (INT32)(largestY + scaled_font->font_matrix.yy * 2);
+ if (fontArea.left < 0)
+ fontArea.left = 0;
+ if (fontArea.top < 0)
+ fontArea.top = 0;
+ if (fontArea.bottom > dst->extents.height) {
+ fontArea.bottom = dst->extents.height;
+ }
+ if (fontArea.right > dst->extents.width) {
+ fontArea.right = dst->extents.width;
+ }
+ if (fontArea.right <= fontArea.left ||
+ fontArea.bottom <= fontArea.top) {
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+ if (fontArea.right > dst->extents.width) {
+ fontArea.right = dst->extents.width;
+ }
+ if (fontArea.bottom > dst->extents.height) {
+ fontArea.bottom = dst->extents.height;
+ }
+
+ run.bidiLevel = 0;
+ run.fontFace = dwriteff->dwriteface;
+ run.isSideways = FALSE;
+ if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 &&
+ dwritesf->mat.xx == scaled_font->font_matrix.xx &&
+ dwritesf->mat.yy == scaled_font->font_matrix.yy) {
+
+ for (int i = 0; i < num_glyphs; i++) {
+ indices[i] = (WORD) glyphs[i].index;
+ // Since we will multiply by our ctm matrix later for rotation effects
+ // and such, adjust positions by the inverse matrix now.
+ offsets[i].ascenderOffset = (FLOAT)(fontArea.top - glyphs[i].y);
+ offsets[i].advanceOffset = (FLOAT)(glyphs[i].x - fontArea.left);
+ advances[i] = 0.0;
+ }
+ run.fontEmSize = (FLOAT)scaled_font->font_matrix.yy;
+ } else {
+ transform = TRUE;
+ // See comment about EPSILON in _cairo_dwrite_glyph_run_from_glyphs
+ const double EPSILON = 0.0001;
+ for (int i = 0; i < num_glyphs; i++) {
+ indices[i] = (WORD) glyphs[i].index;
+ double x = glyphs[i].x - fontArea.left + EPSILON;
+ double y = glyphs[i].y - fontArea.top;
+ cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y);
+ /**
+ * Since we will multiply by our ctm matrix later for rotation effects
+ * and such, adjust positions by the inverse matrix now. The Y-axis
+ * is inverted so the offset becomes negative.
+ */
+ offsets[i].ascenderOffset = -(FLOAT)y;
+ offsets[i].advanceOffset = (FLOAT)x;
+ advances[i] = 0.0;
+ }
+ run.fontEmSize = 1.0f;
+ }
+
+ cairo_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<cairo_dwrite_font_face_t*>(face);
+
+ RefPtr<IDWriteGdiInterop> 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/cairo-dwrite-private.h b/gfx/cairo/cairo/src/cairo-dwrite-private.h
new file mode 100644
index 000000000..7c76abf5e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-dwrite-private.h
@@ -0,0 +1,224 @@
+/* -*- 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 <bschouten@mozilla.com>
+ */
+#include <dwrite_1.h>
+#include <d2d1.h>
+
+// 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;
+ cairo_bool_t manual_show_glyphs_allowed;
+ cairo_d2d_surface_t::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<IUnknown**>(&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_d2d_surface_t::TextRenderingState mode)
+ {
+ if (!mDefaultRenderingParams ||
+ !mForceGDIClassicRenderingParams ||
+ !mCustomClearTypeRenderingParams)
+ {
+ CreateRenderingParams();
+ }
+ IDWriteRenderingParams *params;
+ if (mode == cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE) {
+ params = mDefaultRenderingParams;
+ } else if (mode == cairo_d2d_surface_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/cairo-eagle-context.c b/gfx/cairo/cairo/src/cairo-eagle-context.c
new file mode 100644
index 000000000..23766a944
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-eagle-context.c
@@ -0,0 +1,181 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include <i915_drm.h> /* XXX dummy surface for glewInit() */
+#include <sys/ioctl.h>
+
+typedef struct _cairo_eagle_context {
+ cairo_gl_context_t base;
+
+ EGLDisplay display;
+ EGLContext context;
+} cairo_eagle_context_t;
+
+typedef struct _cairo_eagle_surface {
+ cairo_gl_surface_t base;
+
+ EGLSurface eagle;
+} cairo_eagle_surface_t;
+
+static void
+_eagle_make_current (void *abstract_ctx,
+ cairo_gl_surface_t *abstract_surface)
+{
+ cairo_eagle_context_t *ctx = abstract_ctx;
+ cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface;
+
+ eagleMakeCurrent (ctx->display, surface->eagle, surface->eagle, ctx->context);
+}
+
+static void
+_eagle_swap_buffers (void *abstract_ctx,
+ cairo_gl_surface_t *abstract_surface)
+{
+ cairo_eagle_context_t *ctx = abstract_ctx;
+ cairo_eagle_surface_t *surface = (cairo_eagle_surface_t *) abstract_surface;
+
+ eagleSwapBuffers (ctx->display, surface->eagle);
+}
+
+static void
+_eagle_destroy (void *abstract_ctx)
+{
+}
+
+static cairo_bool_t
+_eagle_init (EGLDisplay display, EGLContext context)
+{
+ const EGLint config_attribs[] = {
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_NONE
+ };
+ const EGLint surface_attribs[] = {
+ EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
+ EGL_NONE
+ };
+ EGLConfig config;
+ EGLSurface dummy;
+ struct drm_i915_gem_create create;
+ struct drm_gem_flink flink;
+ int fd;
+ GLenum err;
+
+ if (! eagleChooseConfig (display, config_attribs, &config, 1, NULL)) {
+ fprintf (stderr, "Unable to choose config\n");
+ return FALSE;
+ }
+
+ /* XXX */
+ fd = eagleGetDisplayFD (display);
+
+ create.size = 4096;
+ if (ioctl (fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) {
+ fprintf (stderr, "gem create failed: %m\n");
+ return FALSE;
+ }
+ flink.handle = create.handle;
+ if (ioctl (fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) {
+ fprintf (stderr, "gem flink failed: %m\n");
+ return FALSE;
+ }
+
+ dummy = eagleCreateSurfaceForName (display, config, flink.name,
+ 1, 1, 4, surface_attribs);
+ if (dummy == NULL) {
+ fprintf (stderr, "Failed to create dummy surface\n");
+ return FALSE;
+ }
+
+ eagleMakeCurrent (display, dummy, dummy, context);
+}
+
+cairo_gl_context_t *
+cairo_eagle_context_create (EGLDisplay display, EGLContext context)
+{
+ cairo_eagle_context_t *ctx;
+ cairo_status_t status;
+
+ if (! _eagle_init (display, context)) {
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ ctx = calloc (1, sizeof (cairo_eagle_context_t));
+ if (ctx == NULL)
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ ctx->display = display;
+ ctx->context = context;
+
+ ctx->base.make_current = _eagle_make_current;
+ ctx->base.swap_buffers = _eagle_swap_buffers;
+ ctx->base.destroy = _eagle_destroy;
+
+ status = _cairo_gl_context_init (&ctx->base);
+ if (status) {
+ free (ctx);
+ return _cairo_gl_context_create_in_error (status);
+ }
+
+ return &ctx->base;
+}
+
+cairo_surface_t *
+cairo_gl_surface_create_for_eagle (cairo_gl_context_t *ctx,
+ EGLSurface eagle,
+ int width,
+ int height)
+{
+ cairo_eagle_surface_t *surface;
+
+ if (ctx->status)
+ return _cairo_surface_create_in_error (ctx->status);
+
+ surface = calloc (1, sizeof (cairo_eagle_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_gl_surface_init (ctx, &surface->base,
+ CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ surface->eagle = eagle;
+
+ return &surface->base.base;
+}
diff --git a/gfx/cairo/cairo/src/cairo-error-private.h b/gfx/cairo/cairo/src/cairo-error-private.h
new file mode 100644
index 000000000..fc0c56438
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-error-private.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 <cworth@cworth.org>
+ */
+
+#ifndef _CAIRO_ERROR_PRIVATE_H_
+#define _CAIRO_ERROR_PRIVATE_H_
+
+#include "cairo.h"
+#include "cairo-compiler-private.h"
+
+CAIRO_BEGIN_DECLS
+
+#define _cairo_status_is_error(status) \
+ (status != CAIRO_STATUS_SUCCESS && status <= CAIRO_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-features-win32.h b/gfx/cairo/cairo/src/cairo-features-win32.h
new file mode 100644
index 000000000..ef202fc14
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-features-win32.h
@@ -0,0 +1,16 @@
+/* Generated by configure. Do not edit. */
+#ifndef CAIRO_FEATURES_H
+#define CAIRO_FEATURES_H
+
+#define HAVE_WINDOWS_H 1
+
+#define CAIRO_HAS_WIN32_SURFACE 1
+#define CAIRO_HAS_WIN32_FONT 1
+#define CAIRO_HAS_PNG_FUNCTIONS 1
+#define CAIRO_HAS_PS_SURFACE 1
+#define CAIRO_HAS_PDF_SURFACE 1
+#define CAIRO_HAS_SVG_SURFACE 1
+#define CAIRO_HAS_IMAGE_SURFACE 1
+#define CAIRO_HAS_USER_FONT 1
+
+#endif
diff --git a/gfx/cairo/cairo/src/cairo-features.h.in b/gfx/cairo/cairo/src/cairo-features.h.in
new file mode 100644
index 000000000..ee76be9ca
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-features.h.in
@@ -0,0 +1,95 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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
+
+#define CAIRO_VERSION_MAJOR 1
+#define CAIRO_VERSION_MINOR 9
+#define CAIRO_VERSION_MICRO 5
+
+@PS_SURFACE_FEATURE@
+
+@PDF_SURFACE_FEATURE@
+
+@SVG_SURFACE_FEATURE@
+
+@XLIB_SURFACE_FEATURE@
+
+@XLIB_XRENDER_SURFACE_FEATURE@
+
+@QUARTZ_SURFACE_FEATURE@
+
+@QUARTZ_IMAGE_SURFACE_FEATURE@
+
+@WIN32_SURFACE_FEATURE@
+
+@OS2_SURFACE_FEATURE@
+
+@DIRECTFB_SURFACE_FEATURE@
+
+@QT_SURFACE_FEATURE@
+
+@FT_FONT_FEATURE@
+
+@WIN32_FONT_FEATURE@
+
+@WIN32_DWRITE_FONT_FEATURE@
+
+@WIN32_D2D_SURFACE_FEATURE@
+
+@QUARTZ_FONT_FEATURE@
+
+@TEE_SURFACE_FEATURE@
+
+@PNG_FUNCTIONS_FEATURE@
+
+@FC_FONT_FEATURE@
+#endif
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 000000000..9478d7d4f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-fixed-private.h
@@ -0,0 +1,358 @@
+/* -*- 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 <vladimir@pobox.com>
+ */
+
+#ifndef CAIRO_FIXED_PRIVATE_H
+#define CAIRO_FIXED_PRIVATE_H
+
+#include "cairo-fixed-type-private.h"
+
+#include "cairo-wideint-private.h"
+
+/* Implementation */
+
+#if (CAIRO_FIXED_BITS != 32)
+# error CAIRO_FIXED_BITS must be 32, and the type must be a 32-bit type.
+# error To remove this limitation, you will have to fix the tesselator.
+#endif
+
+#define CAIRO_FIXED_ONE ((cairo_fixed_t)(1 << CAIRO_FIXED_FRAC_BITS))
+#define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS))
+#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS))
+#define CAIRO_FIXED_EPSILON ((cairo_fixed_t)(1))
+
+#define CAIRO_FIXED_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 float
+_cairo_fixed_to_float (cairo_fixed_t f)
+{
+ return ((float) f) / CAIRO_FIXED_ONE_FLOAT;
+}
+
+static inline int
+_cairo_fixed_is_integer (cairo_fixed_t f)
+{
+ 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_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 - (-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);
+}
+
+
+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;
+}
+
+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;
+}
+
+#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 000000000..2bbd5f786
--- /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 <vladimir@pobox.com>
+ */
+
+#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 tesselator 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 000000000..03e055923
--- /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 <cworth@cworth.org>
+ */
+
+#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 000000000..ff09cb2be
--- /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 000000000..98c6dd8a9
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-font-face-twin.c
@@ -0,0 +1,761 @@
+/*
+ * 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 <keithp@keithp.com>
+ * Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+#include <math.h>
+
+/*
+ * 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; i<n_elements; i++)
+ {
+ if (map[i].str[0] && field_matches (map[i].str, str, len))
+ {
+ if (val)
+ *val = map[i].value;
+ return TRUE;
+ }
+ }
+
+ if (!what || had_prefix)
+ return parse_int (str, len, val);
+
+ return FALSE;
+}
+
+static void
+parse_field (twin_face_properties_t *props,
+ const char *str,
+ int len)
+{
+ if (field_matches ("Normal", str, len))
+ return;
+
+#define FIELD(NAME) \
+ if (find_field (STRINGIFY (NAME), NAME##_map, ARRAY_LENGTH (NAME##_map), str, len, \
+ (int *)(void *)&props->NAME)) \
+ return; \
+
+ FIELD (weight);
+ FIELD (slant);
+ FIELD (stretch);
+ FIELD (smallcaps);
+ FIELD (monospace);
+
+#undef FIELD
+}
+
+static void
+face_props_parse (twin_face_properties_t *props,
+ const char *s)
+{
+ const char *start, *end;
+
+ for (start = end = s; *end; end++) {
+ if (*end != ' ' && *end != ':')
+ continue;
+
+ if (start < end)
+ parse_field (props, start, end - start);
+ start = end + 1;
+ }
+ if (start < end)
+ parse_field (props, start, end - start);
+}
+
+static cairo_status_t
+twin_font_face_create_properties (cairo_font_face_t *twin_face,
+ twin_face_properties_t **props_out)
+{
+ twin_face_properties_t *props;
+ cairo_status_t status;
+
+ props = malloc (sizeof (twin_face_properties_t));
+ if (unlikely (props == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ props->stretch = TWIN_STRETCH_NORMAL;
+ props->slant = CAIRO_FONT_SLANT_NORMAL;
+ props->weight = TWIN_WEIGHT_NORMAL;
+ props->monospace = FALSE;
+ props->smallcaps = FALSE;
+
+ status = cairo_font_face_set_user_data (twin_face,
+ &twin_properties_key,
+ props, free);
+ if (unlikely (status)) {
+ free (props);
+ return status;
+ }
+
+ if (props_out)
+ *props_out = props;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face,
+ cairo_toy_font_face_t *toy_face)
+{
+ cairo_status_t status;
+ twin_face_properties_t *props;
+
+ status = twin_font_face_create_properties (twin_face, &props);
+ if (unlikely (status))
+ return status;
+
+ props->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 = 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;
+ cairo_status_t status;
+
+ twin_font_face = _cairo_font_face_twin_create_internal ();
+ status = twin_font_face_create_properties (twin_font_face, NULL);
+ if (status) {
+ 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 000000000..a66054ead
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-font-face.c
@@ -0,0 +1,308 @@
+/* -*- 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 <cworth@cworth.org>
+ * Graydon Hoare <graydon@redhat.com>
+ * Owen Taylor <otaylor@redhat.com>
+ */
+
+#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 <firstterm>font-backend</firstterm>-specific
+ * constructors, typically of the form
+ * cairo_<emphasis>backend</emphasis>_font_face_create(), or implicitly
+ * using the <firstterm>toy</firstterm> 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
+};
+
+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.
+ *
+ * The number of references to a #cairo_font_face_t can be get using
+ * cairo_font_face_get_reference_count().
+ *
+ * Return value: the referenced #cairo_font_face_t.
+ **/
+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);
+
+/**
+ * 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().
+ **/
+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));
+
+ if (! _cairo_reference_count_dec_and_test (&font_face->ref_count))
+ return;
+
+ if (font_face->backend->destroy)
+ font_face->backend->destroy (font_face);
+
+ /* We allow resurrection to deal with some memory management for the
+ * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t
+ * need to effectively mutually reference each other
+ */
+ if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count))
+ 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.
+ **/
+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.
+ **/
+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.
+ **/
+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 (! _cairo_reference_count_dec_and_test (&unscaled_font->ref_count))
+ return;
+
+ unscaled_font->backend->destroy (unscaled_font);
+
+ 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 000000000..5d59fb0f2
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-font-options.c
@@ -0,0 +1,513 @@
+/* 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 <otaylor@redhat.com>
+ */
+
+#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
+};
+
+/**
+ * _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;
+}
+
+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;
+}
+
+/**
+ * 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().
+ **/
+cairo_font_options_t *
+cairo_font_options_create (void)
+{
+ cairo_font_options_t *options;
+
+ options = 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().
+ **/
+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 = 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;
+}
+
+/**
+ * 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().
+ **/
+void
+cairo_font_options_destroy (cairo_font_options_t *options)
+{
+ if (cairo_font_options_status (options))
+ return;
+
+ 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
+ **/
+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_OPERATION_OVER.
+ **/
+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;
+}
+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.
+ **/
+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);
+}
+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.
+ **/
+unsigned long
+cairo_font_options_hash (const cairo_font_options_t *options)
+{
+ if (cairo_font_options_status ((cairo_font_options_t *) options))
+ options = &_cairo_font_options_nil; /* force default values */
+
+ return ((options->antialias) |
+ (options->subpixel_order << 4) |
+ (options->lcd_filter << 8) |
+ (options->hint_style << 12) |
+ (options->hint_metrics << 16));
+}
+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.
+ **/
+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
+ **/
+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.
+ **/
+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
+ **/
+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.
+ *
+ * Since: 1.8
+ **/
+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
+ *
+ * Since: 1.8
+ **/
+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.
+ *
+ * Since: 1.12
+ **/
+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.
+ *
+ * Since: 1.12
+ **/
+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.
+ **/
+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
+ **/
+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.
+ **/
+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
+ **/
+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;
+}
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 000000000..ea873abe7
--- /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 <graydon@redhat.com>
+ * Owen Taylor <otaylor@redhat.com>
+ * Keith Packard <keithp@keithp.com>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef _CAIRO_FONTCONFIG_PRIVATE_H
+#define _CAIRO_FONTCONFIG_PRIVATE_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_FC_FONT
+#include <fontconfig/fontconfig.h>
+#include <fontconfig/fcfreetype.h>
+#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 000000000..c73e593c0
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-freed-pool-private.h
@@ -0,0 +1,129 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_FREED_POOL_H
+#define CAIRO_FREED_POOL_H
+
+#include "cairoint.h"
+#include "cairo-atomic-private.h"
+
+#if HAS_ATOMIC_OPS
+/* Keep a stash of recently freed clip_paths, since we need to
+ * reallocate them frequently.
+ */
+#define MAX_FREED_POOL_SIZE 4
+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
+
+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
+
+#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 000000000..5b1c4c0bb
--- /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 <chris@chris-wilson.co.uk>
+ */
+
+#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 000000000..703181b56
--- /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 (unlikely (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_NOACCESS (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 000000000..4dd056461
--- /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 000000000..d596eab81
--- /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 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_NOACCESS (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_NOACCESS (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_NOACCESS (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 = 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_NOACCESS (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 000000000..e51923a5d
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-ft-font.c
@@ -0,0 +1,3355 @@
+/* -*- 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 <graydon@redhat.com>
+ * Owen Taylor <otaylor@redhat.com>
+ * Keith Packard <keithp@keithp.com>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#define _BSD_SOURCE /* for strdup() */
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-ft-private.h"
+
+#include <float.h>
+
+#include "cairo-fontconfig-private.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_IMAGE_H
+#include FT_BITMAP_H
+#include FT_TRUETYPE_TABLES_H
+#if HAVE_FT_GLYPHSLOT_EMBOLDEN
+#include FT_SYNTHESIS_H
+#endif
+
+#if HAVE_FT_LIBRARY_SETLCDFILTER
+#include FT_LCD_FILTER_H
+#endif
+
+#define _GNU_SOURCE /* for RTLD_DEFAULT */
+#include <dlfcn.h>
+
+#ifndef RTLD_DEFAULT
+#define RTLD_DEFAULT ((void *) 0)
+#endif
+
+/* Fontconfig version older than 2.6 didn't have these options */
+#ifndef FC_LCD_FILTER
+#define FC_LCD_FILTER "lcdfilter"
+#endif
+/* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */
+#ifndef FC_LCD_NONE
+#define FC_LCD_NONE 0
+#define FC_LCD_DEFAULT 1
+#define FC_LCD_LIGHT 2
+#define FC_LCD_LEGACY 3
+#endif
+
+/* 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
+
+typedef FT_Error (*setLcdFilterFunc)(FT_Library, int);
+static setLcdFilterFunc setLcdFilter;
+
+#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
+#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
+#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
+#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0)
+
+/* This is the max number of FT_face objects we keep open at once
+ */
+#define MAX_OPEN_FACES 10
+/* This is the maximum font size we allow to be passed to FT_Set_Char_Size
+ */
+#define MAX_FONT_SIZE 1000
+
+/**
+ * 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.
+ */
+
+/**
+ * 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.
+ */
+
+/*
+ * 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 */
+
+ /* 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;
+
+ 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 enum _cairo_ft_extra_flags {
+ CAIRO_FT_OPTIONS_HINT_METRICS = (1 << 0),
+ CAIRO_FT_OPTIONS_EMBOLDEN = (1 << 1)
+} cairo_ft_extra_flags_t;
+
+typedef struct _cairo_ft_options {
+ cairo_font_options_t base;
+ int load_flags; /* flags for FT_Load_Glyph */
+ cairo_ft_extra_flags_t extra_flags; /* other flags that affect results */
+} cairo_ft_options_t;
+
+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
+
+/*
+ * 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 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 = 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)
+ _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_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)
+{
+ unsigned long hash;
+
+ key->from_face = from_face;
+ key->filename = filename;
+ key->id = id;
+ key->face = face;
+
+ 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)
+{
+ _cairo_unscaled_font_init (&unscaled->base,
+ &cairo_ft_unscaled_font_backend);
+
+ if (from_face) {
+ unscaled->from_face = TRUE;
+ _cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face);
+ } else {
+ char *filename_copy;
+
+ unscaled->from_face = FALSE;
+ unscaled->face = 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);
+ }
+
+ 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);
+
+ if (unscaled->filename) {
+ free (unscaled->filename);
+ unscaled->filename = NULL;
+ }
+
+ 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;
+
+ 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,
+ 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);
+
+ /* 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 = 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);
+ 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;
+
+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 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;
+ }
+
+ /* 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,
+ out);
+}
+#endif
+
+static cairo_status_t
+_cairo_ft_unscaled_font_create_from_face (FT_Face face,
+ cairo_ft_unscaled_font_t **out)
+{
+ return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out);
+}
+
+static void
+_cairo_ft_unscaled_font_destroy (void *abstract_font)
+{
+ cairo_ft_unscaled_font_t *unscaled = abstract_font;
+ cairo_ft_unscaled_font_map_t *font_map;
+
+ if (unscaled == NULL)
+ return;
+
+ font_map = _cairo_ft_unscaled_font_map_lock ();
+ /* All created objects must have been mapped in the font map. */
+ assert (font_map != NULL);
+
+ if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) {
+ /* somebody recreated the font whilst we waited for the lock */
+ _cairo_ft_unscaled_font_map_unlock ();
+ return;
+ }
+
+ _cairo_hash_table_remove (font_map->hash_table,
+ &unscaled->base.hash_entry);
+
+ 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_font_face_destroy (&unscaled->faces->base);
+ }
+ } else {
+ _font_map_release_face_lock_held (font_map, unscaled);
+ }
+ unscaled->face = NULL;
+
+ _cairo_ft_unscaled_font_map_unlock ();
+
+ _cairo_ft_unscaled_font_fini (unscaled);
+}
+
+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.
+ */
+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;
+
+ CAIRO_MUTEX_LOCK (unscaled->mutex);
+ 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 ();
+
+ if (FT_New_Face (font_map->ft_library,
+ unscaled->filename,
+ unscaled->id,
+ &face) != FT_Err_Ok)
+ {
+ unscaled->lock_count--;
+ CAIRO_MUTEX_UNLOCK (unscaled->mutex);
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ unscaled->face = face;
+
+ font_map->num_open_faces++;
+
+ return face;
+}
+
+
+/* Unlock unscaled font locked with _cairo_ft_unscaled_font_lock_face
+ */
+void
+_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled)
+{
+ assert (unscaled->lock_count > 0);
+
+ unscaled->lock_count--;
+
+ CAIRO_MUTEX_UNLOCK (unscaled->mutex);
+}
+
+
+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;
+ int best_i = 0;
+ 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_i = i;
+ 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;
+ double x_scale, y_scale;
+
+ 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);
+
+ x_scale = MIN(sf.x_scale, MAX_FONT_SIZE);
+ y_scale = MIN(sf.y_scale, MAX_FONT_SIZE);
+ error = FT_Set_Char_Size (unscaled->face,
+ x_scale * 64.0 + .5,
+ y_scale * 64.0 + .5,
+ 0, 0);
+ if (error)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ 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;
+
+ 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;
+
+ default: /* 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;
+ }
+ }
+ }
+ }
+}
+
+
+/* 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)
+{
+ int width, height, stride;
+ unsigned char *data;
+ int format = CAIRO_FORMAT_A8;
+ 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;
+ int copy_len = MIN (stride, bitmap->pitch);
+ int pad_len = stride - bitmap->pitch;
+
+ source = bitmap->buffer;
+ dest = data;
+ for (i = height; i; i--) {
+ memcpy (dest, source, copy_len);
+ source += bitmap->pitch;
+ dest += stride;
+ }
+ /* do we really care about zeroing any extra row padding in dest? */
+ if (pad_len > 0) {
+ dest = data + copy_len;
+ for (i = height; i; i--) {
+ memset (dest, '\0', pad_len);
+ dest += stride;
+ }
+ }
+ }
+ }
+
+#ifndef WORDS_BIGENDIAN
+ {
+ uint8_t *d = data;
+ int count = stride * height;
+
+ 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);
+ }
+ 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;
+
+ format = CAIRO_FORMAT_A8;
+
+ align = cairo_format_stride_for_width (format, bitmap->width);
+
+ FT_Bitmap_New( &tmp );
+
+ if (FT_Bitmap_Convert( library, bitmap, &tmp, align ))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ 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;
+ }
+ /* These could be triggered by very rare types of TrueType fonts */
+ default:
+ if (own_buffer)
+ free (bitmap->buffer);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* 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 fterror;
+ 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:
+ 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:
+ 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);
+ if ((*surface)->base.status)
+ return (*surface)->base.status;
+ } else {
+
+ int bitmap_size;
+ static int initialized_setLcdFilter = 0;
+
+ switch (render_mode) {
+ case FT_RENDER_MODE_LCD:
+ if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR) {
+ 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 (!initialized_setLcdFilter) {
+ initialized_setLcdFilter = 1;
+#ifdef HAVE_FT_LIBRARY_SETLCDFILTER
+ setLcdFilter = &FT_Library_SetLcdFilter;
+#else
+ setLcdFilter = (setLcdFilterFunc) dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter");
+#endif
+ }
+
+ if (setLcdFilter)
+ setLcdFilter (library, lcd_filter);
+
+ fterror = FT_Render_Glyph (face->glyph, render_mode);
+
+ if (setLcdFilter)
+ setLcdFilter (library, FT_LCD_FILTER_NONE);
+
+ if (fterror != 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ bitmap_size = _compute_xrender_bitmap_size (&bitmap,
+ face->glyph,
+ render_mode);
+ if (bitmap_size < 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ 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 (cairo_image_surface_get_format (*surface) == 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;
+ 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
+
+ _cairo_font_options_init_default (&ft_options.base);
+ ft_options.load_flags = FT_LOAD_DEFAULT;
+ ft_options.extra_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.extra_flags |= CAIRO_FT_OPTIONS_EMBOLDEN;
+
+ *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;
+ } else if (options->base.antialias != CAIRO_ANTIALIAS_GRAY) {
+ /* The surface supports subpixel aa, so let the font face options
+ * choose whether to use subpixel aa. If the surface has
+ * CAIRO_ANTIALIAS_GRAY (e.g. PS, PDF, SVG, translucent part of a
+ * CONTENT_COLOR_ALPHA surface), then don't accept subpixel aa. */
+ if (other->base.antialias != CAIRO_ANTIALIAS_DEFAULT)
+ options->base.antialias = other->base.antialias;
+ /* If the surface knows the subpixel order then use that. */
+ if (options->base.subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
+ options->base.subpixel_order = other->base.subpixel_order;
+ }
+
+ if (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;
+ }
+ }
+
+ options->load_flags = load_flags | load_target;
+ options->extra_flags = other->extra_flags;
+ if (options->base.hint_metrics != CAIRO_HINT_METRICS_OFF)
+ options->extra_flags |= CAIRO_FT_OPTIONS_HINT_METRICS;
+}
+
+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 = 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 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;
+ FT_Error error;
+ 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;
+
+ face = _cairo_ft_unscaled_font_lock_face (unscaled);
+ if (!face)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled,
+ &scaled_font->base.scale);
+ if (unlikely (status))
+ goto FAIL;
+
+ /* Ignore global advance unconditionally */
+ load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+
+ if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 &&
+ (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0)
+ 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
+ /* Color-glyph support:
+ *
+ * This flags needs plumbing through fontconfig (does it?), and
+ * maybe we should cache color and grayscale bitmaps separately
+ * such that users of the font (ie. the surface) can choose which
+ * version to use based on target content type.
+ */
+
+ load_flags |= FT_LOAD_COLOR;
+#endif
+
+ error = FT_Load_Glyph (scaled_font->unscaled->face,
+ _cairo_scaled_glyph_index(scaled_glyph),
+ load_flags);
+ /* XXX ignoring all other errors for now. They are not fatal, typically
+ * just a glyph-not-found. */
+ if (error == FT_Err_Out_Of_Memory) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL;
+ }
+
+ glyph = face->glyph;
+
+#if HAVE_FT_GLYPHSLOT_EMBOLDEN
+ /*
+ * embolden glyphs if requested
+ */
+ if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN)
+ FT_GlyphSlot_Embolden (glyph);
+#endif
+
+ if (vertical_layout)
+ _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph);
+
+ if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) {
+
+ cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
+ /*
+ * 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);
+ }
+
+ if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) {
+ cairo_image_surface_t *surface;
+
+ 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;
+
+ _cairo_scaled_glyph_set_surface (scaled_glyph,
+ &scaled_font->base,
+ surface);
+ }
+
+ 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) != 0) {
+ error = FT_Load_Glyph (face,
+ _cairo_scaled_glyph_index(scaled_glyph),
+ load_flags | FT_LOAD_NO_BITMAP);
+ /* XXX ignoring all other errors for now. They are not fatal, typically
+ * just a glyph-not-found. */
+ if (error == FT_Err_Out_Of_Memory) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL;
+ }
+#if HAVE_FT_GLYPHSLOT_EMBOLDEN
+ /*
+ * embolden glyphs if requested
+ */
+ if (scaled_font->ft_options.extra_flags & CAIRO_FT_OPTIONS_EMBOLDEN)
+ FT_GlyphSlot_Embolden (glyph);
+#endif
+ if (vertical_layout)
+ _cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, 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;
+
+ 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) &&
+ 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 const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = {
+ CAIRO_FONT_TYPE_FT,
+ _cairo_ft_scaled_font_fini,
+ _cairo_ft_scaled_glyph_init,
+ NULL, /* text_to_glyphs */
+ _cairo_ft_ucs4_to_index,
+ NULL, /* show_glyphs */
+ _cairo_ft_load_truetype_table,
+ _cairo_ft_index_to_ucs4
+};
+
+/* #cairo_ft_font_face_t */
+
+#if CAIRO_HAS_FC_FONT
+static cairo_status_t
+_cairo_ft_font_face_create_for_pattern (FcPattern *pattern,
+ cairo_font_face_t **out);
+
+static cairo_status_t
+_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
+ cairo_font_face_t **font_face)
+{
+ FcPattern *pattern;
+ int fcslant;
+ int fcweight;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ pattern = FcPatternCreate ();
+ if (!pattern)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (!FcPatternAddString (pattern,
+ FC_FAMILY, (unsigned char *) toy_face->family))
+ {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FREE_PATTERN;
+ }
+
+ switch (toy_face->slant)
+ {
+ case CAIRO_FONT_SLANT_ITALIC:
+ fcslant = FC_SLANT_ITALIC;
+ break;
+ case CAIRO_FONT_SLANT_OBLIQUE:
+ fcslant = FC_SLANT_OBLIQUE;
+ break;
+ case CAIRO_FONT_SLANT_NORMAL:
+ default:
+ fcslant = FC_SLANT_ROMAN;
+ break;
+ }
+
+ if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FREE_PATTERN;
+ }
+
+ switch (toy_face->weight)
+ {
+ case CAIRO_FONT_WEIGHT_BOLD:
+ fcweight = FC_WEIGHT_BOLD;
+ break;
+ case CAIRO_FONT_WEIGHT_NORMAL:
+ default:
+ fcweight = FC_WEIGHT_MEDIUM;
+ break;
+ }
+
+ if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FREE_PATTERN;
+ }
+
+ status = _cairo_ft_font_face_create_for_pattern (pattern, font_face);
+
+ FREE_PATTERN:
+ FcPatternDestroy (pattern);
+
+ return status;
+}
+#endif
+
+static void
+_cairo_ft_font_face_destroy (void *abstract_face)
+{
+ 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 &&
+ font_face->unscaled->from_face &&
+ font_face->next == NULL &&
+ font_face->unscaled->faces == font_face &&
+ CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1)
+ {
+ cairo_font_face_reference (&font_face->base);
+
+ _cairo_unscaled_font_destroy (&font_face->unscaled->base);
+ font_face->unscaled = NULL;
+
+ return;
+ }
+
+ if (font_face->unscaled) {
+ 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_unscaled_font_destroy (&font_face->unscaled->base);
+ font_face->unscaled = NULL;
+ }
+
+#if CAIRO_HAS_FC_FONT
+ if (font_face->pattern) {
+ FcPatternDestroy (font_face->pattern);
+ cairo_font_face_destroy (font_face->resolved_font_face);
+ }
+#endif
+}
+
+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;
+}
+
+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
+};
+
+#if CAIRO_HAS_FC_FONT
+static cairo_status_t
+_cairo_ft_font_face_create_for_pattern (FcPattern *pattern,
+ cairo_font_face_t **out)
+{
+ cairo_ft_font_face_t *font_face;
+
+ font_face = malloc (sizeof (cairo_ft_font_face_t));
+ if (unlikely (font_face == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font_face->unscaled = NULL;
+ font_face->next = NULL;
+
+ font_face->pattern = FcPatternDuplicate (pattern);
+ if (unlikely (font_face->pattern == NULL)) {
+ free (font_face);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ font_face->resolved_font_face = NULL;
+ font_face->resolved_config = NULL;
+
+ _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend);
+
+ *out = &font_face->base;
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+static cairo_font_face_t *
+_cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled,
+ cairo_ft_options_t *ft_options)
+{
+ cairo_ft_font_face_t *font_face, **prev_font_face;
+
+ /* 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.extra_flags == ft_options->extra_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);
+ return &font_face->base;
+ } else
+ return cairo_font_face_reference (&font_face->base);
+ }
+ }
+
+ /* No match found, create a new one */
+ font_face = malloc (sizeof (cairo_ft_font_face_t));
+ if (unlikely (!font_face)) {
+ _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);
+
+ 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);
+
+ 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().
+ **/
+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);
+
+ 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;
+ }
+
+ _get_pattern_ft_options (resolved, &ft_options);
+ font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
+ _cairo_unscaled_font_destroy (&unscaled->base);
+
+FREE_RESOLVED:
+ FcPatternDestroy (resolved);
+
+FREE_PATTERN:
+ FcPatternDestroy (pattern);
+
+ return font_face;
+}
+
+/**
+ * cairo_ft_font_face_create_for_pattern:
+ * @pattern: A 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 exmaple 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.
+ **/
+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))
+ return (cairo_font_face_t *) &_cairo_font_face_nil;
+ if (unlikely (unscaled == NULL)) {
+ /* Store the pattern. We will resolve it and create unscaled
+ * font when creating scaled fonts */
+ status = _cairo_ft_font_face_create_for_pattern (pattern,
+ &font_face);
+ if (unlikely (status))
+ return (cairo_font_face_t *) &_cairo_font_face_nil;
+
+ return font_face;
+ }
+
+ _get_pattern_ft_options (pattern, &ft_options);
+ font_face = _cairo_ft_font_face_create (unscaled, &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.
+ *
+ * <informalexample><programlisting>
+ * 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;
+ * }
+ * </programlisting></informalexample>
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ * cairo_font_face_destroy() when you are done using it.
+ **/
+cairo_font_face_t *
+cairo_ft_font_face_create_for_ft_face (FT_Face face,
+ int load_flags)
+{
+ cairo_ft_unscaled_font_t *unscaled;
+ cairo_font_face_t *font_face;
+ cairo_ft_options_t ft_options;
+ cairo_status_t status;
+
+ status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled);
+ if (unlikely (status))
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+
+ ft_options.load_flags = load_flags;
+ ft_options.extra_flags = 0;
+ _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_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. 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.
+ **/
+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;
+ }
+
+ /* 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_MUTEX_UNLOCK (scaled_font->unscaled->mutex);
+
+ 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().
+ **/
+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_MUTEX_LOCK (scaled_font->unscaled->mutex);
+
+ _cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled);
+}
+
+/* We expose our unscaled font implementation internally for the the
+ * PDF backend, which needs to keep track of the the different
+ * fonts-on-disk used by a document, so it can embed them.
+ */
+cairo_unscaled_font_t *
+_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *abstract_font)
+{
+ cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font;
+
+ return &scaled_font->unscaled->base;
+}
+
+cairo_bool_t
+_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 000000000..ff6ad4e65
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-ft-private.h
@@ -0,0 +1,73 @@
+/* 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 <graydon@redhat.com>
+ * Owen Taylor <otaylor@redhat.com>
+ */
+
+#ifndef CAIRO_FT_PRIVATE_H
+#define CAIRO_FT_PRIVATE_H
+
+#include "cairo-ft.h"
+#include "cairoint.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 cairo_unscaled_font_t *
+_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *scaled_font);
+
+cairo_private FT_Face
+_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled);
+
+cairo_private void
+_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled);
+
+cairo_private cairo_bool_t
+_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font);
+
+cairo_private unsigned int
+_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font);
+
+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 000000000..56d48c328
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-ft.h
@@ -0,0 +1,82 @@
+/* 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 <graydon@redhat.com>
+ * Owen Taylor <otaylor@redhat.com>
+ */
+
+#ifndef CAIRO_FT_H
+#define CAIRO_FT_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_FT_FONT
+
+/* Fontconfig/Freetype platform-specific font interface */
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#if CAIRO_HAS_FC_FONT
+#include <fontconfig/fontconfig.h>
+#endif
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_font_face_t *
+cairo_ft_font_face_create_for_ft_face (FT_Face face,
+ int load_flags);
+
+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-glyphs.c b/gfx/cairo/cairo/src/cairo-gl-glyphs.c
new file mode 100644
index 000000000..4736e190e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-gl-glyphs.c
@@ -0,0 +1,605 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributors:
+ * Benjamin Otte <otte@gnome.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-error-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_private {
+ cairo_rtree_node_t node;
+ cairo_gl_glyph_cache_t *cache;
+ struct { float x, y; } p1, p2;
+} cairo_gl_glyph_private_t;
+
+static cairo_status_t
+_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx,
+ cairo_gl_glyph_cache_t *cache,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+ cairo_gl_surface_t *cache_surface;
+ cairo_gl_glyph_private_t *glyph_private;
+ cairo_rtree_node_t *node = NULL;
+ cairo_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_STATUS_SUCCESS) {
+ status = _cairo_rtree_node_insert (&cache->rtree,
+ node, width, height, &node);
+ }
+ }
+ if (status)
+ return status;
+
+ cache_surface = (cairo_gl_surface_t *) cache->pattern.surface;
+
+ /* XXX: Make sure we use the mask texture. This should work automagically somehow */
+ glActiveTexture (GL_TEXTURE1);
+ status = _cairo_gl_surface_draw_image (cache_surface,
+ glyph_surface,
+ 0, 0,
+ glyph_surface->width, glyph_surface->height,
+ node->x, node->y);
+ if (unlikely (status))
+ return status;
+
+ scaled_glyph->surface_private = node;
+ node->owner = &scaled_glyph->surface_private;
+
+ glyph_private = (cairo_gl_glyph_private_t *) node;
+ glyph_private->cache = 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 /= cache_surface->width;
+ glyph_private->p1.y /= cache_surface->height;
+ glyph_private->p2.x /= cache_surface->width;
+ glyph_private->p2.y /= cache_surface->height;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_gl_glyph_private_t *
+_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ return _cairo_rtree_pin (&cache->rtree, scaled_glyph->surface_private);
+}
+
+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_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;
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ }
+
+ if (unlikely (cache->pattern.surface == NULL)) {
+ cairo_surface_t *surface;
+ surface = cairo_gl_surface_create (&ctx->base,
+ content,
+ GLYPH_CACHE_WIDTH,
+ GLYPH_CACHE_HEIGHT);
+ if (unlikely (surface->status)) {
+ cairo_status_t status = surface->status;
+ cairo_surface_destroy (surface);
+ return status;
+ }
+ _cairo_surface_release_device_reference (surface);
+ _cairo_pattern_init_for_surface (&cache->pattern, surface);
+ cairo_surface_destroy (surface);
+ cache->pattern.base.has_component_alpha = (content == CAIRO_CONTENT_COLOR_ALPHA);
+ }
+
+ *cache_out = cache;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache)
+{
+ _cairo_rtree_unpin (&cache->rtree);
+}
+
+static cairo_bool_t
+_cairo_gl_surface_owns_font (cairo_gl_surface_t *surface,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_device_t *font_private;
+
+ font_private = scaled_font->surface_private;
+ if ((scaled_font->surface_backend != NULL &&
+ scaled_font->surface_backend != &_cairo_gl_surface_backend) ||
+ (font_private != NULL && font_private != surface->base.device))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+_cairo_gl_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+ cairo_list_del (&scaled_font->link);
+}
+
+void
+_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_gl_glyph_private_t *glyph_private;
+
+ glyph_private = scaled_glyph->surface_private;
+ if (glyph_private != NULL) {
+ glyph_private->node.owner = NULL;
+ if (! glyph_private->node.pinned) {
+ /* XXX thread-safety? Probably ok due to the frozen scaled-font. */
+ _cairo_rtree_node_remove (&glyph_private->cache->rtree,
+ &glyph_private->node);
+ }
+ }
+}
+
+static cairo_status_t
+_render_glyphs (cairo_gl_surface_t *dst,
+ int dst_x, int dst_y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_rectangle_int_t *glyph_extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_bool_t *has_component_alpha,
+ cairo_region_t *clip_region,
+ int *remaining_glyphs)
+{
+ cairo_format_t last_format = CAIRO_FORMAT_INVALID;
+ cairo_gl_glyph_cache_t *cache = NULL;
+ cairo_gl_context_t *ctx;
+ cairo_gl_composite_t setup;
+ cairo_status_t status;
+ int i = 0;
+
+ *has_component_alpha = FALSE;
+
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+
+ status = _cairo_gl_composite_init (&setup, op, dst,
+ TRUE, glyph_extents);
+
+ if (unlikely (status))
+ goto FINISH;
+
+ if (! _cairo_gl_surface_owns_font (dst, scaled_font)) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto FINISH;
+ }
+
+ status = _cairo_gl_composite_set_source (&setup, source,
+ glyph_extents->x, glyph_extents->y,
+ dst_x, dst_y,
+ glyph_extents->width,
+ glyph_extents->height);
+ if (unlikely (status))
+ goto FINISH;
+
+ if (scaled_font->surface_private == NULL) {
+ scaled_font->surface_private = ctx;
+ scaled_font->surface_backend = &_cairo_gl_surface_backend;
+ cairo_list_add (&scaled_font->link, &ctx->fonts);
+ }
+
+ _cairo_gl_composite_set_clip_region (&setup, clip_region);
+
+ for (i = 0; i < num_glyphs; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_gl_glyph_private_t *glyph;
+ double x_offset, y_offset;
+ double x1, x2, y1, y2;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ 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->width > GLYPH_CACHE_MAX_SIZE ||
+ scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE)
+ {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto FINISH;
+ }
+
+ if (scaled_glyph->surface->format != last_format) {
+ status = cairo_gl_context_get_glyph_cache (ctx,
+ scaled_glyph->surface->format,
+ &cache);
+ if (unlikely (status))
+ goto FINISH;
+
+ last_format = scaled_glyph->surface->format;
+
+ status = _cairo_gl_composite_set_mask (&setup,
+ &cache->pattern.base,
+ 0, 0, 0, 0, 0, 0);
+ if (unlikely (status))
+ goto FINISH;
+
+ *has_component_alpha |= cache->pattern.base.has_component_alpha;
+
+ /* XXX: _cairo_gl_composite_begin() acquires the context a
+ * second time. Need to refactor this loop so this doesn't happen.
+ */
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status))
+ goto FINISH;
+ }
+
+ if (scaled_glyph->surface_private == NULL) {
+ 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_status_is_error (status)))
+ goto FINISH;
+ }
+
+ x_offset = scaled_glyph->surface->base.device_transform.x0;
+ y_offset = scaled_glyph->surface->base.device_transform.y0;
+
+ x1 = _cairo_lround (glyphs[i].x - x_offset);
+ y1 = _cairo_lround (glyphs[i].y - y_offset);
+ x2 = x1 + scaled_glyph->surface->width;
+ y2 = y1 + scaled_glyph->surface->height;
+
+ glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph);
+ _cairo_gl_composite_emit_glyph (ctx,
+ x1, y1, x2, y2,
+ glyph->p1.x, glyph->p1.y,
+ glyph->p2.x, glyph->p2.y);
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ FINISH:
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ _cairo_gl_composite_fini (&setup);
+
+ *remaining_glyphs = num_glyphs - i;
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_rectangle_int_t *glyph_extents,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_surface_t *mask;
+ cairo_status_t status;
+ cairo_bool_t has_component_alpha;
+ int i;
+
+ /* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */
+ mask = cairo_gl_surface_create (dst->base.device,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ glyph_extents->width,
+ glyph_extents->height);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ for (i = 0; i < num_glyphs; i++) {
+ glyphs[i].x -= glyph_extents->x;
+ glyphs[i].y -= glyph_extents->y;
+ }
+
+ status = _render_glyphs ((cairo_gl_surface_t *) mask, 0, 0,
+ CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base,
+ glyphs, num_glyphs, glyph_extents,
+ scaled_font, &has_component_alpha,
+ NULL, remaining_glyphs);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ cairo_surface_pattern_t mask_pattern;
+
+ mask->is_clear = FALSE;
+ _cairo_pattern_init_for_surface (&mask_pattern, mask);
+ mask_pattern.base.has_component_alpha = has_component_alpha;
+ cairo_matrix_init_translate (&mask_pattern.base.matrix,
+ -glyph_extents->x, -glyph_extents->y);
+ status = _cairo_surface_mask (&dst->base, op,
+ source, &mask_pattern.base, clip);
+ _cairo_pattern_fini (&mask_pattern.base);
+ } else {
+ for (i = 0; i < num_glyphs; i++) {
+ glyphs[i].x += glyph_extents->x;
+ glyphs[i].y += glyph_extents->y;
+ }
+ *remaining_glyphs = num_glyphs;
+ }
+
+ cairo_surface_destroy (mask);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_gl_surface_show_glyphs (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_rectangle_int_t surface_extents;
+ cairo_rectangle_int_t extents;
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t overlap, use_mask = FALSE;
+ cairo_bool_t has_component_alpha;
+ cairo_status_t status;
+ int i;
+
+ if (! _cairo_gl_operator_is_supported (op))
+ return UNSUPPORTED ("unsupported operator");
+
+ if (! _cairo_operator_bounded_by_mask (op))
+ use_mask |= TRUE;
+
+ /* If any of the glyphs are component alpha, we have to go through a mask,
+ * since only _cairo_gl_surface_composite() currently supports component
+ * alpha.
+ */
+ if (!use_mask && op != CAIRO_OPERATOR_OVER) {
+ for (i = 0; i < num_glyphs; i++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+ if (!_cairo_status_is_error (status) &&
+ scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32)
+ {
+ use_mask = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* For CLEAR, cairo's rendering equation (quoting Owen's description in:
+ * http://lists.cairographics.org/archives/cairo/2005-August/004992.html)
+ * is:
+ * mask IN clip ? src OP dest : dest
+ * or more simply:
+ * mask IN CLIP ? 0 : dest
+ *
+ * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C).
+ *
+ * The model we use in _cairo_gl_set_operator() is Render's:
+ * src IN mask IN clip OP dest
+ * which would boil down to:
+ * 0 (bounded by the extents of the drawing).
+ *
+ * However, we can do a Render operation using an opaque source
+ * and DEST_OUT to produce:
+ * 1 IN mask IN clip DEST_OUT dest
+ * which is
+ * mask IN clip ? 0 : dest
+ */
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ source = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ /* For SOURCE, cairo's rendering equation is:
+ * (mask IN clip) ? src OP dest : dest
+ * or more simply:
+ * (mask IN clip) ? src : dest.
+ *
+ * If we just used the Render equation, we would get:
+ * (src IN mask IN clip) OP dest
+ * or:
+ * (src IN mask IN clip) bounded by extents of the drawing.
+ *
+ * The trick is that for GL blending, we only get our 4 source values
+ * into the blender, and since we need all 4 components of source, we
+ * can't also get the mask IN clip into the blender. But if we did
+ * two passes we could make it work:
+ * dest = (mask IN clip) DEST_OUT dest
+ * dest = src IN mask IN clip ADD dest
+ *
+ * But for now, composite via an intermediate mask.
+ */
+ if (op == CAIRO_OPERATOR_SOURCE)
+ use_mask |= TRUE;
+
+ /* XXX we don't need ownership of the font as we use a global
+ * glyph cache -- but we do need scaled_glyph eviction notification. :-(
+ */
+ if (! _cairo_gl_surface_owns_font (dst, scaled_font))
+ return UNSUPPORTED ("do not control font");
+
+ /* If the glyphs overlap, we need to build an intermediate mask rather
+ * then perform the compositing directly.
+ */
+ status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+ glyphs, num_glyphs,
+ &extents,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ use_mask |= overlap;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ /* the empty clip should never be propagated this far */
+ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+
+ use_mask |= status == CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_rectangle_intersect (&extents,
+ _cairo_clip_get_extents (clip)))
+ goto EMPTY;
+ }
+
+ surface_extents.x = surface_extents.y = 0;
+ surface_extents.width = dst->width;
+ surface_extents.height = dst->height;
+ if (! _cairo_rectangle_intersect (&extents, &surface_extents))
+ goto EMPTY;
+
+ if (use_mask) {
+ return _cairo_gl_surface_show_glyphs_via_mask (dst, op,
+ source,
+ glyphs, num_glyphs,
+ &extents,
+ scaled_font,
+ clip,
+ remaining_glyphs);
+ }
+
+ return _render_glyphs (dst, extents.x, extents.y,
+ op, source,
+ glyphs, num_glyphs, &extents,
+ scaled_font, &has_component_alpha,
+ clip_region, remaining_glyphs);
+
+EMPTY:
+ *remaining_glyphs = 0;
+ if (! _cairo_operator_bounded_by_mask (op))
+ return _cairo_surface_paint (&dst->base, op, source, clip);
+ else
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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_private_t));
+}
+
+void
+_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
+ cairo_gl_glyph_cache_t *cache)
+{
+ _cairo_rtree_fini (&cache->rtree);
+
+ if (cache->pattern.surface) {
+ _cairo_pattern_fini (&cache->pattern.base);
+ cache->pattern.surface = NULL;
+ }
+}
+
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 000000000..54f226f42
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-gl-private.h
@@ -0,0 +1,485 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ * T. Zachary Laine <whatwasthataddress@gmail.com>
+ */
+
+#ifndef CAIRO_GL_PRIVATE_H
+#define CAIRO_GL_PRIVATE_H
+
+#include "cairoint.h"
+
+#include "cairo-gl-gradient-private.h"
+
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-rtree-private.h"
+
+#include <assert.h>
+
+#include <GL/glew.h>
+
+#include "cairo-gl.h"
+
+#include <GL/gl.h>
+#define GL_GLEXT_PROTOTYPES
+#include <GL/glext.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
+
+/* maximal number of shaders we keep in the cache.
+ * Random number that is hopefully big enough to not cause many cache evictions. */
+#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64
+
+/* VBO size that we allocate, smaller size means we gotta flush more often */
+#define CAIRO_GL_VBO_SIZE 16384
+
+typedef struct _cairo_gl_surface {
+ cairo_surface_t base;
+
+ int width, height;
+
+ GLuint tex; /* GL texture object containing our data. */
+ GLuint fb; /* GL framebuffer object wrapping our data. */
+ GLuint depth; /* GL framebuffer object holding depth */
+ int owns_tex;
+} cairo_gl_surface_t;
+
+typedef struct cairo_gl_glyph_cache {
+ cairo_rtree_t rtree;
+ cairo_surface_pattern_t pattern;
+} 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 enum cairo_gl_operand_type {
+ CAIRO_GL_OPERAND_NONE,
+ CAIRO_GL_OPERAND_CONSTANT,
+ CAIRO_GL_OPERAND_TEXTURE,
+ CAIRO_GL_OPERAND_LINEAR_GRADIENT,
+ CAIRO_GL_OPERAND_RADIAL_GRADIENT,
+ CAIRO_GL_OPERAND_SPANS,
+
+ CAIRO_GL_OPERAND_COUNT
+} cairo_gl_operand_type_t;
+
+typedef struct cairo_gl_shader_impl cairo_gl_shader_impl_t;
+
+typedef struct cairo_gl_shader {
+ GLuint fragment_shader;
+ GLuint program;
+} 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_COVERAGE
+} cairo_gl_var_type_t;
+
+#define cairo_gl_var_type_hash(src,mask,dest) ((mask) << 2 | (src << 1) | (dest))
+#define CAIRO_GL_VAR_TYPE_MAX ((CAIRO_GL_VAR_COVERAGE << 2) | (CAIRO_GL_VAR_TEXCOORDS << 1) | CAIRO_GL_VAR_TEXCOORDS)
+
+/* This union structure describes a potential source or mask operand to the
+ * compositing equation.
+ */
+typedef struct cairo_gl_operand {
+ cairo_gl_operand_type_t type;
+ union {
+ struct {
+ GLuint tex;
+ cairo_gl_surface_t *surface;
+ cairo_surface_attributes_t attributes;
+ } texture;
+ struct {
+ GLfloat color[4];
+ } constant;
+ struct {
+ cairo_gl_gradient_t *gradient;
+ cairo_matrix_t m;
+ float segment_x;
+ float segment_y;
+ cairo_extend_t extend;
+ } linear;
+ struct {
+ cairo_gl_gradient_t *gradient;
+ cairo_matrix_t m;
+ float circle_1_x;
+ float circle_1_y;
+ float radius_0;
+ float radius_1;
+ cairo_extend_t extend;
+ } radial;
+ };
+ unsigned int vertex_offset;
+} cairo_gl_operand_t;
+
+struct _cairo_gl_context {
+ cairo_device_t base;
+
+ GLuint dummy_tex;
+ GLuint texture_load_pbo;
+ GLuint vbo;
+ GLint max_framebuffer_size;
+ GLint max_texture_size;
+ GLint max_textures;
+ GLenum tex_target;
+
+ const cairo_gl_shader_impl_t *shader_impl;
+
+ GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX + 1];
+ 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];
+
+ char *vb;
+ unsigned int vb_offset;
+ unsigned int vertex_size;
+ cairo_region_t *clip_region;
+
+ 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_gl_composite_t;
+
+cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend;
+
+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);
+
+static cairo_always_inline cairo_bool_t
+_cairo_gl_device_has_glsl (cairo_device_t *device)
+{
+ return ((cairo_gl_context_t *) device)->shader_impl != NULL;
+}
+
+static cairo_always_inline cairo_bool_t
+_cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device)
+{
+ return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE_EXT;
+}
+
+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_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_status_t
+_cairo_gl_composite_init (cairo_gl_composite_t *setup,
+ cairo_operator_t op,
+ cairo_gl_surface_t *dst,
+ cairo_bool_t has_component_alpha,
+ const cairo_rectangle_int_t *rect);
+
+cairo_private void
+_cairo_gl_composite_fini (cairo_gl_composite_t *setup);
+
+cairo_private void
+_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_set_source (cairo_gl_composite_t *setup,
+ const cairo_pattern_t *pattern,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ int width, int height);
+
+cairo_private cairo_int_status_t
+_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup,
+ const cairo_pattern_t *pattern,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ int width, int height);
+
+cairo_private void
+_cairo_gl_composite_set_mask_spans (cairo_gl_composite_t *setup);
+
+cairo_private cairo_status_t
+_cairo_gl_composite_begin (cairo_gl_composite_t *setup,
+ cairo_gl_context_t **ctx);
+
+cairo_private void
+_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx,
+ GLfloat x1,
+ GLfloat y1,
+ GLfloat x2,
+ GLfloat y2,
+ uint8_t alpha);
+
+cairo_private void
+_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx,
+ GLfloat x1,
+ GLfloat y1,
+ GLfloat x2,
+ GLfloat y2,
+ GLfloat glyph_x1,
+ GLfloat glyph_y1,
+ GLfloat glyph_x2,
+ GLfloat glyph_y2);
+
+cairo_private void
+_cairo_gl_composite_flush (cairo_gl_context_t *ctx);
+
+cairo_private void
+_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx,
+ cairo_gl_tex_t tex_unit);
+
+cairo_private cairo_bool_t
+_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
+ GLenum *internal_format, GLenum *format,
+ GLenum *type, cairo_bool_t *has_alpha);
+
+cairo_private void
+_cairo_gl_surface_scaled_font_fini ( cairo_scaled_font_t *scaled_font);
+
+cairo_private void
+_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font);
+
+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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs);
+
+static inline int
+_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
+{
+ if (surface->fb)
+ return y;
+ else
+ return (surface->height - 1) - y;
+}
+
+cairo_private cairo_status_t
+_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx);
+
+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 == NULL;
+}
+
+cairo_private cairo_status_t
+_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
+ cairo_gl_operand_type_t source,
+ cairo_gl_operand_type_t mask,
+ cairo_gl_shader_in_t in,
+ cairo_gl_shader_t **shader);
+
+cairo_private void
+_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
+ const char *name,
+ float value);
+
+cairo_private void
+_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0, float value1);
+
+cairo_private void
+_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0,
+ float value1,
+ float value2);
+
+cairo_private void
+_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0, float value1,
+ float value2, float value3);
+
+cairo_private void
+_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
+ const char *name,
+ cairo_matrix_t* m);
+
+cairo_private void
+_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx,
+ const char *name,
+ GLuint tex_unit);
+
+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);
+
+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 000000000..d7773f567
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-gl-shaders.c
@@ -0,0 +1,995 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 T. Zachary Laine
+ * Copyright © 2010 Eric Anholt
+ * 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 T. Zachary Laine.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Eric Anholt <eric@anholt.net>
+ * T. Zachary Laine <whatwasthataddress@gmail.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-gl-private.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+
+typedef struct cairo_gl_shader_impl {
+ void
+ (*compile_shader) (GLuint *shader, GLenum type, const char *text);
+
+ void
+ (*link_shader) (GLuint *program, GLuint vert, GLuint frag);
+
+ void
+ (*destroy_shader) (GLuint shader);
+
+ void
+ (*destroy_program) (GLuint program);
+
+ void
+ (*bind_float) (cairo_gl_shader_t *shader,
+ const char *name,
+ float value);
+
+ void
+ (*bind_vec2) (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1);
+
+ void
+ (*bind_vec3) (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2);
+
+ void
+ (*bind_vec4) (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0, float value1,
+ float value2, float value3);
+
+ void
+ (*bind_matrix) (cairo_gl_shader_t *shader,
+ const char *name,
+ cairo_matrix_t* m);
+
+ void
+ (*bind_texture) (cairo_gl_shader_t *shader,
+ const char *name,
+ cairo_gl_tex_t tex_unit);
+
+ void
+ (*use) (cairo_gl_shader_t *shader);
+} shader_impl_t;
+
+static cairo_status_t
+_cairo_gl_shader_compile (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader,
+ cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ const char *fragment_text);
+
+/* ARB_shader_objects / ARB_vertex_shader / ARB_fragment_shader extensions
+ API. */
+static void
+compile_shader_arb (GLuint *shader, GLenum type, const char *text)
+{
+ const char* strings[1] = { text };
+ GLint gl_status;
+
+ *shader = glCreateShaderObjectARB (type);
+ glShaderSourceARB (*shader, 1, strings, 0);
+ glCompileShaderARB (*shader);
+ glGetObjectParameterivARB (*shader, GL_OBJECT_COMPILE_STATUS_ARB, &gl_status);
+ if (gl_status == GL_FALSE) {
+ GLint log_size;
+ glGetObjectParameterivARB (*shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size);
+ if (0 < log_size) {
+ char *log = _cairo_malloc (log_size);
+ GLint chars;
+
+ log[log_size - 1] = '\0';
+ glGetInfoLogARB (*shader, log_size, &chars, log);
+ printf ("OpenGL shader compilation failed. Shader:\n"
+ "%s\n"
+ "OpenGL compilation log:\n"
+ "%s\n",
+ text, log);
+
+ free (log);
+ } else {
+ printf ("OpenGL shader compilation failed.\n");
+ }
+
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+link_shader_arb (GLuint *program, GLuint vert, GLuint frag)
+{
+ GLint gl_status;
+
+ *program = glCreateProgramObjectARB ();
+ glAttachObjectARB (*program, vert);
+ glAttachObjectARB (*program, frag);
+ glLinkProgramARB (*program);
+ glGetObjectParameterivARB (*program, GL_OBJECT_LINK_STATUS_ARB, &gl_status);
+ if (gl_status == GL_FALSE) {
+ GLint log_size;
+ glGetObjectParameterivARB (*program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size);
+ if (0 < log_size) {
+ char *log = _cairo_malloc (log_size);
+ GLint chars;
+
+ log[log_size - 1] = '\0';
+ glGetInfoLogARB (*program, log_size, &chars, log);
+ printf ("OpenGL shader link failed:\n%s\n", log);
+
+ free (log);
+ } else {
+ printf ("OpenGL shader link failed.\n");
+ }
+
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+destroy_shader_arb (GLuint shader)
+{
+ glDeleteObjectARB (shader);
+}
+
+static void
+destroy_program_arb (GLuint shader)
+{
+ glDeleteObjectARB (shader);
+}
+
+static void
+bind_float_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ float value)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform1fARB (location, value);
+}
+
+static void
+bind_vec2_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform2fARB (location, value0, value1);
+}
+
+static void
+bind_vec3_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform3fARB (location, value0, value1, value2);
+}
+
+static void
+bind_vec4_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2,
+ float value3)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform4fARB (location, value0, value1, value2, value3);
+}
+
+static void
+bind_matrix_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ cairo_matrix_t* m)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ float gl_m[9] = {
+ m->xx, m->xy, m->x0,
+ m->yx, m->yy, m->y0,
+ 0, 0, 1
+ };
+ assert (location != -1);
+ glUniformMatrix3fvARB (location, 1, GL_TRUE, gl_m);
+}
+
+static void
+bind_texture_arb (cairo_gl_shader_t *shader,
+ const char *name,
+ cairo_gl_tex_t tex_unit)
+{
+ GLint location = glGetUniformLocationARB (shader->program, name);
+ assert (location != -1);
+ glUniform1iARB (location, tex_unit);
+}
+
+static void
+use_program_arb (cairo_gl_shader_t *shader)
+{
+ if (shader)
+ glUseProgramObjectARB (shader->program);
+ else
+ glUseProgramObjectARB (0);
+}
+
+/* OpenGL Core 2.0 API. */
+static void
+compile_shader_core_2_0 (GLuint *shader, GLenum type, const char *text)
+{
+ const char* strings[1] = { text };
+ GLint gl_status;
+
+ *shader = glCreateShader (type);
+ glShaderSource (*shader, 1, strings, 0);
+ glCompileShader (*shader);
+ glGetShaderiv (*shader, GL_COMPILE_STATUS, &gl_status);
+ if (gl_status == GL_FALSE) {
+ GLint log_size;
+ glGetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
+ if (0 < log_size) {
+ char *log = _cairo_malloc (log_size);
+ GLint chars;
+
+ log[log_size - 1] = '\0';
+ glGetShaderInfoLog (*shader, log_size, &chars, log);
+ printf ("OpenGL shader compilation failed. Shader:\n"
+ "%s\n"
+ "OpenGL compilation log:\n"
+ "%s\n",
+ text, log);
+
+ free (log);
+ } else {
+ printf ("OpenGL shader compilation failed.\n");
+ }
+
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+link_shader_core_2_0 (GLuint *program, GLuint vert, GLuint frag)
+{
+ GLint gl_status;
+
+ *program = glCreateProgram ();
+ glAttachShader (*program, vert);
+ glAttachShader (*program, frag);
+ glLinkProgram (*program);
+ glGetProgramiv (*program, GL_LINK_STATUS, &gl_status);
+ if (gl_status == GL_FALSE) {
+ GLint log_size;
+ glGetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
+ if (0 < log_size) {
+ char *log = _cairo_malloc (log_size);
+ GLint chars;
+
+ log[log_size - 1] = '\0';
+ glGetProgramInfoLog (*program, log_size, &chars, log);
+ printf ("OpenGL shader link failed:\n%s\n", log);
+
+ free (log);
+ } else {
+ printf ("OpenGL shader link failed.\n");
+ }
+
+ ASSERT_NOT_REACHED;
+ }
+}
+
+static void
+destroy_shader_core_2_0 (GLuint shader)
+{
+ glDeleteShader (shader);
+}
+
+static void
+destroy_program_core_2_0 (GLuint shader)
+{
+ glDeleteProgram (shader);
+}
+
+static void
+bind_float_core_2_0 (cairo_gl_shader_t *shader,
+ const char *name,
+ float value)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform1f (location, value);
+}
+
+static void
+bind_vec2_core_2_0 (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform2f (location, value0, value1);
+}
+
+static void
+bind_vec3_core_2_0 (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform3f (location, value0, value1, value2);
+}
+
+static void
+bind_vec4_core_2_0 (cairo_gl_shader_t *shader,
+ const char *name,
+ float value0,
+ float value1,
+ float value2,
+ float value3)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform4f (location, value0, value1, value2, value3);
+}
+
+static void
+bind_matrix_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_matrix_t* m)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ float gl_m[16] = {
+ m->xx, m->xy, m->x0,
+ m->yx, m->yy, m->y0,
+ 0, 0, 1
+ };
+ assert (location != -1);
+ glUniformMatrix3fv (location, 1, GL_TRUE, gl_m);
+}
+
+static void
+bind_texture_core_2_0 (cairo_gl_shader_t *shader, const char *name, cairo_gl_tex_t tex_unit)
+{
+ GLint location = glGetUniformLocation (shader->program, name);
+ assert (location != -1);
+ glUniform1i (location, tex_unit);
+}
+
+static void
+use_program_core_2_0 (cairo_gl_shader_t *shader)
+{
+ if (shader)
+ glUseProgram (shader->program);
+ else
+ glUseProgram (0);
+}
+
+static const cairo_gl_shader_impl_t shader_impl_core_2_0 = {
+ compile_shader_core_2_0,
+ link_shader_core_2_0,
+ destroy_shader_core_2_0,
+ destroy_program_core_2_0,
+ bind_float_core_2_0,
+ bind_vec2_core_2_0,
+ bind_vec3_core_2_0,
+ bind_vec4_core_2_0,
+ bind_matrix_core_2_0,
+ bind_texture_core_2_0,
+ use_program_core_2_0,
+};
+
+static const cairo_gl_shader_impl_t shader_impl_arb = {
+ compile_shader_arb,
+ link_shader_arb,
+ destroy_shader_arb,
+ destroy_program_arb,
+ bind_float_arb,
+ bind_vec2_arb,
+ bind_vec3_arb,
+ bind_vec4_arb,
+ bind_matrix_arb,
+ bind_texture_arb,
+ use_program_arb,
+};
+
+typedef struct _cairo_shader_cache_entry {
+ cairo_cache_entry_t base;
+
+ cairo_gl_operand_type_t src;
+ cairo_gl_operand_type_t mask;
+ cairo_gl_operand_type_t dest;
+ cairo_gl_shader_in_t in;
+
+ cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */
+ cairo_gl_shader_t shader;
+} cairo_shader_cache_entry_t;
+
+static cairo_bool_t
+_cairo_gl_shader_cache_equal (const void *key_a, const void *key_b)
+{
+ const cairo_shader_cache_entry_t *a = key_a;
+ const cairo_shader_cache_entry_t *b = key_b;
+
+ return a->src == b->src &&
+ a->mask == b->mask &&
+ a->dest == b->dest &&
+ a->in == b->in;
+}
+
+static unsigned long
+_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry)
+{
+ return (entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in);
+}
+
+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 =
+ "uniform vec4 color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = color;\n"
+ "}\n";
+ cairo_status_t status;
+
+ /* XXX multiple device support? */
+ if (GLEW_VERSION_2_0) {
+ ctx->shader_impl = &shader_impl_core_2_0;
+ } else if (GLEW_ARB_shader_objects &&
+ GLEW_ARB_fragment_shader &&
+ GLEW_ARB_vertex_program) {
+ ctx->shader_impl = &shader_impl_arb;
+ } else {
+ ctx->shader_impl = NULL;
+ }
+
+ memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders));
+
+ status = _cairo_cache_init (&ctx->shaders,
+ _cairo_gl_shader_cache_equal,
+ NULL,
+ _cairo_gl_shader_cache_destroy,
+ CAIRO_GL_MAX_SHADERS_PER_CONTEXT);
+ if (unlikely (status))
+ return status;
+
+ if (ctx->shader_impl != NULL) {
+ _cairo_gl_shader_init (&ctx->fill_rectangles_shader);
+ status = _cairo_gl_shader_compile (ctx,
+ &ctx->fill_rectangles_shader,
+ CAIRO_GL_VAR_NONE,
+ CAIRO_GL_VAR_NONE,
+ fill_fs_source);
+ if (unlikely (status))
+ return status;
+ }
+
+ 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->shader_impl->destroy_shader (ctx->vertex_shaders[i]);
+ }
+
+ _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->shader_impl->destroy_shader (shader->fragment_shader);
+
+ if (shader->program)
+ ctx->shader_impl->destroy_program (shader->program);
+}
+
+static const char *operand_names[] = { "source", "mask", "dest" };
+
+static cairo_gl_var_type_t
+cairo_gl_operand_get_var_type (cairo_gl_operand_type_t type)
+{
+ switch (type) {
+ default:
+ case CAIRO_GL_OPERAND_COUNT:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_OPERAND_NONE:
+ case CAIRO_GL_OPERAND_CONSTANT:
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT:
+ return CAIRO_GL_VAR_NONE;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ return CAIRO_GL_VAR_TEXCOORDS;
+ case CAIRO_GL_OPERAND_SPANS:
+ return CAIRO_GL_VAR_COVERAGE;
+ }
+}
+
+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,
+ "varying vec2 %s_texcoords;\n",
+ operand_names[name]);
+ break;
+ case CAIRO_GL_VAR_COVERAGE:
+ _cairo_output_stream_printf (stream,
+ "varying float %s_coverage;\n",
+ operand_names[name]);
+ break;
+ }
+}
+
+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 = gl_MultiTexCoord%d.xy;\n",
+ operand_names[name], name);
+ break;
+ case CAIRO_GL_VAR_COVERAGE:
+ _cairo_output_stream_printf (stream,
+ " %s_coverage = gl_Color.a;\n",
+ operand_names[name]);
+ break;
+ }
+}
+
+static cairo_status_t
+cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ cairo_gl_var_type_t dest,
+ char **out)
+{
+ cairo_output_stream_t *stream = _cairo_memory_stream_create ();
+ unsigned char *source;
+ unsigned int length;
+ cairo_status_t status;
+
+ cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE);
+ cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK);
+
+ _cairo_output_stream_printf (stream,
+ "void main()\n"
+ "{\n"
+ " gl_Position = ftransform();\n");
+
+ cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE);
+ cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK);
+
+ _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
+cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
+ GLuint tex_target,
+ cairo_gl_operand_type_t type,
+ cairo_gl_tex_t name)
+{
+ const char *namestr = operand_names[name];
+ const char *rectstr = (tex_target == GL_TEXTURE_RECTANGLE_EXT ? "Rect" : "");
+
+ switch (type) {
+ case CAIRO_GL_OPERAND_COUNT:
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ case CAIRO_GL_OPERAND_NONE:
+ _cairo_output_stream_printf (stream,
+ "vec4 get_%s()\n"
+ "{\n"
+ " return vec4 (0, 0, 0, 1);\n"
+ "}\n",
+ namestr);
+ break;
+ case CAIRO_GL_OPERAND_CONSTANT:
+ _cairo_output_stream_printf (stream,
+ "uniform vec4 %s_constant;\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " return %s_constant;\n"
+ "}\n",
+ namestr, namestr, namestr);
+ break;
+ case CAIRO_GL_OPERAND_TEXTURE:
+ _cairo_output_stream_printf (stream,
+ "uniform sampler2D%s %s_sampler;\n"
+ "varying vec2 %s_texcoords;\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " return texture2D%s(%s_sampler, %s_texcoords);\n"
+ "}\n",
+ rectstr, namestr, namestr, namestr, rectstr, namestr, namestr);
+ break;
+ case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
+ _cairo_output_stream_printf (stream,
+ "uniform sampler1D %s_sampler;\n"
+ "uniform mat3 %s_matrix;\n"
+ "uniform vec2 %s_segment;\n"
+ "\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n"
+ " float t = dot (pos, %s_segment) / dot (%s_segment, %s_segment);\n"
+ " return texture1D (%s_sampler, t);\n"
+ "}\n",
+ namestr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr);
+ break;
+ case CAIRO_GL_OPERAND_RADIAL_GRADIENT:
+ _cairo_output_stream_printf (stream,
+ "uniform sampler1D %s_sampler;\n"
+ "uniform mat3 %s_matrix;\n"
+ "uniform vec2 %s_circle_1;\n"
+ "uniform float %s_radius_0;\n"
+ "uniform float %s_radius_1;\n"
+ "\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n"
+ " \n"
+ " float dr = %s_radius_1 - %s_radius_0;\n"
+ " float dot_circle_1 = dot (%s_circle_1, %s_circle_1);\n"
+ " float dot_pos_circle_1 = dot (pos, %s_circle_1);\n"
+ " \n"
+ " float A = dot_circle_1 - dr * dr;\n"
+ " float B = -2.0 * (dot_pos_circle_1 + %s_radius_0 * dr);\n"
+ " float C = dot (pos, pos) - %s_radius_0 * %s_radius_0;\n"
+ " float det = B * B - 4.0 * A * C;\n"
+ " det = max (det, 0.0);\n"
+ " \n"
+ " float sqrt_det = sqrt (det);\n"
+ " sqrt_det *= sign(A);\n"
+ " \n"
+ " float t = (-B + sqrt_det) / (2.0 * A);\n"
+ " return texture1D (%s_sampler, t);\n"
+ "}\n",
+ namestr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr, namestr,
+ namestr, namestr, namestr, namestr, namestr,
+ namestr);
+ break;
+ case CAIRO_GL_OPERAND_SPANS:
+ _cairo_output_stream_printf (stream,
+ "varying float %s_coverage;\n"
+ "vec4 get_%s()\n"
+ "{\n"
+ " return vec4(0, 0, 0, %s_coverage);\n"
+ "}\n",
+ namestr, namestr, namestr);
+ break;
+ }
+}
+
+static cairo_status_t
+cairo_gl_shader_get_fragment_source (GLuint tex_target,
+ cairo_gl_shader_in_t in,
+ cairo_gl_operand_type_t src,
+ cairo_gl_operand_type_t mask,
+ cairo_gl_operand_type_t dest,
+ char **out)
+{
+ cairo_output_stream_t *stream = _cairo_memory_stream_create ();
+ unsigned char *source;
+ unsigned int length;
+ cairo_status_t status;
+
+ cairo_gl_shader_emit_color (stream, tex_target, src, CAIRO_GL_TEX_SOURCE);
+ cairo_gl_shader_emit_color (stream, tex_target, mask, CAIRO_GL_TEX_MASK);
+
+ _cairo_output_stream_printf (stream,
+ "void main()\n"
+ "{\n");
+ switch (in) {
+ case CAIRO_GL_SHADER_IN_COUNT:
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_GL_SHADER_IN_NORMAL:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source() * get_mask().a;\n");
+ break;
+ case CAIRO_GL_SHADER_IN_CA_SOURCE:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source() * get_mask();\n");
+ break;
+ case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA:
+ _cairo_output_stream_printf (stream,
+ " gl_FragColor = get_source().a * get_mask();\n");
+ break;
+ }
+
+ _cairo_output_stream_write (stream,
+ "}\n\0", 3);
+
+ status = _cairo_memory_stream_destroy (stream, &source, &length);
+ if (unlikely (status))
+ return status;
+
+ *out = (char *) source;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_shader_compile (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader,
+ cairo_gl_var_type_t src,
+ cairo_gl_var_type_t mask,
+ const char *fragment_text)
+{
+ unsigned int vertex_shader;
+ cairo_status_t status;
+
+ if (ctx->shader_impl == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (shader->program == 0);
+
+ vertex_shader = cairo_gl_var_type_hash (src, mask, CAIRO_GL_VAR_NONE);
+ if (ctx->vertex_shaders[vertex_shader] == 0) {
+ char *source;
+
+ status = cairo_gl_shader_get_vertex_source (src,
+ mask,
+ CAIRO_GL_VAR_NONE,
+ &source);
+ if (unlikely (status))
+ goto FAILURE;
+
+ ctx->shader_impl->compile_shader (&ctx->vertex_shaders[vertex_shader],
+ GL_VERTEX_SHADER,
+ source);
+ free (source);
+ }
+
+ ctx->shader_impl->compile_shader (&shader->fragment_shader,
+ GL_FRAGMENT_SHADER,
+ fragment_text);
+
+ ctx->shader_impl->link_shader (&shader->program,
+ ctx->vertex_shaders[vertex_shader],
+ shader->fragment_shader);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ FAILURE:
+ _cairo_gl_shader_fini (ctx, shader);
+ shader->fragment_shader = 0;
+ shader->program = 0;
+
+ return status;
+}
+
+void
+_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
+ const char *name,
+ float value)
+{
+ ctx->shader_impl->bind_float (ctx->current_shader, name, value);
+}
+
+void
+_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0,
+ float value1)
+{
+ ctx->shader_impl->bind_vec2 (ctx->current_shader, name, value0, value1);
+}
+
+void
+_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0,
+ float value1,
+ float value2)
+{
+ ctx->shader_impl->bind_vec3 (ctx->current_shader, name, value0, value1, value2);
+}
+
+void
+_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
+ const char *name,
+ float value0, float value1,
+ float value2, float value3)
+{
+ ctx->shader_impl->bind_vec4 (ctx->current_shader, name, value0, value1, value2, value3);
+}
+
+void
+_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
+ const char *name, cairo_matrix_t* m)
+{
+ ctx->shader_impl->bind_matrix (ctx->current_shader, name, m);
+}
+
+void
+_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx,
+ const char *name, GLuint tex_unit)
+{
+ ctx->shader_impl->bind_texture (ctx->current_shader, name, tex_unit);
+}
+
+void
+_cairo_gl_set_shader (cairo_gl_context_t *ctx,
+ cairo_gl_shader_t *shader)
+{
+ if (ctx->shader_impl == NULL)
+ return;
+
+ if (ctx->current_shader == shader)
+ return;
+
+ ctx->shader_impl->use (shader);
+
+ ctx->current_shader = shader;
+}
+
+cairo_status_t
+_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
+ cairo_gl_operand_type_t source,
+ cairo_gl_operand_type_t mask,
+ cairo_gl_shader_in_t in,
+ cairo_gl_shader_t **shader)
+{
+ cairo_shader_cache_entry_t lookup, *entry;
+ char *fs_source;
+ cairo_status_t status;
+
+ if (ctx->shader_impl == NULL) {
+ *shader = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ lookup.src = source;
+ lookup.mask = mask;
+ lookup.dest = CAIRO_GL_OPERAND_NONE;
+ lookup.in = in;
+ 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->tex_target,
+ in,
+ source,
+ mask,
+ CAIRO_GL_OPERAND_NONE,
+ &fs_source);
+ if (unlikely (status))
+ return status;
+
+ entry = 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 (ctx,
+ &entry->shader,
+ cairo_gl_operand_get_var_type (source),
+ cairo_gl_operand_get_var_type (mask),
+ fs_source);
+ free (fs_source);
+
+ if (unlikely (status)) {
+ 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-surface.c b/gfx/cairo/cairo/src/cairo-gl-surface.c
new file mode 100644
index 000000000..278e6429d
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-gl-surface.c
@@ -0,0 +1,1637 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005,2010 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Benjamin Otte <otte@gnome.org>
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-gl-private.h"
+
+static cairo_int_status_t
+_cairo_gl_surface_fill_rectangles (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects);
+
+static cairo_int_status_t
+_cairo_gl_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region);
+
+static cairo_status_t
+_cairo_gl_surface_flush (void *abstract_surface);
+
+static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
+{
+ return surface->backend == &_cairo_gl_surface_backend;
+}
+
+cairo_bool_t
+_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
+ GLenum *internal_format, GLenum *format,
+ GLenum *type, cairo_bool_t *has_alpha)
+{
+ *has_alpha = TRUE;
+
+ 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;
+
+ 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:
+ default:
+ return FALSE;
+ }
+}
+
+cairo_bool_t
+_cairo_gl_operator_is_supported (cairo_operator_t op)
+{
+ return op < CAIRO_OPERATOR_SATURATE;
+}
+
+void
+_cairo_gl_surface_init (cairo_device_t *device,
+ cairo_gl_surface_t *surface,
+ cairo_content_t content,
+ int width, int height)
+{
+ _cairo_surface_init (&surface->base,
+ &_cairo_gl_surface_backend,
+ device,
+ content);
+
+ surface->width = width;
+ surface->height = 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;
+
+ assert (width <= ctx->max_framebuffer_size && height <= ctx->max_framebuffer_size);
+
+ surface = calloc (1, sizeof (cairo_gl_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_gl_surface_init (&ctx->base, surface, content, width, height);
+ surface->tex = tex;
+
+ /* 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 *
+_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ 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:
+ /* We want to be trying GL_ALPHA framebuffer objects here. */
+ 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;
+}
+
+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);
+ 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);
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+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 (_cairo_format_from_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);
+
+ surface = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_scratch (ctx, content, width, height);
+ if (unlikely (surface->base.status)) {
+ status = _cairo_gl_context_release (ctx, surface->base.status);
+ cairo_surface_destroy (&surface->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ /* Cairo surfaces start out initialized to transparent (black) */
+ status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
+
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&surface->base);
+ 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.
+ **/
+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_SURFACE_TYPE_MISMATCH));
+
+ status = _cairo_gl_context_acquire (abstract_device, &ctx);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ 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;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (! _cairo_surface_is_gl (abstract_surface) ||
+ ! _cairo_gl_surface_is_texture (surface)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ 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;
+ cairo_status_t status;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ if (! _cairo_surface_is_gl (abstract_surface)) {
+ status = _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;
+
+ cairo_surface_flush (abstract_surface);
+
+ 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 (width < 1 || height < 1)
+ return cairo_image_surface_create (_cairo_format_from_content (content),
+ width, height);
+
+ status = _cairo_gl_context_acquire (surface->device, &ctx);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ if (width > ctx->max_framebuffer_size ||
+ height > ctx->max_framebuffer_size)
+ {
+ surface = NULL;
+ goto RELEASE;
+ }
+
+ surface = _cairo_gl_surface_create_scratch (ctx, content, width, height);
+
+RELEASE:
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status)) {
+ cairo_surface_destroy (surface);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return surface;
+}
+
+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)
+{
+ GLenum internal_format, format, type;
+ cairo_bool_t has_alpha;
+ cairo_image_surface_t *clone = NULL;
+ cairo_gl_context_t *ctx;
+ int cpp;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (! _cairo_gl_get_image_format_and_type (src->pixman_format,
+ &internal_format,
+ &format,
+ &type,
+ &has_alpha))
+ {
+ cairo_bool_t is_supported;
+
+ clone = _cairo_image_surface_coerce (src);
+ if (unlikely (clone->base.status))
+ return clone->base.status;
+
+ is_supported =
+ _cairo_gl_get_image_format_and_type (clone->pixman_format,
+ &internal_format,
+ &format,
+ &type,
+ &has_alpha);
+ assert (is_supported);
+ src = clone;
+ }
+
+ cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
+
+ status = _cairo_gl_context_acquire (dst->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_surface_flush (&dst->base);
+ if (unlikely (status))
+ goto FAIL;
+
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
+ if (_cairo_gl_surface_is_texture (dst)) {
+ _cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
+ glBindTexture (ctx->tex_target, dst->tex);
+ glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexSubImage2D (ctx->tex_target, 0,
+ dst_x, dst_y, width, height,
+ format, type,
+ src->data + src_y * src->stride + src_x * cpp);
+
+ /* If we just treated some rgb-only data as rgba, then we have to
+ * go back and fix up the alpha channel where we filled in this
+ * texture data.
+ */
+ if (!has_alpha) {
+ cairo_rectangle_int_t rect;
+
+ rect.x = dst_x;
+ rect.y = dst_y;
+ rect.width = width;
+ rect.height = height;
+
+ _cairo_gl_composite_flush (ctx);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ _cairo_gl_surface_fill_rectangles (dst,
+ CAIRO_OPERATOR_SOURCE,
+ CAIRO_COLOR_BLACK,
+ &rect, 1);
+ _cairo_gl_composite_flush (ctx);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
+ } else {
+ cairo_surface_t *tmp;
+
+ tmp = _cairo_gl_surface_create_scratch (ctx,
+ dst->base.content,
+ width, height);
+ if (unlikely (tmp->status)) {
+ cairo_surface_destroy (tmp);
+ goto FAIL;
+ }
+ status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp,
+ src,
+ src_x, src_y,
+ width, height,
+ 0, 0);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ cairo_surface_pattern_t tmp_pattern;
+
+ _cairo_pattern_init_for_surface (&tmp_pattern, tmp);
+ _cairo_gl_surface_composite (CAIRO_OPERATOR_SOURCE,
+ &tmp_pattern.base,
+ NULL,
+ dst,
+ 0, 0,
+ 0, 0,
+ dst_x, dst_y,
+ width, height,
+ NULL);
+ _cairo_pattern_fini (&tmp_pattern.base);
+ }
+
+ cairo_surface_destroy (tmp);
+ }
+
+FAIL:
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ if (clone)
+ cairo_surface_destroy (&clone->base);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_gl_surface_get_image (cairo_gl_surface_t *surface,
+ cairo_rectangle_int_t *interest,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *rect_out)
+{
+ cairo_image_surface_t *image;
+ cairo_gl_context_t *ctx;
+ GLenum format, type;
+ cairo_format_t cairo_format;
+ unsigned int cpp;
+ cairo_status_t status;
+
+ /* Want to use a switch statement here but the compiler gets whiny. */
+ if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
+ format = GL_BGRA;
+ cairo_format = CAIRO_FORMAT_ARGB32;
+ type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ cpp = 4;
+ } else if (surface->base.content == CAIRO_CONTENT_COLOR) {
+ format = GL_BGRA;
+ cairo_format = CAIRO_FORMAT_RGB24;
+ type = GL_UNSIGNED_INT_8_8_8_8_REV;
+ cpp = 4;
+ } else if (surface->base.content == CAIRO_CONTENT_ALPHA) {
+ format = GL_ALPHA;
+ cairo_format = CAIRO_FORMAT_A8;
+ type = GL_UNSIGNED_BYTE;
+ cpp = 1;
+ } else {
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ image = (cairo_image_surface_t*)
+ cairo_image_surface_create (cairo_format,
+ interest->width, interest->height);
+ if (unlikely (image->base.status))
+ return image->base.status;
+
+ /* This is inefficient, as we'd rather just read the thing without making
+ * it the destination. But then, this is the fallback path, so let's not
+ * fall back instead.
+ */
+ status = _cairo_gl_context_acquire (surface->base.device, &ctx);
+ if (unlikely (status))
+ return status;
+
+ _cairo_gl_composite_flush (ctx);
+ _cairo_gl_context_set_destination (ctx, surface);
+
+ glPixelStorei (GL_PACK_ALIGNMENT, 1);
+ glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp);
+ if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert)
+ glPixelStorei (GL_PACK_INVERT_MESA, 1);
+ glReadPixels (interest->x, interest->y,
+ interest->width, interest->height,
+ format, type, image->data);
+ if (! _cairo_gl_surface_is_texture (surface) && GLEW_MESA_pack_invert)
+ glPixelStorei (GL_PACK_INVERT_MESA, 0);
+
+ status = _cairo_gl_context_release (ctx, status);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&image->base);
+ return status;
+ }
+
+ *image_out = image;
+ if (rect_out != NULL)
+ *rect_out = *interest;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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->depth)
+ glDeleteFramebuffersEXT (1, &surface->depth);
+ if (surface->fb)
+ glDeleteFramebuffersEXT (1, &surface->fb);
+ if (surface->owns_tex)
+ glDeleteTextures (1, &surface->tex);
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+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;
+ return _cairo_gl_surface_get_image (surface, &extents, image_out, NULL);
+}
+
+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_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;
+
+ *image_extra = NULL;
+ return _cairo_gl_surface_get_image (surface, interest_rect, image_out,
+ image_rect_out);
+}
+
+static void
+_cairo_gl_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_status_t status;
+
+ status = _cairo_gl_surface_draw_image (abstract_surface, image,
+ 0, 0,
+ image->width, image->height,
+ image_rect->x, image_rect->y);
+ /* as we created the image, its format should be directly applicable */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_gl_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */
+ if (src->device == surface->base.device &&
+ _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) {
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ } else if (_cairo_surface_is_image (src)) {
+ cairo_image_surface_t *image_src = (cairo_image_surface_t *)src;
+ cairo_gl_surface_t *clone;
+ cairo_status_t status;
+
+ clone = (cairo_gl_surface_t *)
+ _cairo_gl_surface_create_similar (&surface->base,
+ src->content,
+ width, height);
+ if (clone == NULL)
+ return UNSUPPORTED ("create_similar failed");
+ if (clone->base.status)
+ return clone->base.status;
+
+ status = _cairo_gl_surface_draw_image (clone, image_src,
+ src_x, src_y,
+ width, height,
+ 0, 0);
+ if (status) {
+ cairo_surface_destroy (&clone->base);
+ return status;
+ }
+
+ *clone_out = &clone->base;
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return UNSUPPORTED ("unknown src surface type in clone_similar");
+}
+
+/** Creates a cairo-gl pattern surface for the given trapezoids */
+static cairo_status_t
+_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst,
+ int dst_x, int dst_y,
+ int width, int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_antialias_t antialias,
+ cairo_surface_pattern_t *pattern)
+{
+ pixman_format_code_t pixman_format;
+ pixman_image_t *image;
+ cairo_surface_t *surface;
+ int i;
+
+ pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1,
+ image = pixman_image_create_bits (pixman_format, width, height, NULL, 0);
+ if (unlikely (image == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (i = 0; i < num_traps; i++) {
+ pixman_trapezoid_t trap;
+
+ trap.top = _cairo_fixed_to_16_16 (traps[i].top);
+ trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom);
+
+ trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x);
+ trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y);
+ trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x);
+ trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y);
+
+ trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x);
+ trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y);
+ trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x);
+ trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y);
+
+ pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+ }
+
+ surface = _cairo_image_surface_create_for_pixman_image (image,
+ pixman_format);
+ if (unlikely (surface->status)) {
+ pixman_image_unref (image);
+ return surface->status;
+ }
+
+ _cairo_pattern_init_for_surface (pattern, surface);
+ cairo_surface_destroy (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+ cairo_gl_composite_t setup;
+ cairo_rectangle_int_t rect = { dst_x, dst_y, width, height };
+ int dx, dy;
+
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ mask == NULL &&
+ src->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ _cairo_surface_is_image (((cairo_surface_pattern_t *) src)->surface) &&
+ _cairo_matrix_is_integer_translation (&src->matrix, &dx, &dy)) {
+ cairo_image_surface_t *image = (cairo_image_surface_t *)
+ ((cairo_surface_pattern_t *) src)->surface;
+ dx += src_x;
+ dy += src_y;
+ if (dx >= 0 &&
+ dy >= 0 &&
+ dx + width <= (unsigned int) image->width &&
+ dy + height <= (unsigned int) image->height) {
+ status = _cairo_gl_surface_draw_image (dst, image,
+ dx, dy,
+ width, height,
+ dst_x, dst_y);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+ }
+
+ status = _cairo_gl_composite_init (&setup, op, dst,
+ mask && mask->has_component_alpha,
+ &rect);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_set_source (&setup, src,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_set_mask (&setup, mask,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ if (clip_region != NULL) {
+ int i, num_rectangles;
+
+ num_rectangles = cairo_region_num_rectangles (clip_region);
+
+ for (i = 0; i < num_rectangles; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, i, &rect);
+ _cairo_gl_composite_emit_rect (ctx,
+ rect.x, rect.y,
+ rect.x + rect.width, rect.y + rect.height,
+ 0);
+ }
+ } else {
+ _cairo_gl_composite_emit_rect (ctx,
+ dst_x, dst_y,
+ dst_x + width, dst_y + height,
+ 0);
+ }
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ CLEANUP:
+ _cairo_gl_composite_fini (&setup);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_surface_pattern_t traps_pattern;
+ cairo_int_status_t status;
+
+ if (! _cairo_gl_operator_is_supported (op))
+ return UNSUPPORTED ("unsupported operator");
+
+ status = _cairo_gl_get_traps_pattern (dst,
+ dst_x, dst_y, width, height,
+ traps, num_traps, antialias,
+ &traps_pattern);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_gl_surface_composite (op,
+ pattern, &traps_pattern.base, dst,
+ src_x, src_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height,
+ clip_region);
+
+ _cairo_pattern_fini (&traps_pattern.base);
+
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_fill_rectangles (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_solid_pattern_t solid;
+ cairo_gl_context_t *ctx;
+ cairo_status_t status;
+ cairo_gl_composite_t setup;
+ int i;
+
+ status = _cairo_gl_composite_init (&setup, op, dst,
+ FALSE,
+ /* XXX */ NULL);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ _cairo_pattern_init_solid (&solid, color);
+ status = _cairo_gl_composite_set_source (&setup, &solid.base,
+ 0, 0,
+ 0, 0,
+ 0, 0);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_set_mask (&setup, NULL,
+ 0, 0,
+ 0, 0,
+ 0, 0);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_gl_composite_begin (&setup, &ctx);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ for (i = 0; i < num_rects; i++) {
+ _cairo_gl_composite_emit_rect (ctx,
+ rects[i].x,
+ rects[i].y,
+ rects[i].x + rects[i].width,
+ rects[i].y + rects[i].height,
+ 0);
+ }
+
+ status = _cairo_gl_context_release (ctx, status);
+
+ CLEANUP:
+ _cairo_gl_composite_fini (&setup);
+
+ return status;
+}
+
+typedef struct _cairo_gl_surface_span_renderer {
+ cairo_span_renderer_t base;
+
+ cairo_gl_composite_t setup;
+
+ int xmin, xmax;
+ int ymin, ymax;
+
+ cairo_gl_context_t *ctx;
+} cairo_gl_surface_span_renderer_t;
+
+static cairo_status_t
+_cairo_gl_render_bounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ do {
+ if (spans[0].coverage) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ spans[0].x, y,
+ spans[1].x, y + height,
+ spans[0].coverage);
+ }
+
+ spans++;
+ } while (--num_spans > 1);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_render_unbounded_spans (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (y > renderer->ymin) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ renderer->xmin, renderer->ymin,
+ renderer->xmax, y,
+ 0);
+ }
+
+ if (num_spans == 0) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ renderer->xmin, y,
+ renderer->xmax, y + height,
+ 0);
+ } else {
+ if (spans[0].x != renderer->xmin) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ renderer->xmin, y,
+ spans[0].x, y + height,
+ 0);
+ }
+
+ do {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ spans[0].x, y,
+ spans[1].x, y + height,
+ spans[0].coverage);
+ spans++;
+ } while (--num_spans > 1);
+
+ if (spans[0].x != renderer->xmax) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ spans[0].x, y,
+ renderer->xmax, y + height,
+ 0);
+ }
+ }
+
+ renderer->ymin = y + height;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_gl_finish_unbounded_spans (void *abstract_renderer)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (renderer->ymax > renderer->ymin) {
+ _cairo_gl_composite_emit_rect (renderer->ctx,
+ renderer->xmin, renderer->ymin,
+ renderer->xmax, renderer->ymax,
+ 0);
+ }
+
+ return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static cairo_status_t
+_cairo_gl_finish_bounded_spans (void *abstract_renderer)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS);
+}
+
+static void
+_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer)
+{
+ cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
+
+ if (!renderer)
+ return;
+
+ _cairo_gl_composite_fini (&renderer->setup);
+
+ free (renderer);
+}
+
+static cairo_bool_t
+_cairo_gl_surface_check_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias)
+{
+ if (! _cairo_gl_operator_is_supported (op))
+ return FALSE;
+
+ return TRUE;
+
+ (void) pattern;
+ (void) abstract_dst;
+ (void) antialias;
+}
+
+static cairo_span_renderer_t *
+_cairo_gl_surface_create_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects,
+ cairo_region_t *clip_region)
+{
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_gl_surface_span_renderer_t *renderer;
+ cairo_status_t status;
+ const cairo_rectangle_int_t *extents;
+
+ renderer = calloc (1, sizeof (*renderer));
+ if (unlikely (renderer == NULL))
+ return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy;
+ if (rects->is_bounded) {
+ renderer->base.render_rows = _cairo_gl_render_bounded_spans;
+ renderer->base.finish = _cairo_gl_finish_bounded_spans;
+ extents = &rects->bounded;
+ } else {
+ renderer->base.render_rows = _cairo_gl_render_unbounded_spans;
+ renderer->base.finish = _cairo_gl_finish_unbounded_spans;
+ extents = &rects->unbounded;
+ }
+ renderer->xmin = extents->x;
+ renderer->xmax = extents->x + extents->width;
+ renderer->ymin = extents->y;
+ renderer->ymax = extents->y + extents->height;
+
+ status = _cairo_gl_composite_init (&renderer->setup,
+ op, dst,
+ FALSE, extents);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = _cairo_gl_composite_set_source (&renderer->setup, src,
+ extents->x, extents->y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_gl_composite_set_mask_spans (&renderer->setup);
+ _cairo_gl_composite_set_clip_region (&renderer->setup, clip_region);
+
+ status = _cairo_gl_composite_begin (&renderer->setup, &renderer->ctx);
+ if (unlikely (status))
+ goto FAIL;
+
+ return &renderer->base;
+
+FAIL:
+ _cairo_gl_composite_fini (&renderer->setup);
+ free (renderer);
+ return _cairo_span_renderer_create_in_error (status);
+}
+
+static cairo_bool_t
+_cairo_gl_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = surface->width;
+ rectangle->height = surface->height;
+
+ return TRUE;
+}
+
+static void
+_cairo_gl_surface_get_font_options (void *abstract_surface,
+ cairo_font_options_t *options)
+{
+ _cairo_font_options_init_default (options);
+
+ cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+static cairo_status_t
+_cairo_gl_surface_flush (void *abstract_surface)
+{
+ cairo_gl_surface_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) ||
+ (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);
+
+ return _cairo_gl_context_release (ctx, status);
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ /* simplify the common case of clearing the surface */
+ if (clip == NULL) {
+ if (op == CAIRO_OPERATOR_CLEAR)
+ return _cairo_gl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
+ else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) {
+ return _cairo_gl_surface_clear (abstract_surface,
+ &((cairo_solid_pattern_t *) source)->color);
+ }
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_polygon (cairo_gl_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_region_t *clip_region = NULL;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ return UNSUPPORTED ("a clip surface would be required");
+ }
+
+ if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias))
+ return UNSUPPORTED ("no span renderer");
+
+ if (op == CAIRO_OPERATOR_SOURCE)
+ return UNSUPPORTED ("SOURCE compositing doesn't work in GL");
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ op = CAIRO_OPERATOR_DEST_OUT;
+ src = &_cairo_pattern_white.base;
+ }
+
+ status = _cairo_surface_composite_polygon (&dst->base,
+ op,
+ src,
+ fill_rule,
+ antialias,
+ extents,
+ polygon,
+ clip_region);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ surface->width,
+ surface->height,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _cairo_gl_surface_polygon (surface, op, source, &polygon,
+ CAIRO_FILL_RULE_WINDING, antialias,
+ &extents, clip);
+ }
+
+ _cairo_polygon_fini (&polygon);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_gl_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_gl_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ surface->width,
+ surface->height,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+#if 0
+ if (extents.is_bounded && clip != NULL) {
+ cairo_clip_path_t *clip_path;
+
+ if (((clip_path = _clip_get_single_path (clip)) != NULL) &&
+ _cairo_path_fixed_equal (&clip_path->path, path))
+ {
+ clip = NULL;
+ }
+ }
+#endif
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _cairo_gl_surface_polygon (surface, op, source, &polygon,
+ fill_rule, antialias,
+ &extents, clip);
+ }
+
+ _cairo_polygon_fini (&polygon);
+
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+const cairo_surface_backend_t _cairo_gl_surface_backend = {
+ CAIRO_SURFACE_TYPE_GL,
+ _cairo_gl_surface_create_similar,
+ _cairo_gl_surface_finish,
+
+ _cairo_gl_surface_acquire_source_image,
+ _cairo_gl_surface_release_source_image,
+ _cairo_gl_surface_acquire_dest_image,
+ _cairo_gl_surface_release_dest_image,
+
+ _cairo_gl_surface_clone_similar,
+ _cairo_gl_surface_composite,
+ _cairo_gl_surface_fill_rectangles,
+ _cairo_gl_surface_composite_trapezoids,
+ _cairo_gl_surface_create_span_renderer,
+ _cairo_gl_surface_check_span_renderer,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_gl_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_gl_surface_get_font_options,
+ _cairo_gl_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+ _cairo_gl_surface_scaled_font_fini,
+ _cairo_gl_surface_scaled_glyph_fini,
+ _cairo_gl_surface_paint,
+ NULL, /* mask */
+ _cairo_gl_surface_stroke,
+ _cairo_gl_surface_fill,
+ _cairo_gl_surface_show_glyphs, /* show_glyphs */
+ NULL /* snapshot */
+};
diff --git a/gfx/cairo/cairo/src/cairo-gl.h b/gfx/cairo/cairo/src/cairo-gl.h
new file mode 100644
index 000000000..131d1148b
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-gl.h
@@ -0,0 +1,119 @@
+/* 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.
+ */
+
+#ifndef CAIRO_GL_H
+#define CAIRO_GL_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_GL_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);
+
+#if CAIRO_HAS_GLX_FUNCTIONS
+#include <GL/glx.h>
+
+cairo_public cairo_device_t *
+cairo_glx_device_create (Display *dpy, GLXContext gl_ctx);
+
+cairo_public Display *
+cairo_glx_device_get_display (cairo_device_t *device);
+
+cairo_public GLXContext
+cairo_glx_device_get_context (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_window (cairo_device_t *device,
+ Window win,
+ int width, int height);
+#endif
+
+#if CAIRO_HAS_WGL_FUNCTIONS
+#include <windows.h>
+
+cairo_public cairo_device_t *
+cairo_wgl_device_create (HGLRC rc);
+
+cairo_public HGLRC
+cairo_wgl_device_get_context (cairo_device_t *device);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_dc (cairo_device_t *device,
+ HDC dc,
+ int width,
+ int height);
+#endif
+
+#if CAIRO_HAS_EGL_FUNCTIONS
+#include <EGL/egl.h>
+
+cairo_public cairo_device_t *
+cairo_egl_device_create (EGLDisplay dpy, EGLContext egl);
+
+cairo_public cairo_surface_t *
+cairo_gl_surface_create_for_egl (cairo_device_t *device,
+ EGLSurface egl,
+ int width,
+ int height);
+
+#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-glitz-private.h b/gfx/cairo/cairo/src/cairo-glitz-private.h
new file mode 100644
index 000000000..8a876eeab
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-glitz-private.h
@@ -0,0 +1,41 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ */
+
+#ifndef CAIRO_GLITZ_PRIVATE_H
+#define CAIRO_GLITZ_PRIVATE_H
+
+#include "cairoint.h"
+#include "cairo-glitz.h"
+
+slim_hidden_proto (cairo_glitz_surface_create);
+
+#endif /* CAIRO_GLITZ_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-glitz-surface.c b/gfx/cairo/cairo/src/cairo-glitz-surface.c
new file mode 100644
index 000000000..5f97f65e8
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-glitz-surface.c
@@ -0,0 +1,2446 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 David Reveman
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of David
+ * Reveman not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. David Reveman makes no representations about the
+ * suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: David Reveman <davidr@novell.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-glitz.h"
+#include "cairo-glitz-private.h"
+#include "cairo-region-private.h"
+
+typedef struct _cairo_glitz_surface {
+ cairo_surface_t base;
+
+ glitz_surface_t *surface;
+ glitz_format_t *format;
+
+ cairo_region_t *clip_region;
+ cairo_bool_t has_clip;
+ glitz_box_t *clip_boxes;
+ int num_clip_boxes;
+} cairo_glitz_surface_t;
+
+static const cairo_surface_backend_t *
+_cairo_glitz_surface_get_backend (void);
+
+static cairo_status_t
+_cairo_glitz_surface_finish (void *abstract_surface)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+
+ if (surface->clip_boxes)
+ free (surface->clip_boxes);
+
+ cairo_region_destroy (surface->clip_region);
+ glitz_surface_destroy (surface->surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static glitz_format_name_t
+_glitz_format_from_content (cairo_content_t content)
+{
+ switch (content) {
+ case CAIRO_CONTENT_COLOR:
+ return GLITZ_STANDARD_RGB24;
+ case CAIRO_CONTENT_ALPHA:
+ return GLITZ_STANDARD_A8;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ return GLITZ_STANDARD_ARGB32;
+ }
+
+ ASSERT_NOT_REACHED;
+ return GLITZ_STANDARD_ARGB32;
+}
+
+static cairo_surface_t *
+_cairo_glitz_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_glitz_surface_t *src = abstract_src;
+ cairo_surface_t *crsurface;
+ glitz_drawable_t *drawable;
+ glitz_surface_t *surface;
+ glitz_format_t *gformat;
+
+ drawable = glitz_surface_get_drawable (src->surface);
+
+ gformat =
+ glitz_find_standard_format (drawable,
+ _glitz_format_from_content (content));
+ if (!gformat)
+ return NULL;
+
+ surface = glitz_surface_create (drawable, gformat,
+ width <= 0 ? 1 : width,
+ height <= 0 ? 1 : height,
+ 0, NULL);
+
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ crsurface = cairo_glitz_surface_create (surface);
+
+ glitz_surface_destroy (surface);
+
+ return crsurface;
+}
+
+static cairo_status_t
+_cairo_glitz_surface_get_image (cairo_glitz_surface_t *surface,
+ cairo_rectangle_int_t *interest,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *rect_out)
+{
+ cairo_image_surface_t *image;
+ cairo_rectangle_int_t extents;
+ cairo_format_t format;
+ cairo_format_masks_t masks;
+ glitz_buffer_t *buffer;
+ glitz_pixel_format_t pf;
+
+ extents.x = 0;
+ extents.y = 0;
+ extents.width = glitz_surface_get_width (surface->surface);
+ extents.height = glitz_surface_get_height (surface->surface);
+
+ if (interest != NULL) {
+ if (! _cairo_rectangle_intersect (&extents, interest)) {
+ *image_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ if (rect_out != NULL)
+ *rect_out = extents;
+
+ if (surface->format->color.fourcc == GLITZ_FOURCC_RGB) {
+ if (surface->format->color.red_size > 0) {
+ if (surface->format->color.alpha_size > 0)
+ format = CAIRO_FORMAT_ARGB32;
+ else
+ format = CAIRO_FORMAT_RGB24;
+ } else {
+ format = CAIRO_FORMAT_A8;
+ }
+ } else
+ format = CAIRO_FORMAT_ARGB32;
+
+ image = (cairo_image_surface_t*)
+ cairo_image_surface_create (format, extents.width, extents.height);
+ if (image->base.status)
+ return image->base.status;
+
+ _pixman_format_to_masks (image->pixman_format, &masks);
+ pf.fourcc = GLITZ_FOURCC_RGB;
+ pf.masks.bpp = masks.bpp;
+ pf.masks.alpha_mask = masks.alpha_mask;
+ pf.masks.red_mask = masks.red_mask;
+ pf.masks.green_mask = masks.green_mask;
+ pf.masks.blue_mask = masks.blue_mask;
+ pf.xoffset = 0;
+ pf.skip_lines = 0;
+
+ /* XXX: we should eventually return images with negative stride,
+ need to verify that libpixman have no problem with this first. */
+ pf.bytes_per_line = image->stride;
+ pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN;
+
+ buffer = glitz_buffer_create_for_data (image->data);
+ if (buffer == NULL) {
+ cairo_surface_destroy (&image->base);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* clear out the glitz clip; the clip affects glitz_get_pixels */
+ if (surface->has_clip)
+ glitz_surface_set_clip_region (surface->surface,
+ 0, 0, NULL, 0);
+
+ glitz_get_pixels (surface->surface,
+ extents.x, extents.y,
+ extents.width, extents.height,
+ &pf,
+ buffer);
+
+ glitz_buffer_destroy (buffer);
+
+ /* restore the clip, if any */
+ if (surface->has_clip) {
+ glitz_surface_set_clip_region (surface->surface,
+ 0, 0,
+ surface->clip_boxes,
+ surface->num_clip_boxes);
+ }
+
+ *image_out = image;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_glitz_surface_set_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int x_dst,
+ int y_dst)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+ glitz_buffer_t *buffer;
+ glitz_pixel_format_t pf;
+ cairo_format_masks_t masks;
+ char *data;
+
+ _pixman_format_to_masks (image->pixman_format, &masks);
+
+ pf.fourcc = GLITZ_FOURCC_RGB;
+ pf.masks.bpp = masks.bpp;
+ pf.masks.alpha_mask = masks.alpha_mask;
+ pf.masks.red_mask = masks.red_mask;
+ pf.masks.green_mask = masks.green_mask;
+ pf.masks.blue_mask = masks.blue_mask;
+ pf.xoffset = src_x;
+ pf.skip_lines = src_y;
+
+ /* check for negative stride */
+ if (image->stride < 0) {
+ pf.bytes_per_line = -image->stride;
+ pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP;
+ data = (char *) image->data + image->stride * (image->height - 1);
+ } else {
+ pf.bytes_per_line = image->stride;
+ pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_TOP_DOWN;
+ data = (char *) image->data;
+ }
+
+ buffer = glitz_buffer_create_for_data (data);
+ if (buffer == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ glitz_set_pixels (surface->surface,
+ x_dst, y_dst,
+ width, height,
+ &pf,
+ buffer);
+
+ glitz_buffer_destroy (buffer);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_glitz_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+
+ *image_extra = NULL;
+
+ return _cairo_glitz_surface_get_image (surface, NULL, image_out, NULL);
+}
+
+static cairo_surface_t *
+_cairo_glitz_surface_snapshot (void *abstract_surface)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ cairo_image_surface_t *image;
+
+ status = _cairo_glitz_surface_get_image (surface, NULL, &image, NULL);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ return &image->base;
+}
+
+static void
+_cairo_glitz_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_glitz_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+
+ status = _cairo_glitz_surface_get_image (surface, interest_rect, &image,
+ image_rect_out);
+ if (status)
+ return status;
+
+ *image_out = image;
+ *image_extra = NULL;
+
+ return status;
+}
+
+static void
+_cairo_glitz_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = _cairo_glitz_surface_set_image (surface, image, 0, 0,
+ image->width, image->height,
+ image_rect->x, image_rect->y);
+ if (status)
+ status = _cairo_surface_set_error (&surface->base, status);
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_glitz_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+ cairo_glitz_surface_t *clone;
+ cairo_status_t status;
+
+ if (surface->base.status)
+ return surface->base.status;
+
+ if (src->backend == surface->base.backend)
+ {
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+ else if (_cairo_surface_is_image (src))
+ {
+ cairo_image_surface_t *image_src = (cairo_image_surface_t *) src;
+
+ clone = (cairo_glitz_surface_t *)
+ _cairo_glitz_surface_create_similar (surface, src->content,
+ width, height);
+ if (clone == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (clone->base.status)
+ return clone->base.status;
+
+ status = _cairo_glitz_surface_set_image (clone, image_src,
+ src_x, src_y,
+ width, height,
+ 0, 0);
+ if (status) {
+ cairo_surface_destroy (&clone->base);
+ return status;
+ }
+
+ *clone_out = &clone->base;
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static void
+_cairo_glitz_surface_set_matrix (cairo_glitz_surface_t *surface,
+ cairo_matrix_t *matrix)
+{
+ glitz_transform_t transform;
+
+ transform.matrix[0][0] = _cairo_fixed_16_16_from_double (matrix->xx);
+ transform.matrix[0][1] = _cairo_fixed_16_16_from_double (matrix->xy);
+ transform.matrix[0][2] = _cairo_fixed_16_16_from_double (matrix->x0);
+
+ transform.matrix[1][0] = _cairo_fixed_16_16_from_double (matrix->yx);
+ transform.matrix[1][1] = _cairo_fixed_16_16_from_double (matrix->yy);
+ transform.matrix[1][2] = _cairo_fixed_16_16_from_double (matrix->y0);
+
+ transform.matrix[2][0] = 0;
+ transform.matrix[2][1] = 0;
+ transform.matrix[2][2] = _cairo_fixed_16_16_from_double (1);
+
+ glitz_surface_set_transform (surface->surface, &transform);
+}
+
+static cairo_bool_t
+_is_supported_operator (cairo_operator_t op)
+{
+ /* This is really just a if (op < SATURATE), but we use a switch
+ * so the compiler will warn if we ever add more operators.
+ */
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+ return TRUE;
+
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_OPERATOR_SATURATE:
+ /* nobody likes saturate, expect that it's required to do
+ * seamless polygons!
+ */
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return FALSE;
+ }
+}
+
+static glitz_operator_t
+_glitz_operator (cairo_operator_t op)
+{
+ switch ((int) op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return GLITZ_OPERATOR_CLEAR;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return GLITZ_OPERATOR_SRC;
+ case CAIRO_OPERATOR_OVER:
+ return GLITZ_OPERATOR_OVER;
+ case CAIRO_OPERATOR_IN:
+ return GLITZ_OPERATOR_IN;
+ case CAIRO_OPERATOR_OUT:
+ return GLITZ_OPERATOR_OUT;
+ case CAIRO_OPERATOR_ATOP:
+ return GLITZ_OPERATOR_ATOP;
+
+ case CAIRO_OPERATOR_DEST:
+ return GLITZ_OPERATOR_DST;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return GLITZ_OPERATOR_OVER_REVERSE;
+ case CAIRO_OPERATOR_DEST_IN:
+ return GLITZ_OPERATOR_IN_REVERSE;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return GLITZ_OPERATOR_OUT_REVERSE;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return GLITZ_OPERATOR_ATOP_REVERSE;
+
+ case CAIRO_OPERATOR_XOR:
+ return GLITZ_OPERATOR_XOR;
+ case CAIRO_OPERATOR_ADD:
+ return GLITZ_OPERATOR_ADD;
+
+ default:
+ ASSERT_NOT_REACHED;
+
+ /* Something's very broken if this line of code can be reached, so
+ * we want to return something that would give a noticeably
+ * incorrect result. The XOR operator seems so rearely desired
+ * that it should fit the bill here.
+ */
+ return CAIRO_OPERATOR_XOR;
+ }
+}
+
+#define CAIRO_GLITZ_FEATURE_OK(surface, name) \
+ (glitz_drawable_get_features (glitz_surface_get_drawable (surface)) & \
+ (GLITZ_FEATURE_ ## name ## _MASK))
+
+static glitz_status_t
+_glitz_ensure_target (glitz_surface_t *surface)
+{
+ if (!glitz_surface_get_attached_drawable (surface))
+ {
+ glitz_drawable_format_t *target_format, templ;
+ glitz_format_t *format;
+ glitz_drawable_t *drawable, *target;
+ unsigned int width, height;
+ unsigned long mask;
+
+ drawable = glitz_surface_get_drawable (surface);
+ format = glitz_surface_get_format (surface);
+ width = glitz_surface_get_width (surface);
+ height = glitz_surface_get_height (surface);
+
+ if (format->color.fourcc != GLITZ_FOURCC_RGB)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ templ.color = format->color;
+ templ.depth_size = 0;
+ templ.stencil_size = 0;
+ templ.doublebuffer = 0;
+ templ.samples = 1;
+
+ mask =
+ GLITZ_FORMAT_RED_SIZE_MASK |
+ GLITZ_FORMAT_GREEN_SIZE_MASK |
+ GLITZ_FORMAT_BLUE_SIZE_MASK |
+ GLITZ_FORMAT_ALPHA_SIZE_MASK |
+ GLITZ_FORMAT_DEPTH_SIZE_MASK |
+ GLITZ_FORMAT_STENCIL_SIZE_MASK |
+ GLITZ_FORMAT_DOUBLEBUFFER_MASK |
+ GLITZ_FORMAT_SAMPLES_MASK;
+
+ target_format = glitz_find_drawable_format (drawable, mask, &templ, 0);
+ if (!target_format)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ target = glitz_create_drawable (drawable, target_format,
+ width, height);
+ if (!target)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ glitz_surface_attach (surface, target,
+ GLITZ_DRAWABLE_BUFFER_FRONT_COLOR);
+
+ glitz_drawable_destroy (target);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _cairo_glitz_surface_attributes {
+ cairo_surface_attributes_t base;
+
+ glitz_fill_t fill;
+ glitz_filter_t filter;
+ glitz_fixed16_16_t *params;
+ int n_params;
+ cairo_bool_t acquired;
+} cairo_glitz_surface_attributes_t;
+
+static cairo_int_status_t
+_cairo_glitz_pattern_acquire_surface (const cairo_pattern_t *pattern,
+ cairo_glitz_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glitz_surface_t **surface_out,
+ cairo_glitz_surface_attributes_t *attr)
+{
+ cairo_glitz_surface_t *src = NULL;
+
+ attr->acquired = FALSE;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL: {
+ cairo_gradient_pattern_t *gradient =
+ (cairo_gradient_pattern_t *) pattern;
+ char *data;
+ glitz_fixed16_16_t *params;
+ unsigned int n_params;
+ unsigned int *pixels;
+ unsigned int i, n_base_params;
+ glitz_buffer_t *buffer;
+ static const glitz_pixel_format_t format = {
+ GLITZ_FOURCC_RGB,
+ {
+ 32,
+ 0xff000000,
+ 0x00ff0000,
+ 0x0000ff00,
+ 0x000000ff
+ },
+ 0, 0, 0,
+ GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP
+ };
+
+ /* XXX: the current color gradient acceleration provided by glitz is
+ * experimental, it's been proven inappropriate in a number of ways,
+ * most importantly, it's currently implemented as filters and
+ * gradients are not filters. eventually, it will be replaced with
+ * something more appropriate.
+ */
+
+ if (gradient->n_stops < 2)
+ break;
+
+ if (!CAIRO_GLITZ_FEATURE_OK (dst->surface, FRAGMENT_PROGRAM))
+ break;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+ n_base_params = 6;
+ else
+ n_base_params = 4;
+
+ n_params = gradient->n_stops * 3 + n_base_params;
+
+ /* check for int overflow */
+ {
+ int size1, size2;
+ if (n_params >= INT32_MAX / sizeof (glitz_fixed16_16_t) ||
+ gradient->n_stops >= INT32_MAX / sizeof (unsigned int))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ size1 = n_params * sizeof (glitz_fixed16_16_t);
+ size2 = gradient->n_stops * sizeof (unsigned int);
+
+ if (size1 >= INT32_MAX - size2)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ data = malloc (size1 + size2);
+ }
+
+ if (!data)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ params = (glitz_fixed16_16_t *) data;
+ pixels = (unsigned int *)
+ (data + sizeof (glitz_fixed16_16_t) * n_params);
+
+ buffer = glitz_buffer_create_for_data (pixels);
+ if (!buffer) {
+ free (data);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ src = (cairo_glitz_surface_t *)
+ _cairo_glitz_surface_create_similar (&dst->base,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ gradient->n_stops, 1);
+ if (src->base.status) {
+ glitz_buffer_destroy (buffer);
+ free (data);
+ return src->base.status;
+ }
+
+ for (i = 0; i < gradient->n_stops; i++)
+ {
+ pixels[i] =
+ (((int) (gradient->stops[i].color.alpha_short >> 8)) << 24) |
+ (((int) (gradient->stops[i].color.red_short >> 8)) << 16) |
+ (((int) (gradient->stops[i].color.green_short >> 8)) << 8) |
+ (((int) (gradient->stops[i].color.blue_short >> 8)));
+
+ params[n_base_params + 3 * i + 0] = _cairo_fixed_16_16_from_double (gradient->stops[i].offset);
+ params[n_base_params + 3 * i + 1] = i << 16;
+ params[n_base_params + 3 * i + 2] = 0;
+ }
+
+ glitz_set_pixels (src->surface, 0, 0, gradient->n_stops, 1,
+ (glitz_pixel_format_t *)&format, buffer);
+
+ glitz_buffer_destroy (buffer);
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_linear_pattern_t *grad = (cairo_linear_pattern_t *) pattern;
+
+ params[0] = _cairo_fixed_to_16_16 (grad->p1.x);
+ params[1] = _cairo_fixed_to_16_16 (grad->p1.y);
+ params[2] = _cairo_fixed_to_16_16 (grad->p2.x);
+ params[3] = _cairo_fixed_to_16_16 (grad->p2.y);
+ attr->filter = GLITZ_FILTER_LINEAR_GRADIENT;
+ }
+ else
+ {
+ cairo_radial_pattern_t *grad = (cairo_radial_pattern_t *) pattern;
+
+ params[0] = _cairo_fixed_to_16_16 (grad->c1.x);
+ params[1] = _cairo_fixed_to_16_16 (grad->c1.y);
+ params[2] = _cairo_fixed_to_16_16 (grad->r1);
+ params[3] = _cairo_fixed_to_16_16 (grad->c2.x);
+ params[4] = _cairo_fixed_to_16_16 (grad->c2.y);
+ params[5] = _cairo_fixed_to_16_16 (grad->r2);
+ attr->filter = GLITZ_FILTER_RADIAL_GRADIENT;
+ }
+
+ switch (pattern->extend) {
+ case CAIRO_EXTEND_NONE:
+ attr->fill = GLITZ_FILL_TRANSPARENT;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ attr->fill = GLITZ_FILL_REPEAT;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ attr->fill = GLITZ_FILL_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ attr->fill = GLITZ_FILL_NEAREST;
+ break;
+ }
+
+ attr->params = params;
+ attr->n_params = n_params;
+ attr->base.matrix = pattern->matrix;
+ attr->base.x_offset = 0;
+ attr->base.y_offset = 0;
+ } break;
+ case CAIRO_PATTERN_TYPE_SOLID:
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ default:
+ break;
+ }
+
+ if (!src)
+ {
+ cairo_int_status_t status;
+
+ status = _cairo_pattern_acquire_surface (pattern, &dst->base,
+ x, y, width, height,
+ CAIRO_PATTERN_ACQUIRE_NONE,
+ (cairo_surface_t **) &src,
+ &attr->base);
+ if (status)
+ return status;
+
+ if (src)
+ {
+ switch (attr->base.extend) {
+ case CAIRO_EXTEND_NONE:
+ attr->fill = GLITZ_FILL_TRANSPARENT;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ attr->fill = GLITZ_FILL_REPEAT;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ attr->fill = GLITZ_FILL_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ default:
+ attr->fill = GLITZ_FILL_NEAREST;
+ break;
+ }
+
+ switch (attr->base.filter) {
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ attr->filter = GLITZ_FILTER_NEAREST;
+ break;
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ default:
+ attr->filter = GLITZ_FILTER_BILINEAR;
+ break;
+ }
+
+ attr->params = NULL;
+ attr->n_params = 0;
+ attr->acquired = TRUE;
+ }
+ }
+
+ *surface_out = src;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_glitz_pattern_release_surface (const cairo_pattern_t *pattern,
+ cairo_glitz_surface_t *surface,
+ cairo_glitz_surface_attributes_t *attr)
+{
+ if (attr->acquired)
+ _cairo_pattern_release_surface (pattern, &surface->base, &attr->base);
+ else
+ cairo_surface_destroy (&surface->base);
+}
+
+static cairo_int_status_t
+_cairo_glitz_pattern_acquire_surfaces (const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ cairo_glitz_surface_t *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glitz_surface_t **src_out,
+ cairo_glitz_surface_t **mask_out,
+ cairo_glitz_surface_attributes_t *sattr,
+ cairo_glitz_surface_attributes_t *mattr)
+{
+ cairo_int_status_t status;
+ cairo_solid_pattern_t tmp;
+
+ /* If src and mask are both solid, then the mask alpha can be
+ * combined into src and mask can be ignored. */
+
+ /* XXX: This optimization assumes that there is no color
+ * information in mask, so this will need to change when we
+ * support RENDER-style 4-channel masks. */
+
+ if (src->type == CAIRO_PATTERN_TYPE_SOLID &&
+ mask->type == CAIRO_PATTERN_TYPE_SOLID)
+ {
+ cairo_color_t combined;
+ cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src;
+ cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask;
+
+ combined = src_solid->color;
+ _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha);
+
+ _cairo_pattern_init_solid (&tmp, &combined, CAIRO_CONTENT_COLOR_ALPHA);
+
+ mask = NULL;
+ src = &tmp.base;
+ }
+
+ status = _cairo_glitz_pattern_acquire_surface (src, dst,
+ src_x, src_y,
+ width, height,
+ src_out, sattr);
+
+ if (src == &tmp.base)
+ _cairo_pattern_fini (&tmp.base);
+
+ if (status)
+ return status;
+
+ if (mask)
+ {
+ status = _cairo_glitz_pattern_acquire_surface (mask, dst,
+ mask_x, mask_y,
+ width, height,
+ mask_out, mattr);
+
+ if (status) {
+ /* XXX src == &tmp.base -> invalid (currently inconsequential) */
+ _cairo_glitz_pattern_release_surface (src, *src_out, sattr);
+ }
+
+ return status;
+ }
+ else
+ {
+ *mask_out = NULL;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_glitz_surface_set_attributes (cairo_glitz_surface_t *surface,
+ cairo_glitz_surface_attributes_t *a)
+{
+ _cairo_glitz_surface_set_matrix (surface, &a->base.matrix);
+ glitz_surface_set_fill (surface->surface, a->fill);
+ glitz_surface_set_filter (surface->surface, a->filter,
+ a->params, a->n_params);
+}
+
+static cairo_status_t
+_cairo_glitz_get_boxes_from_region (cairo_region_t *region,
+ glitz_box_t **boxes,
+ int *nboxes)
+{
+ pixman_box32_t *pboxes;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ int n, i;
+
+ n = 0;
+ pboxes = pixman_region32_rectangles (&region->rgn, &n);
+ if (n == 0) {
+ *nboxes = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (n > *nboxes) {
+ *boxes = _cairo_malloc_ab (n, sizeof (glitz_box_t));
+ if (*boxes == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto done;
+ }
+ }
+
+ for (i = 0; i < n; i++) {
+ (*boxes)[i].x1 = pboxes[i].x1;
+ (*boxes)[i].y1 = pboxes[i].y1;
+ (*boxes)[i].x2 = pboxes[i].x2;
+ (*boxes)[i].y2 = pboxes[i].y2;
+ }
+
+ *nboxes = n;
+done:
+ return status;
+}
+
+static cairo_status_t
+_cairo_glitz_surface_set_clip_region (void *abstract_surface,
+ cairo_region_t *region)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+
+ if (region == surface->clip_region)
+ return CAIRO_STATUS_SUCCESS;
+
+ cairo_region_destroy (surface->clip_region);
+ surface->clip_region = cairo_region_reference (region);
+
+ if (region != NULL) {
+ cairo_status_t status;
+
+ status = _cairo_glitz_get_boxes_from_region (region,
+ &surface->clip_boxes,
+ &surface->num_clip_boxes);
+ if (status)
+ return status;
+
+ glitz_surface_set_clip_region (surface->surface,
+ 0, 0,
+ surface->clip_boxes,
+ surface->num_clip_boxes);
+ surface->has_clip = TRUE;
+ } else {
+ glitz_surface_set_clip_region (surface->surface, 0, 0, NULL, 0);
+ surface->has_clip = FALSE;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_glitz_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ const cairo_pattern_t *mask_pattern,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_glitz_surface_attributes_t src_attr, mask_attr;
+ cairo_glitz_surface_t *dst = abstract_dst;
+ cairo_glitz_surface_t *src;
+ cairo_glitz_surface_t *mask;
+ cairo_int_status_t status;
+
+ if (! _is_supported_operator (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (_glitz_ensure_target (dst->surface))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_glitz_surface_set_clip_region (dst, clip_region);
+ if (status)
+ return status;
+
+ status = _cairo_glitz_pattern_acquire_surfaces (src_pattern, mask_pattern,
+ dst,
+ src_x, src_y,
+ mask_x, mask_y,
+ width, height,
+ &src, &mask,
+ &src_attr, &mask_attr);
+ if (status)
+ return status;
+
+ _cairo_glitz_surface_set_attributes (src, &src_attr);
+ if (mask)
+ {
+ _cairo_glitz_surface_set_attributes (mask, &mask_attr);
+ glitz_composite (_glitz_operator (op),
+ src->surface,
+ mask->surface,
+ dst->surface,
+ src_x + src_attr.base.x_offset,
+ src_y + src_attr.base.y_offset,
+ mask_x + mask_attr.base.x_offset,
+ mask_y + mask_attr.base.y_offset,
+ dst_x, dst_y,
+ width, height);
+ }
+ else
+ {
+ glitz_composite (_glitz_operator (op),
+ src->surface,
+ NULL,
+ dst->surface,
+ src_x + src_attr.base.x_offset,
+ src_y + src_attr.base.y_offset,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+ }
+
+ if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED)
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (status == CAIRO_STATUS_SUCCESS &&
+ ! _cairo_operator_bounded_by_source (op))
+ {
+ int src_width, src_height;
+ int mask_width, mask_height;
+
+ src_width = glitz_surface_get_width (src->surface);
+ src_height = glitz_surface_get_height (src->surface);
+ if (mask)
+ {
+ mask_width = glitz_surface_get_width (mask->surface);
+ mask_height = glitz_surface_get_height (mask->surface);
+ }
+ else
+ {
+ mask_width = 0;
+ mask_height = 0;
+ }
+ status = _cairo_surface_composite_fixup_unbounded (&dst->base,
+ &src_attr.base,
+ src_width, src_height,
+ mask ? &mask_attr.base : NULL,
+ mask_width, mask_height,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y, width, height,
+ clip_region);
+ }
+
+ if (mask)
+ {
+ if (mask_attr.n_params)
+ free (mask_attr.params);
+
+ _cairo_glitz_pattern_release_surface (mask_pattern, mask, &mask_attr);
+ }
+
+ if (src_attr.n_params)
+ free (src_attr.params);
+
+ _cairo_glitz_pattern_release_surface (src_pattern, src, &src_attr);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_glitz_surface_fill_rectangles (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int n_rects)
+{
+ cairo_glitz_surface_t *dst = abstract_dst;
+ cairo_glitz_surface_t *src;
+ glitz_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (glitz_rectangle_t)];
+ glitz_rectangle_t *glitz_rects = stack_rects;
+ glitz_rectangle_t *current_rect;
+ cairo_status_t status;
+ int i;
+
+ if (! _is_supported_operator (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_glitz_surface_set_clip_region (dst, NULL);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ if (n_rects > ARRAY_LENGTH (stack_rects)) {
+ glitz_rects = _cairo_malloc_ab (n_rects, sizeof (glitz_rectangle_t));
+ if (glitz_rects == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < n_rects; i++) {
+ glitz_rects[i].x = rects[i].x;
+ glitz_rects[i].y = rects[i].y;
+ glitz_rects[i].width = rects[i].width;
+ glitz_rects[i].height = rects[i].height;
+ }
+
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE: {
+ glitz_color_t glitz_color;
+ glitz_format_t *format;
+
+ glitz_color.red = color->red_short;
+ glitz_color.green = color->green_short;
+ glitz_color.blue = color->blue_short;
+ glitz_color.alpha = color->alpha_short;
+
+ /*
+ * XXX even if the dst surface don't have an alpha channel, the
+ * above alpha still effect the dst surface because the
+ * underlying glitz drawable may have an alpha channel. So
+ * replacing the color with an opaque one is needed.
+ */
+ format = glitz_surface_get_format (dst->surface);
+ if (format->color.alpha_size == 0)
+ glitz_color.alpha = 0xffff;
+
+ glitz_set_rectangles (dst->surface, &glitz_color,
+ glitz_rects, n_rects);
+ } break;
+ case CAIRO_OPERATOR_SATURATE:
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+ default:
+ if (_glitz_ensure_target (dst->surface))
+ {
+ if (glitz_rects != stack_rects)
+ free (glitz_rects);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ src = (cairo_glitz_surface_t *)
+ _cairo_surface_create_similar_solid (&dst->base,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ 1, 1,
+ (cairo_color_t *) color,
+ FALSE);
+ if (src == NULL || src->base.status) {
+ if (glitz_rects != stack_rects)
+ free (glitz_rects);
+ return src ? src->base.status : CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ glitz_surface_set_fill (src->surface, GLITZ_FILL_REPEAT);
+
+ current_rect = glitz_rects;
+ while (n_rects--)
+ {
+ glitz_composite (_glitz_operator (op),
+ src->surface,
+ NULL,
+ dst->surface,
+ 0, 0,
+ 0, 0,
+ current_rect->x, current_rect->y,
+ current_rect->width, current_rect->height);
+ current_rect++;
+ }
+
+ cairo_surface_destroy (&src->base);
+ break;
+ }
+
+ if (glitz_rects != stack_rects)
+ free (glitz_rects);
+
+ if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_glitz_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int n_traps,
+ cairo_region_t *clip_region)
+{
+ cairo_glitz_surface_attributes_t attributes;
+ cairo_glitz_surface_t *dst = abstract_dst;
+ cairo_glitz_surface_t *src;
+ cairo_glitz_surface_t *mask = NULL;
+ glitz_buffer_t *buffer = NULL;
+ void *data = NULL;
+ cairo_int_status_t status;
+ unsigned short alpha;
+ pixman_trapezoid_t stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)];
+ pixman_trapezoid_t *pixman_traps = stack_traps;
+ int i;
+
+ if (antialias != CAIRO_ANTIALIAS_DEFAULT &&
+ antialias != CAIRO_ANTIALIAS_GRAY)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (! _is_supported_operator (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (_glitz_ensure_target (dst->surface))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_glitz_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
+
+ /* Convert traps to pixman traps */
+ if (n_traps > ARRAY_LENGTH (stack_traps)) {
+ pixman_traps = _cairo_malloc_ab (n_traps, sizeof (pixman_trapezoid_t));
+ if (pixman_traps == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < n_traps; i++) {
+ pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top);
+ pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom);
+ pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x);
+ pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y);
+ pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x);
+ pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y);
+ pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x);
+ pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y);
+ pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x);
+ pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y);
+ }
+
+ status = _cairo_glitz_pattern_acquire_surface (pattern, dst,
+ src_x, src_y,
+ width, height,
+ &src, &attributes);
+ if (status)
+ goto FAIL;
+
+ alpha = 0xffff;
+
+ if (op == CAIRO_OPERATOR_ADD || n_traps <= 1) {
+ static const glitz_color_t clear_black = { 0, 0, 0, 0 };
+ glitz_color_t color;
+ glitz_geometry_format_t format;
+ int n_trap_added;
+ int offset = 0;
+ int data_size = 0;
+ int size = 30 * n_traps; /* just a guess */
+
+ format.vertex.primitive = GLITZ_PRIMITIVE_QUADS;
+ format.vertex.type = GLITZ_DATA_TYPE_FLOAT;
+ format.vertex.bytes_per_vertex = 3 * sizeof (glitz_float_t);
+ format.vertex.attributes = GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK;
+ format.vertex.mask.type = GLITZ_DATA_TYPE_FLOAT;
+ format.vertex.mask.size = GLITZ_COORDINATE_SIZE_X;
+ format.vertex.mask.offset = 2 * sizeof (glitz_float_t);
+
+ mask = (cairo_glitz_surface_t *)
+ _cairo_glitz_surface_create_similar (&dst->base,
+ CAIRO_CONTENT_ALPHA,
+ 2, 1);
+ if (mask == NULL) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto FAIL;
+ }
+ if (mask->base.status) {
+ status = mask->base.status;
+ goto FAIL;
+ }
+
+ color.red = color.green = color.blue = color.alpha = 0xffff;
+
+ glitz_set_rectangle (mask->surface, &clear_black, 0, 0, 1, 1);
+ glitz_set_rectangle (mask->surface, &color, 1, 0, 1, 1);
+
+ glitz_surface_set_fill (mask->surface, GLITZ_FILL_NEAREST);
+ glitz_surface_set_filter (mask->surface,
+ GLITZ_FILTER_BILINEAR,
+ NULL, 0);
+
+ size *= format.vertex.bytes_per_vertex;
+
+ while (n_traps) {
+ if (data_size < size) {
+ void *p;
+
+ data_size = size;
+ p = realloc (data, data_size);
+ if (p == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL;
+ }
+ data = p;
+
+ if (buffer)
+ glitz_buffer_destroy (buffer);
+
+ buffer = glitz_buffer_create_for_data (data);
+ if (buffer == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ free (data);
+ goto FAIL;
+ }
+ }
+
+ offset +=
+ glitz_add_trapezoids (buffer,
+ offset, size - offset,
+ format.vertex.type, mask->surface,
+ (glitz_trapezoid_t *) pixman_traps, n_traps,
+ &n_trap_added);
+
+ n_traps -= n_trap_added;
+ traps += n_trap_added;
+ size *= 2;
+ }
+
+ glitz_set_geometry (dst->surface,
+ GLITZ_GEOMETRY_TYPE_VERTEX,
+ &format, buffer);
+ glitz_set_array (dst->surface, 0, 3,
+ offset / format.vertex.bytes_per_vertex,
+ 0, 0);
+ } else {
+ cairo_image_surface_t *image;
+ unsigned char *ptr;
+ int stride;
+
+ stride = (width + 3) & -4;
+ data = calloc (stride, height);
+ if (data == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL;
+ }
+
+ /* using negative stride */
+ ptr = (unsigned char *) data + stride * (height - 1);
+
+ image = (cairo_image_surface_t *)
+ cairo_image_surface_create_for_data (ptr,
+ CAIRO_FORMAT_A8,
+ width, height,
+ -stride);
+ status = image->base.status;
+ if (status) {
+ free (data);
+ goto FAIL;
+ }
+
+ pixman_add_trapezoids (image->pixman_image, -dst_x, -dst_y,
+ n_traps, (pixman_trapezoid_t *) pixman_traps);
+
+ mask = (cairo_glitz_surface_t *)
+ _cairo_glitz_surface_create_similar (&dst->base,
+ CAIRO_CONTENT_ALPHA,
+ width, height);
+ status = mask->base.status;
+ if (status) {
+ free (data);
+ cairo_surface_destroy (&image->base);
+ goto FAIL;
+ }
+
+ status = _cairo_glitz_surface_set_image (mask, image,
+ 0, 0, width, height, 0, 0);
+
+ cairo_surface_destroy (&image->base);
+
+ if (status)
+ goto FAIL;
+ }
+
+ _cairo_glitz_surface_set_attributes (src, &attributes);
+
+ glitz_composite (_glitz_operator (op),
+ src->surface,
+ mask->surface,
+ dst->surface,
+ src_x + attributes.base.x_offset,
+ src_y + attributes.base.y_offset,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+
+ if (attributes.n_params)
+ free (attributes.params);
+
+ glitz_set_geometry (dst->surface,
+ GLITZ_GEOMETRY_TYPE_NONE,
+ NULL, NULL);
+
+ if (buffer)
+ glitz_buffer_destroy (buffer);
+
+ free (data);
+
+ if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto FAIL;
+ }
+
+ if (! _cairo_operator_bounded_by_mask (op)) {
+ status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base,
+ &attributes.base,
+ glitz_surface_get_width (src->surface),
+ glitz_surface_get_height (src->surface),
+ width, height,
+ src_x, src_y,
+ 0, 0,
+ dst_x, dst_y,
+ width, height,
+ clip_region);
+ }
+
+FAIL:
+ _cairo_glitz_pattern_release_surface (pattern, src, &attributes);
+
+ if (mask != NULL)
+ cairo_surface_destroy (&mask->base);
+
+ if (pixman_traps != stack_traps)
+ free (pixman_traps);
+
+ return status;
+}
+
+static cairo_bool_t
+_cairo_glitz_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = glitz_surface_get_width (surface->surface);
+ rectangle->height = glitz_surface_get_height (surface->surface);
+
+ return TRUE;
+}
+
+#define CAIRO_GLITZ_AREA_AVAILABLE 0
+#define CAIRO_GLITZ_AREA_DIVIDED 1
+#define CAIRO_GLITZ_AREA_OCCUPIED 2
+
+typedef struct _cairo_glitz_root_area cairo_glitz_root_area_t;
+
+typedef struct _cairo_glitz_area {
+ int state;
+ int level;
+ int x, y;
+ int width, height;
+ struct _cairo_glitz_area *area[4];
+ cairo_glitz_root_area_t *root;
+ void *closure;
+} cairo_glitz_area_t;
+
+static cairo_glitz_area_t _empty_area = {
+ 0, 0, 0, 0, 0, 0,
+ { NULL, NULL, NULL, NULL },
+ NULL,
+ NULL
+};
+
+typedef struct _cairo_glitz_area_funcs {
+ cairo_status_t (*move_in) (cairo_glitz_area_t *area,
+ void *closure);
+
+ void (*move_out) (cairo_glitz_area_t *area,
+ void *closure);
+
+ int (*compare_score) (cairo_glitz_area_t *area,
+ void *closure1,
+ void *closure2);
+} cairo_glitz_area_funcs_t;
+
+struct _cairo_glitz_root_area {
+ int max_level;
+ int width, height;
+ cairo_glitz_area_t *area;
+ const cairo_glitz_area_funcs_t *funcs;
+};
+
+static cairo_status_t
+_cairo_glitz_area_move_in (cairo_glitz_area_t *area,
+ void *closure)
+{
+ area->closure = closure;
+ area->state = CAIRO_GLITZ_AREA_OCCUPIED;
+
+ return (*area->root->funcs->move_in) (area, area->closure);
+}
+
+static void
+_cairo_glitz_area_move_out (cairo_glitz_area_t *area)
+{
+ if (area->root)
+ {
+ (*area->root->funcs->move_out) (area, area->closure);
+
+ area->closure = NULL;
+ area->state = CAIRO_GLITZ_AREA_AVAILABLE;
+ }
+}
+
+static cairo_glitz_area_t *
+_cairo_glitz_area_create (cairo_glitz_root_area_t *root,
+ int level,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ cairo_glitz_area_t *area;
+ int n = 4;
+
+ area = malloc (sizeof (cairo_glitz_area_t));
+ if (!area) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ area->level = level;
+ area->x = x;
+ area->y = y;
+ area->width = width;
+ area->height = height;
+ area->root = root;
+ area->closure = NULL;
+ area->state = CAIRO_GLITZ_AREA_AVAILABLE;
+
+ while (n--)
+ area->area[n] = NULL;
+
+ return area;
+}
+
+static void
+_cairo_glitz_area_destroy (cairo_glitz_area_t *area)
+{
+ if (area == NULL)
+ return;
+
+ if (area->state == CAIRO_GLITZ_AREA_OCCUPIED)
+ {
+ _cairo_glitz_area_move_out (area);
+ }
+ else
+ {
+ int n = 4;
+
+ while (n--)
+ _cairo_glitz_area_destroy (area->area[n]);
+ }
+
+ free (area);
+}
+
+static cairo_glitz_area_t *
+_cairo_glitz_area_get_top_scored_sub_area (cairo_glitz_area_t *area)
+{
+ if (!area)
+ return NULL;
+
+ switch (area->state) {
+ case CAIRO_GLITZ_AREA_OCCUPIED:
+ return area;
+ case CAIRO_GLITZ_AREA_AVAILABLE:
+ break;
+ case CAIRO_GLITZ_AREA_DIVIDED: {
+ cairo_glitz_area_t *tmp, *top = NULL;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ tmp = _cairo_glitz_area_get_top_scored_sub_area (area->area[i]);
+ if (tmp && top)
+ {
+ if ((*area->root->funcs->compare_score) (tmp,
+ tmp->closure,
+ top->closure) > 0)
+ top = tmp;
+ }
+ else if (tmp)
+ {
+ top = tmp;
+ }
+ }
+ return top;
+ }
+ }
+
+ return NULL;
+}
+
+static cairo_int_status_t
+_cairo_glitz_area_find (cairo_glitz_area_t *area,
+ int width,
+ int height,
+ cairo_bool_t kick_out,
+ void *closure)
+{
+ cairo_status_t status;
+
+ if (area->width < width || area->height < height)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ switch (area->state) {
+ case CAIRO_GLITZ_AREA_OCCUPIED:
+ if (kick_out)
+ {
+ if ((*area->root->funcs->compare_score) (area,
+ area->closure,
+ closure) >= 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_glitz_area_move_out (area);
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* fall-through */
+ case CAIRO_GLITZ_AREA_AVAILABLE: {
+ if (area->level == area->root->max_level ||
+ (area->width == width && area->height == height))
+ {
+ return _cairo_glitz_area_move_in (area, closure);
+ }
+ else
+ {
+ int dx[4], dy[4], w[4], h[4], i;
+
+ dx[0] = dx[2] = dy[0] = dy[1] = 0;
+
+ w[0] = w[2] = dx[1] = dx[3] = width;
+ h[0] = h[1] = dy[2] = dy[3] = height;
+
+ w[1] = w[3] = area->width - width;
+ h[2] = h[3] = area->height - height;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (w[i])
+ area->area[i] =
+ _cairo_glitz_area_create (area->root,
+ area->level + 1,
+ area->x + dx[i],
+ area->y + dy[i],
+ w[i], h[i]);
+ }
+
+ for (; i < 4; i++)
+ {
+ if (w[i] && h[i])
+ area->area[i] =
+ _cairo_glitz_area_create (area->root,
+ area->level + 1,
+ area->x + dx[i],
+ area->y + dy[i],
+ w[i], h[i]);
+ }
+
+ area->state = CAIRO_GLITZ_AREA_DIVIDED;
+
+ status = _cairo_glitz_area_find (area->area[0],
+ width, height,
+ kick_out, closure);
+ if (status == CAIRO_STATUS_SUCCESS)
+ return CAIRO_STATUS_SUCCESS;
+ }
+ } break;
+ case CAIRO_GLITZ_AREA_DIVIDED: {
+ cairo_glitz_area_t *to_area;
+ int i, rejected = FALSE;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (area->area[i])
+ {
+ if (area->area[i]->width >= width &&
+ area->area[i]->height >= height)
+ {
+ status = _cairo_glitz_area_find (area->area[i],
+ width, height,
+ kick_out, closure);
+ if (status == CAIRO_STATUS_SUCCESS)
+ return CAIRO_STATUS_SUCCESS;
+
+ rejected = TRUE;
+ }
+ }
+ }
+
+ if (rejected)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ to_area = _cairo_glitz_area_get_top_scored_sub_area (area);
+ if (to_area)
+ {
+ if (kick_out)
+ {
+ if ((*area->root->funcs->compare_score) (to_area,
+ to_area->closure,
+ closure) >= 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ _cairo_glitz_area_destroy (area->area[i]);
+ area->area[i] = NULL;
+ }
+
+ area->closure = NULL;
+ area->state = CAIRO_GLITZ_AREA_AVAILABLE;
+
+ status = _cairo_glitz_area_find (area, width, height,
+ TRUE, closure);
+ if (status == CAIRO_STATUS_SUCCESS)
+ return CAIRO_STATUS_SUCCESS;
+
+ } break;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_glitz_root_area_init (cairo_glitz_root_area_t *root,
+ int max_level,
+ int width,
+ int height,
+ const cairo_glitz_area_funcs_t *funcs)
+{
+ root->max_level = max_level;
+ root->funcs = funcs;
+
+ root->area = _cairo_glitz_area_create (root, 0, 0, 0, width, height);
+ if (!root->area)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_glitz_root_area_fini (cairo_glitz_root_area_t *root)
+{
+ _cairo_glitz_area_destroy (root->area);
+}
+
+typedef struct _cairo_glitz_surface_font_private {
+ cairo_glitz_root_area_t root;
+ glitz_surface_t *surface;
+} cairo_glitz_surface_font_private_t;
+
+typedef struct _cairo_glitz_surface_glyph_private {
+ cairo_glitz_area_t *area;
+ cairo_bool_t locked;
+ cairo_point_double_t p1, p2;
+} cairo_glitz_surface_glyph_private_t;
+
+static cairo_status_t
+_cairo_glitz_glyph_move_in (cairo_glitz_area_t *area,
+ void *closure)
+{
+ cairo_glitz_surface_glyph_private_t *glyph_private = closure;
+
+ glyph_private->area = area;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_glitz_glyph_move_out (cairo_glitz_area_t *area,
+ void *closure)
+{
+ cairo_glitz_surface_glyph_private_t *glyph_private = closure;
+
+ glyph_private->area = NULL;
+}
+
+static int
+_cairo_glitz_glyph_compare (cairo_glitz_area_t *area,
+ void *closure1,
+ void *closure2)
+{
+ cairo_glitz_surface_glyph_private_t *glyph_private = closure1;
+
+ if (glyph_private->locked)
+ return 1;
+
+ return -1;
+}
+
+static const cairo_glitz_area_funcs_t _cairo_glitz_area_funcs = {
+ _cairo_glitz_glyph_move_in,
+ _cairo_glitz_glyph_move_out,
+ _cairo_glitz_glyph_compare
+};
+
+#define GLYPH_CACHE_TEXTURE_SIZE 512
+#define GLYPH_CACHE_MAX_LEVEL 64
+#define GLYPH_CACHE_MAX_HEIGHT 96
+#define GLYPH_CACHE_MAX_WIDTH 96
+
+#define WRITE_VEC2(ptr, _x, _y) \
+ *(ptr)++ = (_x); \
+ *(ptr)++ = (_y)
+
+#define WRITE_BOX(ptr, _vx1, _vy1, _vx2, _vy2, p1, p2) \
+ WRITE_VEC2 (ptr, _vx1, _vy1); \
+ WRITE_VEC2 (ptr, (p1)->x, (p2)->y); \
+ WRITE_VEC2 (ptr, _vx2, _vy1); \
+ WRITE_VEC2 (ptr, (p2)->x, (p2)->y); \
+ WRITE_VEC2 (ptr, _vx2, _vy2); \
+ WRITE_VEC2 (ptr, (p2)->x, (p1)->y); \
+ WRITE_VEC2 (ptr, _vx1, _vy2); \
+ WRITE_VEC2 (ptr, (p1)->x, (p1)->y)
+
+static cairo_status_t
+_cairo_glitz_surface_font_init (cairo_glitz_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ cairo_format_t format)
+{
+ cairo_glitz_surface_font_private_t *font_private;
+ glitz_drawable_t *drawable;
+ glitz_format_t *surface_format = NULL;
+ cairo_int_status_t status;
+
+ drawable = glitz_surface_get_drawable (surface->surface);
+
+ switch (format) {
+ case CAIRO_FORMAT_A1:
+ case CAIRO_FORMAT_A8:
+ surface_format =
+ glitz_find_standard_format (drawable, GLITZ_STANDARD_A8);
+ break;
+ case CAIRO_FORMAT_RGB24:
+ ASSERT_NOT_REACHED;
+ break;
+ case CAIRO_FORMAT_ARGB32:
+ surface_format =
+ glitz_find_standard_format (drawable, GLITZ_STANDARD_ARGB32);
+ default:
+ break;
+ }
+
+ if (!surface_format)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ font_private = malloc (sizeof (cairo_glitz_surface_font_private_t));
+ if (!font_private)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font_private->surface = glitz_surface_create (drawable, surface_format,
+ GLYPH_CACHE_TEXTURE_SIZE,
+ GLYPH_CACHE_TEXTURE_SIZE,
+ 0, NULL);
+ if (font_private->surface == NULL)
+ {
+ free (font_private);
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (format == CAIRO_FORMAT_ARGB32)
+ glitz_surface_set_component_alpha (font_private->surface, 1);
+
+ status = _cairo_glitz_root_area_init (&font_private->root,
+ GLYPH_CACHE_MAX_LEVEL,
+ GLYPH_CACHE_TEXTURE_SIZE,
+ GLYPH_CACHE_TEXTURE_SIZE,
+ &_cairo_glitz_area_funcs);
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ glitz_surface_destroy (font_private->surface);
+ free (font_private);
+
+ return status;
+ }
+
+ scaled_font->surface_private = font_private;
+ scaled_font->surface_backend = _cairo_glitz_surface_get_backend ();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_glitz_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+ cairo_glitz_surface_font_private_t *font_private;
+
+ font_private = scaled_font->surface_private;
+ if (font_private)
+ {
+ _cairo_glitz_root_area_fini (&font_private->root);
+ glitz_surface_destroy (font_private->surface);
+ free (font_private);
+ }
+}
+
+static void
+_cairo_glitz_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_glitz_surface_glyph_private_t *glyph_private;
+
+ glyph_private = scaled_glyph->surface_private;
+ if (glyph_private)
+ {
+ if (glyph_private->area)
+ _cairo_glitz_area_move_out (glyph_private->area);
+
+ free (glyph_private);
+ }
+}
+
+#define FIXED_TO_FLOAT(f) (((glitz_float_t) (f)) / 65536)
+
+static cairo_status_t
+_cairo_glitz_surface_add_glyph (cairo_glitz_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+ cairo_glitz_surface_font_private_t *font_private;
+ cairo_glitz_surface_glyph_private_t *glyph_private;
+ glitz_point_fixed_t p1, p2;
+ glitz_pixel_format_t pf;
+ glitz_buffer_t *buffer;
+ cairo_format_masks_t masks;
+ cairo_int_status_t status;
+
+ glyph_private = scaled_glyph->surface_private;
+ if (glyph_private == NULL)
+ {
+ glyph_private = malloc (sizeof (cairo_glitz_surface_glyph_private_t));
+ if (!glyph_private)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ glyph_private->area = NULL;
+ glyph_private->locked = FALSE;
+
+ scaled_glyph->surface_private = (void *) glyph_private;
+ }
+
+ if (glyph_surface->width > GLYPH_CACHE_MAX_WIDTH ||
+ glyph_surface->height > GLYPH_CACHE_MAX_HEIGHT)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (scaled_font->surface_private == NULL)
+ {
+ status = _cairo_glitz_surface_font_init (surface, scaled_font,
+ glyph_surface->format);
+ if (status)
+ return status;
+ }
+
+ font_private = scaled_font->surface_private;
+
+ if (glyph_surface->width == 0 || glyph_surface->height == 0)
+ {
+ glyph_private->area = &_empty_area;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (_cairo_glitz_area_find (font_private->root.area,
+ glyph_surface->width,
+ glyph_surface->height,
+ FALSE, glyph_private))
+ {
+ if (_cairo_glitz_area_find (font_private->root.area,
+ glyph_surface->width,
+ glyph_surface->height,
+ TRUE, glyph_private))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ buffer = glitz_buffer_create_for_data (glyph_surface->data);
+ if (!buffer)
+ {
+ _cairo_glitz_area_move_out (glyph_private->area);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _pixman_format_to_masks (glyph_surface->pixman_format, &masks);
+
+ pf.fourcc = GLITZ_FOURCC_RGB;
+ pf.masks.bpp = masks.bpp;
+ pf.masks.alpha_mask = masks.alpha_mask;
+ pf.masks.red_mask = masks.red_mask;
+ pf.masks.green_mask = masks.green_mask;
+ pf.masks.blue_mask = masks.blue_mask;
+
+ pf.bytes_per_line = glyph_surface->stride;
+ pf.scanline_order = GLITZ_PIXEL_SCANLINE_ORDER_BOTTOM_UP;
+ pf.xoffset = 0;
+ pf.skip_lines = 0;
+
+ glitz_set_pixels (font_private->surface,
+ glyph_private->area->x,
+ glyph_private->area->y,
+ glyph_surface->width,
+ glyph_surface->height,
+ &pf, buffer);
+
+ glitz_buffer_destroy (buffer);
+
+ p1.x = glyph_private->area->x << 16;
+ p1.y = glyph_private->area->y << 16;
+ p2.x = (glyph_private->area->x + glyph_surface->width) << 16;
+ p2.y = (glyph_private->area->y + glyph_surface->height) << 16;
+
+ glitz_surface_translate_point (font_private->surface, &p1, &p1);
+ glitz_surface_translate_point (font_private->surface, &p2, &p2);
+
+ glyph_private->p1.x = FIXED_TO_FLOAT (p1.x);
+ glyph_private->p1.y = FIXED_TO_FLOAT (p1.y);
+ glyph_private->p2.x = FIXED_TO_FLOAT (p2.x);
+ glyph_private->p2.y = FIXED_TO_FLOAT (p2.y);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#define N_STACK_BUF 256
+
+static cairo_int_status_t
+_cairo_glitz_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_surface,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_region_t *clip_region)
+{
+ cairo_glitz_surface_attributes_t attributes;
+ cairo_glitz_surface_glyph_private_t *glyph_private;
+ cairo_glitz_surface_t *dst = abstract_surface;
+ cairo_glitz_surface_t *src;
+ cairo_scaled_glyph_t *stack_scaled_glyphs[N_STACK_BUF];
+ cairo_scaled_glyph_t **scaled_glyphs;
+ glitz_float_t stack_vertices[N_STACK_BUF * 16];
+ glitz_float_t *vertices;
+ glitz_buffer_t *buffer;
+ cairo_int_status_t status;
+ int x_offset, y_offset;
+ int i, cached_glyphs = 0;
+ int remaining_glyps = num_glyphs;
+ glitz_float_t x1, y1, x2, y2;
+ static const glitz_vertex_format_t format = {
+ GLITZ_PRIMITIVE_QUADS,
+ GLITZ_DATA_TYPE_FLOAT,
+ sizeof (glitz_float_t) * 4,
+ GLITZ_VERTEX_ATTRIBUTE_MASK_COORD_MASK,
+ { 0 },
+ {
+ GLITZ_DATA_TYPE_FLOAT,
+ GLITZ_COORDINATE_SIZE_XY,
+ sizeof (glitz_float_t) * 2,
+ }
+ };
+
+ if (scaled_font->surface_backend != NULL &&
+ scaled_font->surface_backend != _cairo_glitz_surface_get_backend ())
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _is_supported_operator (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* XXX Unbounded operators are not handled correctly */
+ if (! _cairo_operator_bounded_by_mask (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (_glitz_ensure_target (dst->surface))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_glitz_surface_set_clip_region (dst, NULL);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_glitz_pattern_acquire_surface (pattern, dst,
+ src_x, src_y,
+ width, height,
+ &src, &attributes);
+ if (status)
+ return status;
+
+ _cairo_glitz_surface_set_attributes (src, &attributes);
+
+ if (num_glyphs > N_STACK_BUF)
+ {
+ char *data;
+ size_t size1, size2;
+
+ if ((size_t)num_glyphs >= INT32_MAX / sizeof(void*) ||
+ (size_t)num_glyphs >= INT32_MAX / sizeof(glitz_float_t) ||
+ ((size_t)num_glyphs * sizeof(glitz_float_t)) >= INT32_MAX / 16)
+ goto FAIL1;
+
+ size1 = num_glyphs * sizeof(void *);
+ size2 = num_glyphs * sizeof(glitz_float_t) * 16;
+ if (size1 >= INT32_MAX - size2)
+ goto FAIL1;
+
+ data = malloc (size1 + size2);
+ if (!data) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL1;
+ }
+
+ scaled_glyphs = (cairo_scaled_glyph_t **) data;
+ vertices = (glitz_float_t *) (data + num_glyphs * sizeof (void *));
+ }
+ else
+ {
+ scaled_glyphs = stack_scaled_glyphs;
+ vertices = stack_vertices;
+ }
+
+ buffer = glitz_buffer_create_for_data (vertices);
+ if (!buffer)
+ goto FAIL2;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+
+ for (i = 0; i < num_glyphs; i++)
+ {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyphs[i]);
+ if (status != CAIRO_STATUS_SUCCESS)
+ {
+ num_glyphs = i;
+ goto UNLOCK;
+ }
+
+ glyph_private = scaled_glyphs[i]->surface_private;
+ if (!glyph_private || !glyph_private->area)
+ {
+ status = _cairo_glitz_surface_add_glyph (dst,
+ scaled_font,
+ scaled_glyphs[i]);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ num_glyphs = i;
+ goto UNLOCK;
+ }
+ }
+ glyph_private = scaled_glyphs[i]->surface_private;
+ if (glyph_private && glyph_private->area)
+ {
+ remaining_glyps--;
+
+ if (glyph_private->area->width)
+ {
+ x_offset = scaled_glyphs[i]->surface->base.device_transform.x0;
+ y_offset = scaled_glyphs[i]->surface->base.device_transform.y0;
+
+ x1 = _cairo_lround (glyphs[i].x - x_offset);
+ y1 = _cairo_lround (glyphs[i].y - y_offset);
+ x2 = x1 + glyph_private->area->width;
+ y2 = y1 + glyph_private->area->height;
+
+ WRITE_BOX (vertices, x1, y1, x2, y2,
+ &glyph_private->p1, &glyph_private->p2);
+
+ glyph_private->locked = TRUE;
+
+ cached_glyphs++;
+ }
+ }
+ }
+
+ if (remaining_glyps)
+ {
+ cairo_surface_t *image;
+ cairo_glitz_surface_t *clone;
+
+ for (i = 0; i < num_glyphs; i++)
+ {
+ glyph_private = scaled_glyphs[i]->surface_private;
+ if (!glyph_private || !glyph_private->area)
+ {
+ int glyph_width, glyph_height;
+ int clone_offset_x, clone_offset_y;
+
+ image = &scaled_glyphs[i]->surface->base;
+ glyph_width = scaled_glyphs[i]->surface->width;
+ glyph_height = scaled_glyphs[i]->surface->height;
+ status =
+ _cairo_glitz_surface_clone_similar (abstract_surface,
+ image,
+ 0,
+ 0,
+ glyph_width,
+ glyph_height,
+ &clone_offset_x,
+ &clone_offset_y,
+ (cairo_surface_t **)
+ &clone);
+ if (status)
+ goto UNLOCK;
+
+ assert (clone_offset_x == 0);
+ assert (clone_offset_y == 0);
+
+ x_offset = scaled_glyphs[i]->surface->base.device_transform.x0;
+ y_offset = scaled_glyphs[i]->surface->base.device_transform.y0;
+ x1 = _cairo_lround (glyphs[i].x - x_offset);
+ y1 = _cairo_lround (glyphs[i].y - y_offset);
+
+ glitz_composite (_glitz_operator (op),
+ src->surface,
+ clone->surface,
+ dst->surface,
+ src_x + attributes.base.x_offset + x1,
+ src_y + attributes.base.y_offset + y1,
+ 0, 0,
+ x1, y1,
+ glyph_width,
+ glyph_height);
+
+ cairo_surface_destroy (&clone->base);
+
+ if (glitz_surface_get_status (dst->surface) ==
+ GLITZ_STATUS_NOT_SUPPORTED)
+ {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto UNLOCK;
+ }
+ }
+ }
+ }
+
+ if (cached_glyphs)
+ {
+ cairo_glitz_surface_font_private_t *font_private;
+
+ glitz_set_geometry (dst->surface,
+ GLITZ_GEOMETRY_TYPE_VERTEX,
+ (glitz_geometry_format_t *) &format,
+ buffer);
+
+ glitz_set_array (dst->surface, 0, 4, cached_glyphs * 4, 0, 0);
+
+ font_private = scaled_font->surface_private;
+
+ glitz_composite (_glitz_operator (op),
+ src->surface,
+ font_private->surface,
+ dst->surface,
+ src_x + attributes.base.x_offset,
+ src_y + attributes.base.y_offset,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+
+ glitz_set_geometry (dst->surface,
+ GLITZ_GEOMETRY_TYPE_NONE,
+ NULL, NULL);
+ }
+
+UNLOCK:
+ if (cached_glyphs)
+ {
+ for (i = 0; i < num_glyphs; i++)
+ {
+ glyph_private = scaled_glyphs[i]->surface_private;
+ if (glyph_private)
+ glyph_private->locked = FALSE;
+ }
+ }
+
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ glitz_buffer_destroy (buffer);
+
+ FAIL2:
+ if (num_glyphs > N_STACK_BUF)
+ free (scaled_glyphs);
+
+ FAIL1:
+ if (attributes.n_params)
+ free (attributes.params);
+
+ _cairo_glitz_pattern_release_surface (pattern, src, &attributes);
+
+ if (status)
+ return status;
+
+ if (glitz_surface_get_status (dst->surface) == GLITZ_STATUS_NOT_SUPPORTED)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_glitz_surface_flush (void *abstract_surface)
+{
+ cairo_glitz_surface_t *surface = abstract_surface;
+
+ glitz_surface_flush (surface->surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_glitz_surface_is_similar (void *surface_a,
+ void *surface_b,
+ cairo_content_t content)
+{
+ cairo_glitz_surface_t *a = (cairo_glitz_surface_t *) surface_a;
+ cairo_glitz_surface_t *b = (cairo_glitz_surface_t *) surface_b;
+
+ glitz_drawable_t *drawable_a = glitz_surface_get_drawable (a->surface);
+ glitz_drawable_t *drawable_b = glitz_surface_get_drawable (b->surface);
+
+ /* XXX Disable caching of glitz surfaces by the solid pattern cache.
+ * Until glitz has a mechanism for releasing resources on connection
+ * closure, we will attempt to access invalid pointers when evicting
+ * old surfaces from the solid pattern cache.
+ */
+ return FALSE;
+
+ return drawable_a == drawable_b;
+}
+
+static const cairo_surface_backend_t cairo_glitz_surface_backend = {
+ CAIRO_SURFACE_TYPE_GLITZ,
+ _cairo_glitz_surface_create_similar,
+ _cairo_glitz_surface_finish,
+ _cairo_glitz_surface_acquire_source_image,
+ _cairo_glitz_surface_release_source_image,
+
+ _cairo_glitz_surface_acquire_dest_image,
+ _cairo_glitz_surface_release_dest_image,
+ _cairo_glitz_surface_clone_similar,
+ _cairo_glitz_surface_composite,
+ _cairo_glitz_surface_fill_rectangles,
+ _cairo_glitz_surface_composite_trapezoids,
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_glitz_surface_get_extents,
+ _cairo_glitz_surface_old_show_glyphs,
+ NULL, /* get_font_options */
+ _cairo_glitz_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+ _cairo_glitz_surface_scaled_font_fini,
+ _cairo_glitz_surface_scaled_glyph_fini,
+
+ NULL, /* paint */
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+ NULL, /* show_glyphs */
+
+ _cairo_glitz_surface_snapshot,
+ _cairo_glitz_surface_is_similar,
+};
+
+static const cairo_surface_backend_t *
+_cairo_glitz_surface_get_backend (void)
+{
+ return &cairo_glitz_surface_backend;
+}
+
+static cairo_content_t
+_glitz_format_to_content (glitz_format_t * format)
+{
+ assert (format->color.fourcc == GLITZ_FOURCC_RGB);
+
+ if (format->color.alpha_size != 0) {
+ if (format->color.red_size != 0 &&
+ format->color.green_size != 0 &&
+ format->color.blue_size != 0)
+ return CAIRO_CONTENT_COLOR_ALPHA;
+ else
+ return CAIRO_CONTENT_ALPHA;
+ }
+ return CAIRO_CONTENT_COLOR;
+}
+
+cairo_surface_t *
+cairo_glitz_surface_create (glitz_surface_t *surface)
+{
+ cairo_glitz_surface_t *crsurface;
+ glitz_format_t *format;
+
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+
+ crsurface = malloc (sizeof (cairo_glitz_surface_t));
+ if (crsurface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ format = glitz_surface_get_format (surface);
+ _cairo_surface_init (&crsurface->base, &cairo_glitz_surface_backend,
+ _glitz_format_to_content (format));
+
+ glitz_surface_reference (surface);
+
+ crsurface->surface = surface;
+ crsurface->format = format;
+
+ crsurface->has_clip = FALSE;
+ crsurface->clip_boxes = NULL;
+ crsurface->num_clip_boxes = 0;
+ crsurface->clip_region = NULL;
+
+ return &crsurface->base;
+}
+slim_hidden_def (cairo_glitz_surface_create);
diff --git a/gfx/cairo/cairo/src/cairo-glitz.h b/gfx/cairo/cairo/src/cairo-glitz.h
new file mode 100644
index 000000000..08519dcbd
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-glitz.h
@@ -0,0 +1,57 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_GLITZ_H
+#define CAIRO_GLITZ_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_GLITZ_SURFACE
+
+#include <glitz.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_glitz_surface_create (glitz_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else /* CAIRO_HAS_GLITZ_SURFACE */
+# error Cairo was not compiled with support for the glitz backend
+#endif /* CAIRO_HAS_GLITZ_SURFACE */
+
+#endif /* CAIRO_GLITZ_H */
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 000000000..fa9d8be96
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-glx-context.c
@@ -0,0 +1,260 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Eric Anholt
+ * Copyright © 2009 Chris Wilson
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+
+#include "cairo-error-private.h"
+
+#include <X11/Xutil.h>
+
+/* XXX needs hooking into XCloseDisplay() */
+
+typedef struct _cairo_glx_context {
+ cairo_gl_context_t base;
+
+ Display *display;
+ Window dummy_window;
+ GLXContext context;
+} cairo_glx_context_t;
+
+typedef struct _cairo_glx_surface {
+ cairo_gl_surface_t base;
+
+ Window win;
+} cairo_glx_surface_t;
+
+static void
+_glx_acquire (void *abstract_ctx)
+{
+ cairo_glx_context_t *ctx = abstract_ctx;
+ GLXDrawable current_drawable;
+
+ if (ctx->base.current_target == NULL ||
+ _cairo_gl_surface_is_texture (ctx->base.current_target)) {
+ current_drawable = ctx->dummy_window;
+ } else {
+ cairo_glx_surface_t *surface = (cairo_glx_surface_t *) ctx->base.current_target;
+ current_drawable = surface->win;
+ }
+
+ 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;
+
+ 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, 0, 0);
+}
+
+static cairo_status_t
+_glx_dummy_ctx (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;
+
+ status = _glx_dummy_ctx (dpy, gl_ctx, &dummy);
+ if (unlikely (status))
+ return _cairo_gl_context_create_in_error (status);
+
+ ctx = calloc (1, sizeof (cairo_glx_context_t));
+ if (unlikely (ctx == NULL))
+ return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ 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_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;
+}
+
+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));
+
+ 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 000000000..b41c7a296
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-gstate-private.h
@@ -0,0 +1,385 @@
+/* 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 <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_GSTATE_PRIVATE_H
+#define CAIRO_GSTATE_PRIVATE_H
+
+#include "cairo-clip-private.h"
+
+struct _cairo_gstate {
+ cairo_operator_t op;
+
+ 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_redirected (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_parent_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_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_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
+_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_select_font_face (cairo_gstate_t *gstate,
+ const char *family,
+ cairo_font_slant_t slant,
+ cairo_font_weight_t weight);
+
+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_text_to_glyphs (cairo_gstate_t *gstate,
+ double x,
+ double y,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t **glyphs,
+ int *num_glyphs,
+ cairo_text_cluster_t **clusters,
+ int *num_clusters,
+ cairo_text_cluster_flags_t *cluster_flags);
+
+cairo_private cairo_status_t
+_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_text_extents_t *extents);
+
+cairo_private cairo_status_t
+_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
+ const char *utf8,
+ int utf8_len,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags);
+
+cairo_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 000000000..cb07b511f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-gstate.c
@@ -0,0 +1,2331 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-gstate-private.h"
+
+#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
+#define ISFINITE(x) isfinite (x)
+#else
+#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */
+#endif
+
+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 cairo_status_t
+_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)
+{
+ cairo_status_t status;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
+
+ gstate->next = NULL;
+
+ gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT;
+
+ 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);
+
+ _cairo_clip_init (&gstate->clip);
+
+ 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). */
+ status = target->status;
+ if (unlikely (status))
+ return status;
+
+ status = gstate->source->status;
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _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->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);
+
+ _cairo_clip_init_copy (&gstate->clip, &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_reset (&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_NOACCESS (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 = 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().
+ *
+ * Unless the redirected target has the same device offsets as the
+ * original #cairo_t target, the clip will be INVALID after this call,
+ * and the caller should either recreate or reset the clip.
+ **/
+cairo_status_t
+_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child)
+{
+ cairo_matrix_t matrix;
+
+ /* If this gstate is already redirected, this is an error; we need a
+ * new gstate to be able to redirect */
+ assert (gstate->parent_target == NULL);
+
+ /* Set up our new parent_target based on our current target;
+ * gstate->parent_target will take the ref that is held by gstate->target
+ */
+ cairo_surface_destroy (gstate->parent_target);
+ gstate->parent_target = gstate->target;
+
+ /* Now set up our new target; we overwrite gstate->target directly,
+ * 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_matrix_init_translate (&matrix,
+ child->device_transform.x0 - gstate->parent_target->device_transform.x0,
+ child->device_transform.y0 - gstate->parent_target->device_transform.y0);
+ _cairo_clip_reset (&gstate->clip);
+ return _cairo_clip_init_copy_transformed (&gstate->clip,
+ &gstate->next->clip,
+ &matrix);
+}
+
+/**
+ * _cairo_gstate_is_redirected
+ * @gstate: a #cairo_gstate_t
+ *
+ * This space left intentionally blank.
+ *
+ * Return value: %TRUE if the gstate is redirected to a target
+ * different than the original, %FALSE otherwise.
+ **/
+cairo_bool_t
+_cairo_gstate_is_redirected (cairo_gstate_t *gstate)
+{
+ return (gstate->target != gstate->original_target);
+}
+
+/**
+ * _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_parent_target:
+ * @gstate: a #cairo_gstate_t
+ *
+ * Return the parent surface of the current drawing target surface;
+ * if this particular gstate isn't a redirect gstate, this will return %NULL.
+ **/
+cairo_surface_t *
+_cairo_gstate_get_parent_target (cairo_gstate_t *gstate)
+{
+ return gstate->parent_target;
+}
+
+/**
+ * _cairo_gstate_get_original_target:
+ * @gstate: a #cairo_gstate_t
+ *
+ * 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_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)
+{
+ unsigned int i;
+ double dash_total;
+
+ if (gstate->stroke_style.dash)
+ 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);
+ }
+
+ memcpy (gstate->stroke_style.dash, dash, gstate->stroke_style.num_dashes * sizeof (double));
+
+ dash_total = 0.0;
+ for (i = 0; i < gstate->stroke_style.num_dashes; i++) {
+ if (gstate->stroke_style.dash[i] < 0)
+ return _cairo_error (CAIRO_STATUS_INVALID_DASH);
+
+ dash_total += gstate->stroke_style.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;
+
+ /* 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_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
+_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;
+
+ 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);
+}
+
+/* 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_transform (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);
+}
+
+/* We need to take a copy of the clip so that the lower layers may modify it
+ * by, perhaps, intersecting it with the operation extents and other paths.
+ */
+#define _gstate_get_clip(G, C) _cairo_clip_init_copy ((C), &(G)->clip)
+
+static cairo_bool_t
+_clipped (cairo_gstate_t *gstate)
+{
+ cairo_rectangle_int_t extents;
+
+ if (gstate->clip.all_clipped)
+ return TRUE;
+
+ /* XXX consider applying a surface clip? */
+
+ if (gstate->clip.path == NULL)
+ return FALSE;
+
+ if (_cairo_surface_get_extents (gstate->target, &extents)) {
+ if (extents.width == 0 || extents.height == 0)
+ return TRUE;
+
+ if (! _cairo_rectangle_intersect (&extents,
+ &gstate->clip.path->extents))
+ {
+ return TRUE;
+ }
+ }
+
+ /* perform a simple query to exclude trivial all-clipped cases */
+ return _cairo_clip_get_region (&gstate->clip, NULL) == CAIRO_INT_STATUS_NOTHING_TO_DO;
+}
+
+static cairo_operator_t
+_reduce_op (cairo_gstate_t *gstate)
+{
+ 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;
+}
+
+cairo_status_t
+_cairo_gstate_paint (cairo_gstate_t *gstate)
+{
+ cairo_pattern_union_t source_pattern;
+ const cairo_pattern_t *pattern;
+ cairo_clip_t clip;
+ cairo_status_t status;
+ cairo_operator_t op;
+
+ if (unlikely (gstate->source->status))
+ return gstate->source->status;
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_clipped (gstate))
+ 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;
+ }
+
+ status = _cairo_surface_paint (gstate->target,
+ op, pattern,
+ _gstate_get_clip (gstate, &clip));
+ _cairo_clip_fini (&clip);
+
+ return status;
+}
+
+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_clip_t clip;
+ cairo_status_t status;
+
+ if (unlikely (mask->status))
+ return mask->status;
+
+ if (unlikely (gstate->source->status))
+ return gstate->source->status;
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_clipped (gstate))
+ return CAIRO_STATUS_SUCCESS;
+
+ 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.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_get_clip (gstate, &clip));
+ }
+ else
+ {
+ status = _cairo_surface_mask (gstate->target, op,
+ source,
+ &mask_pattern.base,
+ _gstate_get_clip (gstate, &clip));
+ }
+ _cairo_clip_fini (&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_clip_t clip;
+ cairo_status_t status;
+
+ if (unlikely (gstate->source->status))
+ return gstate->source->status;
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (gstate->stroke_style.line_width <= 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_clipped (gstate))
+ return CAIRO_STATUS_SUCCESS;
+
+ memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style));
+ if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &gstate->ctm, 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);
+
+ status = _cairo_surface_stroke (gstate->target,
+ gstate->op,
+ &source_pattern.base,
+ path,
+ &style,
+ &gstate->ctm,
+ &gstate->ctm_inverse,
+ gstate->tolerance,
+ gstate->antialias,
+ _gstate_get_clip (gstate, &clip));
+ _cairo_clip_fini (&clip);
+
+ return status;
+}
+
+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,
+ &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) - 5;
+ limit.p1.y = _cairo_fixed_from_double (y) - 5;
+ limit.p2.x = limit.p1.x + 10;
+ limit.p2.y = limit.p1.y + 10;
+
+ _cairo_traps_init (&traps);
+ _cairo_traps_limit (&traps, &limit, 1);
+
+ status = _cairo_path_fixed_stroke_to_traps (path,
+ &gstate->stroke_style,
+ &gstate->ctm,
+ &gstate->ctm_inverse,
+ gstate->tolerance,
+ &traps);
+ 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_clip_t clip;
+ cairo_status_t status;
+
+ if (unlikely (gstate->source->status))
+ return gstate->source->status;
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_clipped (gstate))
+ return CAIRO_STATUS_SUCCESS;
+
+ 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_get_clip (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_get_clip (gstate, &clip));
+ }
+ else
+ {
+ status = _cairo_surface_fill (gstate->target, op, pattern,
+ path,
+ gstate->fill_rule,
+ gstate->tolerance,
+ gstate->antialias,
+ _gstate_get_clip (gstate, &clip));
+ }
+ }
+
+ _cairo_clip_fini (&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_path_t *clip_path;
+
+ if (gstate->clip.all_clipped)
+ return FALSE;
+
+ clip_path = gstate->clip.path;
+ if (clip_path == NULL)
+ return TRUE;
+
+ _cairo_gstate_user_to_backend (gstate, &x, &y);
+
+ if (x < clip_path->extents.x ||
+ x >= clip_path->extents.x + clip_path->extents.width ||
+ y < clip_path->extents.y ||
+ y >= clip_path->extents.y + clip_path->extents.height)
+ {
+ return FALSE;
+ }
+
+ 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_traps_extents_to_user_rectangle (cairo_gstate_t *gstate,
+ cairo_traps_t *traps,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_box_t extents;
+
+ if (traps->num_traps == 0) {
+ /* no traps, so we actually won't draw anything */
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+ } else {
+ double px1, py1, px2, py2;
+
+ _cairo_traps_extents (traps, &extents);
+
+ px1 = _cairo_fixed_to_double (extents.p1.x);
+ py1 = _cairo_fixed_to_double (extents.p1.y);
+ px2 = _cairo_fixed_to_double (extents.p2.x);
+ py2 = _cairo_fixed_to_double (extents.p2.y);
+
+ _cairo_gstate_backend_to_user_rectangle (gstate,
+ &px1, &py1, &px2, &py2,
+ NULL);
+ if (x1)
+ *x1 = px1;
+ if (y1)
+ *y1 = py1;
+ if (x2)
+ *x2 = px2;
+ if (y2)
+ *y2 = py2;
+ }
+}
+
+cairo_status_t
+_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
+ cairo_path_fixed_t *path,
+ double *x1, double *y1,
+ double *x2, double *y2)
+{
+ cairo_status_t status;
+ cairo_traps_t traps;
+
+ if (gstate->stroke_style.line_width <= 0.0) {
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_traps_init (&traps);
+
+ status = _cairo_path_fixed_stroke_to_traps (path,
+ &gstate->stroke_style,
+ &gstate->ctm,
+ &gstate->ctm_inverse,
+ gstate->tolerance,
+ &traps);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
+ x1, y1, x2, y2);
+ }
+
+ _cairo_traps_fini (&traps);
+
+ 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_traps_t traps;
+
+ if (path->is_empty_fill) {
+ if (x1)
+ *x1 = 0.0;
+ if (y1)
+ *y1 = 0.0;
+ if (x2)
+ *x2 = 0.0;
+ if (y2)
+ *y2 = 0.0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_traps_init (&traps);
+
+ status = _cairo_path_fixed_fill_to_traps (path,
+ gstate->fill_rule,
+ gstate->tolerance,
+ &traps);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
+ x1, y1, x2, y2);
+ }
+
+ _cairo_traps_fini (&traps);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_reset_clip (cairo_gstate_t *gstate)
+{
+ _cairo_clip_reset (&gstate->clip);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
+{
+ return _cairo_clip_clip (&gstate->clip,
+ path, gstate->fill_rule,
+ gstate->tolerance, gstate->antialias);
+}
+
+static cairo_bool_t
+_cairo_gstate_int_clip_extents (cairo_gstate_t *gstate,
+ cairo_rectangle_int_t *extents)
+{
+ const cairo_rectangle_int_t *clip_extents;
+ cairo_bool_t is_bounded;
+
+ is_bounded = _cairo_surface_get_extents (gstate->target, extents);
+
+ clip_extents = _cairo_clip_get_extents (&gstate->clip);
+ if (clip_extents != NULL) {
+ cairo_bool_t is_empty;
+
+ is_empty = _cairo_rectangle_intersect (extents, clip_extents);
+ 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_clip_t clip;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_list_t *list;
+
+ _cairo_clip_init_copy (&clip, &gstate->clip);
+
+ if (_cairo_surface_get_extents (gstate->target, &extents))
+ _cairo_clip_rectangle (&clip, &extents);
+
+ list = _cairo_clip_copy_rectangle_list (&clip, gstate);
+ _cairo_clip_fini (&clip);
+
+ return list;
+}
+
+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_select_font_face (cairo_gstate_t *gstate,
+ const char *family,
+ cairo_font_slant_t slant,
+ cairo_font_weight_t weight)
+{
+ cairo_font_face_t *font_face;
+ cairo_status_t status;
+
+ font_face = cairo_toy_font_face_create (family, slant, weight);
+ if (font_face->status)
+ return font_face->status;
+
+ status = _cairo_gstate_set_font_face (gstate, font_face);
+ cairo_font_face_destroy (font_face);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_gstate_set_font_size (cairo_gstate_t *gstate,
+ double size)
+{
+ _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;
+
+ if (! _cairo_matrix_is_invertible (matrix)) {
+ /* rank 0 matrices are ok even though they are not invertible */
+ if (!(matrix->xx == 0. && matrix->xy == 0. &&
+ matrix->yx == 0. && matrix->yy == 0.)) {
+ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
+ }
+ }
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
+ gstate->font_matrix = *matrix;
+
+ 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;
+
+ 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);
+
+ scaled_font = cairo_scaled_font_create (gstate->font_face,
+ &gstate->font_matrix,
+ &gstate->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_text_to_glyphs (cairo_gstate_t *gstate,
+ double x,
+ double y,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t **glyphs,
+ int *num_glyphs,
+ cairo_text_cluster_t **clusters,
+ int *num_clusters,
+ cairo_text_cluster_flags_t *cluster_flags)
+{
+ cairo_status_t status;
+
+ status = _cairo_gstate_ensure_scaled_font (gstate);
+ if (unlikely (status))
+ return status;
+
+ return cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags);
+}
+
+cairo_status_t
+_cairo_gstate_set_font_face (cairo_gstate_t *gstate,
+ cairo_font_face_t *font_face)
+{
+ 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 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_pattern_union_t source_pattern;
+ const cairo_pattern_t *pattern;
+ cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+ cairo_glyph_t *transformed_glyphs;
+ cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
+ cairo_text_cluster_t *transformed_clusters;
+ cairo_operator_t op;
+ cairo_status_t status;
+ cairo_clip_t clip;
+
+ if (unlikely (gstate->source->status))
+ return gstate->source->status;
+
+ if (gstate->op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_clipped (gstate))
+ 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)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_GLYPHS;
+ }
+ }
+
+ /* Just in case */
+ if (!clusters)
+ num_clusters = 0;
+
+ if (num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) {
+ transformed_clusters = cairo_text_cluster_allocate (num_clusters);
+ if (unlikely (transformed_clusters == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_GLYPHS;
+ }
+ }
+
+ status = _cairo_gstate_transform_glyphs_to_backend (gstate,
+ glyphs, num_glyphs,
+ clusters,
+ num_clusters,
+ cluster_flags,
+ transformed_glyphs,
+ &num_glyphs,
+ transformed_clusters);
+
+ if (status || num_glyphs == 0)
+ 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;
+ }
+ _cairo_clip_init(&clip);
+
+ /* For really huge font sizes, we can just do path;fill instead of
+ * show_glyphs, as show_glyphs would put excess pressure on the cache,
+ * not all components below us correctly handle huge font sizes, and
+ * path filling can be cheaper since parts of glyphs are likely to be
+ * clipped out. 256 seems like a good limit. But alas, seems like cairo's
+ * rasterizer is something like ten times slower than freetype's for huge
+ * sizes. So, no win just yet when we're using cairo's rasterizer.
+ * For now, if we're using cairo's rasterizer, use path filling only
+ * for insanely-huge sizes, just to make sure we don't make anyone
+ * unhappy. When we get a really fast rasterizer in cairo, we may
+ * want to readjust this. The threshold calculation is
+ * encapsulated in _cairo_surface_get_text_path_fill_threshold.
+ *
+ * Needless to say, do this only if show_text_glyphs is not available. */
+ if (cairo_surface_has_show_text_glyphs (gstate->target) ||
+ _cairo_scaled_font_get_max_scale (gstate->scaled_font) <=
+ _cairo_surface_get_text_path_fill_threshold (gstate->target))
+ {
+ status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
+ utf8, utf8_len,
+ transformed_glyphs, num_glyphs,
+ transformed_clusters, num_clusters,
+ cluster_flags,
+ gstate->scaled_font,
+ _gstate_get_clip (gstate, &clip));
+ }
+ 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 && !_cairo_path_fixed_fill_is_empty (&path)) {
+ status = _cairo_surface_fill (gstate->target, op, pattern,
+ &path,
+ CAIRO_FILL_RULE_WINDING,
+ gstate->tolerance,
+ gstate->scaled_font->options.antialias,
+ _gstate_get_clip (gstate, &clip));
+ } else {
+ /* if _cairo_scaled_font_glyph_path() failed, maybe the font doesn't support
+ * returning paths, so try the _cairo_surface_show_text_glyphs() option
+ */
+ status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
+ utf8, utf8_len,
+ transformed_glyphs, num_glyphs,
+ transformed_clusters, num_clusters,
+ cluster_flags,
+ gstate->scaled_font,
+ _gstate_get_clip (gstate, &clip));
+ }
+
+ _cairo_path_fixed_fini (&path);
+ }
+
+ _cairo_clip_fini (&clip);
+
+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_status_t status;
+ cairo_glyph_t *transformed_glyphs;
+ cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
+
+ 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);
+ }
+
+ status = _cairo_gstate_transform_glyphs_to_backend (gstate,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ transformed_glyphs,
+ NULL, NULL);
+ if (unlikely (status))
+ goto CLEANUP_GLYPHS;
+
+ status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
+ transformed_glyphs, num_glyphs,
+ path);
+
+ CLEANUP_GLYPHS:
+ if (transformed_glyphs != stack_transformed_glyphs)
+ cairo_glyph_free (transformed_glyphs);
+
+ 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 cairo_status_t
+_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)
+{
+ int i, j, k;
+ 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;
+
+ if (num_transformed_glyphs != NULL) {
+ cairo_rectangle_int_t surface_extents;
+
+ drop = TRUE;
+ if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) {
+ drop = FALSE; /* unbounded surface */
+ } else {
+ double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font);
+ if (surface_extents.width == 0 || surface_extents.height == 0) {
+ /* No visible area. Don't draw anything */
+ *num_transformed_glyphs = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ /* XXX We currently drop any glyphs that has its position outside
+ * of the surface boundaries by a safety margin depending on the
+ * font scale. This however can fail in extreme cases where the
+ * font has really long swashes for example... We can correctly
+ * handle that by looking the glyph up and using its device bbox
+ * to device if it's going to be visible, but I'm not inclined to
+ * do that now.
+ */
+ x1 = surface_extents.x - scale10;
+ y1 = surface_extents.y - scale10;
+ x2 = surface_extents.x + (int) surface_extents.width + scale10;
+ y2 = surface_extents.y + (int) surface_extents.height + scale10;
+ }
+
+ if (!drop)
+ *num_transformed_glyphs = num_glyphs;
+ } else
+ num_transformed_glyphs = &j;
+
+#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));
+ 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++;
+ }
+ } 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++;
+ }
+ } 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;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
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 000000000..30e51ffe6
--- /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 <keithp@keithp.com>
+ * Graydon Hoare <graydon@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#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 000000000..81a48a235
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-hash.c
@@ -0,0 +1,542 @@
+/* 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 <keithp@keithp.com>
+ * Graydon Hoare <graydon@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#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)
+
+/* We expect keys will not be destroyed frequently, so our table does not
+ * contain any explicit shrinking code nor any chain-coalescing code for
+ * entries randomly deleted by memory pressure (except during rehashing, of
+ * course). These assumptions are potentially bad, but they make the
+ * implementation straightforward.
+ *
+ * Revisit later if evidence appears that we're using excessive memory from
+ * a mostly-dead table.
+ *
+ * This table is open-addressed with double hashing. Each table size is a
+ * prime chosen to be a little more than double the high water mark for a
+ * given arrangement, so the tables should remain < 50% full. The table
+ * size makes for the "first" hash modulus; a second prime (2 less than the
+ * first prime) serves as the "second" hash modulus, which is co-prime and
+ * thus guarantees a complete permutation of table indices.
+ *
+ * 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.
+ */
+
+typedef struct _cairo_hash_table_arrangement {
+ unsigned long high_water_mark;
+ unsigned long size;
+ unsigned long rehash;
+} cairo_hash_table_arrangement_t;
+
+static const cairo_hash_table_arrangement_t hash_table_arrangements [] = {
+ { 16, 43, 41 },
+ { 32, 73, 71 },
+ { 64, 151, 149 },
+ { 128, 283, 281 },
+ { 256, 571, 569 },
+ { 512, 1153, 1151 },
+ { 1024, 2269, 2267 },
+ { 2048, 4519, 4517 },
+ { 4096, 9013, 9011 },
+ { 8192, 18043, 18041 },
+ { 16384, 36109, 36107 },
+ { 32768, 72091, 72089 },
+ { 65536, 144409, 144407 },
+ { 131072, 288361, 288359 },
+ { 262144, 576883, 576881 },
+ { 524288, 1153459, 1153457 },
+ { 1048576, 2307163, 2307161 },
+ { 2097152, 4613893, 4613891 },
+ { 4194304, 9227641, 9227639 },
+ { 8388608, 18455029, 18455027 },
+ { 16777216, 36911011, 36911009 },
+ { 33554432, 73819861, 73819859 },
+ { 67108864, 147639589, 147639587 },
+ { 134217728, 295279081, 295279079 },
+ { 268435456, 590559793, 590559791 }
+};
+
+#define NUM_HASH_TABLE_ARRANGEMENTS ARRAY_LENGTH (hash_table_arrangements)
+
+struct _cairo_hash_table {
+ cairo_hash_keys_equal_func_t keys_equal;
+
+ const cairo_hash_table_arrangement_t *arrangement;
+ cairo_hash_entry_t **entries;
+
+ unsigned long live_entries;
+ unsigned long iterating; /* Iterating, no insert, no resize */
+};
+
+/**
+ * _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).
+ *
+ * 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 = malloc (sizeof (cairo_hash_table_t));
+ if (unlikely (hash_table == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ hash_table->keys_equal = keys_equal;
+
+ hash_table->arrangement = &hash_table_arrangements[0];
+
+ hash_table->entries = calloc (hash_table->arrangement->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->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 notifiy callbacks.
+ *
+ * WARNING: The hash_table must have no running iterators in it when
+ * _cairo_hash_table_destroy is called. It is a fatal error otherwise,
+ * and this function will halt.
+ **/
+void
+_cairo_hash_table_destroy (cairo_hash_table_t *hash_table)
+{
+ /* The hash table must be empty. Otherwise, halt. */
+ assert (hash_table->live_entries == 0);
+ /* No iterators can be running. Otherwise, halt. */
+ assert (hash_table->iterating == 0);
+
+ free (hash_table->entries);
+ hash_table->entries = NULL;
+
+ 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->arrangement->size;
+ idx = key->hash % table_size;
+
+ entry = &hash_table->entries[idx];
+ if (! ENTRY_IS_LIVE (*entry))
+ return entry;
+
+ i = 1;
+ step = key->hash % hash_table->arrangement->rehash;
+ if (step == 0)
+ step = 1;
+ do {
+ idx += step;
+ if (idx >= table_size)
+ idx -= table_size;
+
+ entry = &hash_table->entries[idx];
+ if (! ENTRY_IS_LIVE (*entry))
+ return entry;
+ } while (++i < table_size);
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+
+/**
+ * _cairo_hash_table_resize:
+ * @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.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful or
+ * %CAIRO_STATUS_NO_MEMORY if out of memory.
+ **/
+static cairo_status_t
+_cairo_hash_table_resize (cairo_hash_table_t *hash_table)
+{
+ cairo_hash_table_t tmp;
+ unsigned long new_size, i;
+
+ /* This keeps the hash table between 25% and 50% full. */
+ unsigned long high = hash_table->arrangement->high_water_mark;
+ unsigned long low = high >> 2;
+
+ if (hash_table->live_entries >= low && hash_table->live_entries <= high)
+ return CAIRO_STATUS_SUCCESS;
+
+ tmp = *hash_table;
+
+ if (hash_table->live_entries > high)
+ {
+ tmp.arrangement = hash_table->arrangement + 1;
+ /* This code is being abused if we can't make a table big enough. */
+ assert (tmp.arrangement - hash_table_arrangements <
+ NUM_HASH_TABLE_ARRANGEMENTS);
+ }
+ else /* hash_table->live_entries < low */
+ {
+ /* Can't shrink if we're at the smallest size */
+ if (hash_table->arrangement == &hash_table_arrangements[0])
+ return CAIRO_STATUS_SUCCESS;
+ tmp.arrangement = hash_table->arrangement - 1;
+ }
+
+ new_size = tmp.arrangement->size;
+ tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*));
+ if (unlikely (tmp.entries == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (i = 0; i < hash_table->arrangement->size; ++i) {
+ 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->arrangement = tmp.arrangement;
+
+ 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;
+
+ table_size = hash_table->arrangement->size;
+ idx = key->hash % table_size;
+
+ entry = hash_table->entries[idx];
+ if (ENTRY_IS_LIVE (entry)) {
+ if (hash_table->keys_equal (key, entry))
+ return entry;
+ } else if (ENTRY_IS_FREE (entry))
+ return NULL;
+
+ i = 1;
+ step = key->hash % hash_table->arrangement->rehash;
+ if (step == 0)
+ step = 1;
+ do {
+ idx += step;
+ if (idx >= table_size)
+ idx -= table_size;
+
+ entry = hash_table->entries[idx];
+ if (ENTRY_IS_LIVE (entry)) {
+ if (hash_table->keys_equal (key, entry))
+ return entry;
+ } else if (ENTRY_IS_FREE (entry))
+ return NULL;
+ } while (++i < table_size);
+
+ return NULL;
+}
+
+/**
+ * _cairo_hash_table_random_entry:
+ * @hash_table: a hash table
+ * @predicate: a predicate function.
+ *
+ * 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->arrangement->size;
+ hash = rand ();
+ idx = hash % table_size;
+
+ entry = hash_table->entries[idx];
+ if (ENTRY_IS_LIVE (entry) && predicate (entry))
+ return entry;
+
+ i = 1;
+ step = hash % hash_table->arrangement->rehash;
+ if (step == 0)
+ step = 1;
+ do {
+ idx += step;
+ if (idx >= table_size)
+ idx -= table_size;
+
+ entry = hash_table->entries[idx];
+ if (ENTRY_IS_LIVE (entry) && predicate (entry))
+ return entry;
+ } while (++i < table_size);
+
+ return NULL;
+}
+
+/**
+ * _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_status_t status;
+
+ /* Insert is illegal while an iterator is running. */
+ assert (hash_table->iterating == 0);
+
+ hash_table->live_entries++;
+ status = _cairo_hash_table_resize (hash_table);
+ if (unlikely (status)) {
+ /* abort the insert... */
+ hash_table->live_entries--;
+ return status;
+ }
+
+ *_cairo_hash_table_lookup_unique_key (hash_table,
+ key_and_value) = key_and_value;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_hash_entry_t **
+_cairo_hash_table_lookup_exact_key (cairo_hash_table_t *hash_table,
+ cairo_hash_entry_t *key)
+{
+ unsigned long table_size, i, idx, step;
+ cairo_hash_entry_t **entry;
+
+ table_size = hash_table->arrangement->size;
+ idx = key->hash % table_size;
+
+ entry = &hash_table->entries[idx];
+ if (*entry == key)
+ return entry;
+
+ i = 1;
+ step = key->hash % hash_table->arrangement->rehash;
+ if (step == 0)
+ step = 1;
+ do {
+ idx += step;
+ if (idx >= table_size)
+ idx -= table_size;
+
+ entry = &hash_table->entries[idx];
+ if (*entry == key)
+ return entry;
+ } while (++i < table_size);
+
+ ASSERT_NOT_REACHED;
+ return NULL;
+}
+/**
+ * _cairo_hash_table_remove:
+ * @hash_table: a hash table
+ * @key: key of entry to be removed
+ *
+ * Remove an entry from the hash table which 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--;
+
+ /* Check for table resize. Don't do this when iterating as this will
+ * reorder elements of the table and cause the iteration to potentially
+ * skip some elements. */
+ if (hash_table->iterating == 0) {
+ /* This call _can_ fail, but only in failing to allocate new
+ * memory to shrink the hash table. It does leave the table in a
+ * consistent state, and we've already succeeded in removing the
+ * entry, so we don't examine the failure status of this call. */
+ _cairo_hash_table_resize (hash_table);
+ }
+}
+
+/**
+ * _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->arrangement->size; i++) {
+ entry = hash_table->entries[i];
+ if (ENTRY_IS_LIVE(entry))
+ hash_callback (entry, closure);
+ }
+ /* If some elements were deleted during the iteration,
+ * the table may need resizing. Just do this every time
+ * as the check is inexpensive.
+ */
+ if (--hash_table->iterating == 0) {
+ /* Should we fail to shrink the hash table, it is left unaltered,
+ * and we don't need to propagate the error status. */
+ _cairo_hash_table_resize (hash_table);
+ }
+}
diff --git a/gfx/cairo/cairo/src/cairo-hull.c b/gfx/cairo/cairo/src/cairo-hull.c
new file mode 100644
index 000000000..c65593327
--- /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 <cworth@cworth.org>
+ */
+
+#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-info-private.h b/gfx/cairo/cairo/src/cairo-image-info-private.h
new file mode 100644
index 000000000..0d9ef8498
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-image-info-private.h
@@ -0,0 +1,63 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_IMAGE_INFO_PRIVATE_H
+#define CAIRO_IMAGE_INFO_PRIVATE_H
+
+#include "cairoint.h"
+
+typedef struct _cairo_image_info {
+ int width;
+ int height;
+ int num_components;
+ int bits_per_component;
+} cairo_image_info_t;
+
+cairo_private cairo_int_status_t
+_cairo_image_info_get_jpeg_info (cairo_image_info_t *info,
+ const unsigned char *data,
+ long length);
+
+cairo_private cairo_int_status_t
+_cairo_image_info_get_jpx_info (cairo_image_info_t *info,
+ const unsigned char *data,
+ 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);
+
+#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 000000000..63201e65b
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-image-info.c
@@ -0,0 +1,290 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2008 Adrian Johnson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Adrian Johnson.
+ *
+ * Contributor(s):
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-image-info-private.h"
+
+static uint32_t
+_get_be32 (const unsigned char *p)
+{
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+/* JPEG (image/jpeg)
+ *
+ * http://www.w3.org/Graphics/JPEG/itu-t81.pdf
+ */
+
+/* Markers with no parameters. All other markers are followed by a two
+ * byte length of the parameters. */
+#define TEM 0x01
+#define RST_begin 0xd0
+#define RST_end 0xd7
+#define SOI 0xd8
+#define EOI 0xd9
+
+/* Start of frame markers. */
+#define SOF0 0xc0
+#define SOF1 0xc1
+#define SOF2 0xc2
+#define SOF3 0xc3
+#define SOF5 0xc5
+#define SOF6 0xc6
+#define SOF7 0xc7
+#define SOF9 0xc9
+#define SOF10 0xca
+#define SOF11 0xcb
+#define SOF13 0xcd
+#define SOF14 0xce
+#define SOF15 0xcf
+
+static const unsigned char *
+_jpeg_skip_segment (const unsigned char *p)
+{
+ int len;
+
+ p++;
+ len = (p[0] << 8) | p[1];
+
+ return p + len;
+}
+
+static void
+_jpeg_extract_info (cairo_image_info_t *info, const unsigned char *p)
+{
+ info->width = (p[6] << 8) + p[7];
+ info->height = (p[4] << 8) + p[5];
+ info->num_components = p[8];
+ info->bits_per_component = p[3];
+}
+
+cairo_int_status_t
+_cairo_image_info_get_jpeg_info (cairo_image_info_t *info,
+ const unsigned char *data,
+ long length)
+{
+ const unsigned char *p = data;
+
+ while (p + 1 < data + length) {
+ if (*p != 0xff)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ p++;
+
+ switch (*p) {
+ /* skip fill bytes */
+ case 0xff:
+ p++;
+ break;
+
+ case TEM:
+ case SOI:
+ case EOI:
+ p++;
+ break;
+
+ case SOF0:
+ case SOF1:
+ case SOF2:
+ case SOF3:
+ case SOF5:
+ case SOF6:
+ case SOF7:
+ case SOF9:
+ case SOF10:
+ case SOF11:
+ case SOF13:
+ case SOF14:
+ case SOF15:
+ /* Start of frame found. Extract the image parameters. */
+ if (p + 8 > data + length)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _jpeg_extract_info (info, p);
+ return CAIRO_STATUS_SUCCESS;
+
+ default:
+ if (*p >= RST_begin && *p <= RST_end) {
+ p++;
+ break;
+ }
+
+ if (p + 2 > data + length)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ p = _jpeg_skip_segment (p);
+ break;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* JPEG 2000 (image/jp2)
+ *
+ * http://www.jpeg.org/public/15444-1annexi.pdf
+ */
+
+#define JPX_FILETYPE 0x66747970
+#define JPX_JP2_HEADER 0x6A703268
+#define JPX_IMAGE_HEADER 0x69686472
+
+static const unsigned char _jpx_signature[] = {
+ 0x00, 0x00, 0x00, 0x0c, 0x6a, 0x50, 0x20, 0x20, 0x0d, 0x0a, 0x87, 0x0a
+};
+
+static const unsigned char *
+_jpx_next_box (const unsigned char *p)
+{
+ return p + _get_be32 (p);
+}
+
+static const unsigned char *
+_jpx_get_box_contents (const unsigned char *p)
+{
+ return p + 8;
+}
+
+static cairo_bool_t
+_jpx_match_box (const unsigned char *p, const unsigned char *end, uint32_t type)
+{
+ uint32_t length;
+
+ if (p + 8 < end) {
+ length = _get_be32 (p);
+ if (_get_be32 (p + 4) == type && p + length < end)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static const unsigned char *
+_jpx_find_box (const unsigned char *p, const unsigned char *end, uint32_t type)
+{
+ while (p < end) {
+ if (_jpx_match_box (p, end, type))
+ return p;
+ p = _jpx_next_box (p);
+ }
+
+ return NULL;
+}
+
+static void
+_jpx_extract_info (const unsigned char *p, cairo_image_info_t *info)
+{
+ info->height = _get_be32 (p);
+ info->width = _get_be32 (p + 4);
+ info->num_components = (p[8] << 8) + p[9];
+ info->bits_per_component = p[10];
+}
+
+cairo_int_status_t
+_cairo_image_info_get_jpx_info (cairo_image_info_t *info,
+ const unsigned char *data,
+ 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_be32 (p) != PNG_IHDR)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ p += 4;
+ info->width = _get_be32 (p);
+ p += 4;
+ info->height = _get_be32 (p);
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/gfx/cairo/cairo/src/cairo-image-surface.c b/gfx/cairo/cairo/src/cairo-image-surface.c
new file mode 100644
index 000000000..4aaf1c159
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-image-surface.c
@@ -0,0 +1,4788 @@
+/* -*- 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 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-scaled-font-private.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
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+
+/**
+ * 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_int_status_t
+_cairo_image_surface_fill (void *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+static pixman_image_t *
+_pixman_image_for_solid (const cairo_solid_pattern_t *pattern);
+
+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_a8r8g8b8:
+ return CAIRO_FORMAT_ARGB32;
+ 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;
+ 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_x2b10g10r10:
+ case PIXMAN_a2b10g10r10:
+ case PIXMAN_x2r10g10b10:
+ case PIXMAN_a2r10g10b10:
+ 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;
+}
+
+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;
+ int width = pixman_image_get_width (pixman_image);
+ int height = pixman_image_get_height (pixman_image);
+
+ surface = malloc (sizeof (cairo_image_surface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_image_surface_backend,
+ NULL, /* device */
+ _cairo_content_from_pixman_format (pixman_format));
+
+ surface->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->width = width;
+ surface->height = height;
+ surface->stride = pixman_image_get_stride (pixman_image);
+ surface->depth = pixman_image_get_depth (pixman_image);
+
+ 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_RGB16_565:
+ ret = PIXMAN_r5g6b5;
+ 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 ? stride : 4);
+
+ 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 all
+ * 0. (Specifically, within each pixel, each color or alpha channel
+ * belonging to format will be 0. The contents of bits within a pixel,
+ * but not belonging to the given format are undefined).
+ *
+ * Return value: a pointer to the newly created surface. The caller
+ * owns the surface and should call cairo_surface_destroy() when done
+ * with it.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if an error such as out of memory
+ * occurs. You can use cairo_surface_status() to check for this.
+ **/
+cairo_surface_t *
+cairo_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:
+ *
+ * <informalexample><programlisting>
+ * 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);
+ * </programlisting></informalexample>
+ *
+ * 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.
+ **/
+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.
+ *
+ * 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.
+ **/
+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.
+ **/
+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_ARGB32:
+ return CAIRO_CONTENT_COLOR_ALPHA;
+ 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_ARGB32:
+ return 32;
+ 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;
+ }
+}
+
+static 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;
+
+ 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);
+}
+
+static 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;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface)
+{
+ surface->owns_data = TRUE;
+}
+
+static 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;
+}
+
+static void
+_cairo_image_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+}
+
+/* XXX: I think we should fix pixman to match the names/order of the
+ * cairo operators, but that will likely be better done at the same
+ * time the X server is ported to pixman, (which will change a lot of
+ * things in pixman I think).
+ */
+static pixman_op_t
+_pixman_operator (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return PIXMAN_OP_CLEAR;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return PIXMAN_OP_SRC;
+ case CAIRO_OPERATOR_OVER:
+ return PIXMAN_OP_OVER;
+ case CAIRO_OPERATOR_IN:
+ return PIXMAN_OP_IN;
+ case CAIRO_OPERATOR_OUT:
+ return PIXMAN_OP_OUT;
+ case CAIRO_OPERATOR_ATOP:
+ return PIXMAN_OP_ATOP;
+
+ case CAIRO_OPERATOR_DEST:
+ return PIXMAN_OP_DST;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return PIXMAN_OP_OVER_REVERSE;
+ case CAIRO_OPERATOR_DEST_IN:
+ return PIXMAN_OP_IN_REVERSE;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return PIXMAN_OP_OUT_REVERSE;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return PIXMAN_OP_ATOP_REVERSE;
+
+ case CAIRO_OPERATOR_XOR:
+ return PIXMAN_OP_XOR;
+ case CAIRO_OPERATOR_ADD:
+ return PIXMAN_OP_ADD;
+ case CAIRO_OPERATOR_SATURATE:
+ return PIXMAN_OP_SATURATE;
+
+ case CAIRO_OPERATOR_MULTIPLY:
+ return PIXMAN_OP_MULTIPLY;
+ case CAIRO_OPERATOR_SCREEN:
+ return PIXMAN_OP_SCREEN;
+ case CAIRO_OPERATOR_OVERLAY:
+ return PIXMAN_OP_OVERLAY;
+ case CAIRO_OPERATOR_DARKEN:
+ return PIXMAN_OP_DARKEN;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return PIXMAN_OP_LIGHTEN;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return PIXMAN_OP_COLOR_DODGE;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return PIXMAN_OP_COLOR_BURN;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return PIXMAN_OP_HARD_LIGHT;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return PIXMAN_OP_SOFT_LIGHT;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return PIXMAN_OP_DIFFERENCE;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return PIXMAN_OP_EXCLUSION;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return PIXMAN_OP_HSL_HUE;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return PIXMAN_OP_HSL_SATURATION;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return PIXMAN_OP_HSL_COLOR;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return PIXMAN_OP_HSL_LUMINOSITY;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return PIXMAN_OP_OVER;
+ }
+}
+
+static cairo_status_t
+_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface,
+ cairo_region_t *region)
+{
+ if (! pixman_image_set_clip_region32 (surface->pixman_image, &region->rgn))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_surface_unset_clip_region (cairo_image_surface_t *surface)
+{
+ pixman_image_set_clip_region32 (surface->pixman_image, NULL);
+}
+
+static double
+_pixman_nearest_sample (double d)
+{
+ return ceil (d - .5);
+}
+
+static cairo_bool_t
+_nearest_sample (cairo_filter_t filter, double *tx, double *ty)
+{
+ if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) {
+ *tx = _pixman_nearest_sample (*tx);
+ *ty = _pixman_nearest_sample (*ty);
+ } else {
+ if (*tx != floor (*tx) || *ty != floor (*ty))
+ return FALSE;
+ }
+ return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT;
+}
+
+#if PIXMAN_HAS_ATOMIC_OPS
+static pixman_image_t *__pixman_transparent_image;
+static pixman_image_t *__pixman_black_image;
+static pixman_image_t *__pixman_white_image;
+
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ pixman_image_t *image;
+
+ image = __pixman_transparent_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0x00;
+ color.green = 0x00;
+ color.blue = 0x00;
+ color.alpha = 0x00;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ pixman_image_t *image;
+
+ image = __pixman_black_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0x00;
+ color.green = 0x00;
+ color.blue = 0x00;
+ color.alpha = 0xffff;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ pixman_image_t *image;
+
+ image = __pixman_white_image;
+ if (unlikely (image == NULL)) {
+ pixman_color_t color;
+
+ color.red = 0xffff;
+ color.green = 0xffff;
+ color.blue = 0xffff;
+ color.alpha = 0xffff;
+
+ image = pixman_image_create_solid_fill (&color);
+ if (unlikely (image == NULL))
+ return NULL;
+
+ if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
+ NULL, image))
+ {
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static struct {
+ cairo_color_t color;
+ pixman_image_t *image;
+} cache[16];
+static int n_cached;
+
+#else /* !PIXMAN_HAS_ATOMIC_OPS */
+static pixman_image_t *
+_pixman_transparent_image (void)
+{
+ return _pixman_image_for_solid (&_cairo_pattern_clear);
+}
+
+static pixman_image_t *
+_pixman_black_image (void)
+{
+ return _pixman_image_for_solid (&_cairo_pattern_black);
+}
+
+static pixman_image_t *
+_pixman_white_image (void)
+{
+ return _pixman_image_for_solid (&_cairo_pattern_white);
+}
+#endif /* !PIXMAN_HAS_ATOMIC_OPS */
+
+void
+_cairo_image_reset_static_data (void)
+{
+#if PIXMAN_HAS_ATOMIC_OPS
+ while (n_cached)
+ pixman_image_unref (cache[--n_cached].image);
+
+ if (__pixman_transparent_image) {
+ pixman_image_unref (__pixman_transparent_image);
+ __pixman_transparent_image = NULL;
+ }
+
+ if (__pixman_black_image) {
+ pixman_image_unref (__pixman_black_image);
+ __pixman_black_image = NULL;
+ }
+
+ if (__pixman_white_image) {
+ pixman_image_unref (__pixman_white_image);
+ __pixman_white_image = NULL;
+ }
+#endif
+}
+
+static pixman_image_t *
+_pixman_image_for_solid (const cairo_solid_pattern_t *pattern)
+{
+ pixman_color_t color;
+ pixman_image_t *image;
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ int i;
+
+ if (pattern->color.alpha_short <= 0x00ff)
+ return _pixman_transparent_image ();
+
+ if (pattern->color.alpha_short >= 0xff00) {
+ if (pattern->color.red_short <= 0x00ff &&
+ pattern->color.green_short <= 0x00ff &&
+ pattern->color.blue_short <= 0x00ff)
+ {
+ return _pixman_black_image ();
+ }
+
+ if (pattern->color.red_short >= 0xff00 &&
+ pattern->color.green_short >= 0xff00 &&
+ pattern->color.blue_short >= 0xff00)
+ {
+ return _pixman_white_image ();
+ }
+ }
+
+ CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
+ for (i = 0; i < n_cached; i++) {
+ if (_cairo_color_equal (&cache[i].color, &pattern->color)) {
+ image = pixman_image_ref (cache[i].image);
+ goto UNLOCK;
+ }
+ }
+#endif
+
+ color.red = pattern->color.red_short;
+ color.green = pattern->color.green_short;
+ color.blue = pattern->color.blue_short;
+ color.alpha = pattern->color.alpha_short;
+
+ image = pixman_image_create_solid_fill (&color);
+#if PIXMAN_HAS_ATOMIC_OPS
+ if (image == NULL)
+ goto UNLOCK;
+
+ if (n_cached < ARRAY_LENGTH (cache)) {
+ i = n_cached++;
+ } else {
+ i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
+ pixman_image_unref (cache[i].image);
+ }
+ cache[i].image = pixman_image_ref (image);
+ cache[i].color = pattern->color;
+
+UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
+#endif
+ return image;
+}
+
+static double
+clamp (double val, double min, double max)
+{
+ return val < min ? min : (val > max ? max : val);
+}
+
+static pixman_image_t *
+_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image;
+ pixman_gradient_stop_t pixman_stops_static[2];
+ pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
+ cairo_matrix_t matrix = pattern->base.matrix;
+ double tx, ty;
+ unsigned int i;
+
+ if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
+ pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+ sizeof(pixman_gradient_stop_t));
+ if (unlikely (pixman_stops == NULL))
+ return NULL;
+ }
+
+ for (i = 0; i < pattern->n_stops; i++) {
+ pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+ pixman_stops[i].color.red = pattern->stops[i].color.red_short;
+ pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+ pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
+ pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+ }
+
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
+ pixman_point_fixed_t p1, p2;
+ double x0, y0, x1, y1, maxabs;
+
+ /*
+ * Transform the matrix to avoid overflow when converting between
+ * cairo_fixed_t and pixman_fixed_t (without incurring performance
+ * loss when the transformation is unnecessary).
+ *
+ * Having a function to compute the required transformation to
+ * "normalize" a given bounding box would be generally useful -
+ * cf linear patterns, gradient patterns, surface patterns...
+ */
+ x0 = _cairo_fixed_to_double (linear->p1.x);
+ y0 = _cairo_fixed_to_double (linear->p1.y);
+ x1 = _cairo_fixed_to_double (linear->p2.x);
+ y1 = _cairo_fixed_to_double (linear->p2.y);
+ cairo_matrix_transform_point (&matrix, &x0, &y0);
+ cairo_matrix_transform_point (&matrix, &x1, &y1);
+ maxabs = MAX (MAX (fabs (x0), fabs (x1)), MAX (fabs (y0), fabs (y1)));
+
+ if (maxabs > PIXMAN_MAX_INT)
+ {
+ double sf;
+ cairo_matrix_t scale;
+
+ sf = PIXMAN_MAX_INT / maxabs;
+
+ p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
+ p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
+ p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
+ p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
+
+ /* cairo_matrix_scale does a pre-scale, we want a post-scale */
+ cairo_matrix_init_scale (&scale, sf, sf);
+ cairo_matrix_multiply (&matrix, &matrix, &scale);
+ }
+ else
+ {
+ p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
+ p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
+ p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
+ p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
+ }
+
+ pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
+ pixman_stops,
+ pattern->n_stops);
+ } else {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
+ pixman_point_fixed_t c1, c2;
+ pixman_fixed_t r1, r2;
+
+ c1.x = _cairo_fixed_to_16_16 (radial->c1.x);
+ c1.y = _cairo_fixed_to_16_16 (radial->c1.y);
+ r1 = _cairo_fixed_to_16_16 (radial->r1);
+
+ c2.x = _cairo_fixed_to_16_16 (radial->c2.x);
+ c2.y = _cairo_fixed_to_16_16 (radial->c2.y);
+ r2 = _cairo_fixed_to_16_16 (radial->r2);
+
+ pixman_image = pixman_image_create_radial_gradient (&c1, &c2, r1, r2,
+ pixman_stops,
+ pattern->n_stops);
+ }
+
+ if (pixman_stops != pixman_stops_static)
+ free (pixman_stops);
+
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+
+ tx = matrix.x0;
+ ty = matrix.y0;
+ if (! _cairo_matrix_is_translation (&matrix) ||
+ ! _nearest_sample (pattern->base.filter, &tx, &ty))
+ {
+ pixman_transform_t pixman_transform;
+
+ if (tx != 0. || ty != 0.) {
+ cairo_matrix_t m, inv;
+ cairo_status_t status;
+ double x, y, max_x, max_y;
+
+ /* Pixman also limits the [xy]_offset to 16 bits. We try to evenly
+ * spread the bits between the two, but we need to ensure that
+ * fabs (tx + extents->x + extents->width) < PIXMAN_MAX_INT &&
+ * fabs (ty + extents->y + extents->height) < PIXMAN_MAX_INT,
+ * otherwise the gradient won't render.
+ */
+ inv = matrix;
+ status = cairo_matrix_invert (&inv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ x = _cairo_lround (inv.x0 / 2);
+ y = _cairo_lround (inv.y0 / 2);
+
+ max_x = PIXMAN_MAX_INT - 1 - fabs (extents->x + extents->width);
+ x = clamp(x, -max_x, max_x);
+ max_y = PIXMAN_MAX_INT - 1 - fabs (extents->y + extents->height);
+ y = clamp(y, -max_y, max_y);
+
+ tx = -x;
+ ty = -y;
+ cairo_matrix_init_translate (&inv, x, y);
+ cairo_matrix_multiply (&m, &inv, &matrix);
+ _cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ } else {
+ tx = ty = 0;
+ _cairo_matrix_to_pixman_matrix (&pattern->base.matrix,
+ &pixman_transform,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ }
+
+ if (! pixman_image_set_transform (pixman_image, &pixman_transform)) {
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+ }
+ *ix = tx;
+ *iy = ty;
+
+ {
+ pixman_repeat_t pixman_repeat;
+
+ switch (pattern->base.extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ pixman_repeat = PIXMAN_REPEAT_NONE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_repeat = PIXMAN_REPEAT_NORMAL;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_repeat = PIXMAN_REPEAT_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_repeat = PIXMAN_REPEAT_PAD;
+ break;
+ }
+
+ pixman_image_set_repeat (pixman_image, pixman_repeat);
+ }
+
+ return pixman_image;
+}
+
+struct acquire_source_cleanup {
+ cairo_surface_t *surface;
+ cairo_image_surface_t *image;
+ void *image_extra;
+};
+
+static void
+_acquire_source_cleanup (pixman_image_t *pixman_image,
+ void *closure)
+{
+ struct acquire_source_cleanup *data = closure;
+
+ _cairo_surface_release_source_image (data->surface,
+ data->image,
+ data->image_extra);
+ free (data);
+}
+
+static cairo_filter_t
+sampled_area (const cairo_surface_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ cairo_rectangle_int_t *sample)
+{
+ cairo_filter_t filter;
+ double x1, x2, y1, y2;
+ double pad;
+
+ x1 = extents->x;
+ y1 = extents->y;
+ x2 = extents->x + (int) extents->width;
+ y2 = extents->y + (int) extents->height;
+
+ _cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+ &x1, &y1, &x2, &y2,
+ NULL);
+
+ filter = _cairo_pattern_analyze_filter (&pattern->base, &pad);
+ sample->x = floor (x1 - pad);
+ sample->y = floor (y1 - pad);
+ sample->width = ceil (x2 + pad) - sample->x;
+ sample->height = ceil (y2 + pad) - sample->y;
+
+ return filter;
+}
+
+static uint16_t
+expand_channel (uint16_t v, uint32_t bits)
+{
+ int offset = 16 - bits;
+ while (offset > 0) {
+ v |= v >> bits;
+ offset -= bits;
+ bits += bits;
+ }
+ return v;
+}
+
+static pixman_image_t *
+_pixel_to_solid (cairo_image_surface_t *image, int x, int y)
+{
+ uint32_t pixel;
+ pixman_color_t color;
+
+ switch (image->format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ return NULL;
+
+ case CAIRO_FORMAT_A1:
+ pixel = *(uint8_t *) (image->data + y * image->stride + x/8);
+ return pixel & (1 << (x&7)) ? _pixman_white_image () : _pixman_transparent_image ();
+
+ case CAIRO_FORMAT_A8:
+ color.alpha = *(uint8_t *) (image->data + y * image->stride + x);
+ color.alpha |= color.alpha << 8;
+ if (color.alpha == 0)
+ return _pixman_transparent_image ();
+
+ color.red = color.green = color.blue = 0;
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_RGB16_565:
+ pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x);
+ if (pixel == 0)
+ return _pixman_black_image ();
+ if (pixel == 0xffff)
+ return _pixman_white_image ();
+
+ color.alpha = 0xffff;
+ color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5);
+ color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6);
+ color.blue = expand_channel ((pixel & 0x1f) << 11, 5);
+ return pixman_image_create_solid_fill (&color);
+
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x);
+ color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff;
+ if (color.alpha == 0)
+ return _pixman_transparent_image ();
+ if (pixel == 0xffffffff)
+ return _pixman_white_image ();
+ if (color.alpha == 0xffff && (pixel & 0xffffff) == 0)
+ return _pixman_black_image ();
+
+ color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00);
+ color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00);
+ color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00);
+ return pixman_image_create_solid_fill (&color);
+ }
+}
+
+static pixman_image_t *
+_pixman_image_for_surface (const cairo_surface_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ int *ix, int *iy)
+{
+ pixman_image_t *pixman_image;
+ cairo_rectangle_int_t sample;
+ cairo_extend_t extend;
+ cairo_filter_t filter;
+ double tx, ty;
+
+ tx = pattern->base.matrix.x0;
+ ty = pattern->base.matrix.y0;
+
+ extend = pattern->base.extend;
+ filter = sampled_area (pattern, extents, &sample);
+
+ pixman_image = NULL;
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE &&
+ (! is_mask || ! pattern->base.has_component_alpha ||
+ (pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
+ {
+ cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
+ cairo_surface_type_t type;
+
+ if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+ source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
+
+ type = source->base.backend->type;
+ if (type == CAIRO_SURFACE_TYPE_IMAGE) {
+ if (sample.width == 1 && sample.height == 1) {
+ if (sample.x < 0 ||
+ sample.y < 0 ||
+ sample.x >= source->width ||
+ sample.y >= source->height)
+ {
+ if (extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ else
+ {
+ return _pixel_to_solid (source, sample.x, sample.y);
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ /* avoid allocating a 'pattern' image if we can reuse the original */
+ if (extend == CAIRO_EXTEND_NONE &&
+ _cairo_matrix_is_translation (&pattern->base.matrix) &&
+ _nearest_sample (filter, &tx, &ty))
+ {
+ *ix = tx;
+ *iy = ty;
+ return pixman_image_ref (source->pixman_image);
+ }
+#endif
+
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ source->width,
+ source->height,
+ (uint32_t *) source->data,
+ source->stride);
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+ } else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub;
+ cairo_bool_t is_contained = FALSE;
+
+ sub = (cairo_surface_subsurface_t *) source;
+ source = (cairo_image_surface_t *) sub->target;
+
+ if (sample.x >= 0 &&
+ sample.y >= 0 &&
+ sample.x + sample.width <= sub->extents.width &&
+ sample.y + sample.height <= sub->extents.height)
+ {
+ is_contained = TRUE;
+ }
+
+ if (sample.width == 1 && sample.height == 1) {
+ if (is_contained) {
+ return _pixel_to_solid (source,
+ sub->extents.x + sample.x,
+ sub->extents.y + sample.y);
+ } else {
+ if (extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ }
+
+#if PIXMAN_HAS_ATOMIC_OPS
+ if (is_contained &&
+ _cairo_matrix_is_translation (&pattern->base.matrix) &&
+ _nearest_sample (filter, &tx, &ty))
+ {
+ *ix = tx + sub->extents.x;
+ *iy = ty + sub->extents.y;
+ return pixman_image_ref (source->pixman_image);
+ }
+#endif
+
+ /* Avoid sub-byte offsets, force a copy in that case. */
+ if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
+ void *data = source->data
+ + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
+ + sub->extents.y * source->stride;
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ sub->extents.width,
+ sub->extents.height,
+ data,
+ source->stride);
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+ }
+ }
+ }
+
+ if (pixman_image == NULL) {
+ struct acquire_source_cleanup *cleanup;
+ cairo_image_surface_t *image;
+ void *extra;
+ cairo_status_t status;
+
+ status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra);
+ if (unlikely (status))
+ return NULL;
+
+ if (sample.width == 1 && sample.height == 1) {
+ if (sample.x < 0 ||
+ sample.y < 0 ||
+ sample.x >= image->width ||
+ sample.y >= image->height)
+ {
+ if (extend == CAIRO_EXTEND_NONE) {
+ pixman_image = _pixman_transparent_image ();
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ return pixman_image;
+ }
+ }
+ else
+ {
+ pixman_image = _pixel_to_solid (image, sample.x, sample.y);
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ return pixman_image;
+ }
+ }
+
+ pixman_image = pixman_image_create_bits (image->pixman_format,
+ image->width,
+ image->height,
+ (uint32_t *) image->data,
+ image->stride);
+ if (unlikely (pixman_image == NULL)) {
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ return NULL;
+ }
+
+ cleanup = malloc (sizeof (*cleanup));
+ if (unlikely (cleanup == NULL)) {
+ _cairo_surface_release_source_image (pattern->surface, image, extra);
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+
+ cleanup->surface = pattern->surface;
+ cleanup->image = image;
+ cleanup->image_extra = extra;
+ pixman_image_set_destroy_function (pixman_image,
+ _acquire_source_cleanup, cleanup);
+ }
+
+ if (! _cairo_matrix_is_translation (&pattern->base.matrix) ||
+ ! _nearest_sample (filter, &tx, &ty))
+ {
+ pixman_transform_t pixman_transform;
+ cairo_matrix_t m;
+
+ m = pattern->base.matrix;
+ if (m.x0 != 0. || m.y0 != 0.) {
+ cairo_matrix_t inv;
+ cairo_status_t status;
+ double x, y;
+
+ /* pixman also limits the [xy]_offset to 16 bits so evenly
+ * spread the bits between the two.
+ */
+ inv = m;
+ status = cairo_matrix_invert (&inv);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ x = floor (inv.x0 / 2);
+ y = floor (inv.y0 / 2);
+ tx = -x;
+ ty = -y;
+ cairo_matrix_init_translate (&inv, x, y);
+ cairo_matrix_multiply (&m, &inv, &m);
+ } else {
+ tx = ty = 0;
+ }
+
+ _cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
+ extents->x + extents->width/2.,
+ extents->y + extents->height/2.);
+ if (! pixman_image_set_transform (pixman_image, &pixman_transform)) {
+ pixman_image_unref (pixman_image);
+ return NULL;
+ }
+ }
+ *ix = tx;
+ *iy = ty;
+
+ if (_cairo_matrix_has_unity_scale (&pattern->base.matrix) &&
+ tx == pattern->base.matrix.x0 &&
+ ty == pattern->base.matrix.y0)
+ {
+ pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
+ }
+ else
+ {
+ pixman_filter_t pixman_filter;
+
+ switch (filter) {
+ case CAIRO_FILTER_FAST:
+ pixman_filter = PIXMAN_FILTER_FAST;
+ break;
+ case CAIRO_FILTER_GOOD:
+ pixman_filter = PIXMAN_FILTER_GOOD;
+ break;
+ case CAIRO_FILTER_BEST:
+ pixman_filter = PIXMAN_FILTER_BEST;
+ break;
+ case CAIRO_FILTER_NEAREST:
+ pixman_filter = PIXMAN_FILTER_NEAREST;
+ break;
+ case CAIRO_FILTER_BILINEAR:
+ pixman_filter = PIXMAN_FILTER_BILINEAR;
+ break;
+ case CAIRO_FILTER_GAUSSIAN:
+ /* XXX: The GAUSSIAN value has no implementation in cairo
+ * whatsoever, so it was really a mistake to have it in the
+ * API. We could fix this by officially deprecating it, or
+ * else inventing semantics and providing an actual
+ * implementation for it. */
+ default:
+ pixman_filter = PIXMAN_FILTER_BEST;
+ }
+
+ pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
+ }
+
+ {
+ pixman_repeat_t pixman_repeat;
+
+ switch (extend) {
+ default:
+ case CAIRO_EXTEND_NONE:
+ pixman_repeat = PIXMAN_REPEAT_NONE;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_repeat = PIXMAN_REPEAT_NORMAL;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_repeat = PIXMAN_REPEAT_REFLECT;
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_repeat = PIXMAN_REPEAT_PAD;
+ break;
+ }
+
+ pixman_image_set_repeat (pixman_image, pixman_repeat);
+ }
+
+ if (pattern->base.has_component_alpha)
+ pixman_image_set_component_alpha (pixman_image, TRUE);
+
+ return pixman_image;
+}
+
+static pixman_image_t *
+_pixman_image_for_pattern (const cairo_pattern_t *pattern,
+ cairo_bool_t is_mask,
+ const cairo_rectangle_int_t *extents,
+ int *tx, int *ty)
+{
+ *tx = *ty = 0;
+
+ if (pattern == NULL)
+ return _pixman_white_image ();
+
+ switch (pattern->type) {
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern,
+ extents, tx, ty);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern,
+ is_mask, extents, tx, ty);
+ }
+}
+
+static cairo_status_t
+_cairo_image_surface_fixup_unbounded (cairo_image_surface_t *dst,
+ const cairo_composite_rectangles_t *rects,
+ cairo_clip_t *clip)
+{
+ pixman_image_t *mask = NULL;
+ pixman_box32_t boxes[4];
+ int i, mask_x = 0, mask_y = 0, n_boxes = 0;
+
+ if (clip != NULL) {
+ cairo_surface_t *clip_surface;
+ int clip_x, clip_y;
+
+ clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ return clip_surface->status;
+
+ mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
+ mask_x = -clip_x;
+ mask_y = -clip_y;
+ } else {
+ if (rects->bounded.width == rects->unbounded.width &&
+ rects->bounded.height == rects->unbounded.height)
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ /* wholly unbounded? */
+ if (rects->bounded.width == 0 || rects->bounded.height == 0) {
+ int x = rects->unbounded.x;
+ int y = rects->unbounded.y;
+ int width = rects->unbounded.width;
+ int height = rects->unbounded.height;
+
+ if (mask != NULL) {
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ x + mask_x, y + mask_y,
+ 0, 0,
+ x, y,
+ width, height);
+ } else {
+ pixman_color_t color = { 0, };
+ pixman_box32_t box = { x, y, x + width, y + height };
+
+ if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
+ dst->pixman_image,
+ &color,
+ 1, &box))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* top */
+ if (rects->bounded.y != rects->unbounded.y) {
+ boxes[n_boxes].x1 = rects->unbounded.x;
+ boxes[n_boxes].y1 = rects->unbounded.y;
+ boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+ boxes[n_boxes].y2 = rects->bounded.y;
+ n_boxes++;
+ }
+
+ /* left */
+ if (rects->bounded.x != rects->unbounded.x) {
+ boxes[n_boxes].x1 = rects->unbounded.x;
+ boxes[n_boxes].y1 = rects->bounded.y;
+ boxes[n_boxes].x2 = rects->bounded.x;
+ boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height;
+ n_boxes++;
+ }
+
+ /* right */
+ if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
+ boxes[n_boxes].x1 = rects->bounded.x + rects->bounded.width;
+ boxes[n_boxes].y1 = rects->bounded.y;
+ boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+ boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height;
+ n_boxes++;
+ }
+
+ /* bottom */
+ if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
+ boxes[n_boxes].x1 = rects->unbounded.x;
+ boxes[n_boxes].y1 = rects->bounded.y + rects->bounded.height;
+ boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
+ boxes[n_boxes].y2 = rects->unbounded.y + rects->unbounded.height;
+ n_boxes++;
+ }
+
+ if (mask != NULL) {
+ for (i = 0; i < n_boxes; i++) {
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ boxes[i].x1 + mask_x, boxes[i].y1 + mask_y,
+ 0, 0,
+ boxes[i].x1, boxes[i].y1,
+ boxes[i].x2 - boxes[i].x1, boxes[i].y2 - boxes[i].y1);
+ }
+ } else {
+ pixman_color_t color = { 0, };
+
+ if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
+ dst->pixman_image,
+ &color,
+ n_boxes,
+ boxes))
+ {
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_image_surface_fixup_unbounded_boxes (cairo_image_surface_t *dst,
+ const cairo_composite_rectangles_t *extents,
+ cairo_region_t *clip_region,
+ cairo_boxes_t *boxes)
+{
+ cairo_boxes_t clear;
+ cairo_box_t box;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ int i;
+
+ // If we have no boxes then we need to clear the entire extents
+ // because we have nothing to draw.
+ if (boxes->num_boxes < 1 && clip_region == NULL) {
+ int x = extents->unbounded.x;
+ int y = extents->unbounded.y;
+ int width = extents->unbounded.width;
+ int height = extents->unbounded.height;
+
+ pixman_color_t color = { 0 };
+ pixman_box32_t box = { x, y, x + width, y + height };
+
+ if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
+ dst->pixman_image,
+ &color,
+ 1, &box)) {
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_boxes_init (&clear);
+
+ box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
+ box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
+ box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
+ box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
+
+ if (clip_region == NULL) {
+ cairo_boxes_t tmp;
+
+ _cairo_boxes_init (&tmp);
+
+ status = _cairo_boxes_add (&tmp, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ tmp.chunks.next = &boxes->chunks;
+ tmp.num_boxes += boxes->num_boxes;
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+
+ tmp.chunks.next = NULL;
+ } else {
+ pixman_box32_t *pbox;
+
+ pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
+ _cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
+
+ status = _cairo_boxes_add (&clear, &box);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_boxes_add (&clear, &chunk->base[i]);
+ if (unlikely (status)) {
+ _cairo_boxes_fini (&clear);
+ return status;
+ }
+ }
+ }
+
+ status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
+ CAIRO_FILL_RULE_WINDING,
+ &clear);
+ }
+
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
+ int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
+ int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
+ int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
+
+ x1 = (x1 < 0 ? 0 : x1);
+ y1 = (y1 < 0 ? 0 : y1);
+ if (x2 <= x1 || y2 <= y1)
+ continue;
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ 0);
+ }
+ }
+ }
+
+ _cairo_boxes_fini (&clear);
+
+ return status;
+}
+
+static cairo_bool_t
+can_reduce_alpha_op (cairo_operator_t op)
+{
+ int iop = op;
+ switch (iop) {
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_ADD:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static cairo_bool_t
+reduce_alpha_op (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ return dst->base.is_clear &&
+ dst->base.content == CAIRO_CONTENT_ALPHA &&
+ _cairo_pattern_is_opaque_solid (pattern) &&
+ can_reduce_alpha_op (op);
+}
+
+/* low level compositor */
+typedef cairo_status_t
+(*image_draw_func_t) (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region);
+
+static pixman_image_t *
+_create_composite_mask_pattern (cairo_clip_t *clip,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_image_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_region_t *clip_region = NULL;
+ pixman_image_t *mask;
+ cairo_status_t status;
+ cairo_bool_t need_clip_surface = FALSE;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (! _cairo_status_is_error (status));
+
+ /* The all-clipped state should never propagate this far. */
+ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+ }
+
+ mask = pixman_image_create_bits (PIXMAN_a8, extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL))
+ return NULL;
+
+ /* Is it worth setting the clip region here? */
+ if (clip_region != NULL) {
+ pixman_bool_t ret;
+
+ pixman_region32_translate (&clip_region->rgn, -extents->x, -extents->y);
+ ret = pixman_image_set_clip_region32 (mask, &clip_region->rgn);
+ pixman_region32_translate (&clip_region->rgn, extents->x, extents->y);
+
+ if (! ret) {
+ pixman_image_unref (mask);
+ return NULL;
+ }
+ }
+
+ status = draw_func (draw_closure,
+ mask, PIXMAN_a8,
+ CAIRO_OPERATOR_ADD, NULL,
+ extents->x, extents->y,
+ extents, NULL);
+ if (unlikely (status)) {
+ pixman_image_unref (mask);
+ return NULL;
+ }
+
+ if (need_clip_surface) {
+ cairo_surface_t *tmp;
+
+ tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8);
+ if (unlikely (tmp->status)) {
+ pixman_image_unref (mask);
+ return NULL;
+ }
+
+ pixman_image_ref (mask);
+
+ status = _cairo_clip_combine_with_surface (clip, tmp, extents->x, extents->y);
+ cairo_surface_destroy (tmp);
+ if (unlikely (status)) {
+ pixman_image_unref (mask);
+ return NULL;
+ }
+ }
+
+ if (clip_region != NULL)
+ pixman_image_set_clip_region (mask, NULL);
+
+ return mask;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+_clip_and_composite_with_mask (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_image_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ pixman_image_t *mask;
+
+ mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (pattern == NULL) {
+ if (dst->pixman_format == PIXMAN_a8) {
+ pixman_image_composite32 (_pixman_operator (op),
+ mask, NULL, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ } else {
+ pixman_image_t *src;
+
+ src = _pixman_white_image ();
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ pixman_image_unref (src);
+ }
+ } else {
+ pixman_image_t *src;
+ int src_x, src_y;
+
+ src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ pixman_image_unref (src);
+ }
+
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+_clip_and_composite_combine (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_image_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ pixman_image_t *tmp;
+ cairo_surface_t *clip_surface;
+ int clip_x, clip_y;
+ cairo_status_t status;
+
+ tmp = pixman_image_create_bits (dst->pixman_format,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (tmp == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (src == NULL) {
+ status = (*draw_func) (draw_closure,
+ tmp, dst->pixman_format,
+ CAIRO_OPERATOR_ADD, NULL,
+ extents->x, extents->y,
+ extents, NULL);
+ } else {
+ /* Initialize the temporary surface from the destination surface */
+ if (! dst->base.is_clear) {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ dst->pixman_image, NULL, tmp,
+ extents->x, extents->y,
+ 0, 0,
+ 0, 0,
+ extents->width, extents->height);
+ }
+
+ status = (*draw_func) (draw_closure,
+ tmp, dst->pixman_format,
+ op, src,
+ extents->x, extents->y,
+ extents, NULL);
+ }
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ assert (clip->path != NULL);
+ clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ goto CLEANUP_SURFACE;
+
+ if (! dst->base.is_clear) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP,
+ tmp,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+#else
+ /* Punch the clip out of the destination */
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ NULL, dst->pixman_image,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ /* Now add the two results together */
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ tmp,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+#endif
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ tmp,
+ ((cairo_image_surface_t *) clip_surface)->pixman_image,
+ dst->pixman_image,
+ 0, 0,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ }
+
+ CLEANUP_SURFACE:
+ pixman_image_unref (tmp);
+
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+_clip_and_composite_source (cairo_clip_t *clip,
+ const cairo_pattern_t *pattern,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_image_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ pixman_image_t *mask, *src;
+ int src_x, src_y;
+
+ if (pattern == NULL) {
+ cairo_region_t *clip_region;
+ cairo_status_t status;
+
+ status = draw_func (draw_closure,
+ dst->pixman_image, dst->pixman_format,
+ CAIRO_OPERATOR_SOURCE, NULL,
+ extents->x, extents->y,
+ extents, NULL);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_get_region (clip, &clip_region) == CAIRO_INT_STATUS_UNSUPPORTED)
+ status = _cairo_clip_combine_with_surface (clip, &dst->base, 0, 0);
+
+ return status;
+ }
+
+ /* Create a surface that is mask IN clip */
+ mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents);
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ pixman_image_unref (mask);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (! dst->base.is_clear) {
+#if PIXMAN_HAS_OP_LERP
+ pixman_image_composite32 (PIXMAN_OP_LERP,
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+#else
+ /* Compute dest' = dest OUT (mask IN clip) */
+ pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
+ mask, NULL, dst->pixman_image,
+ 0, 0, 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+
+ /* Now compute (src IN (mask IN clip)) ADD dest' */
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+#endif
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ src, mask, dst->pixman_image,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height);
+ }
+
+ pixman_image_unref (src);
+ pixman_image_unref (mask);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_clip_and_composite (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ image_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_composite_rectangles_t*extents,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t need_clip_surface = FALSE;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (clip_region != NULL) {
+ cairo_rectangle_int_t rect;
+ cairo_bool_t is_empty;
+
+ cairo_region_get_extents (clip_region, &rect);
+ is_empty = ! _cairo_rectangle_intersect (&extents->unbounded, &rect);
+ if (unlikely (is_empty))
+ return CAIRO_STATUS_SUCCESS;
+
+ is_empty = ! _cairo_rectangle_intersect (&extents->bounded, &rect);
+ if (unlikely (is_empty && extents->is_bounded))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+ }
+ }
+
+ if (clip_region != NULL) {
+ status = _cairo_image_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (reduce_alpha_op (dst, op, src)) {
+ op = CAIRO_OPERATOR_ADD;
+ src = NULL;
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = _clip_and_composite_source (clip, src,
+ draw_func, draw_closure,
+ dst, &extents->bounded);
+ } else {
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src = NULL;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ if (need_clip_surface) {
+ if (extents->is_bounded) {
+ status = _clip_and_composite_with_mask (clip, op, src,
+ draw_func, draw_closure,
+ dst, &extents->bounded);
+ } else {
+ status = _clip_and_composite_combine (clip, op, src,
+ draw_func, draw_closure,
+ dst, &extents->bounded);
+ }
+ } else {
+ status = draw_func (draw_closure,
+ dst->pixman_image, dst->pixman_format,
+ op, src,
+ 0, 0,
+ &extents->bounded,
+ clip_region);
+ }
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
+ status = _cairo_image_surface_fixup_unbounded (dst, extents,
+ need_clip_surface ? clip : NULL);
+ }
+
+ if (clip_region != NULL)
+ _cairo_image_surface_unset_clip_region (dst);
+
+ return status;
+}
+
+#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
+#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
+
+static cairo_bool_t
+_line_exceeds_16_16 (const cairo_line_t *line)
+{
+ return
+ line->p1.x <= CAIRO_FIXED_16_16_MIN ||
+ line->p1.x >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p2.x <= CAIRO_FIXED_16_16_MIN ||
+ line->p2.x >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p1.y <= CAIRO_FIXED_16_16_MIN ||
+ line->p1.y >= CAIRO_FIXED_16_16_MAX ||
+
+ line->p2.y <= CAIRO_FIXED_16_16_MIN ||
+ line->p2.y >= CAIRO_FIXED_16_16_MAX;
+}
+
+static void
+_project_line_x_onto_16_16 (const cairo_line_t *line,
+ cairo_fixed_t top,
+ cairo_fixed_t bottom,
+ pixman_line_fixed_t *out)
+{
+ cairo_point_double_t p1, p2;
+ double m;
+
+ p1.x = _cairo_fixed_to_double (line->p1.x);
+ p1.y = _cairo_fixed_to_double (line->p1.y);
+
+ p2.x = _cairo_fixed_to_double (line->p2.x);
+ p2.y = _cairo_fixed_to_double (line->p2.y);
+
+ m = (p2.x - p1.x) / (p2.y - p1.y);
+ out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+ out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+
+
+typedef struct {
+ cairo_trapezoid_t *traps;
+ int num_traps;
+ cairo_antialias_t antialias;
+} composite_traps_info_t;
+
+static void
+_pixman_image_add_traps (pixman_image_t *image,
+ int dst_x, int dst_y,
+ composite_traps_info_t *info)
+{
+ cairo_trapezoid_t *t = info->traps;
+ int num_traps = info->num_traps;
+ while (num_traps--) {
+ pixman_trapezoid_t trap;
+
+ /* top/bottom will be clamped to surface bounds */
+ trap.top = _cairo_fixed_to_16_16 (t->top);
+ trap.bottom = _cairo_fixed_to_16_16 (t->bottom);
+
+ /* However, all the other coordinates will have been left untouched so
+ * as not to introduce numerical error. Recompute them if they
+ * exceed the 16.16 limits.
+ */
+ if (unlikely (_line_exceeds_16_16 (&t->left))) {
+ _project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left);
+ trap.left.p1.y = trap.top;
+ trap.left.p2.y = trap.bottom;
+ } else {
+ trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x);
+ trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y);
+ trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x);
+ trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y);
+ }
+
+ if (unlikely (_line_exceeds_16_16 (&t->right))) {
+ _project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right);
+ trap.right.p1.y = trap.top;
+ trap.right.p2.y = trap.bottom;
+ } else {
+ trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x);
+ trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y);
+ trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x);
+ trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y);
+ }
+
+ pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+
+ t++;
+ }
+}
+
+static cairo_status_t
+_composite_traps (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ composite_traps_info_t *info = closure;
+ pixman_image_t *src, *mask;
+ pixman_format_code_t format;
+ int src_x = 0, src_y = 0;
+ cairo_status_t status;
+
+ /* Special case adding trapezoids onto a mask surface; we want to avoid
+ * creating an intermediate temporary mask unnecessarily.
+ *
+ * We make the assumption here that the portion of the trapezoids
+ * contained within the surface is bounded by [dst_x,dst_y,width,height];
+ * the Cairo core code passes bounds based on the trapezoid extents.
+ */
+ format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
+ if (dst_format == format &&
+ (pattern == NULL ||
+ (op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern))))
+ {
+ _pixman_image_add_traps (dst, dst_x, dst_y, info);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ mask = pixman_image_create_bits (format, extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_SOURCE;
+ }
+
+ _pixman_image_add_traps (mask, extents->x, extents->y, info);
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ pixman_image_unref (mask);
+
+ status = CAIRO_STATUS_SUCCESS;
+ CLEANUP_SOURCE:
+ pixman_image_unref (src);
+
+ return status;
+}
+
+static inline uint32_t
+color_to_uint32 (const cairo_color_t *color)
+{
+ return
+ (color->alpha_short >> 8 << 24) |
+ (color->red_short >> 8 << 16) |
+ (color->green_short & 0xff00) |
+ (color->blue_short >> 8);
+}
+
+static inline cairo_bool_t
+color_to_pixel (const cairo_color_t *color,
+ pixman_format_code_t format,
+ uint32_t *pixel)
+{
+ uint32_t c;
+
+ if (!(format == PIXMAN_a8r8g8b8 ||
+ format == PIXMAN_x8r8g8b8 ||
+ format == PIXMAN_a8b8g8r8 ||
+ format == PIXMAN_x8b8g8r8 ||
+ format == PIXMAN_b8g8r8a8 ||
+ format == PIXMAN_b8g8r8x8 ||
+ format == PIXMAN_r5g6b5 ||
+ format == PIXMAN_b5g6r5 ||
+ format == PIXMAN_a8))
+ {
+ return FALSE;
+ }
+
+ c = color_to_uint32 (color);
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
+ c = ((c & 0xff000000) >> 0) |
+ ((c & 0x00ff0000) >> 16) |
+ ((c & 0x0000ff00) >> 0) |
+ ((c & 0x000000ff) << 16);
+ }
+
+ if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
+ c = ((c & 0xff000000) >> 24) |
+ ((c & 0x00ff0000) >> 8) |
+ ((c & 0x0000ff00) << 8) |
+ ((c & 0x000000ff) << 24);
+ }
+
+ if (format == PIXMAN_a8) {
+ c = c >> 24;
+ } else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
+ c = ((((c) >> 3) & 0x001f) |
+ (((c) >> 5) & 0x07e0) |
+ (((c) >> 8) & 0xf800));
+ }
+
+ *pixel = c;
+ return TRUE;
+}
+
+static inline cairo_bool_t
+pattern_to_pixel (const cairo_solid_pattern_t *solid,
+ cairo_operator_t op,
+ pixman_format_code_t format,
+ uint32_t *pixel)
+{
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ *pixel = 0;
+ return TRUE;
+ }
+
+ if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID)
+ return FALSE;
+
+ if (op == CAIRO_OPERATOR_OVER) {
+ if (solid->color.alpha_short >= 0xff00)
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ if (op != CAIRO_OPERATOR_SOURCE)
+ return FALSE;
+
+ return color_to_pixel (&solid->color, format, pixel);
+}
+
+typedef struct _fill_span {
+ cairo_span_renderer_t base;
+
+ uint8_t *mask_data;
+ pixman_image_t *src, *dst, *mask;
+} fill_span_renderer_t;
+
+static cairo_status_t
+_fill_span (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ fill_span_renderer_t *renderer = abstract_renderer;
+ uint8_t *row;
+ unsigned i;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ row = renderer->mask_data - spans[0].x;
+ for (i = 0; i < num_spans - 1; i++) {
+ /* We implement setting the most common single pixel wide
+ * span case to avoid the overhead of a memset call.
+ * Open coding setting longer spans didn't show a
+ * noticeable improvement over memset.
+ */
+ if (spans[i+1].x == spans[i].x + 1) {
+ row[spans[i].x] = spans[i].coverage;
+ } else {
+ memset (row + spans[i].x,
+ spans[i].coverage,
+ spans[i+1].x - spans[i].x);
+ }
+ }
+
+ do {
+ pixman_image_composite32 (PIXMAN_OP_OVER,
+ renderer->src, renderer->mask, renderer->dst,
+ 0, 0, 0, 0,
+ spans[0].x, y++,
+ spans[i].x - spans[0].x, 1);
+ } while (--height);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* avoid using region code to re-validate boxes */
+static cairo_status_t
+_fill_unaligned_boxes (cairo_image_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ uint32_t pixel,
+ const cairo_boxes_t *boxes,
+ const cairo_composite_rectangles_t *extents)
+{
+ uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
+ fill_span_renderer_t renderer;
+ cairo_rectangular_scan_converter_t converter;
+ const struct _cairo_boxes_chunk *chunk;
+ cairo_status_t status;
+ int i;
+
+ /* XXX
+ * using composite for fill:
+ * spiral-box-nonalign-evenodd-fill.512 2201957 2.202
+ * spiral-box-nonalign-nonzero-fill.512 336726 0.337
+ * spiral-box-pixalign-evenodd-fill.512 352256 0.352
+ * spiral-box-pixalign-nonzero-fill.512 147056 0.147
+ * using fill:
+ * spiral-box-nonalign-evenodd-fill.512 3174565 3.175
+ * spiral-box-nonalign-nonzero-fill.512 182710 0.183
+ * spiral-box-pixalign-evenodd-fill.512 353863 0.354
+ * spiral-box-pixalign-nonzero-fill.512 147402 0.147
+ *
+ * cairo-perf-trace seems to favour using fill.
+ */
+
+ renderer.base.render_rows = _fill_span;
+ renderer.dst = dst->pixman_image;
+
+ if ((unsigned) extents->bounded.width <= sizeof (buf)) {
+ renderer.mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->bounded.width, 1,
+ (uint32_t *) buf,
+ sizeof (buf));
+ } else {
+ renderer.mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->bounded.width, 1,
+ NULL, 0);
+ }
+ if (unlikely (renderer.mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask);
+
+ renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
+ if (unlikely (renderer.src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_MASK;
+ }
+
+ _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+
+ /* first blit any aligned part of the boxes */
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_ceil (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_ceil (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_floor (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_floor (box[i].p2.y);
+
+ x1 = (x1 < 0 ? 0 : x1);
+ y1 = (y1 < 0 ? 0 : y1);
+ if (x2 > x1 && y2 > y1) {
+ cairo_box_t b;
+
+ pixman_fill ((uint32_t *) dst->data,
+ dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ pixel);
+
+ /* top */
+ if (! _cairo_fixed_is_integer (box[i].p1.y)) {
+ b.p1.x = box[i].p1.x;
+ b.p1.y = box[i].p1.y;
+ b.p2.x = box[i].p2.x;
+ b.p2.y = _cairo_fixed_from_int (y1);
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+
+ /* left */
+ if (! _cairo_fixed_is_integer (box[i].p1.x)) {
+ b.p1.x = box[i].p1.x;
+ b.p1.y = box[i].p1.y;
+ b.p2.x = _cairo_fixed_from_int (x1);
+ b.p2.y = box[i].p2.y;
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+
+ /* right */
+ if (! _cairo_fixed_is_integer (box[i].p2.x)) {
+ b.p1.x = _cairo_fixed_from_int (x2);
+ b.p1.y = box[i].p1.y;
+ b.p2.x = box[i].p2.x;
+ b.p2.y = box[i].p2.y;
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+
+ /* bottom */
+ if (! _cairo_fixed_is_integer (box[i].p2.y)) {
+ b.p1.x = box[i].p1.x;
+ b.p1.y = _cairo_fixed_from_int (y2);
+ b.p2.x = box[i].p2.x;
+ b.p2.y = box[i].p2.y;
+
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+ } else {
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+ }
+ }
+ }
+
+ status = converter.base.generate (&converter.base, &renderer.base);
+
+ CLEANUP_CONVERTER:
+ converter.base.destroy (&converter.base);
+ pixman_image_unref (renderer.src);
+ CLEANUP_MASK:
+ pixman_image_unref (renderer.mask);
+
+ return status;
+}
+
+typedef struct _cairo_image_surface_span_renderer {
+ cairo_span_renderer_t base;
+
+ uint8_t *mask_data;
+ uint32_t mask_stride;
+} cairo_image_surface_span_renderer_t;
+
+cairo_status_t
+_cairo_image_surface_span (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
+ uint8_t *row;
+ unsigned i;
+
+ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* XXX will it be quicker to repeat the sparse memset,
+ * or perform a simpler memcpy?
+ * The fairly dense spiral benchmarks suggests that the sparse
+ * memset is a win there as well.
+ */
+ row = renderer->mask_data + y * renderer->mask_stride;
+ do {
+ for (i = 0; i < num_spans - 1; i++) {
+ if (! spans[i].coverage)
+ continue;
+
+ /* We implement setting rendering the most common single
+ * pixel wide span case to avoid the overhead of a memset
+ * call. Open coding setting longer spans didn't show a
+ * noticeable improvement over memset. */
+ if (spans[i+1].x == spans[i].x + 1) {
+ row[spans[i].x] = spans[i].coverage;
+ } else {
+ memset (row + spans[i].x,
+ spans[i].coverage,
+ spans[i+1].x - spans[i].x);
+ }
+ }
+ row += renderer->mask_stride;
+ } while (--height);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_composite_unaligned_boxes (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_boxes_t *boxes,
+ const cairo_composite_rectangles_t *extents)
+{
+ uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
+ cairo_image_surface_span_renderer_t renderer;
+ cairo_rectangular_scan_converter_t converter;
+ pixman_image_t *mask, *src;
+ cairo_status_t status;
+ const struct _cairo_boxes_chunk *chunk;
+ int i, src_x, src_y;
+
+ i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height;
+ if ((unsigned) i <= sizeof (buf)) {
+ mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->bounded.width,
+ extents->bounded.height,
+ (uint32_t *) buf,
+ CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8));
+ memset (buf, 0, i);
+ } else {
+ mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->bounded.width,
+ extents->bounded.height,
+ NULL, 0);
+ }
+ if (unlikely (mask == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ renderer.base.render_rows = _cairo_image_surface_span;
+ renderer.mask_stride = pixman_image_get_stride (mask);
+ renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
+ renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x;
+
+ _cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+ }
+
+ status = converter.base.generate (&converter.base, &renderer.base);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ extents->bounded.x + src_x, extents->bounded.y + src_y,
+ 0, 0,
+ extents->bounded.x, extents->bounded.y,
+ extents->bounded.width, extents->bounded.height);
+ pixman_image_unref (src);
+
+ CLEANUP:
+ converter.base.destroy (&converter.base);
+ pixman_image_unref (mask);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_boxes (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ const cairo_composite_rectangles_t *extents)
+{
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t need_clip_mask = FALSE;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ uint32_t pixel;
+ int i;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+ need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ if (need_clip_mask &&
+ (op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+ clip_region = NULL;
+ }
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ if (! boxes->is_pixel_aligned) {
+ if (need_clip_mask)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op,
+ dst->pixman_format, &pixel))
+ {
+ return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents);
+ }
+ else
+ {
+ return _composite_unaligned_boxes (dst, op, pattern, boxes, extents);
+ }
+ }
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (! need_clip_mask &&
+ pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format,
+ &pixel))
+ {
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round_down (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_round_down (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_round_down (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_round_down (box[i].p2.y);
+
+ x1 = (x1 < 0 ? 0 : x1);
+ y1 = (y1 < 0 ? 0 : y1);
+ if (x2 <= x1 || y2 <= y1)
+ continue;
+
+ pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
+ PIXMAN_FORMAT_BPP (dst->pixman_format),
+ x1, y1, x2 - x1, y2 - y1,
+ pixel);
+ }
+ }
+ }
+ else
+ {
+ pixman_image_t *src = NULL, *mask = NULL;
+ int src_x, src_y, mask_x = 0, mask_y = 0;
+ pixman_op_t pixman_op = _pixman_operator (op);
+
+ if (need_clip_mask) {
+ cairo_surface_t *clip_surface;
+ int clip_x, clip_y;
+
+ clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ return clip_surface->status;
+
+ mask_x = -clip_x;
+ mask_y = -clip_y;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ pattern = NULL;
+ pixman_op = PIXMAN_OP_OUT_REVERSE;
+ }
+
+ mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
+ }
+
+ if (pattern != NULL) {
+ src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else {
+ src = mask;
+ src_x = mask_x;
+ src_y = mask_y;
+ mask = NULL;
+ }
+
+ for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
+ const cairo_box_t *box = chunk->base;
+
+ for (i = 0; i < chunk->count; i++) {
+ int x1 = _cairo_fixed_integer_round_down (box[i].p1.x);
+ int y1 = _cairo_fixed_integer_round_down (box[i].p1.y);
+ int x2 = _cairo_fixed_integer_round_down (box[i].p2.x);
+ int y2 = _cairo_fixed_integer_round_down (box[i].p2.y);
+
+ if (x2 == x1 || y2 == y1)
+ continue;
+
+ pixman_image_composite32 (pixman_op,
+ src, mask, dst->pixman_image,
+ x1 + src_x, y1 + src_y,
+ x1 + mask_x, y1 + mask_y,
+ x1, y1,
+ x2 - x1, y2 - y1);
+ }
+ }
+
+ if (pattern != NULL)
+ pixman_image_unref (src);
+
+ if (! extents->is_bounded) {
+ status =
+ _cairo_image_surface_fixup_unbounded_boxes (dst, extents,
+ clip_region, boxes);
+ }
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_boxes (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_boxes_t *boxes,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_traps_t traps;
+ cairo_status_t status;
+ composite_traps_info_t info;
+
+ if (boxes->num_boxes == 0 && extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Use a fast path if the boxes are pixel aligned */
+ status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ /* Otherwise render via a mask and composite in the usual fashion. */
+ status = _cairo_traps_init_boxes (&traps, boxes);
+ if (unlikely (status))
+ return status;
+
+ info.num_traps = traps.num_traps;
+ info.traps = traps.traps;
+ info.antialias = antialias;
+ status = _clip_and_composite (dst, op, src,
+ _composite_traps, &info,
+ extents, clip);
+
+ _cairo_traps_fini (&traps);
+ return status;
+}
+
+static cairo_bool_t
+_mono_edge_is_vertical (const cairo_line_t *line)
+{
+ return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
+}
+
+static cairo_bool_t
+_traps_are_pixel_aligned (cairo_traps_t *traps,
+ cairo_antialias_t antialias)
+{
+ int i;
+
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ if (! _mono_edge_is_vertical (&traps->traps[i].left) ||
+ ! _mono_edge_is_vertical (&traps->traps[i].right))
+ {
+ traps->maybe_region = FALSE;
+ return FALSE;
+ }
+ }
+ } else {
+ for (i = 0; i < traps->num_traps; i++) {
+ if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
+ traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
+ ! _cairo_fixed_is_integer (traps->traps[i].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_boxes_for_traps (cairo_boxes_t *boxes,
+ cairo_traps_t *traps,
+ cairo_antialias_t antialias)
+{
+ int i;
+
+ _cairo_boxes_init (boxes);
+
+ boxes->num_boxes = traps->num_traps;
+ boxes->chunks.base = (cairo_box_t *) traps->traps;
+ boxes->chunks.count = traps->num_traps;
+ boxes->chunks.size = traps->num_traps;
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ for (i = 0; i < traps->num_traps; i++) {
+ /* Note the traps and boxes alias so we need to take the local copies first. */
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ boxes->chunks.base[i].p1.x = x1;
+ boxes->chunks.base[i].p1.y = y1;
+ boxes->chunks.base[i].p2.x = x2;
+ boxes->chunks.base[i].p2.y = y2;
+
+ if (boxes->is_pixel_aligned) {
+ boxes->is_pixel_aligned =
+ _cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
+ _cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
+ }
+ }
+ } else {
+ boxes->is_pixel_aligned = TRUE;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ /* Note the traps and boxes alias so we need to take the local copies first. */
+ cairo_fixed_t x1 = traps->traps[i].left.p1.x;
+ cairo_fixed_t x2 = traps->traps[i].right.p1.x;
+ cairo_fixed_t y1 = traps->traps[i].top;
+ cairo_fixed_t y2 = traps->traps[i].bottom;
+
+ /* round down here to match Pixman's behavior when using traps. */
+ boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
+ boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
+ boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
+ boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
+ }
+ }
+}
+
+static cairo_status_t
+_clip_and_composite_trapezoids (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ composite_traps_info_t info;
+ cairo_bool_t need_clip_surface = FALSE;
+ cairo_status_t status;
+
+ if (traps->num_traps == 0 && extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (clip != NULL) {
+ cairo_region_t *clip_region;
+
+ status = _cairo_clip_get_region (clip, &clip_region);
+ need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (traps->has_intersections) {
+ if (traps->is_rectangular)
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
+ else if (traps->is_rectilinear)
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
+ else
+ status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Use a fast path if the trapezoids consist of a simple region,
+ * but we can only do this if we do not have a clip surface, or can
+ * substitute the mask with the clip.
+ */
+ if (traps->maybe_region && _traps_are_pixel_aligned (traps, antialias) &&
+ (! need_clip_surface ||
+ (extents->is_bounded && op != CAIRO_OPERATOR_SOURCE)))
+ {
+ cairo_boxes_t boxes;
+
+ _boxes_for_traps (&boxes, traps, antialias);
+ return _clip_and_composite_boxes (dst, op, src,
+ &boxes, antialias,
+ extents, clip);
+ }
+
+ /* No fast path, exclude self-intersections and clip trapezoids. */
+ /* Otherwise render the trapezoids to a mask and composite in the usual
+ * fashion.
+ */
+ info.traps = traps->traps;
+ info.num_traps = traps->num_traps;
+ info.antialias = antialias;
+ return _clip_and_composite (dst, op, src,
+ _composite_traps, &info,
+ extents, clip);
+}
+
+static cairo_clip_path_t *
+_clip_get_single_path (cairo_clip_t *clip)
+{
+ if (clip->path->prev == NULL)
+ return clip->path;
+
+ return NULL;
+}
+
+/* high level image interface */
+
+static cairo_int_status_t
+_cairo_image_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_clip_path_t *clip_path;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ &rect,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ /* If the clip cannot be reduced to a set of boxes, we will need to
+ * use a clipmask. Paint is special as it is the only operation that
+ * does not implicitly use a mask, so we may be able to reduce this
+ * operation to a fill...
+ */
+ if (clip != NULL &&
+ extents.is_bounded &&
+ (clip_path = _clip_get_single_path (clip)) != NULL)
+ {
+ status = _cairo_image_surface_fill (surface, op, source,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL);
+ }
+ else
+ {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, CAIRO_ANTIALIAS_DEFAULT,
+ &extents, clip);
+ }
+
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_mask (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ const cairo_pattern_t *mask_pattern = closure;
+ pixman_image_t *src, *mask = NULL;
+ int src_x = 0, src_y = 0;
+ int mask_x = 0, mask_y = 0;
+
+ if (src_pattern != NULL) {
+ src = _pixman_image_for_pattern (src_pattern, FALSE, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ mask = _pixman_image_for_pattern (mask_pattern, TRUE, extents, &mask_x, &mask_y);
+ if (unlikely (mask == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (mask_pattern->has_component_alpha)
+ pixman_image_set_component_alpha (mask, TRUE);
+ } else {
+ src = _pixman_image_for_pattern (mask_pattern, FALSE, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ extents->x + mask_x, extents->y + mask_y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+ if (mask != NULL)
+ pixman_image_unref (mask);
+ pixman_image_unref (src);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ &rect,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status)) {
+ _cairo_clip_fini (&local_clip);
+ return status;
+ }
+
+ have_clip = TRUE;
+ }
+
+ status = _clip_and_composite (surface, op, source,
+ _composite_mask, (void *) mask,
+ &extents, clip);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+typedef struct {
+ cairo_polygon_t *polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+} composite_spans_info_t;
+
+//#define USE_BOTOR_SCAN_CONVERTER
+static cairo_status_t
+_composite_spans (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE];
+ composite_spans_info_t *info = closure;
+ cairo_image_surface_span_renderer_t renderer;
+#if USE_BOTOR_SCAN_CONVERTER
+ cairo_box_t box;
+ cairo_botor_scan_converter_t converter;
+#else
+ cairo_scan_converter_t *converter;
+#endif
+ pixman_image_t *mask;
+ cairo_status_t status;
+
+#if USE_BOTOR_SCAN_CONVERTER
+ box.p1.x = _cairo_fixed_from_int (extents->x);
+ box.p1.y = _cairo_fixed_from_int (extents->y);
+ box.p2.x = _cairo_fixed_from_int (extents->x + extents->width);
+ box.p2.y = _cairo_fixed_from_int (extents->y + extents->height);
+ _cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
+ status = converter.base.add_polygon (&converter.base, info->polygon);
+#else
+ converter = _cairo_tor_scan_converter_create (extents->x, extents->y,
+ extents->x + extents->width,
+ extents->y + extents->height,
+ info->fill_rule);
+ status = converter->add_polygon (converter, info->polygon);
+#endif
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+
+ /* TODO: support rendering to A1 surfaces (or: go add span
+ * compositing to pixman.) */
+
+ if (pattern == NULL &&
+ dst_format == PIXMAN_a8 &&
+ op == CAIRO_OPERATOR_SOURCE)
+ {
+ mask = dst;
+ dst = NULL;
+ }
+ else
+ {
+ int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, 8);
+ uint8_t *data = mask_buf;
+
+ if (extents->height * stride <= (int) sizeof (mask_buf))
+ memset (data, 0, extents->height * stride);
+ else
+ data = NULL, stride = 0;
+
+ mask = pixman_image_create_bits (PIXMAN_a8,
+ extents->width,
+ extents->height,
+ (uint32_t *) data,
+ stride);
+ if (unlikely (mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_CONVERTER;
+ }
+ }
+
+ renderer.base.render_rows = _cairo_image_surface_span;
+ renderer.mask_stride = pixman_image_get_stride (mask);
+ renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
+ if (dst != NULL)
+ renderer.mask_data -= extents->y * renderer.mask_stride + extents->x;
+ else
+ renderer.mask_data -= dst_y * renderer.mask_stride + dst_x;
+
+#if USE_BOTOR_SCAN_CONVERTER
+ status = converter.base.generate (&converter.base, &renderer.base);
+#else
+ status = converter->generate (converter, &renderer.base);
+#endif
+ if (unlikely (status))
+ goto CLEANUP_RENDERER;
+
+ if (dst != NULL) {
+ pixman_image_t *src;
+ int src_x, src_y;
+
+ src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_RENDERER;
+ }
+
+ pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0, /* mask.x, mask.y */
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+ pixman_image_unref (src);
+ }
+
+ CLEANUP_RENDERER:
+ if (dst != NULL)
+ pixman_image_unref (mask);
+ CLEANUP_CONVERTER:
+#if USE_BOTOR_SCAN_CONVERTER
+ converter.base.destroy (&converter.base);
+#else
+ converter->destroy (converter);
+#endif
+ return status;
+}
+
+static cairo_status_t
+_clip_and_composite_polygon (cairo_image_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_polygon_t *polygon,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ cairo_composite_rectangles_t *extents,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+
+ if (polygon->num_edges == 0) {
+ cairo_traps_t traps;
+
+ if (extents->is_bounded)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_traps_init (&traps);
+ status = _clip_and_composite_trapezoids (dst, op, src,
+ &traps, antialias,
+ extents, clip);
+ _cairo_traps_fini (&traps);
+
+ return status;
+ }
+
+ if (_cairo_operator_bounded_by_mask(op)) {
+ _cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
+ if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ composite_spans_info_t info;
+
+ info.polygon = polygon;
+ info.fill_rule = fill_rule;
+ info.antialias = antialias;
+
+ status = _clip_and_composite (dst, op, src,
+ _composite_spans, &info,
+ extents, clip);
+ } else {
+ cairo_traps_t traps;
+
+ _cairo_traps_init (&traps);
+
+ /* Fall back to trapezoid fills. */
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ polygon,
+ fill_rule);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_trapezoids (dst, op, src,
+ &traps, antialias,
+ extents, clip);
+ }
+
+ _cairo_traps_fini (&traps);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &rect,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (path->is_rectilinear) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
+ style,
+ ctm,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, antialias,
+ &extents, clip);
+ }
+
+ _cairo_boxes_fini (&boxes);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ cairo_polygon_t polygon;
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_polygon (surface, op, source, &polygon,
+ CAIRO_FILL_RULE_WINDING, antialias,
+ &extents, clip);
+ }
+
+ _cairo_polygon_fini (&polygon);
+ }
+
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ &rect,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (extents.is_bounded && clip != NULL) {
+ cairo_clip_path_t *clip_path;
+
+ if (((clip_path = _clip_get_single_path (clip)) != NULL) &&
+ _cairo_path_fixed_equal (&clip_path->path, path))
+ {
+ clip = NULL;
+ }
+ }
+
+ if (clip != NULL) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ have_clip = TRUE;
+ }
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status)) {
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+ }
+
+ if (_cairo_path_fixed_is_rectilinear_fill (path)) {
+ cairo_boxes_t boxes;
+
+ _cairo_boxes_init (&boxes);
+ _cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
+ fill_rule,
+ &boxes);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_boxes (surface, op, source,
+ &boxes, antialias,
+ &extents, clip);
+ }
+
+ _cairo_boxes_fini (&boxes);
+ } else {
+ cairo_polygon_t polygon;
+
+ assert (! path->is_empty_fill);
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _clip_and_composite_polygon (surface, op, source, &polygon,
+ fill_rule, antialias,
+ &extents, clip);
+ }
+
+ _cairo_polygon_fini (&polygon);
+ }
+
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ return status;
+}
+
+typedef struct {
+ cairo_scaled_font_t *font;
+ cairo_glyph_t *glyphs;
+ int num_glyphs;
+} composite_glyphs_info_t;
+
+static cairo_status_t
+_composite_glyphs_via_mask (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ composite_glyphs_info_t *info = closure;
+ cairo_scaled_font_t *font = info->font;
+ cairo_glyph_t *glyphs = info->glyphs;
+ int num_glyphs = info->num_glyphs;
+ pixman_image_t *mask = NULL;
+ pixman_image_t *src;
+ pixman_image_t *white;
+ pixman_format_code_t mask_format = 0; /* silence gcc */
+ cairo_status_t status;
+ int src_x, src_y;
+ int i;
+
+ src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ white = _pixman_white_image ();
+ if (unlikely (white == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_scaled_font_freeze_cache (font);
+
+ for (i = 0; i < num_glyphs; i++) {
+ int x, y;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ status = _cairo_scaled_glyph_lookup (font, glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ goto CLEANUP;
+
+ glyph_surface = scaled_glyph->surface;
+
+ if (glyph_surface->width == 0 || glyph_surface->height == 0)
+ continue;
+
+ /* To start, create the mask using the format from the first
+ * glyph. Later we'll deal with different formats. */
+ if (mask == NULL) {
+ mask_format = glyph_surface->pixman_format;
+ mask = pixman_image_create_bits (mask_format,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ if (PIXMAN_FORMAT_RGB (mask_format))
+ pixman_image_set_component_alpha (mask, TRUE);
+ }
+
+ /* If we have glyphs of different formats, we "upgrade" the mask
+ * to the wider of the formats. */
+ if (glyph_surface->pixman_format != mask_format &&
+ PIXMAN_FORMAT_BPP (mask_format) <
+ PIXMAN_FORMAT_BPP (glyph_surface->pixman_format))
+ {
+ pixman_image_t *new_mask;
+
+ mask_format = glyph_surface->pixman_format;
+ new_mask = pixman_image_create_bits (mask_format,
+ extents->width, extents->height,
+ NULL, 0);
+ if (unlikely (new_mask == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP;
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ white, mask, new_mask,
+ 0, 0, 0, 0, 0, 0,
+ extents->width, extents->height);
+
+ pixman_image_unref (mask);
+ mask = new_mask;
+ if (PIXMAN_FORMAT_RGB (mask_format))
+ pixman_image_set_component_alpha (mask, TRUE);
+ }
+
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+ if (glyph_surface->pixman_format == mask_format) {
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ glyph_surface->pixman_image, NULL, mask,
+ 0, 0, 0, 0,
+ x - extents->x, y - extents->y,
+ glyph_surface->width,
+ glyph_surface->height);
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_ADD,
+ white, glyph_surface->pixman_image, mask,
+ 0, 0, 0, 0,
+ x - extents->x, y - extents->y,
+ glyph_surface->width,
+ glyph_surface->height);
+ }
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+
+CLEANUP:
+ _cairo_scaled_font_thaw_cache (font);
+ if (mask != NULL)
+ pixman_image_unref (mask);
+ pixman_image_unref (src);
+ pixman_image_unref (white);
+
+ return status;
+}
+
+static cairo_status_t
+_composite_glyphs (void *closure,
+ pixman_image_t *dst,
+ pixman_format_code_t dst_format,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ composite_glyphs_info_t *info = closure;
+ cairo_scaled_glyph_t *glyph_cache[64];
+ pixman_op_t pixman_op = _pixman_operator (op);
+ pixman_image_t *src = NULL;
+ int src_x = 0, src_y = 0;
+ cairo_status_t status;
+ int i;
+
+ memset (glyph_cache, 0, sizeof (glyph_cache));
+ status = CAIRO_STATUS_SUCCESS;
+
+ _cairo_scaled_font_freeze_cache (info->font);
+ for (i = 0; i < info->num_glyphs; i++) {
+ int x, y;
+ cairo_image_surface_t *glyph_surface;
+ cairo_scaled_glyph_t *scaled_glyph;
+ unsigned long glyph_index = info->glyphs[i].index;
+ int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
+
+ scaled_glyph = glyph_cache[cache_index];
+ if (scaled_glyph == NULL ||
+ _cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
+ {
+ status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+
+ if (unlikely (status))
+ break;
+
+ glyph_cache[cache_index] = scaled_glyph;
+ }
+
+ glyph_surface = scaled_glyph->surface;
+ if (glyph_surface->width && glyph_surface->height) {
+ int x1, y1, x2, y2;
+
+ /* round glyph locations to the nearest pixel */
+ /* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
+ x = _cairo_lround (info->glyphs[i].x -
+ glyph_surface->base.device_transform.x0);
+ y = _cairo_lround (info->glyphs[i].y -
+ glyph_surface->base.device_transform.y0);
+
+ x1 = x;
+ if (x1 < extents->x)
+ x1 = extents->x;
+ x2 = x + glyph_surface->width;
+ if (x2 > extents->x + extents->width)
+ x2 = extents->x + extents->width;
+
+ y1 = y;
+ if (y1 < extents->y)
+ y1 = extents->y;
+ y2 = y + glyph_surface->height;
+ if (y2 > extents->y + extents->height)
+ y2 = extents->y + extents->height;
+
+ if (glyph_surface->format == CAIRO_FORMAT_A8 ||
+ glyph_surface->format == CAIRO_FORMAT_A1 ||
+ (glyph_surface->format == CAIRO_FORMAT_ARGB32 &&
+ pixman_image_get_component_alpha (glyph_surface->pixman_image)))
+ {
+ if (unlikely (src == NULL)) {
+ if (pattern != NULL) {
+ src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
+ src_x -= dst_x;
+ src_y -= dst_y;
+ } else {
+ src = _pixman_white_image ();
+ }
+ if (unlikely (src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ break;
+ }
+ }
+
+ pixman_image_composite32 (pixman_op,
+ src, glyph_surface->pixman_image, dst,
+ x1 + src_x, y1 + src_y,
+ x1 - x, y1 - y,
+ x1 - dst_x, y1 - dst_y,
+ x2 - x1, y2 - y1);
+ } else {
+ pixman_image_composite32 (pixman_op,
+ glyph_surface->pixman_image, NULL, dst,
+ x1 - x, y1 - y,
+ 0, 0,
+ x1 - dst_x, y1 - dst_y,
+ x2 - x1, y2 - y1);
+ }
+ }
+ }
+ _cairo_scaled_font_thaw_cache (info->font);
+
+ if (src != NULL)
+ pixman_image_unref (src);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *num_remaining)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ composite_glyphs_info_t glyph_info;
+ cairo_clip_t local_clip;
+ cairo_bool_t have_clip = FALSE;
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ // For performance reasons we don't want to use two passes for overlapping glyphs
+ // on mobile
+ cairo_bool_t overlap = FALSE;
+#else
+ cairo_bool_t overlap;
+#endif
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ &rect,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ NULL);
+#else
+ &overlap);
+#endif
+
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_rectangle (clip, &extents.mask))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ clip = _cairo_clip_init_copy (&local_clip, clip);
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status))
+ return status;
+
+ have_clip = TRUE;
+ }
+
+ glyph_info.font = scaled_font;
+ glyph_info.glyphs = glyphs;
+ glyph_info.num_glyphs = num_glyphs;
+
+ status = _clip_and_composite (surface, op, source,
+ overlap || extents.is_bounded == 0 ? _composite_glyphs_via_mask : _composite_glyphs,
+ &glyph_info,
+ &extents, clip);
+
+ if (have_clip)
+ _cairo_clip_fini (&local_clip);
+
+ *num_remaining = 0;
+ return status;
+}
+
+static cairo_bool_t
+_cairo_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;
+}
+
+static 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);
+}
+
+/* legacy interface kept for compatibility until surface-fallback is removed */
+static cairo_status_t
+_cairo_image_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ image_rect_out->x = 0;
+ image_rect_out->y = 0;
+ image_rect_out->width = surface->width;
+ image_rect_out->height = surface->height;
+
+ *image_out = surface;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+}
+
+static cairo_status_t
+_cairo_image_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ if (src->backend == surface->base.backend) {
+ *clone_offset_x = *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ const cairo_pattern_t *mask_pattern,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_image_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ pixman_image_t *src;
+ int src_offset_x, src_offset_y;
+ cairo_status_t status;
+
+ if (clip_region != NULL) {
+ status = _cairo_image_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
+ }
+
+ extents.source.x = src_x;
+ extents.source.y = src_y;
+ extents.source.width = width;
+ extents.source.height = height;
+
+ extents.mask.x = mask_x;
+ extents.mask.y = mask_y;
+ extents.mask.width = width;
+ extents.mask.height = height;
+
+ extents.bounded.x = dst_x;
+ extents.bounded.y = dst_y;
+ extents.bounded.width = width;
+ extents.bounded.height = height;
+
+ extents.unbounded.x = 0;
+ extents.unbounded.y = 0;
+ extents.unbounded.width = dst->width;
+ extents.unbounded.height = dst->height;
+
+ if (clip_region != NULL) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_extents (clip_region, &rect);
+ if (! _cairo_rectangle_intersect (&extents.unbounded, &rect))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ extents.is_bounded = _cairo_operator_bounded_by_either (op);
+
+ src = _pixman_image_for_pattern (src_pattern, FALSE, &extents.source, &src_offset_x, &src_offset_y);
+ if (unlikely (src == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (mask_pattern != NULL) {
+ pixman_image_t *mask;
+ int mask_offset_x, mask_offset_y;
+
+ mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents.mask, &mask_offset_x, &mask_offset_y);
+ if (unlikely (mask == NULL)) {
+ pixman_image_unref (src);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ pixman_image_composite32 (_pixman_operator (op),
+ src, mask, dst->pixman_image,
+ src_x + src_offset_x,
+ src_y + src_offset_y,
+ mask_x + mask_offset_x,
+ mask_y + mask_offset_y,
+ dst_x, dst_y, width, height);
+
+ pixman_image_unref (mask);
+ } else {
+ pixman_image_composite32 (_pixman_operator (op),
+ src, NULL, dst->pixman_image,
+ src_x + src_offset_x,
+ src_y + src_offset_y,
+ 0, 0,
+ dst_x, dst_y, width, height);
+ }
+
+ pixman_image_unref (src);
+
+ if (! extents.is_bounded)
+ status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL);
+
+ if (clip_region != NULL)
+ _cairo_image_surface_unset_clip_region (dst);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_image_surface_t *surface = abstract_surface;
+
+ pixman_color_t pixman_color;
+ pixman_box32_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)];
+ pixman_box32_t *pixman_boxes = stack_boxes;
+ int i;
+
+ cairo_int_status_t status;
+
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ pixman_color.red = color->red_short;
+ pixman_color.green = color->green_short;
+ pixman_color.blue = color->blue_short;
+ pixman_color.alpha = color->alpha_short;
+
+ if (num_rects > ARRAY_LENGTH (stack_boxes)) {
+ pixman_boxes = _cairo_malloc_ab (num_rects, sizeof (pixman_box32_t));
+ if (unlikely (pixman_boxes == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < num_rects; i++) {
+ pixman_boxes[i].x1 = rects[i].x;
+ pixman_boxes[i].y1 = rects[i].y;
+ pixman_boxes[i].x2 = rects[i].x + rects[i].width;
+ pixman_boxes[i].y2 = rects[i].y + rects[i].height;
+ }
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (! pixman_image_fill_boxes (_pixman_operator (op),
+ surface->pixman_image,
+ &pixman_color,
+ num_rects,
+ pixman_boxes))
+ {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ if (pixman_boxes != stack_boxes)
+ free (pixman_boxes);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_image_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region)
+{
+ cairo_image_surface_t *dst = abstract_dst;
+ cairo_composite_rectangles_t extents;
+ cairo_pattern_union_t source_pattern;
+ composite_traps_info_t info;
+ cairo_status_t status;
+
+ if (height == 0 || width == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ extents.source.x = src_x;
+ extents.source.y = src_y;
+ extents.source.width = width;
+ extents.source.height = height;
+
+ extents.mask.x = dst_x;
+ extents.mask.y = dst_y;
+ extents.mask.width = width;
+ extents.mask.height = height;
+
+ extents.bounded.x = dst_x;
+ extents.bounded.y = dst_y;
+ extents.bounded.width = width;
+ extents.bounded.height = height;
+
+ extents.unbounded.x = 0;
+ extents.unbounded.y = 0;
+ extents.unbounded.width = dst->width;
+ extents.unbounded.height = dst->height;
+
+ if (clip_region != NULL) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_extents (clip_region, &rect);
+ if (! _cairo_rectangle_intersect (&extents.unbounded, &rect))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ extents.is_bounded = _cairo_operator_bounded_by_either (op);
+
+ if (clip_region != NULL) {
+ status = _cairo_image_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ return status;
+ }
+
+ _cairo_pattern_init_static_copy (&source_pattern.base, pattern);
+ cairo_matrix_translate (&source_pattern.base.matrix,
+ src_x - extents.bounded.x,
+ src_y - extents.bounded.y);
+
+ info.traps = traps;
+ info.num_traps = num_traps;
+ info.antialias = antialias;
+ status = _composite_traps (&info,
+ dst->pixman_image,
+ dst->pixman_format,
+ op,
+ &source_pattern.base,
+ 0, 0,
+ &extents.bounded,
+ clip_region);
+
+ if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded)
+ status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL);
+
+ if (clip_region != NULL)
+ _cairo_image_surface_unset_clip_region (dst);
+
+ return status;
+}
+
+typedef struct _legacy_image_surface_span_renderer {
+ cairo_span_renderer_t base;
+
+ cairo_operator_t op;
+ const cairo_pattern_t *pattern;
+ cairo_antialias_t antialias;
+ cairo_region_t *clip_region;
+
+ pixman_image_t *mask;
+ uint8_t *mask_data;
+ uint32_t mask_stride;
+
+ cairo_image_surface_t *dst;
+ cairo_composite_rectangles_t composite_rectangles;
+} legacy_image_surface_span_renderer_t;
+
+void
+_cairo_image_surface_span_render_row (
+ int y,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans,
+ uint8_t *data,
+ uint32_t stride)
+{
+ uint8_t *row;
+ unsigned i;
+
+ if (num_spans == 0)
+ return;
+
+ row = data + y * stride;
+ for (i = 0; i < num_spans - 1; i++) {
+ if (! spans[i].coverage)
+ continue;
+
+ /* We implement setting the most common single pixel wide
+ * span case to avoid the overhead of a memset call.
+ * Open coding setting longer spans didn't show a
+ * noticeable improvement over memset.
+ */
+ if (spans[i+1].x == spans[i].x + 1) {
+ row[spans[i].x] = spans[i].coverage;
+ } else {
+ memset (row + spans[i].x,
+ spans[i].coverage,
+ spans[i+1].x - spans[i].x);
+ }
+ }
+}
+
+static cairo_status_t
+_cairo_image_surface_span_renderer_render_rows (
+ void *abstract_renderer,
+ int y,
+ int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
+ while (height--)
+ _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_image_surface_span_renderer_destroy (void *abstract_renderer)
+{
+ legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
+ if (renderer == NULL)
+ return;
+
+ pixman_image_unref (renderer->mask);
+
+ free (renderer);
+}
+
+static cairo_status_t
+_cairo_image_surface_span_renderer_finish (void *abstract_renderer)
+{
+ legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
+ cairo_composite_rectangles_t *rects = &renderer->composite_rectangles;
+ cairo_image_surface_t *dst = renderer->dst;
+ pixman_image_t *src;
+ int src_x, src_y;
+ cairo_status_t status;
+
+ if (renderer->clip_region != NULL) {
+ status = _cairo_image_surface_set_clip_region (dst, renderer->clip_region);
+ if (unlikely (status))
+ return status;
+ }
+
+ src = _pixman_image_for_pattern (renderer->pattern, FALSE, &rects->bounded, &src_x, &src_y);
+ if (src == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = CAIRO_STATUS_SUCCESS;
+ pixman_image_composite32 (_pixman_operator (renderer->op),
+ src,
+ renderer->mask,
+ dst->pixman_image,
+ rects->bounded.x + src_x,
+ rects->bounded.y + src_y,
+ 0, 0,
+ rects->bounded.x, rects->bounded.y,
+ rects->bounded.width, rects->bounded.height);
+
+ if (! rects->is_bounded)
+ status = _cairo_image_surface_fixup_unbounded (dst, rects, NULL);
+
+ if (renderer->clip_region != NULL)
+ _cairo_image_surface_unset_clip_region (dst);
+
+ return status;
+}
+
+static cairo_bool_t
+_cairo_image_surface_check_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias)
+{
+ return TRUE;
+ (void) op;
+ (void) pattern;
+ (void) abstract_dst;
+ (void) antialias;
+}
+
+static cairo_span_renderer_t *
+_cairo_image_surface_create_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects,
+ cairo_region_t *clip_region)
+{
+ cairo_image_surface_t *dst = abstract_dst;
+ legacy_image_surface_span_renderer_t *renderer;
+
+ renderer = calloc(1, sizeof(*renderer));
+ if (unlikely (renderer == NULL))
+ return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
+
+ renderer->base.destroy = _cairo_image_surface_span_renderer_destroy;
+ renderer->base.finish = _cairo_image_surface_span_renderer_finish;
+ renderer->base.render_rows = _cairo_image_surface_span_renderer_render_rows;
+ renderer->op = op;
+ renderer->pattern = pattern;
+ renderer->antialias = antialias;
+ renderer->dst = dst;
+ renderer->clip_region = clip_region;
+
+ renderer->composite_rectangles = *rects;
+
+ /* TODO: support rendering to A1 surfaces (or: go add span
+ * compositing to pixman.) */
+ renderer->mask = pixman_image_create_bits (PIXMAN_a8,
+ rects->bounded.width,
+ rects->bounded.height,
+ NULL, 0);
+ if (renderer->mask == NULL) {
+ free (renderer);
+ return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ renderer->mask_stride = pixman_image_get_stride (renderer->mask);
+ renderer->mask_data = (uint8_t *) pixman_image_get_data (renderer->mask) - rects->bounded.x - rects->bounded.y * renderer->mask_stride;
+
+ return &renderer->base;
+}
+
+/**
+ * _cairo_surface_is_image:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is an #cairo_image_surface_t
+ *
+ * Return value: %TRUE if the surface is an image surface
+ **/
+cairo_bool_t
+_cairo_surface_is_image (const cairo_surface_t *surface)
+{
+ return surface->backend == &_cairo_image_surface_backend;
+}
+
+const cairo_surface_backend_t _cairo_image_surface_backend = {
+ CAIRO_SURFACE_TYPE_IMAGE,
+ _cairo_image_surface_create_similar,
+ _cairo_image_surface_finish,
+ _cairo_image_surface_acquire_source_image,
+ _cairo_image_surface_release_source_image,
+ _cairo_image_surface_acquire_dest_image,
+ _cairo_image_surface_release_dest_image,
+ _cairo_image_surface_clone_similar,
+ _cairo_image_surface_composite,
+ _cairo_image_surface_fill_rectangles,
+ _cairo_image_surface_composite_trapezoids,
+ _cairo_image_surface_create_span_renderer,
+ _cairo_image_surface_check_span_renderer,
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_image_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_image_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark dirty */
+ NULL, /* font_fini */
+ NULL, /* glyph_fini */
+
+ _cairo_image_surface_paint,
+ _cairo_image_surface_mask,
+ _cairo_image_surface_stroke,
+ _cairo_image_surface_fill,
+ _cairo_image_surface_glyphs,
+ NULL, /* show_text_glyphs */
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+};
+
+/* 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_transparency_t
+_cairo_image_analyze_transparency (cairo_image_surface_t *image)
+{
+ int x, y;
+
+ if (image->transparency != CAIRO_IMAGE_UNKNOWN)
+ return image->transparency;
+
+ if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0)
+ return image->transparency = CAIRO_IMAGE_IS_OPAQUE;
+
+ if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) {
+ if (image->format == CAIRO_FORMAT_A1)
+ return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+ else
+ return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
+ }
+
+ if (image->format == CAIRO_FORMAT_RGB16_565) {
+ image->transparency = CAIRO_IMAGE_IS_OPAQUE;
+ return CAIRO_IMAGE_IS_OPAQUE;
+ }
+
+ if (image->format != CAIRO_FORMAT_ARGB32)
+ return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
+
+ image->transparency = CAIRO_IMAGE_IS_OPAQUE;
+ for (y = 0; y < image->height; y++) {
+ uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
+
+ for (x = 0; x < image->width; x++, pixel++) {
+ int a = (*pixel & 0xff000000) >> 24;
+ if (a > 0 && a < 255) {
+ return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
+ } else if (a == 0) {
+ image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
+ }
+ }
+ }
+
+ return image->transparency;
+}
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 000000000..ddfd0a4c6
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-list-private.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 <chris@chris-wilson.co.uk>
+ *
+ */
+
+#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;
+
+#define cairo_list_entry(ptr, type, member) \
+ cairo_container_of(ptr, type, member)
+
+#define cairo_list_first_entry(ptr, type, member) \
+ cairo_list_entry((ptr)->next, type, member)
+
+#define cairo_list_last_entry(ptr, type, member) \
+ cairo_list_entry((ptr)->prev, type, member)
+
+#define cairo_list_foreach(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define cairo_list_foreach_entry(pos, type, head, member) \
+ for (pos = cairo_list_entry((head)->next, type, member);\
+ &pos->member != (head); \
+ pos = cairo_list_entry(pos->member.next, type, member))
+
+#define cairo_list_foreach_entry_safe(pos, n, type, head, member) \
+ for (pos = cairo_list_entry ((head)->next, type, member),\
+ n = cairo_list_entry (pos->member.next, type, member);\
+ &pos->member != (head); \
+ pos = n, n = cairo_list_entry (n->member.next, type, member))
+
+#define cairo_list_foreach_entry_reverse(pos, type, head, member) \
+ for (pos = cairo_list_entry((head)->prev, type, member);\
+ &pos->member != (head); \
+ pos = cairo_list_entry(pos->member.prev, type, member))
+
+#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \
+ for (pos = cairo_list_entry((head)->prev, type, member),\
+ n = cairo_list_entry (pos->member.prev, type, member);\
+ &pos->member != (head); \
+ pos = n, n = cairo_list_entry (n->member.prev, type, member))
+
+#ifdef CAIRO_LIST_DEBUG
+static inline void
+_cairo_list_validate (const cairo_list_t *link)
+{
+ assert (link->next->prev == link);
+ assert (link->prev->next == link);
+}
+static inline void
+cairo_list_validate (const cairo_list_t *head)
+{
+ cairo_list_t *link;
+
+ cairo_list_foreach (link, head)
+ _cairo_list_validate (link);
+}
+static inline cairo_bool_t
+cairo_list_is_empty (const cairo_list_t *head);
+static inline void
+cairo_list_validate_is_empty (const cairo_list_t *head)
+{
+ assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev));
+}
+#else
+#define _cairo_list_validate(link)
+#define cairo_list_validate(head)
+#define cairo_list_validate_is_empty(head)
+#endif
+
+static inline void
+cairo_list_init (cairo_list_t *entry)
+{
+ entry->next = entry;
+ entry->prev = entry;
+}
+
+static inline void
+__cairo_list_add (cairo_list_t *entry,
+ cairo_list_t *prev,
+ cairo_list_t *next)
+{
+ next->prev = entry;
+ entry->next = next;
+ entry->prev = prev;
+ prev->next = entry;
+}
+
+static inline void
+cairo_list_add (cairo_list_t *entry, cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ cairo_list_validate_is_empty (entry);
+ __cairo_list_add (entry, head, head->next);
+ cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ cairo_list_validate_is_empty (entry);
+ __cairo_list_add (entry, head->prev, head);
+ cairo_list_validate (head);
+}
+
+static inline void
+__cairo_list_del (cairo_list_t *prev, cairo_list_t *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static inline void
+cairo_list_del (cairo_list_t *entry)
+{
+ __cairo_list_del (entry->prev, entry->next);
+ cairo_list_init (entry);
+}
+
+static inline void
+cairo_list_move (cairo_list_t *entry, cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ __cairo_list_del (entry->prev, entry->next);
+ __cairo_list_add (entry, head, head->next);
+ cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ __cairo_list_del (entry->prev, entry->next);
+ __cairo_list_add (entry, head->prev, head);
+ cairo_list_validate (head);
+}
+
+static inline void
+cairo_list_swap (cairo_list_t *entry, cairo_list_t *other)
+{
+ __cairo_list_add (entry, other->prev, other->next);
+ cairo_list_init (other);
+}
+
+static inline cairo_bool_t
+cairo_list_is_first (const cairo_list_t *entry,
+ const cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ return entry->prev == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_last (const cairo_list_t *entry,
+ const cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ return entry->next == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_empty (const cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ return head->next == head;
+}
+
+static inline cairo_bool_t
+cairo_list_is_singular (const cairo_list_t *head)
+{
+ cairo_list_validate (head);
+ return head->next == head || head->next == head->prev;
+}
+
+#endif /* CAIRO_LIST_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-lzw.c b/gfx/cairo/cairo/src/cairo-lzw.c
new file mode 100644
index 000000000..de7f99983
--- /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 <cworth@cworth.org>
+ */
+
+#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 = 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<<LZW_BITS_MAX)
+
+#define LZW_SYMBOL_TABLE_SIZE 9013
+#define LZW_SYMBOL_MOD1 LZW_SYMBOL_TABLE_SIZE
+#define LZW_SYMBOL_MOD2 9011
+
+typedef struct _lzw_symbol_table {
+ lzw_symbol_t table[LZW_SYMBOL_TABLE_SIZE];
+} lzw_symbol_table_t;
+
+/* Initialize the hash table to entirely empty */
+static void
+_lzw_symbol_table_init (lzw_symbol_table_t *table)
+{
+ memset (table->table, 0, LZW_SYMBOL_TABLE_SIZE * sizeof (lzw_symbol_t));
+}
+
+/* Lookup a symbol in the symbol table. The PREV and NEXT fields of
+ * symbol form the key for the lookup.
+ *
+ * If 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 000000000..e5776abd0
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-malloc-private.h
@@ -0,0 +1,148 @@
+/* -*- 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 <vladimir@pobox.com>
+ */
+
+#ifndef CAIRO_MALLOC_PRIVATE_H
+#define CAIRO_MALLOC_PRIVATE_H
+
+#include "cairo-wideint-private.h"
+
+#if HAVE_MEMFAULT
+#include <memfault.h>
+#define CAIRO_INJECT_FAULT() MEMFAULT_INJECT_FAULT()
+#else
+#define CAIRO_INJECT_FAULT() 0
+#endif
+
+/**
+ * _cairo_malloc:
+ * @size: size in bytes
+ *
+ * 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) ? malloc((unsigned) (size)) : NULL)
+
+/**
+ * _cairo_malloc_ab:
+ * @n: number of elements to allocate
+ * @size: size of each element
+ *
+ * Allocates @n*@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
+ * @n: number of elements to allocate
+ * @size: size of each element
+ *
+ * Reallocates @ptr a block of @n*@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:
+ * @n: first factor of number of elements to allocate
+ * @b: second factor of number of elements to allocate
+ * @size: size of each element
+ *
+ * Allocates @n*@b*@size memory using _cairo_malloc(), taking care to not
+ * 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:
+ * @n: number of elements to allocate
+ * @size: size of each element
+ * @k: additional size to allocate
+ *
+ * Allocates @n*@ksize+@k memory using _cairo_malloc(), taking care to not
+ * overflow when doing the arithmetic. Behaves like
+ * _cairo_malloc_ab(). The memory should be freed using free().
+ *
+ * Return value: A pointer to the newly allocated memory, or %NULL in
+ * case of malloc() failure or overflow.
+ */
+
+#define _cairo_malloc_ab_plus_c(n, size, k) \
+ ((size) && (unsigned) (n) >= INT32_MAX / (unsigned) (size) ? NULL : \
+ (unsigned) (k) >= INT32_MAX - (unsigned) (n) * (unsigned) (size) ? NULL : \
+ _cairo_malloc((unsigned) (n) * (unsigned) (size) + (unsigned) (k)))
+
+#endif /* CAIRO_MALLOC_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-matrix.c b/gfx/cairo/cairo/src/cairo-matrix.c
new file mode 100644
index 000000000..583a7a649
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-matrix.c
@@ -0,0 +1,1006 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
+#define ISFINITE(x) isfinite (x)
+#else
+#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */
+#endif
+
+/**
+ * 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 (<literal>x</literal>,<literal>y</literal>)
+ * is given by:
+ *
+ * <programlisting>
+ * x_new = xx * x + xy * y + x0;
+ * y_new = yx * x + yy * y + y0;
+ * </programlisting>
+ *
+ * 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.
+ **/
+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:
+ * <programlisting>
+ * x_new = xx * x + xy * y + x0;
+ * y_new = yx * x + yy * y + y0;
+ * </programlisting>
+ **/
+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.
+ **/
+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.
+ **/
+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.
+ **/
+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.
+ **/
+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.
+ **/
+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.
+ **/
+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.
+ **/
+/*
+ * 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);
+
+/**
+ * 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:
+ *
+ * <programlisting>
+ * dx2 = dx1 * a + dy1 * c;
+ * dy2 = dx1 * b + dy1 * d;
+ * </programlisting>
+ *
+ * Affine transformations are position invariant, so the same vector
+ * always transforms to the same vector. If (@x1,@y1) transforms
+ * to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to
+ * (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2.
+ **/
+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.
+ **/
+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 <firstterm>degenerate</firstterm>),
+ * 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.
+ **/
+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_identity (const cairo_matrix_t *matrix)
+{
+ return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
+ matrix->xy == 0.0 && matrix->yy == 1.0 &&
+ matrix->x0 == 0.0 && matrix->y0 == 0.0);
+}
+
+cairo_bool_t
+_cairo_matrix_is_translation (const cairo_matrix_t *matrix)
+{
+ return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
+ matrix->xy == 0.0 && matrix->yy == 1.0);
+}
+
+cairo_bool_t
+_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix,
+ int *itx, int *ity)
+{
+ 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;
+}
+
+cairo_bool_t
+_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix)
+{
+ if (matrix->xy == 0.0 && matrix->yx == 0.0) {
+ if (! (matrix->xx == 1.0 || matrix->xx == -1.0))
+ return FALSE;
+ if (! (matrix->yy == 1.0 || matrix->yy == -1.0))
+ return FALSE;
+ } else if (matrix->xx == 0.0 && matrix->yy == 0.0) {
+ if (! (matrix->xy == 1.0 || matrix->xy == -1.0))
+ return FALSE;
+ if (! (matrix->yx == 1.0 || matrix->yx == -1.0))
+ return FALSE;
+ } else
+ return FALSE;
+
+ return TRUE;
+}
+
+/* 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 <wbrisken@aoc.nrao.edu> 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 and minor axes of a circle of the given
+ radius after applying the transformation matrix. */
+void
+_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
+ double radius,
+ double *major,
+ double *minor)
+{
+ double a, b, c, d, f, g, h, i, j, k;
+
+ _cairo_matrix_get_affine (matrix,
+ &a, &b,
+ &c, &d,
+ NULL, NULL);
+
+ i = a*a + b*b;
+ j = c*c + d*d;
+ k = a*c + b*d;
+
+ f = 0.5 * (i + j);
+ g = 0.5 * (i - j);
+ h = hypot (g, k);
+
+ if (major)
+ *major = radius * sqrt (f + h);
+ if (minor)
+ *minor = radius * sqrt (f - h);
+}
+
+/* determine the length of the major axis of a circle of the given radius
+ after applying the transformation matrix. */
+double
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+ double radius)
+{
+ double major;
+
+ _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL);
+
+ return major;
+}
+
+void
+_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
+ pixman_transform_t *pixman_transform,
+ double xc,
+ double yc)
+{
+ static const pixman_transform_t pixman_identity_transform = {{
+ {1 << 16, 0, 0},
+ { 0, 1 << 16, 0},
+ { 0, 0, 1 << 16}
+ }};
+
+ if (_cairo_matrix_is_identity (matrix)) {
+ *pixman_transform = pixman_identity_transform;
+ } else {
+ cairo_matrix_t inv;
+ unsigned max_iterations;
+
+ 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;
+
+ /* Note: If we can't invert the transformation, skip the adjustment. */
+ inv = *matrix;
+ if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS)
+ return;
+
+ /* find the pattern space coordinate that maps to (xc, yc) */
+ xc += .5; yc += .5; /* offset for the pixel centre */
+ max_iterations = 5;
+ do {
+ double x,y;
+ pixman_vector_t vector;
+ cairo_fixed_16_16_t dx, dy;
+
+ vector.vector[0] = _cairo_fixed_16_16_from_double (xc);
+ vector.vector[1] = _cairo_fixed_16_16_from_double (yc);
+ vector.vector[2] = 1 << 16;
+
+ if (! pixman_transform_point_3d (pixman_transform, &vector))
+ return;
+
+ x = pixman_fixed_to_double (vector.vector[0]);
+ y = pixman_fixed_to_double (vector.vector[1]);
+ cairo_matrix_transform_point (&inv, &x, &y);
+
+ /* Ideally, the vector should now be (xc, yc).
+ * We can now compensate for the resulting error.
+ */
+ x -= xc;
+ y -= yc;
+ cairo_matrix_transform_distance (matrix, &x, &y);
+ dx = _cairo_fixed_16_16_from_double (x);
+ dy = _cairo_fixed_16_16_from_double (y);
+ pixman_transform->matrix[0][2] -= dx;
+ pixman_transform->matrix[1][2] -= dy;
+
+ if (dx == 0 && dy == 0)
+ break;
+ } while (--max_iterations);
+ }
+}
diff --git a/gfx/cairo/cairo/src/cairo-meta-surface-private.h b/gfx/cairo/cairo/src/cairo-meta-surface-private.h
new file mode 100644
index 000000000..f0c95c19c
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-meta-surface-private.h
@@ -0,0 +1,187 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Kristian Høgsberg <krh@redhat.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_META_SURFACE_H
+#define CAIRO_META_SURFACE_H
+
+#include "cairoint.h"
+#include "cairo-path-fixed-private.h"
+
+typedef enum {
+ /* The 5 basic drawing operations. */
+ CAIRO_COMMAND_PAINT,
+ CAIRO_COMMAND_MASK,
+ CAIRO_COMMAND_STROKE,
+ CAIRO_COMMAND_FILL,
+ CAIRO_COMMAND_SHOW_TEXT_GLYPHS,
+
+ /* Other junk. For most of these, we should be able to assert that
+ * they never get called except as part of fallbacks for the 5
+ * basic drawing operations (which we implement already so the
+ * fallbacks should never get triggered). So the plan is to
+ * eliminate as many of these as possible. */
+
+ CAIRO_COMMAND_INTERSECT_CLIP_PATH
+
+} cairo_command_type_t;
+
+typedef enum {
+ CAIRO_META_REGION_ALL,
+ CAIRO_META_REGION_NATIVE,
+ CAIRO_META_REGION_IMAGE_FALLBACK
+} cairo_meta_region_type_t;
+
+typedef struct _cairo_command_header {
+ cairo_command_type_t type;
+ cairo_meta_region_type_t region;
+ cairo_rectangle_int_t extents;
+} cairo_command_header_t;
+
+typedef struct _cairo_command_paint {
+ cairo_command_header_t header;
+ cairo_operator_t op;
+ cairo_pattern_union_t source;
+} cairo_command_paint_t;
+
+typedef struct _cairo_command_mask {
+ cairo_command_header_t header;
+ cairo_operator_t op;
+ cairo_pattern_union_t source;
+ cairo_pattern_union_t mask;
+} cairo_command_mask_t;
+
+typedef struct _cairo_command_stroke {
+ cairo_command_header_t header;
+ cairo_operator_t op;
+ cairo_pattern_union_t source;
+ cairo_path_fixed_t path;
+ cairo_stroke_style_t style;
+ cairo_matrix_t ctm;
+ cairo_matrix_t ctm_inverse;
+ double tolerance;
+ cairo_antialias_t antialias;
+} cairo_command_stroke_t;
+
+typedef struct _cairo_command_fill {
+ cairo_command_header_t header;
+ cairo_operator_t op;
+ cairo_pattern_union_t source;
+ cairo_path_fixed_t path;
+ cairo_fill_rule_t fill_rule;
+ double tolerance;
+ cairo_antialias_t antialias;
+} cairo_command_fill_t;
+
+typedef struct _cairo_command_show_text_glyphs {
+ cairo_command_header_t header;
+ cairo_operator_t op;
+ cairo_pattern_union_t source;
+ char *utf8;
+ int utf8_len;
+ cairo_glyph_t *glyphs;
+ unsigned int num_glyphs;
+ cairo_text_cluster_t *clusters;
+ int num_clusters;
+ cairo_text_cluster_flags_t cluster_flags;
+ cairo_scaled_font_t *scaled_font;
+} cairo_command_show_text_glyphs_t;
+
+typedef struct _cairo_command_intersect_clip_path {
+ cairo_command_header_t header;
+ cairo_path_fixed_t *path_pointer;
+ cairo_path_fixed_t path;
+ cairo_fill_rule_t fill_rule;
+ double tolerance;
+ cairo_antialias_t antialias;
+} cairo_command_intersect_clip_path_t;
+
+typedef union _cairo_command {
+ cairo_command_header_t header;
+
+ /* The 5 basic drawing operations. */
+ cairo_command_paint_t paint;
+ cairo_command_mask_t mask;
+ cairo_command_stroke_t stroke;
+ cairo_command_fill_t fill;
+ cairo_command_show_text_glyphs_t show_text_glyphs;
+
+ /* The other junk. */
+ cairo_command_intersect_clip_path_t intersect_clip_path;
+} cairo_command_t;
+
+typedef struct _cairo_meta_surface {
+ cairo_surface_t base;
+
+ cairo_content_t content;
+
+ /* A meta-surface is logically unbounded, but when used as a
+ * source we need to render it to an image, so we need a size at
+ * which to create that image. */
+ double width_pixels;
+ double height_pixels;
+ cairo_rectangle_int_t extents;
+
+ cairo_array_t commands;
+ cairo_surface_t *commands_owner;
+
+ cairo_bool_t is_clipped;
+ int replay_start_idx;
+} cairo_meta_surface_t;
+
+slim_hidden_proto (cairo_meta_surface_create);
+slim_hidden_proto (cairo_meta_surface_replay);
+
+cairo_private cairo_int_status_t
+_cairo_meta_surface_get_path (cairo_surface_t *surface,
+ cairo_path_fixed_t *path);
+
+
+cairo_private cairo_status_t
+_cairo_meta_surface_replay_analyze_meta_pattern (cairo_surface_t *surface,
+ cairo_surface_t *target);
+
+cairo_private cairo_status_t
+_cairo_meta_surface_replay_and_create_regions (cairo_surface_t *surface,
+ cairo_surface_t *target);
+cairo_private cairo_status_t
+_cairo_meta_surface_replay_region (cairo_surface_t *surface,
+ cairo_surface_t *target,
+ cairo_meta_region_type_t region);
+
+cairo_private cairo_bool_t
+_cairo_surface_is_meta (const cairo_surface_t *surface);
+
+#endif /* CAIRO_META_SURFACE_H */
diff --git a/gfx/cairo/cairo/src/cairo-misc.c b/gfx/cairo/cairo/src/cairo-misc.c
new file mode 100644
index 000000000..e0ed70cb6
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-misc.c
@@ -0,0 +1,931 @@
+/* -*- 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 <cworth@cworth.org>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+
+COMPILE_TIME_ASSERT (CAIRO_STATUS_LAST_STATUS < 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 <firstterm>retain</firstterm> 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
+ */
+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";
+ default:
+ case CAIRO_STATUS_LAST_STATUS:
+ return "<unknown error status>";
+ }
+}
+
+
+/**
+ * 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)
+{
+ if (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)
+{
+ if (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 || __STDC_VERSION__ < 199901L
+/* 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);
+ }
+}
+
+
+#ifdef _WIN32
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as ETO_PDY */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include <windows.h>
+#include <io.h>
+
+#if !_WIN32_WCE
+/* tmpfile() replacement for Windows.
+ *
+ * On Windows tmpfile() creates the file in the root directory. This
+ * may fail due to unsufficient privileges. However, this isn't a
+ * 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;
+
+static unsigned long
+_intern_string_hash (const char *str, int len)
+{
+ const signed char *p = (const signed char *) str;
+ unsigned int h = *p;
+
+ for (p += 1; --len; p++)
+ h = (h << 5) - h + *p;
+
+ return h;
+}
+
+static cairo_bool_t
+_intern_string_equal (const void *_a, const void *_b)
+{
+ const cairo_intern_string_t *a = _a;
+ const cairo_intern_string_t *b = _b;
+
+ if (a->len != b->len)
+ return FALSE;
+
+ return memcmp (a->string, b->string, a->len) == 0;
+}
+
+cairo_status_t
+_cairo_intern_string (const char **str_inout, int len)
+{
+ char *str = (char *) *str_inout;
+ cairo_intern_string_t tmpl, *istring;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (len < 0)
+ len = strlen (str);
+ tmpl.hash_entry.hash = _intern_string_hash (str, len);
+ tmpl.len = len;
+ tmpl.string = (char *) str;
+
+ CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex);
+ if (_cairo_intern_string_ht == NULL) {
+ _cairo_intern_string_ht = _cairo_hash_table_create (_intern_string_equal);
+ if (unlikely (_cairo_intern_string_ht == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ }
+
+ istring = _cairo_hash_table_lookup (_cairo_intern_string_ht,
+ &tmpl.hash_entry);
+ if (istring == NULL) {
+ istring = malloc (sizeof (cairo_intern_string_t) + len + 1);
+ if (likely (istring != NULL)) {
+ istring->hash_entry.hash = tmpl.hash_entry.hash;
+ istring->len = tmpl.len;
+ istring->string = (char *) (istring + 1);
+ memcpy (istring->string, str, len);
+ istring->string[len] = '\0';
+
+ status = _cairo_hash_table_insert (_cairo_intern_string_ht,
+ &istring->hash_entry);
+ if (unlikely (status)) {
+ free (istring);
+ goto BAIL;
+ }
+ } else {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ }
+
+ *str_inout = istring->string;
+
+ BAIL:
+ CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex);
+ return status;
+}
+
+static void
+_intern_string_pluck (void *entry, void *closure)
+{
+ _cairo_hash_table_remove (closure, entry);
+ free (entry);
+}
+
+void
+_cairo_intern_string_reset_static_data (void)
+{
+ CAIRO_MUTEX_LOCK (_cairo_intern_string_mutex);
+ if (_cairo_intern_string_ht != NULL) {
+ _cairo_hash_table_foreach (_cairo_intern_string_ht,
+ _intern_string_pluck,
+ _cairo_intern_string_ht);
+ _cairo_hash_table_destroy(_cairo_intern_string_ht);
+ _cairo_intern_string_ht = NULL;
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_intern_string_mutex);
+}
diff --git a/gfx/cairo/cairo/src/cairo-mutex-impl-private.h b/gfx/cairo/cairo/src/cairo-mutex-impl-private.h
new file mode 100644
index 000000000..25223f3ea
--- /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 <cworth@cworth.org>
+ * Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ * Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#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 <lockdep.h>
+#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):
+ *
+ * <programlisting>
+ * cairo_mutex_impl_t _cairo_some_mutex;
+ * </programlisting>
+ *
+ * - #define %CAIRO_MUTEX_IMPL_<NAME> 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:
+ *
+ * <programlisting>
+ * cairo_mutex_impl_t _cairo_some_mutex;
+ *
+ * if (1)
+ * CAIRO_MUTEX_IMPL_LOCK (_cairo_some_mutex);
+ * else
+ * CAIRO_MUTEX_IMPL_UNLOCK (_cairo_some_mutex);
+ * </programlisting>
+ *
+ * - #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:
+ *
+ * <programlisting>
+ * 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);
+ * </programlisting>
+ *
+ * - 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:
+ *
+ * <programlisting>
+ * 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);
+ * </programlisting>
+ *
+ * - 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
+ * <programlisting>
+ * #define CAIRO_MUTEX_IMPL_INITIALIZE() CAIRO_MUTEX_IMPL_NOOP
+ * </programlisting>
+ *
+ * - 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
+ * <programlisting>
+ * #define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP
+ * </programlisting>
+ *
+ * - 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 <windows.h>
+
+ 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 <os2.h>
+
+ 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 <pthread.h>
+
+ 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 000000000..7d5ba0299
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-mutex-list-private.h
@@ -0,0 +1,78 @@
+/* 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 <mathias.hasselmann@gmx.de>
+ */
+
+#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_error_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex)
+
+#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)
+#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 000000000..61a7160a0
--- /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 <cworth@cworth.org>
+ * Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ * Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#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 000000000..e8c493985
--- /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 <cworth@cworth.org>
+ * Mathias Hasselmann <mathias.hasselmann@gmx.de>
+ * Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#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 000000000..0a31dced3
--- /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 <mathias.hasselmann@gmx.de>
+ */
+
+#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-features.h b/gfx/cairo/cairo/src/cairo-no-features.h
new file mode 100644
index 000000000..9b3d86be2
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-no-features.h
@@ -0,0 +1,12 @@
+/* Generated by configure. Do not edit */
+#ifndef CAIRO_NO_FEATURES_H
+#define CAIRO_NO_FEATURES_H
+
+#include <cairo-features.h>
+
+/* This is a dummy header, to trick gtk-doc only */
+
+#define CAIRO_HAS_WIN32_FONT 1
+#define CAIRO_HAS_WIN32_SURFACE 1
+
+#endif
diff --git a/gfx/cairo/cairo/src/cairo-observer.c b/gfx/cairo/cairo/src/cairo-observer.c
new file mode 100644
index 000000000..7c7b69c91
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-observer.c
@@ -0,0 +1,50 @@
+/* -*- 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 <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.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 000000000..829dd3c8d
--- /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 <doodle@scenergy.dfmk.hu>
+ *
+ * Contributor(s):
+ * Peter Weilbacher <mozilla@Weilbacher.org>
+ */
+
+#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 000000000..b9758281d
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-os2-surface.c
@@ -0,0 +1,1474 @@
+/* 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 <doodle@scenergy.dfmk.hu>
+ *
+ * Contributor(s):
+ * Peter Weilbacher <mozilla@Weilbacher.org>
+ * Rich Walsh <dragtext@e-vertise.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-os2-private.h"
+#include "cairo-error-private.h"
+
+#if CAIRO_HAS_FC_FONT
+#include <fontconfig/fontconfig.h>
+#endif
+
+#include <float.h>
+#ifdef BUILD_CAIRO_DLL
+# include "cairo-os2.h"
+# ifndef __WATCOMC__
+# include <emx/startup.h>
+# 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 = 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 *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);
+
+ /* Increase lend counter */
+ local_os2_surface->pixel_array_lend_count++;
+
+ *image_out = local_os2_surface->image_surface;
+ *image_extra = NULL;
+
+ DosReleaseMutexSem (local_os2_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 *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;
+ }
+
+ /* Decrease Lend counter! */
+ DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+ if (local_os2_surface->pixel_array_lend_count > 0)
+ local_os2_surface->pixel_array_lend_count--;
+ DosPostEventSem (local_os2_surface->hev_pixel_array_came_back);
+
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+ return;
+}
+
+static cairo_status_t
+_cairo_os2_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+{
+ 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);
+
+ /* Increase lend counter */
+ local_os2_surface->pixel_array_lend_count++;
+
+ *image_out = local_os2_surface->image_surface;
+ *image_extra = NULL;
+
+ image_rect->x = 0;
+ image_rect->y = 0;
+ image_rect->width = local_os2_surface->bitmap_info.cx;
+ image_rect->height = local_os2_surface->bitmap_info.cy;
+
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_os2_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_os2_surface_t *local_os2_surface;
+ RECTL rclToBlit;
+
+ local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return;
+ }
+
+ /* 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 (local_os2_surface->blit_as_changes) {
+ /* Get mutex, we'll work with the pixel array! */
+ if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) {
+ /* Could not get mutex! */
+ return;
+ }
+
+ 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.
+ *
+ * This is the safe method, which should be preferred every time.
+ */
+ rclToBlit.xLeft = interest_rect->x;
+ rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */
+ rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (interest_rect->y);
+ rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (interest_rect->y+interest_rect->height); /* Noninclusive */
+
+ WinInvalidateRect (local_os2_surface->hwnd_client_window,
+ &rclToBlit,
+ 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 = interest_rect->x;
+ rclToBlit.xRight = interest_rect->x+interest_rect->width; /* Noninclusive */
+ rclToBlit.yBottom = interest_rect->y;
+ rclToBlit.yTop = interest_rect->y+interest_rect->height; /* Noninclusive */
+ /* Now blit there the stuffs! */
+ _cairo_os2_surface_blit_pixels (local_os2_surface,
+ local_os2_surface->hps_client_window,
+ &rclToBlit);
+ }
+
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+ }
+ /* Also decrease Lend counter! */
+ DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
+
+ if (local_os2_surface->pixel_array_lend_count > 0)
+ local_os2_surface->pixel_array_lend_count--;
+ DosPostEventSem (local_os2_surface->hev_pixel_array_came_back);
+
+ DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
+}
+
+static cairo_bool_t
+_cairo_os2_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_os2_surface_t *local_os2_surface;
+
+ local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
+ if ((!local_os2_surface) ||
+ (local_os2_surface->base.backend != &cairo_os2_surface_backend))
+ {
+ /* Invalid parameter (wrong surface)! */
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+ }
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+ rectangle->width = local_os2_surface->bitmap_info.cx;
+ rectangle->height = local_os2_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 *) 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));
+
+ /* Successful exit */
+ return (cairo_surface_t *)local_os2_surface;
+
+ error_exit:
+
+ /* This point will only be reached if an error occured */
+
+ 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,
+ NULL, /* create_similar */
+ _cairo_os2_surface_finish,
+ _cairo_os2_surface_acquire_source_image,
+ _cairo_os2_surface_release_source_image,
+ _cairo_os2_surface_acquire_dest_image,
+ _cairo_os2_surface_release_dest_image,
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_os2_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ _cairo_os2_surface_mark_dirty_rectangle,
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+ NULL, /* paint */
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+ NULL, /* show_glyphs */
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ NULL, /* has_show_text_glyphs */
+ NULL /* show_text_glyphs */
+};
diff --git a/gfx/cairo/cairo/src/cairo-os2.h b/gfx/cairo/cairo/src/cairo-os2.h
new file mode 100644
index 000000000..d23f2dec4
--- /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 <doodle@scenergy.dfmk.hu>
+ *
+ * Contributor(s):
+ * Peter Weilbacher <mozilla@Weilbacher.org>
+ * Rich Walsh <dragtext@e-vertise.com>
+ */
+
+#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 <os2.h>
+
+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 000000000..edaabbe78
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-output-stream-private.h
@@ -0,0 +1,196 @@
+/* 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 <krh@redhat.com>
+ */
+
+#ifndef CAIRO_OUTPUT_STREAM_PRIVATE_H
+#define CAIRO_OUTPUT_STREAM_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+typedef cairo_status_t
+(*cairo_output_stream_write_func_t) (cairo_output_stream_t *output_stream,
+ const unsigned char *data,
+ unsigned int length);
+
+typedef cairo_status_t
+(*cairo_output_stream_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);
+
+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 000000000..1aabe821a
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-output-stream.c
@@ -0,0 +1,769 @@
+/* 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 <krh@redhat.com>
+ */
+
+#define _BSD_SOURCE /* for snprintf() */
+#include "cairoint.h"
+
+#include "cairo-output-stream-private.h"
+#include "cairo-error-private.h"
+#include "cairo-compiler-private.h"
+
+#include <stdio.h>
+#include <locale.h>
+#include <errno.h>
+
+/* 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:
+ *
+ * <programlisting>
+ * FIXED_POINT_DECIMAL_DIGITS = ceil( CAIRO_FIXED_FRAC_BITS * ln(2)/ln(10) )
+ * </programlisting>
+ *
+ * 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 = 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 = 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 <alexl@redhat.com>.
+ * http://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 <krh@redhat.com>
+ */
+static void
+_cairo_dtostr (char *buffer, size_t size, double d, cairo_bool_t limited_precision)
+{
+ struct lconv *locale_data;
+ const char *decimal_point;
+ int decimal_point_len;
+ char *p;
+ int decimal_len;
+ int num_zeros, decimal_digits;
+
+ /* Omit the minus sign from negative zero. */
+ if (d == 0.0)
+ d = 0.0;
+
+#ifdef HAVE_LOCALECONV
+ locale_data = localeconv ();
+ decimal_point = locale_data->decimal_point;
+ decimal_point_len = strlen (decimal_point);
+#else
+ decimal_point = ".";
+ decimal_point_len = 1;
+#endif
+
+ 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':
+ snprintf (buffer, sizeof buffer,
+ single_fmt, va_arg (ap, const char *));
+ 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);
+}
+
+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 = 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;
+
+ if (filename == NULL)
+ return _cairo_null_stream_create ();
+
+ file = fopen (filename, "wb");
+ 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 = 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 = 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 = 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 = 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 000000000..42badbfdf
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-paginated-private.h
@@ -0,0 +1,165 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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.
+ */
+ void
+ (*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);
+};
+
+/* 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_<foo>_surface_create(). The paginated backend will be careful
+ * to not let the user see that they really got a "wrapped"
+ * surface. See test-paginated-surface.c for a fairly minimal example
+ * of a paginated-using surface. That should be a reasonable example
+ * to follow.
+ *
+ * What the paginated surface does is first save all drawing
+ * operations for a page into a 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_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 000000000..ebf4b3424
--- /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 <cworth@cworth.org>
+ */
+
+#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 000000000..af4790e7e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-paginated-surface.c
@@ -0,0 +1,651 @@
+/* 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 <cworth@cworth.org>
+ * Keith Packard <keithp@keithp.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+/* The paginated surface layer exists to provide as much code sharing
+ * as possible for the various paginated surface backends in cairo
+ * (PostScript, PDF, etc.). 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"
+
+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 = 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);
+
+ /* 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;
+}
+
+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_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_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;
+
+ _cairo_clip_init (&clip);
+ status = _cairo_clip_rectangle (&clip, rect);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _cairo_surface_paint (surface->target,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, &clip);
+ }
+
+ _cairo_clip_fini (&clip);
+ _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_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);
+
+ surface->backend->set_paginated_mode (surface->target,
+ CAIRO_PAGINATED_MODE_ANALYZE);
+ status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface,
+ analysis);
+ if (status || analysis->status) {
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = analysis->status;
+ goto FAIL;
+ }
+
+ 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) {
+ surface->backend->set_paginated_mode (surface->target,
+ CAIRO_PAGINATED_MODE_RENDER);
+
+ 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;
+
+ surface->backend->set_paginated_mode (surface->target,
+ CAIRO_PAGINATED_MODE_FALLBACK);
+
+ 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;
+
+ surface->backend->set_paginated_mode (surface->target,
+ CAIRO_PAGINATED_MODE_FALLBACK);
+
+ 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;
+ }
+ }
+
+ 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,
+ 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,
+ 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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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,
+ 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 cairo_surface_t *
+_cairo_paginated_surface_snapshot (void *abstract_other)
+{
+ cairo_paginated_surface_t *other = abstract_other;
+
+ return _cairo_surface_snapshot (other->recording_surface);
+}
+
+static const cairo_surface_backend_t cairo_paginated_surface_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
+ _cairo_paginated_surface_create_similar,
+ _cairo_paginated_surface_finish,
+ _cairo_paginated_surface_acquire_source_image,
+ _cairo_paginated_surface_release_source_image,
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ _cairo_paginated_surface_copy_page,
+ _cairo_paginated_surface_show_page,
+ _cairo_paginated_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_paginated_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+ _cairo_paginated_surface_paint,
+ _cairo_paginated_surface_mask,
+ _cairo_paginated_surface_stroke,
+ _cairo_paginated_surface_fill,
+ NULL, /* show_glyphs */
+ _cairo_paginated_surface_snapshot,
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ _cairo_paginated_surface_has_show_text_glyphs,
+ _cairo_paginated_surface_show_text_glyphs
+};
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 000000000..8ca80fa13
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-path-bounds.c
@@ -0,0 +1,350 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-path-fixed-private.h"
+
+typedef struct cairo_path_bounder {
+ cairo_point_t current_point;
+ cairo_bool_t has_initial_point;
+ cairo_bool_t has_point;
+
+ cairo_box_t extents;
+} cairo_path_bounder_t;
+
+static void
+_cairo_path_bounder_init (cairo_path_bounder_t *bounder)
+{
+ bounder->has_initial_point = FALSE;
+ bounder->has_point = FALSE;
+}
+
+static void
+_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder,
+ const cairo_point_t *point)
+{
+ if (bounder->has_point) {
+ if (point->x < bounder->extents.p1.x)
+ bounder->extents.p1.x = point->x;
+
+ if (point->y < bounder->extents.p1.y)
+ bounder->extents.p1.y = point->y;
+
+ if (point->x > bounder->extents.p2.x)
+ bounder->extents.p2.x = point->x;
+
+ if (point->y > bounder->extents.p2.y)
+ bounder->extents.p2.y = point->y;
+ } else {
+ bounder->extents.p1.x = point->x;
+ bounder->extents.p1.y = point->y;
+ bounder->extents.p2.x = point->x;
+ bounder->extents.p2.y = point->y;
+ bounder->has_point = TRUE;
+ }
+}
+
+static cairo_status_t
+_cairo_path_bounder_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_path_bounder_t *bounder = closure;
+
+ bounder->current_point = *point;
+ bounder->has_initial_point = TRUE;
+
+ 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;
+
+ if (bounder->has_initial_point) {
+ _cairo_path_bounder_add_point (bounder, &bounder->current_point);
+ bounder->has_initial_point = FALSE;
+ }
+
+ _cairo_path_bounder_add_point (bounder, point);
+ bounder->current_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_bounder_curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ cairo_path_bounder_t *bounder = closure;
+
+ /* If the bbox of the control points is entirely inside, then we
+ * do not need to further evaluate the spline.
+ */
+ if (! bounder->has_point ||
+ b->x < bounder->extents.p1.x || b->x > bounder->extents.p2.x ||
+ b->y < bounder->extents.p1.y || b->y > bounder->extents.p2.y ||
+ c->x < bounder->extents.p1.x || c->x > bounder->extents.p2.x ||
+ c->y < bounder->extents.p1.y || c->y > bounder->extents.p2.y ||
+ d->x < bounder->extents.p1.x || d->x > bounder->extents.p2.x ||
+ d->y < bounder->extents.p1.y || d->y > bounder->extents.p2.y)
+ {
+ return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder,
+ &bounder->current_point, b, c, d);
+ }
+ else
+ {
+ /* All control points are within the current extents. */
+ bounder->current_point = *d;
+ return CAIRO_STATUS_SUCCESS;
+ }
+}
+
+static cairo_status_t
+_cairo_path_bounder_close_path (void *closure)
+{
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* This computes the extents of all the points in the path, not those of
+ * the damage area (i.e it does not consider winding and it only inspects
+ * the control points of the curves, not the flattened path).
+ */
+void
+_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path,
+ cairo_rectangle_int_t *extents)
+{
+ if (path->extents.p1.x < path->extents.p2.x) {
+ _cairo_box_round_to_rectangle (&path->extents, extents);
+ } else {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ }
+}
+
+/* A slightly better approximation than above - we actually decompose the
+ * Bezier, but we continue to ignore winding.
+ */
+void
+_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_path_bounder_t bounder;
+ cairo_status_t status;
+
+ if (! path->has_curve_to) {
+ bounder.extents = path->extents;
+ bounder.has_point = path->extents.p1.x < path->extents.p2.x;
+ } else {
+ _cairo_path_bounder_init (&bounder);
+
+ status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
+ _cairo_path_bounder_move_to,
+ _cairo_path_bounder_line_to,
+ _cairo_path_bounder_curve_to,
+ _cairo_path_bounder_close_path,
+ &bounder);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ if (bounder.has_point) {
+ _cairo_box_round_to_rectangle (&bounder.extents, extents);
+ } else {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ }
+}
+
+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_path_bounder_t bounder;
+ cairo_status_t status;
+
+ if (! path->has_curve_to) {
+ bounder.extents = path->extents;
+ bounder.has_point = path->extents.p1.x < path->extents.p2.x;
+ } else {
+ _cairo_path_bounder_init (&bounder);
+
+ status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD,
+ _cairo_path_bounder_move_to,
+ _cairo_path_bounder_line_to,
+ _cairo_path_bounder_close_path,
+ &bounder, tolerance);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ if (bounder.has_point) {
+ _cairo_box_round_to_rectangle (&bounder.extents, extents);
+ } 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_rectangle_int_t *extents)
+{
+ cairo_path_bounder_t bounder;
+ cairo_status_t status;
+
+ if (! path->has_curve_to) {
+ bounder.extents = path->extents;
+
+ /* include trailing move-to for degenerate segments */
+ if (path->has_last_move_point) {
+ const cairo_point_t *point = &path->last_move_point;
+
+ if (point->x < bounder.extents.p1.x)
+ bounder.extents.p1.x = point->x;
+ if (point->y < bounder.extents.p1.y)
+ bounder.extents.p1.y = point->y;
+
+ if (point->x > bounder.extents.p2.x)
+ bounder.extents.p2.x = point->x;
+ if (point->y > bounder.extents.p2.y)
+ bounder.extents.p2.y = point->y;
+ }
+
+ bounder.has_point = bounder.extents.p1.x <= bounder.extents.p2.x;
+ bounder.has_initial_point = FALSE;
+ } else {
+ _cairo_path_bounder_init (&bounder);
+
+ status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
+ _cairo_path_bounder_move_to,
+ _cairo_path_bounder_line_to,
+ _cairo_path_bounder_curve_to,
+ _cairo_path_bounder_close_path,
+ &bounder);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ if (bounder.has_point) {
+ double dx, dy;
+
+ _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);
+
+ bounder.extents.p1.x -= _cairo_fixed_from_double (dx);
+ bounder.extents.p2.x += _cairo_fixed_from_double (dx);
+ bounder.extents.p1.y -= _cairo_fixed_from_double (dy);
+ bounder.extents.p2.y += _cairo_fixed_from_double (dy);
+
+ _cairo_box_round_to_rectangle (&bounder.extents, extents);
+ } else if (bounder.has_initial_point) {
+ double dx, dy;
+
+ /* accommodate capping of degenerate paths */
+
+ _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);
+
+ bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx);
+ bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx);
+ bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy);
+ bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy);
+
+ _cairo_box_round_to_rectangle (&bounder.extents, extents);
+ } 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_traps_t traps;
+ cairo_box_t bbox;
+ cairo_status_t status;
+
+ _cairo_traps_init (&traps);
+
+ status = _cairo_path_fixed_stroke_to_traps (path,
+ stroke_style,
+ ctm,
+ ctm_inverse,
+ tolerance,
+ &traps);
+
+ _cairo_traps_extents (&traps, &bbox);
+ _cairo_traps_fini (&traps);
+
+ _cairo_box_round_to_rectangle (&bbox, extents);
+
+ return status;
+}
+
+cairo_bool_t
+_cairo_path_fixed_extents (const cairo_path_fixed_t *path,
+ cairo_box_t *box)
+{
+ cairo_path_bounder_t bounder;
+ cairo_status_t status;
+
+ if (! path->has_curve_to) {
+ *box = path->extents;
+ return path->extents.p1.x <= path->extents.p2.x;
+ }
+
+ _cairo_path_bounder_init (&bounder);
+
+ status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
+ _cairo_path_bounder_move_to,
+ _cairo_path_bounder_line_to,
+ _cairo_path_bounder_curve_to,
+ _cairo_path_bounder_close_path,
+ &bounder);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ *box = bounder.extents;
+ return bounder.has_point;
+}
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 000000000..24aaa3969
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-path-fill.c
@@ -0,0 +1,465 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-region-private.h"
+
+typedef struct cairo_filler {
+ double tolerance;
+ cairo_polygon_t *polygon;
+} cairo_filler_t;
+
+static void
+_cairo_filler_init (cairo_filler_t *filler,
+ double tolerance,
+ cairo_polygon_t *polygon)
+{
+ filler->tolerance = tolerance;
+ filler->polygon = polygon;
+}
+
+static void
+_cairo_filler_fini (cairo_filler_t *filler)
+{
+}
+
+static cairo_status_t
+_cairo_filler_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_filler_t *filler = closure;
+ cairo_polygon_t *polygon = filler->polygon;
+
+ return _cairo_polygon_close (polygon) ||
+ _cairo_polygon_move_to (polygon, point);
+}
+
+static cairo_status_t
+_cairo_filler_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_filler_t *filler = closure;
+ return _cairo_polygon_line_to (filler->polygon, point);
+}
+
+static cairo_status_t
+_cairo_filler_curve_to (void *closure,
+ const cairo_point_t *b,
+ const cairo_point_t *c,
+ const cairo_point_t *d)
+{
+ cairo_filler_t *filler = closure;
+ cairo_spline_t spline;
+
+ if (! _cairo_spline_init (&spline,
+ _cairo_filler_line_to, filler,
+ &filler->polygon->current_point, b, c, d))
+ {
+ return _cairo_filler_line_to (closure, d);
+ }
+
+ return _cairo_spline_decompose (&spline, filler->tolerance);
+}
+
+static cairo_status_t
+_cairo_filler_close_path (void *closure)
+{
+ cairo_filler_t *filler = closure;
+ return _cairo_polygon_close (filler->polygon);
+}
+
+cairo_status_t
+_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
+ double tolerance,
+ cairo_polygon_t *polygon)
+{
+ cairo_filler_t filler;
+ cairo_status_t status;
+
+ _cairo_filler_init (&filler, tolerance, polygon);
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_filler_move_to,
+ _cairo_filler_line_to,
+ _cairo_filler_curve_to,
+ _cairo_filler_close_path,
+ &filler);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_polygon_close (polygon);
+ _cairo_filler_fini (&filler);
+
+ return status;
+}
+
+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 (path->is_empty_fill)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_polygon_init (&polygon);
+ if (traps->num_limits)
+ _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
+
+ status = _cairo_path_fixed_fill_to_polygon (path,
+ tolerance,
+ &polygon);
+ if (unlikely (status || polygon.num_edges == 0))
+ goto CLEANUP;
+
+ if (path->is_rectilinear) {
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps,
+ &polygon,
+ fill_rule);
+ } else {
+ status = _cairo_bentley_ottmann_tessellate_polygon (traps,
+ &polygon,
+ fill_rule);
+ }
+
+ CLEANUP:
+ _cairo_polygon_fini (&polygon);
+ return status;
+}
+
+static cairo_region_t *
+_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_box_t box;
+ cairo_polygon_t polygon;
+ cairo_traps_t traps;
+ cairo_status_t status;
+ cairo_region_t *region;
+
+ /* first try to bypass fill-to-polygon */
+ _cairo_traps_init (&traps);
+ status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+ fill_rule,
+ &traps);
+ if (_cairo_status_is_error (status))
+ goto CLEANUP_TRAPS;
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _cairo_traps_extract_region (&traps, &region);
+ goto CLEANUP_TRAPS;
+ }
+
+ /* path is not rectangular, try extracting clipped rectilinear edges */
+ _cairo_polygon_init (&polygon);
+ if (extents != NULL) {
+ _cairo_box_from_rectangle (&box, extents);
+ _cairo_polygon_limit (&polygon, &box, 1);
+ }
+
+ /* tolerance will be ignored as the path is rectilinear */
+ status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
+ if (unlikely (status))
+ goto CLEANUP_POLYGON;
+
+ if (polygon.num_edges == 0) {
+ region = cairo_region_create ();
+ } else {
+ status =
+ _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
+ &polygon,
+ fill_rule);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ status = _cairo_traps_extract_region (&traps, &region);
+ }
+
+ CLEANUP_POLYGON:
+ _cairo_polygon_fini (&polygon);
+
+ CLEANUP_TRAPS:
+ _cairo_traps_fini (&traps);
+
+ if (unlikely (status))
+ region = _cairo_region_create_in_error (status);
+
+ return region;
+}
+
+/* This special-case filler supports only a path that describes a
+ * device-axis aligned rectangle. It exists to avoid the overhead of
+ * the general tessellator when drawing very common rectangles.
+ *
+ * If the path described anything but a device-axis aligned rectangle,
+ * this function will abort.
+ */
+cairo_region_t *
+_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+ cairo_box_t box;
+ cairo_region_t *region = NULL;
+
+ assert (path->maybe_fill_region);
+ assert (! path->is_empty_fill);
+
+ if (_cairo_path_fixed_is_box (path, &box)) {
+ rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x);
+ rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y);
+ rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) -
+ rectangle_stack[0].x;
+ rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) -
+ rectangle_stack[0].y;
+ if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents))
+ region = cairo_region_create ();
+ else
+ region = cairo_region_create_rectangle (&rectangle_stack[0]);
+ } else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
+ cairo_rectangle_int_t *rects = rectangle_stack;
+ cairo_path_fixed_iter_t iter;
+ int last_cw = -1;
+ int size = ARRAY_LENGTH (rectangle_stack);
+ int count = 0;
+
+ /* Support a series of rectangles as can be expected to describe a
+ * GdkRegion clip region during exposes.
+ */
+ _cairo_path_fixed_iter_init (&iter, path);
+ while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+ int cw = 0;
+
+ if (box.p1.x > box.p2.x) {
+ cairo_fixed_t t;
+
+ t = box.p1.x;
+ box.p1.x = box.p2.x;
+ box.p2.x = t;
+
+ cw = ! cw;
+ }
+
+ if (box.p1.y > box.p2.y) {
+ cairo_fixed_t t;
+
+ t = box.p1.y;
+ box.p1.y = box.p2.y;
+ box.p2.y = t;
+
+ cw = ! cw;
+ }
+
+ if (last_cw < 0)
+ last_cw = cw;
+ else if (last_cw != cw)
+ goto TESSELLATE;
+
+ if (count == size) {
+ cairo_rectangle_int_t *new_rects;
+
+ size *= 4;
+ if (rects == rectangle_stack) {
+ new_rects = _cairo_malloc_ab (size,
+ sizeof (cairo_rectangle_int_t));
+ if (unlikely (new_rects == NULL)) {
+ /* XXX _cairo_region_nil */
+ break;
+ }
+ memcpy (new_rects, rects, sizeof (rectangle_stack));
+ } else {
+ new_rects = _cairo_realloc_ab (rects, size,
+ sizeof (cairo_rectangle_int_t));
+ if (unlikely (new_rects == NULL)) {
+ /* XXX _cairo_region_nil */
+ break;
+ }
+ }
+ rects = new_rects;
+ }
+
+ rects[count].x = _cairo_fixed_integer_part (box.p1.x);
+ rects[count].y = _cairo_fixed_integer_part (box.p1.y);
+ rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x;
+ rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y;
+ if (_cairo_rectangle_intersect (&rects[count], extents))
+ count++;
+ }
+
+ if (_cairo_path_fixed_iter_at_end (&iter))
+ region = cairo_region_create_rectangles (rects, count);
+
+TESSELLATE:
+ if (rects != rectangle_stack)
+ free (rects);
+ }
+
+ if (region == NULL) {
+ /* Hmm, complex polygon */
+ region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path,
+ fill_rule,
+ extents);
+
+
+ }
+
+ return region;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_traps_t *traps)
+{
+ cairo_box_t box;
+ cairo_status_t status;
+
+ traps->is_rectilinear = TRUE;
+ traps->is_rectangular = TRUE;
+
+ if (_cairo_path_fixed_is_box (path, &box)) {
+ return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
+ } else {
+ cairo_path_fixed_iter_t iter;
+
+ _cairo_path_fixed_iter_init (&iter, path);
+ while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
+ if (box.p1.y > box.p2.y) {
+ cairo_fixed_t t;
+
+ t = box.p1.y;
+ box.p1.y = box.p2.y;
+ box.p2.y = t;
+
+ t = box.p1.x;
+ box.p1.x = box.p2.x;
+ box.p2.x = t;
+ }
+
+ status = _cairo_traps_tessellate_rectangle (traps,
+ &box.p1, &box.p2);
+ if (unlikely (status)) {
+ _cairo_traps_clear (traps);
+ return status;
+ }
+ }
+
+ if (_cairo_path_fixed_iter_at_end (&iter))
+ return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule);
+
+ _cairo_traps_clear (traps);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+}
+
+static cairo_status_t
+_cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_boxes_t *boxes)
+{
+ cairo_polygon_t polygon;
+ cairo_status_t status;
+
+ _cairo_polygon_init (&polygon);
+ if (boxes->num_limits) {
+ _cairo_polygon_limit (&polygon, boxes->limits, boxes->num_limits);
+ boxes->num_limits = 0;
+ }
+
+ /* tolerance will be ignored as the path is rectilinear */
+ status = _cairo_path_fixed_fill_to_polygon (path, 0., &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_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, &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, &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,
+ 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 000000000..42e64eda3
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-path-fixed-private.h
@@ -0,0 +1,165 @@
+/* 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 <cworth@redhat.com>
+ */
+
+#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 <stdio.h>
+#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)))
+
+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;
+
+struct _cairo_path_fixed {
+ cairo_point_t last_move_point;
+ cairo_point_t current_point;
+ unsigned int has_current_point : 1;
+ unsigned int has_last_move_point : 1;
+ unsigned int has_curve_to : 1;
+ unsigned int is_rectilinear : 1;
+ unsigned int maybe_fill_region : 1;
+ unsigned int is_empty_fill : 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_direction_t dir,
+ 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->is_empty_fill;
+}
+
+static inline cairo_bool_t
+_cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path)
+{
+ if (! path->is_rectilinear)
+ return 0;
+
+ if (! path->has_current_point)
+ 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_maybe_fill_region (const cairo_path_fixed_t *path)
+{
+#if WATCH_PATH
+ fprintf (stderr, "_cairo_path_fixed_maybe_fill_region () = %s\n",
+ path->maybe_fill_region ? "true" : "false");
+#endif
+ return path->maybe_fill_region;
+}
+
+#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 000000000..eea8630bd
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-path-fixed.c
@@ -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 © 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.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);
+
+#define cairo_path_head(path__) (&(path__)->buf.base)
+#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__))
+
+#define cairo_path_buf_next(pos__) \
+ cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link)
+#define cairo_path_buf_prev(pos__) \
+ cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link)
+
+#define cairo_path_foreach_buf_start(pos__, path__) \
+ pos__ = cairo_path_head (path__); do
+#define cairo_path_foreach_buf_end(pos__, path__) \
+ while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__))
+
+void
+_cairo_path_fixed_init (cairo_path_fixed_t *path)
+{
+ 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_last_move_point = FALSE;
+ path->has_current_point = FALSE;
+ path->has_curve_to = FALSE;
+ path->is_rectilinear = TRUE;
+ path->maybe_fill_region = TRUE;
+ path->is_empty_fill = TRUE;
+
+ path->extents.p1.x = path->extents.p1.y = INT_MAX;
+ path->extents.p2.x = path->extents.p2.y = INT_MIN;
+}
+
+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_last_move_point = other->has_last_move_point;
+ path->has_current_point = other->has_current_point;
+ path->has_curve_to = other->has_curve_to;
+ path->is_rectilinear = other->is_rectilinear;
+ path->maybe_fill_region = other->maybe_fill_region;
+ path->is_empty_fill = other->is_empty_fill;
+
+ 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;
+ int num_points, num_ops;
+
+ hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents));
+
+ num_ops = num_points = 0;
+ cairo_path_foreach_buf_start (buf, path) {
+ hash = _cairo_hash_bytes (hash, buf->op,
+ buf->num_ops * sizeof (buf->op[0]));
+ hash = _cairo_hash_bytes (hash, buf->points,
+ buf->num_points * sizeof (buf->points[0]));
+
+ num_ops += buf->num_ops;
+ num_points += buf->num_points;
+ } cairo_path_foreach_buf_end (buf, path);
+
+ hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops));
+ hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points));
+
+ 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->is_empty_fill != b->is_empty_fill ||
+ a->has_curve_to != b->has_curve_to ||
+ a->maybe_fill_region != b->maybe_fill_region ||
+ a->is_rectilinear != b->is_rectilinear)
+ {
+ 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 = 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_NOACCESS (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_last_op (cairo_path_fixed_t *path)
+{
+ cairo_path_buf_t *buf;
+
+ buf = cairo_path_tail (path);
+ if (buf->num_ops == 0)
+ return -1;
+
+ return buf->op[buf->num_ops - 1];
+}
+
+static inline void
+_cairo_path_fixed_extents_add (cairo_path_fixed_t *path,
+ const cairo_point_t *point)
+{
+ if (point->x < path->extents.p1.x)
+ path->extents.p1.x = point->x;
+ if (point->y < path->extents.p1.y)
+ path->extents.p1.y = point->y;
+
+ if (point->x > path->extents.p2.x)
+ path->extents.p2.x = point->x;
+ if (point->y > path->extents.p2.y)
+ path->extents.p2.y = point->y;
+}
+
+cairo_status_t
+_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
+ cairo_fixed_t x,
+ cairo_fixed_t y)
+{
+ cairo_status_t status;
+ cairo_point_t point;
+
+ point.x = x;
+ point.y = y;
+
+ /* If the previous op was also a MOVE_TO, then just change its
+ * point rather than adding a new op. */
+ if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) {
+ cairo_path_buf_t *buf;
+
+ buf = cairo_path_tail (path);
+ buf->points[buf->num_points - 1] = point;
+ } else {
+ status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1);
+ if (unlikely (status))
+ return status;
+
+ if (path->has_current_point && path->is_rectilinear) {
+ /* a move-to is first an implicit close */
+ path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
+ path->current_point.y == path->last_move_point.y;
+ path->maybe_fill_region &= path->is_rectilinear;
+ }
+ if (path->maybe_fill_region) {
+ path->maybe_fill_region =
+ _cairo_fixed_is_integer (path->last_move_point.x) &&
+ _cairo_fixed_is_integer (path->last_move_point.y);
+ }
+ }
+
+ path->current_point = point;
+ path->last_move_point = point;
+ path->has_last_move_point = TRUE;
+ path->has_current_point = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
+{
+ 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);
+
+ /* If the previous op was but the initial MOVE_TO and this segment
+ * is degenerate, then we can simply skip this point. Note that
+ * a move-to followed by a degenerate line-to is a valid path for
+ * stroking, but at all other times is simply a degenerate segment.
+ */
+ if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
+ if (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_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+ cairo_path_buf_t *buf;
+ const cairo_point_t *p;
+
+ buf = cairo_path_tail (path);
+ if (likely (buf->num_points >= 2)) {
+ p = &buf->points[buf->num_points-2];
+ } else {
+ cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
+ p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
+ }
+
+ if (p->x == path->current_point.x && p->y == path->current_point.y) {
+ /* previous line element was degenerate, replace */
+ buf->points[buf->num_points - 1] = point;
+ goto FLAGS;
+ } 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))
+ {
+ buf->points[buf->num_points - 1] = point;
+ goto FLAGS;
+ }
+ }
+ }
+
+ status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
+ if (unlikely (status))
+ return status;
+
+ FLAGS:
+ if (path->is_rectilinear) {
+ path->is_rectilinear = path->current_point.x == x ||
+ path->current_point.y == y;
+ path->maybe_fill_region &= path->is_rectilinear;
+ }
+ if (path->maybe_fill_region) {
+ path->maybe_fill_region = _cairo_fixed_is_integer (x) &&
+ _cairo_fixed_is_integer (y);
+ }
+ if (path->is_empty_fill) {
+ path->is_empty_fill = path->current_point.x == x &&
+ path->current_point.y == y;
+ }
+
+ path->current_point = point;
+ if (path->has_last_move_point) {
+ _cairo_path_fixed_extents_add (path, &path->last_move_point);
+ path->has_last_move_point = FALSE;
+ }
+ _cairo_path_fixed_extents_add (path, &point);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_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];
+
+ /* make sure subpaths are started properly */
+ if (! path->has_current_point) {
+ status = _cairo_path_fixed_move_to (path, x0, y0);
+ if (unlikely (status))
+ return status;
+ }
+
+ point[0].x = x0; point[0].y = y0;
+ point[1].x = x1; point[1].y = y1;
+ point[2].x = x2; point[2].y = y2;
+ status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
+ if (unlikely (status))
+ return status;
+
+ path->current_point = point[2];
+ path->has_current_point = TRUE;
+ path->is_empty_fill = FALSE;
+ path->has_curve_to = TRUE;
+ path->is_rectilinear = FALSE;
+ path->maybe_fill_region = FALSE;
+
+ /* coarse bounds */
+ if (path->has_last_move_point) {
+ _cairo_path_fixed_extents_add (path, &path->last_move_point);
+ path->has_last_move_point = FALSE;
+ }
+ _cairo_path_fixed_extents_add (path, &point[0]);
+ _cairo_path_fixed_extents_add (path, &point[1]);
+ _cairo_path_fixed_extents_add (path, &point[2]);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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;
+
+ /* If the previous op was also a LINE_TO back to the start, discard it */
+ if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+ if (path->current_point.x == path->last_move_point.x &&
+ path->current_point.y == path->last_move_point.y)
+ {
+ cairo_path_buf_t *buf;
+ cairo_point_t *p;
+
+ buf = cairo_path_tail (path);
+ if (likely (buf->num_points >= 2)) {
+ p = &buf->points[buf->num_points-2];
+ } else {
+ cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
+ p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
+ }
+
+ path->current_point = *p;
+ buf->num_ops--;
+ buf->num_points--;
+ }
+ }
+
+ status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_path_fixed_move_to (path,
+ path->last_move_point.x,
+ path->last_move_point.y);
+}
+
+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), "]");
+
+ fprintf (stderr,
+ "_cairo_path_fixed_add (%s, %s)\n",
+ op_str[(int) op], buf);
+ }
+
+ _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)
+{
+ 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_direction_t dir,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_curve_to_func_t *curve_to,
+ cairo_path_fixed_close_path_func_t *close_path,
+ void *closure)
+{
+ const uint8_t num_args[] = {
+ 1, /* cairo_path_move_to */
+ 1, /* cairo_path_op_line_to */
+ 3, /* cairo_path_op_curve_to */
+ 0, /* cairo_path_op_close_path */
+ };
+ cairo_status_t status;
+ const cairo_path_buf_t *buf, *first;
+ cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD);
+ int step = forward ? 1 : -1;
+
+ buf = first = forward ? cairo_path_head (path) : cairo_path_tail (path);
+ do {
+ cairo_point_t *points;
+ int start, stop, i;
+
+ if (forward) {
+ start = 0;
+ stop = buf->num_ops;
+ points = buf->points;
+ } else {
+ start = buf->num_ops - 1;
+ stop = -1;
+ points = buf->points + buf->num_points;
+ }
+
+ for (i = start; i != stop; i += step) {
+ cairo_path_op_t op = buf->op[i];
+
+ if (! forward)
+ points -= num_args[(int) op];
+
+ switch (op) {
+ case CAIRO_PATH_OP_MOVE_TO:
+ status = (*move_to) (closure, &points[0]);
+ break;
+ case CAIRO_PATH_OP_LINE_TO:
+ status = (*line_to) (closure, &points[0]);
+ break;
+ case CAIRO_PATH_OP_CURVE_TO:
+ status = (*curve_to) (closure, &points[0], &points[1], &points[2]);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATH_OP_CLOSE_PATH:
+ status = (*close_path) (closure);
+ break;
+ }
+ if (unlikely (status))
+ return status;
+
+ if (forward)
+ points += num_args[(int) op];
+ }
+ } while ((buf = forward ? cairo_path_buf_next (buf) : cairo_path_buf_prev (buf)) != first);
+
+ 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_direction_t dir,
+ 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, dir,
+ _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 (path->maybe_fill_region) {
+ path->maybe_fill_region = _cairo_fixed_is_integer (offx) &&
+ _cairo_fixed_is_integer (offy) &&
+ _cairo_fixed_is_integer (scalex) &&
+ _cairo_fixed_is_integer (scaley);
+ }
+
+ 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;
+ }
+ } cairo_path_foreach_buf_end (buf, path);
+
+ 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;
+
+ 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;
+}
+
+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;
+
+ if (path->maybe_fill_region &&
+ ! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy)))
+ {
+ path->maybe_fill_region = FALSE;
+ }
+
+ path->last_move_point.x += offx;
+ path->last_move_point.y += offy;
+ path->current_point.x += offx;
+ path->current_point.y += offy;
+
+ 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;
+ }
+ } cairo_path_foreach_buf_end (buf, path);
+
+ path->extents.p1.x += offx;
+ path->extents.p1.y += offy;
+ path->extents.p2.x += offx;
+ path->extents.p2.y += offy;
+}
+
+/**
+ * _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_path_buf_t *buf;
+ unsigned int i;
+ double dx, dy;
+
+ /* XXX current_point, last_move_to */
+
+ if (matrix->yx == 0.0 && matrix->xy == 0.0) {
+ /* Fast path for the common case of scale+transform */
+ if (matrix->xx == 1. && matrix->yy == 1.) {
+ _cairo_path_fixed_translate (path,
+ _cairo_fixed_from_double (matrix->x0),
+ _cairo_fixed_from_double (matrix->y0));
+ } else {
+ _cairo_path_fixed_offset_and_scale (path,
+ _cairo_fixed_from_double (matrix->x0),
+ _cairo_fixed_from_double (matrix->y0),
+ _cairo_fixed_from_double (matrix->xx),
+ _cairo_fixed_from_double (matrix->yy));
+ }
+ return;
+ }
+
+ path->extents.p1.x = path->extents.p1.y = INT_MAX;
+ path->extents.p2.x = path->extents.p2.y = INT_MIN;
+ path->maybe_fill_region = FALSE;
+ cairo_path_foreach_buf_start (buf, path) {
+ for (i = 0; i < buf->num_points; i++) {
+ dx = _cairo_fixed_to_double (buf->points[i].x);
+ dy = _cairo_fixed_to_double (buf->points[i].y);
+
+ cairo_matrix_transform_point (matrix, &dx, &dy);
+
+ buf->points[i].x = _cairo_fixed_from_double (dx);
+ buf->points[i].y = _cairo_fixed_from_double (dy);
+
+ /* XXX need to eliminate surplus move-to's? */
+ _cairo_path_fixed_extents_add (path, &buf->points[i]);
+ }
+ } cairo_path_foreach_buf_end (buf, path);
+}
+
+cairo_bool_t
+_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path,
+ const cairo_path_fixed_t *other)
+{
+ const cairo_path_buf_t *path_buf, *other_buf;
+
+ if (path->current_point.x != other->current_point.x ||
+ path->current_point.y != other->current_point.y ||
+ path->has_current_point != other->has_current_point ||
+ path->has_curve_to != other->has_curve_to ||
+ path->is_rectilinear != other->is_rectilinear ||
+ path->maybe_fill_region != other->maybe_fill_region ||
+ path->last_move_point.x != other->last_move_point.x ||
+ path->last_move_point.y != other->last_move_point.y)
+ {
+ return FALSE;
+ }
+
+ other_buf = cairo_path_head (other);
+ cairo_path_foreach_buf_start (path_buf, path) {
+ if (path_buf->num_ops != other_buf->num_ops ||
+ path_buf->num_points != other_buf->num_points ||
+ memcmp (path_buf->op, other_buf->op,
+ sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 ||
+ memcmp (path_buf->points, other_buf->points,
+ sizeof (cairo_point_t) * path_buf->num_points) != 0)
+ {
+ return FALSE;
+ }
+ other_buf = cairo_path_buf_next (other_buf);
+ } cairo_path_foreach_buf_end (path_buf, path);
+
+ return TRUE;
+}
+
+/* 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,
+ 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_direction_t dir,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_close_path_func_t *close_path,
+ void *closure,
+ double tolerance)
+{
+ cpf_t flattener;
+
+ if (! path->has_curve_to) {
+ return _cairo_path_fixed_interpret (path, dir,
+ move_to,
+ line_to,
+ NULL,
+ close_path,
+ closure);
+ }
+
+ flattener.tolerance = tolerance;
+ flattener.move_to = move_to;
+ flattener.line_to = line_to;
+ flattener.close_path = close_path;
+ flattener.closure = closure;
+ return _cairo_path_fixed_interpret (path, dir,
+ _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;
+ }
+}
+
+/*
+ * 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 = cairo_path_head (path);
+
+ if (! path->is_rectilinear)
+ return FALSE;
+
+ /* Do we have the right number of ops? */
+ if (buf->num_ops < 4 || buf->num_ops > 6)
+ return FALSE;
+
+ /* 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;
+ }
+ }
+
+ /* 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:
+ * <informalexample><programlisting>
+ * 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);
+ * </programlisting></informalexample>
+ */
+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;
+
+ buf = cairo_path_head (path);
+ if (buf->points[0].y == buf->points[1].y)
+ 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;
+
+ if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
+ return FALSE;
+ points[2] = iter.buf->points[iter.n_point++];
+ if (! _cairo_path_fixed_iter_next_op (&iter))
+ return FALSE;
+
+ if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
+ return FALSE;
+ points[3] = iter.buf->points[iter.n_point++];
+ if (! _cairo_path_fixed_iter_next_op (&iter))
+ return FALSE;
+
+ /* Now, there are choices. The rectangle might end with a LINE_TO
+ * (to the original point), but this isn't required. If it
+ * doesn't, then it must end with a CLOSE_PATH (which may be implicit). */
+ if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO)
+ {
+ points[4] = iter.buf->points[iter.n_point++];
+ if (points[4].x != points[0].x || points[4].y != points[0].y)
+ return FALSE;
+ }
+ else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH ||
+ iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO))
+ {
+ return FALSE;
+ }
+ if (! _cairo_path_fixed_iter_next_op (&iter))
+ return FALSE;
+
+ /* Ok, we may have a box, if the points line up */
+ if (points[0].y == points[1].y &&
+ points[1].x == points[2].x &&
+ points[2].y == points[3].y &&
+ points[3].x == points[0].x)
+ {
+ box->p1 = points[0];
+ box->p2 = points[2];
+ *_iter = iter;
+ return TRUE;
+ }
+
+ if (points[0].x == points[1].x &&
+ points[1].y == points[2].y &&
+ points[2].x == points[3].x &&
+ points[3].y == points[0].y)
+ {
+ box->p1 = points[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;
+
+ if (iter->n_op == iter->buf->num_ops)
+ return TRUE;
+
+ if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO &&
+ iter->buf->num_ops == iter->n_op + 1)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
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 000000000..b344f529d
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-path-in-fill.c
@@ -0,0 +1,291 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#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_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 (path->is_empty_fill)
+ return FALSE;
+
+ _cairo_in_fill_init (&in_fill, tolerance, x, y);
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_in_fill_move_to,
+ _cairo_in_fill_line_to,
+ _cairo_in_fill_curve_to,
+ _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 000000000..61b4060fa
--- /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 <cworth@redhat.com>
+ */
+
+#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_gstate_t *gstate);
+
+cairo_private cairo_path_t *
+_cairo_path_create_flat (cairo_path_fixed_t *path,
+ cairo_gstate_t *gstate);
+
+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.c b/gfx/cairo/cairo/src/cairo-path-stroke.c
new file mode 100644
index 000000000..505b6ab6a
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-path-stroke.c
@@ -0,0 +1,2143 @@
+/* -*- 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+
+typedef struct _cairo_stroker_dash {
+ cairo_bool_t dashed;
+ unsigned int dash_index;
+ cairo_bool_t dash_on;
+ cairo_bool_t dash_starts_on;
+ double dash_remain;
+
+ double dash_offset;
+ const double *dashes;
+ unsigned int num_dashes;
+} cairo_stroker_dash_t;
+
+typedef struct cairo_stroker {
+ cairo_stroke_style_t style;
+
+ const cairo_matrix_t *ctm;
+ const cairo_matrix_t *ctm_inverse;
+ double 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_dash_start (cairo_stroker_dash_t *dash)
+{
+ double offset;
+ cairo_bool_t on = TRUE;
+ unsigned int i = 0;
+
+ if (! dash->dashed)
+ return;
+
+ offset = dash->dash_offset;
+
+ /* We stop searching for a starting point as soon as the
+ offset reaches zero. Otherwise when an initial dash
+ segment shrinks to zero it will be skipped over. */
+ while (offset > 0.0 && offset >= dash->dashes[i]) {
+ offset -= dash->dashes[i];
+ on = !on;
+ if (++i == dash->num_dashes)
+ i = 0;
+ }
+
+ dash->dash_index = i;
+ dash->dash_on = dash->dash_starts_on = on;
+ dash->dash_remain = dash->dashes[i] - offset;
+}
+
+static void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
+{
+ dash->dash_remain -= step;
+ if (dash->dash_remain <= 0.) {
+ if (++dash->dash_index == dash->num_dashes)
+ dash->dash_index = 0;
+
+ dash->dash_on = ! dash->dash_on;
+ dash->dash_remain = dash->dashes[dash->dash_index];
+ }
+}
+
+static void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+ const cairo_stroke_style_t *style)
+{
+ dash->dashed = style->dash != NULL;
+ if (! dash->dashed)
+ return;
+
+ dash->dashes = style->dash;
+ dash->num_dashes = style->num_dashes;
+ dash->dash_offset = style->dash_offset;
+
+ _cairo_stroker_dash_start (dash);
+}
+
+static cairo_status_t
+_cairo_stroker_init (cairo_stroker_t *stroker,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance)
+{
+ cairo_status_t status;
+
+ stroker->style = *stroke_style;
+ stroker->ctm = ctm;
+ stroker->ctm_inverse = ctm_inverse;
+ stroker->tolerance = tolerance;
+
+ stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
+ stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
+
+ status = _cairo_pen_init (&stroker->pen,
+ stroke_style->line_width / 2.0,
+ tolerance, ctm);
+ if (unlikely (status))
+ return status;
+
+ stroker->has_bounds = FALSE;
+
+ stroker->has_current_face = FALSE;
+ stroker->has_first_face = FALSE;
+ stroker->has_initial_sub_path = FALSE;
+
+ _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+ stroker->add_external_edge = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_stroker_limit (cairo_stroker_t *stroker,
+ const cairo_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, 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 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;
+ int start, stop, step, i, npoints;
+ cairo_status_t status;
+
+ if (clockwise) {
+ step = -1;
+
+ start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+ in_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
+ in_vector) < 0)
+ start = _range_step (start, -1, stroker->pen.num_vertices);
+
+ stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
+ out_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+ out_vector) > 0)
+ {
+ stop = _range_step (stop, 1, stroker->pen.num_vertices);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+ in_vector) < 0)
+ {
+ goto BEVEL;
+ }
+ }
+
+ npoints = start - stop;
+ } else {
+ step = 1;
+
+ start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+ in_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
+ in_vector) < 0)
+ start = _range_step (start, 1, stroker->pen.num_vertices);
+
+ stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
+ out_vector);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
+ out_vector) > 0)
+ {
+ stop = _range_step (stop, -1, stroker->pen.num_vertices);
+ if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
+ in_vector) < 0)
+ {
+ goto BEVEL;
+ }
+ }
+
+ npoints = stop - start;
+ }
+ stop = _range_step (stop, step, stroker->pen.num_vertices);
+
+ if (npoints < 0)
+ npoints += stroker->pen.num_vertices;
+ npoints += 3;
+
+ if (npoints <= 1)
+ goto BEVEL;
+
+ if (npoints > ARRAY_LENGTH (stack_points)) {
+ points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
+ if (unlikely (points == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+
+ /* Construct the fan. */
+ npoints = 0;
+ points[npoints++] = *inpt;
+ for (i = start;
+ i != stop;
+ i = _range_step (i, step, stroker->pen.num_vertices))
+ {
+ points[npoints] = *midpt;
+ _translate_point (&points[npoints], &stroker->pen.vertices[i].point);
+ npoints++;
+ }
+ points[npoints++] = *outpt;
+
+ if (stroker->add_external_edge != NULL) {
+ for (i = 0; i < npoints - 1; i++) {
+ if (clockwise) {
+ status = stroker->add_external_edge (stroker->closure,
+ &points[i], &points[i+1]);
+ } else {
+ status = stroker->add_external_edge (stroker->closure,
+ &points[i+1], &points[i]);
+ }
+ if (unlikely (status))
+ break;
+ }
+ } else {
+ status = stroker->add_triangle_fan (stroker->closure,
+ midpt, points, npoints);
+ }
+
+ 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->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;
+
+ 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, 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->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);
+
+ 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_JOIN_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;
+}
+
+/*
+ * 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_path_fixed_line_to_func_t *line_to;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ line_to = stroker->dash.dashed ?
+ _cairo_stroker_line_to_dashed :
+ _cairo_stroker_line_to;
+
+ if (! _cairo_spline_init (&spline,
+ line_to, stroker,
+ &stroker->current_point, b, c, d))
+ {
+ return line_to (closure, d);
+ }
+
+ /* If the line width is so small that the pen is reduced to a
+ single point, then we have nothing to do. */
+ if (stroker->pen.num_vertices <= 1)
+ 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, stroke_style,
+ ctm, ctm_inverse, tolerance);
+ 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_DIRECTION_FORWARD,
+ _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_to_polygon (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_polygon_t *polygon)
+{
+ cairo_stroker_t stroker;
+ cairo_status_t status;
+
+ status = _cairo_stroker_init (&stroker, stroke_style,
+ ctm, ctm_inverse, tolerance);
+ if (unlikely (status))
+ return status;
+
+ stroker.add_external_edge = _cairo_polygon_add_external_edge,
+ stroker.closure = polygon;
+
+ if (polygon->num_limits)
+ _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits);
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_stroker_move_to,
+ stroker.dash.dashed ?
+ _cairo_stroker_line_to_dashed :
+ _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_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps)
+{
+ cairo_status_t status;
+ cairo_polygon_t polygon;
+
+ /* Before we do anything else, we attempt the rectilinear
+ * stroker. It's careful to generate trapezoids that align to
+ * device-pixel boundaries when possible. Many backends can render
+ * those much faster than non-aligned trapezoids, (by using clip
+ * regions, etc.) */
+ if (path->is_rectilinear) {
+ status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
+ stroke_style,
+ ctm,
+ traps);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ _cairo_polygon_init (&polygon);
+ if (traps->num_limits)
+ _cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
+
+ 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;
+}
+
+typedef struct _segment_t {
+ cairo_point_t p1, p2;
+ cairo_bool_t is_horizontal;
+ cairo_bool_t has_join;
+} segment_t;
+
+typedef struct _cairo_rectilinear_stroker {
+ const cairo_stroke_style_t *stroke_style;
+ const cairo_matrix_t *ctm;
+
+ cairo_fixed_t half_line_width;
+ cairo_bool_t do_traps;
+ void *container;
+ cairo_point_t current_point;
+ cairo_point_t first_point;
+ cairo_bool_t open_sub_path;
+
+ cairo_stroker_dash_t dash;
+
+ cairo_bool_t has_bounds;
+ cairo_box_t bounds;
+
+ int num_segments;
+ int segments_size;
+ segment_t *segments;
+ segment_t segments_embedded[8]; /* common case is a single rectangle */
+} cairo_rectilinear_stroker_t;
+
+static void
+_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
+ const cairo_box_t *boxes,
+ int num_boxes)
+{
+ stroker->has_bounds = TRUE;
+ _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+ stroker->bounds.p1.x -= stroker->half_line_width;
+ stroker->bounds.p2.x += stroker->half_line_width;
+
+ stroker->bounds.p1.y -= stroker->half_line_width;
+ stroker->bounds.p2.y += stroker->half_line_width;
+}
+
+static cairo_bool_t
+_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_bool_t do_traps,
+ void *container)
+{
+ /* This special-case rectilinear stroker only supports
+ * miter-joined lines (not curves) and a translation-only matrix
+ * (though it could probably be extended to support a matrix with
+ * uniform, integer scaling).
+ *
+ * It also only supports horizontal and vertical line_to
+ * elements. But we don't catch that here, but instead return
+ * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
+ * non-rectilinear line_to is encountered.
+ */
+ if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
+ return FALSE;
+
+ /* If the miter limit turns right angles into bevels, then we
+ * can't use this optimization. Remember, the ratio is
+ * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
+ * which we round for safety. */
+ if (stroke_style->miter_limit < M_SQRT2)
+ return FALSE;
+
+ if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
+ stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
+ {
+ return FALSE;
+ }
+
+ if (! _cairo_matrix_has_unity_scale (ctm))
+ return FALSE;
+
+ stroker->stroke_style = stroke_style;
+ stroker->ctm = ctm;
+
+ stroker->half_line_width =
+ _cairo_fixed_from_double (stroke_style->line_width / 2.0);
+ stroker->open_sub_path = FALSE;
+ stroker->segments = stroker->segments_embedded;
+ stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
+ stroker->num_segments = 0;
+
+ _cairo_stroker_dash_init (&stroker->dash, stroke_style);
+
+ stroker->has_bounds = FALSE;
+
+ stroker->do_traps = do_traps;
+ stroker->container = container;
+
+ return TRUE;
+}
+
+static void
+_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
+{
+ if (stroker->segments != stroker->segments_embedded)
+ free (stroker->segments);
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ cairo_bool_t is_horizontal,
+ cairo_bool_t has_join)
+{
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (stroker->num_segments == stroker->segments_size) {
+ int new_size = stroker->segments_size * 2;
+ segment_t *new_segments;
+
+ if (stroker->segments == stroker->segments_embedded) {
+ new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (new_segments, stroker->segments,
+ stroker->num_segments * sizeof (segment_t));
+ } else {
+ new_segments = _cairo_realloc_ab (stroker->segments,
+ new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ stroker->segments_size = new_size;
+ stroker->segments = new_segments;
+ }
+
+ stroker->segments[stroker->num_segments].p1 = *p1;
+ stroker->segments[stroker->num_segments].p2 = *p2;
+ stroker->segments[stroker->num_segments].has_join = has_join;
+ stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
+ stroker->num_segments++;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_status_t status;
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_width = stroker->half_line_width;
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_point_t *a, *b;
+ cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ /* For each segment we generate a single rectangular
+ * trapezoid. This rectangle is based on a perpendicular
+ * extension (by half the line width) of the segment endpoints
+ * after some adjustments of the endpoints to account for caps
+ * and joins.
+ */
+
+ /* We adjust the initial point of the segment to extend the
+ * rectangle to include the previous cap or join, (this
+ * adjustment applies to all segments except for the first
+ * segment of open, butt-capped paths).
+ */
+ lengthen_initial = TRUE;
+ if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
+ lengthen_initial = FALSE;
+
+ /* The adjustment of the final point is trickier. For all but
+ * the last segment we shorten the segment at the final
+ * endpoint to not overlap with the subsequent join. For the
+ * last segment we do the same shortening if the path is
+ * closed. If the path is open and butt-capped we do no
+ * adjustment, while if it's open and square-capped we do a
+ * lengthening adjustment instead to include the cap.
+ */
+ shorten_final = TRUE;
+ lengthen_final = FALSE;
+ if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
+ shorten_final = FALSE;
+ if (line_cap == CAIRO_LINE_CAP_SQUARE)
+ lengthen_final = TRUE;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (a->y == b->y) {
+ if (a->x < b->x) {
+ if (lengthen_initial)
+ a->x -= half_line_width;
+ if (shorten_final)
+ b->x -= half_line_width;
+ else if (lengthen_final)
+ b->x += half_line_width;
+ } else {
+ if (lengthen_initial)
+ a->x += half_line_width;
+ if (shorten_final)
+ b->x += half_line_width;
+ else if (lengthen_final)
+ b->x -= half_line_width;
+ }
+
+ if (a->x > b->x) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+ } else {
+ if (a->y < b->y) {
+ if (lengthen_initial)
+ a->y -= half_line_width;
+ if (shorten_final)
+ b->y -= half_line_width;
+ else if (lengthen_final)
+ b->y += half_line_width;
+ } else {
+ if (lengthen_initial)
+ a->y += half_line_width;
+ if (shorten_final)
+ b->y += half_line_width;
+ else if (lengthen_final)
+ b->y -= half_line_width;
+ }
+
+ if (a->y > b->y) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+ }
+
+ /* Form the rectangle by expanding by half the line width in
+ * either perpendicular direction. */
+ if (a->y == b->y) {
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ }
+
+ if (stroker->do_traps) {
+ status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
+ } else {
+ cairo_box_t box;
+
+ box.p1 = *a;
+ box.p2 = *b;
+
+ status = _cairo_boxes_add (stroker->container, &box);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_status_t status;
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_width = stroker->half_line_width;
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_point_t *a, *b;
+ cairo_bool_t is_horizontal;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ is_horizontal = stroker->segments[i].is_horizontal;
+
+ /* Handle the joins for a potentially degenerate segment. */
+ if (line_cap == CAIRO_LINE_CAP_BUTT &&
+ stroker->segments[i].has_join &&
+ (i != stroker->num_segments - 1 ||
+ (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
+ {
+ cairo_point_t p1 = stroker->segments[i].p1;
+ cairo_point_t p2 = stroker->segments[i].p2;
+ cairo_slope_t out_slope;
+ int j = (i + 1) % stroker->num_segments;
+
+ _cairo_slope_init (&out_slope,
+ &stroker->segments[j].p1,
+ &stroker->segments[j].p2);
+
+ if (is_horizontal) {
+ if (p1.x <= p2.x) {
+ p1.x = p2.x;
+ p2.x += half_line_width;
+ } else {
+ p1.x = p2.x - half_line_width;
+ }
+ if (out_slope.dy >= 0)
+ p1.y -= half_line_width;
+ if (out_slope.dy <= 0)
+ p2.y += half_line_width;
+ } else {
+ if (p1.y <= p2.y) {
+ p1.y = p2.y;
+ p2.y += half_line_width;
+ } else {
+ p1.y = p2.y - half_line_width;
+ }
+ if (out_slope.dx >= 0)
+ p1.x -= half_line_width;
+ if (out_slope.dx <= 0)
+ p2.x += half_line_width;
+ }
+
+ if (stroker->do_traps) {
+ status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2);
+ } else {
+ cairo_box_t box;
+
+ box.p1 = p1;
+ box.p2 = p2;
+
+ status = _cairo_boxes_add (stroker->container, &box);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (is_horizontal) {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->x <= b->x) {
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ } else {
+ a->x += half_line_width;
+ b->x -= half_line_width;
+ }
+ }
+
+ if (a->x > b->x) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->y <= b->y) {
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ a->y += half_line_width;
+ b->y -= half_line_width;
+ }
+ }
+
+ if (a->y > b->y) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ }
+
+ if (a->x == b->x && a->y == b->y)
+ continue;
+
+ if (stroker->do_traps) {
+ status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
+ } else {
+ cairo_box_t box;
+
+ box.p1 = *a;
+ box.p2 = *b;
+
+ status = _cairo_boxes_add (stroker->container, &box);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (unlikely (status))
+ return status;
+
+ /* reset the dash pattern for new sub paths */
+ _cairo_stroker_dash_start (&stroker->dash);
+
+ stroker->current_point = *point;
+ stroker->first_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to (void *closure,
+ const cairo_point_t *b)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_point_t *a = &stroker->current_point;
+ cairo_status_t status;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+ a->y == b->y,
+ TRUE);
+
+ stroker->current_point = *b;
+ stroker->open_sub_path = TRUE;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to_dashed (void *closure,
+ const cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ const cairo_point_t *a = &stroker->current_point;
+ const cairo_point_t *b = point;
+ cairo_bool_t fully_in_bounds;
+ double sign, remain;
+ cairo_fixed_t mag;
+ cairo_status_t status;
+ cairo_line_t segment;
+ cairo_bool_t dash_on = FALSE;
+ cairo_bool_t is_horizontal;
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ fully_in_bounds = TRUE;
+ if (stroker->has_bounds &&
+ (! _cairo_box_contains_point (&stroker->bounds, a) ||
+ ! _cairo_box_contains_point (&stroker->bounds, b)))
+ {
+ fully_in_bounds = FALSE;
+ }
+
+ is_horizontal = a->y == b->y;
+ if (is_horizontal)
+ mag = b->x - a->x;
+ else
+ mag = b->y - a->y;
+ if (mag < 0) {
+ remain = _cairo_fixed_to_double (-mag);
+ sign = 1.;
+ } else {
+ remain = _cairo_fixed_to_double (mag);
+ sign = -1.;
+ }
+
+ segment.p2 = segment.p1 = *a;
+ while (remain > 0.) {
+ double step_length;
+
+ step_length = MIN (stroker->dash.dash_remain, remain);
+ remain -= step_length;
+
+ mag = _cairo_fixed_from_double (sign*remain);
+ if (is_horizontal)
+ segment.p2.x = b->x + mag;
+ else
+ segment.p2.y = b->y + mag;
+
+ if (stroker->dash.dash_on &&
+ (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+ status = _cairo_rectilinear_stroker_add_segment (stroker,
+ &segment.p1,
+ &segment.p2,
+ is_horizontal,
+ remain <= 0.);
+ if (unlikely (status))
+ return status;
+
+ dash_on = TRUE;
+ }
+ else
+ {
+ dash_on = FALSE;
+ }
+
+ _cairo_stroker_dash_step (&stroker->dash, step_length);
+ segment.p1 = segment.p2;
+ }
+
+ if (stroker->dash.dash_on && ! dash_on &&
+ (fully_in_bounds ||
+ _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
+ {
+
+ /* This segment ends on a transition to dash_on, compute a new face
+ * and add cap for the beginning of the next dash_on step.
+ */
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker,
+ &segment.p1,
+ &segment.p1,
+ is_horizontal,
+ TRUE);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->current_point = *point;
+ stroker->open_sub_path = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_close_path (void *closure)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ /* We don't draw anything for degenerate paths. */
+ if (! stroker->open_sub_path)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (stroker->dash.dashed) {
+ status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
+ &stroker->first_point);
+ } else {
+ status = _cairo_rectilinear_stroker_line_to (stroker,
+ &stroker->first_point);
+ }
+ if (unlikely (status))
+ return status;
+
+ stroker->open_sub_path = FALSE;
+
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_traps_t *traps)
+{
+ cairo_rectilinear_stroker_t rectilinear_stroker;
+ cairo_int_status_t status;
+
+ assert (path->is_rectilinear);
+
+ if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+ stroke_style, ctm,
+ TRUE, traps))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (traps->num_limits) {
+ _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+ traps->limits,
+ traps->num_limits);
+ }
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_rectilinear_stroker_move_to,
+ rectilinear_stroker.dash.dashed ?
+ _cairo_rectilinear_stroker_line_to_dashed :
+ _cairo_rectilinear_stroker_line_to,
+ NULL,
+ _cairo_rectilinear_stroker_close_path,
+ &rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (rectilinear_stroker.dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+
+ traps->is_rectilinear = 1;
+ traps->is_rectangular = 1;
+ /* As we incrementally tessellate, we do not eliminate self-intersections */
+ traps->has_intersections = traps->num_traps > 1;
+BAIL:
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+
+ if (unlikely (status))
+ _cairo_traps_clear (traps);
+
+ return status;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_boxes_t *boxes)
+{
+ cairo_rectilinear_stroker_t rectilinear_stroker;
+ cairo_int_status_t status;
+
+ assert (path->is_rectilinear);
+
+ if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+ stroke_style, ctm,
+ FALSE, boxes))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (boxes->num_limits) {
+ _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+ boxes->limits,
+ boxes->num_limits);
+ }
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_rectilinear_stroker_move_to,
+ rectilinear_stroker.dash.dashed ?
+ _cairo_rectilinear_stroker_line_to_dashed :
+ _cairo_rectilinear_stroker_line_to,
+ NULL,
+ _cairo_rectilinear_stroker_close_path,
+ &rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (rectilinear_stroker.dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ /* As we incrementally tessellate, we do not eliminate self-intersections */
+ status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
+ CAIRO_FILL_RULE_WINDING,
+ boxes);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+ _cairo_boxes_clear (boxes);
+ return status;
+}
diff --git a/gfx/cairo/cairo/src/cairo-path.c b/gfx/cairo/cairo/src/cairo-path.c
new file mode 100644
index 000000000..28182c0e4
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-path.c
@@ -0,0 +1,536 @@
+/* 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 <cworth@redhat.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-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;
+ cairo_point_t current_point;
+} cpc_t;
+
+static cairo_status_t
+_cpc_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ cpc_t *cpc = closure;
+
+ cpc->count += 2;
+
+ cpc->current_point = *point;
+
+ 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;
+
+ cpc->current_point = *point;
+
+ 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;
+
+ cpc->current_point = *p3;
+
+ 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;
+ cpc.current_point.x = 0;
+ cpc.current_point.y = 0;
+
+ if (flatten) {
+ status = _cairo_path_fixed_interpret_flat (path_fixed,
+ CAIRO_DIRECTION_FORWARD,
+ _cpc_move_to,
+ _cpc_line_to,
+ _cpc_close_path,
+ &cpc,
+ tolerance);
+ } else {
+ status = _cairo_path_fixed_interpret (path_fixed,
+ CAIRO_DIRECTION_FORWARD,
+ _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_gstate_t *gstate;
+ cairo_point_t current_point;
+} 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_gstate_backend_to_user (cpp->gstate, &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;
+
+ cpp->current_point = *point;
+
+ 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_gstate_backend_to_user (cpp->gstate, &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;
+
+ cpp->current_point = *point;
+
+ 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_gstate_backend_to_user (cpp->gstate, &x1, &y1);
+
+ x2 = _cairo_fixed_to_double (p2->x);
+ y2 = _cairo_fixed_to_double (p2->y);
+ _cairo_gstate_backend_to_user (cpp->gstate, &x2, &y2);
+
+ x3 = _cairo_fixed_to_double (p3->x);
+ y3 = _cairo_fixed_to_double (p3->y);
+ _cairo_gstate_backend_to_user (cpp->gstate, &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;
+
+ cpp->current_point = *p3;
+
+ 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_gstate_t *gstate,
+ cairo_bool_t flatten)
+{
+ cairo_status_t status;
+ cpp_t cpp;
+
+ cpp.data = path->data;
+ cpp.gstate = gstate;
+ cpp.current_point.x = 0;
+ cpp.current_point.y = 0;
+
+ if (flatten) {
+ double tolerance = _cairo_gstate_get_tolerance (gstate);
+ status = _cairo_path_fixed_interpret_flat (path_fixed,
+ CAIRO_DIRECTION_FORWARD,
+ _cpp_move_to,
+ _cpp_line_to,
+ _cpp_close_path,
+ &cpp,
+ tolerance);
+ } else {
+ status = _cairo_path_fixed_interpret (path_fixed,
+ CAIRO_DIRECTION_FORWARD,
+ _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 = 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_gstate_t *gstate,
+ cairo_bool_t flatten)
+{
+ cairo_path_t *path;
+
+ path = malloc (sizeof (cairo_path_t));
+ if (unlikely (path == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_path_t*) &_cairo_path_nil;
+ }
+
+ path->num_data = _cairo_path_count (path, path_fixed,
+ _cairo_gstate_get_tolerance (gstate),
+ 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,
+ gstate, 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.
+ **/
+void
+cairo_path_destroy (cairo_path_t *path)
+{
+ if (path == NULL || path == &_cairo_path_nil)
+ return;
+
+ if (path->data)
+ free (path->data);
+
+ free (path);
+}
+
+/**
+ * _cairo_path_create:
+ * @path: a fixed-point, device-space path to be converted and copied
+ * @gstate: the current graphics state
+ *
+ * Creates a user-space #cairo_path_t copy of the given device-space
+ * @path. The @gstate 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_gstate_t *gstate)
+{
+ return _cairo_path_create_internal (path, gstate, FALSE);
+}
+
+/**
+ * _cairo_path_create_flat:
+ * @path: a fixed-point, device-space path to be flattened, converted and copied
+ * @gstate: the current graphics state
+ *
+ * Creates a flattened, user-space #cairo_path_t copy of the given
+ * device-space @path. The @gstate 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_gstate_t *gstate)
+{
+ return _cairo_path_create_internal (path, gstate, 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;
+ cairo_fixed_t x1_fixed, y1_fixed;
+ cairo_fixed_t x2_fixed, y2_fixed;
+ cairo_fixed_t x3_fixed, y3_fixed;
+ cairo_matrix_t user_to_backend;
+ cairo_status_t status;
+ double x, y;
+
+ user_to_backend = cr->gstate->ctm;
+ cairo_matrix_multiply (&user_to_backend,
+ &user_to_backend,
+ &cr->gstate->target->device_transform);
+
+ end = &path->data[path->num_data];
+ for (p = &path->data[0]; p < end; p += p->header.length) {
+ switch (p->header.type) {
+ case CAIRO_PATH_MOVE_TO:
+ if (unlikely (p->header.length < 2))
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+ x = p[1].point.x, y = p[1].point.y;
+ cairo_matrix_transform_point (&user_to_backend, &x, &y);
+ x1_fixed = _cairo_fixed_from_double (x);
+ y1_fixed = _cairo_fixed_from_double (y);
+
+ status = _cairo_path_fixed_move_to (cr->path, x1_fixed, y1_fixed);
+ break;
+
+ case CAIRO_PATH_LINE_TO:
+ if (unlikely (p->header.length < 2))
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+ x = p[1].point.x, y = p[1].point.y;
+ cairo_matrix_transform_point (&user_to_backend, &x, &y);
+ x1_fixed = _cairo_fixed_from_double (x);
+ y1_fixed = _cairo_fixed_from_double (y);
+
+ status = _cairo_path_fixed_line_to (cr->path, x1_fixed, y1_fixed);
+ break;
+
+ case CAIRO_PATH_CURVE_TO:
+ if (unlikely (p->header.length < 4))
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+ x = p[1].point.x, y = p[1].point.y;
+ cairo_matrix_transform_point (&user_to_backend, &x, &y);
+ x1_fixed = _cairo_fixed_from_double (x);
+ y1_fixed = _cairo_fixed_from_double (y);
+
+ x = p[2].point.x, y = p[2].point.y;
+ cairo_matrix_transform_point (&user_to_backend, &x, &y);
+ x2_fixed = _cairo_fixed_from_double (x);
+ y2_fixed = _cairo_fixed_from_double (y);
+
+ x = p[3].point.x, y = p[3].point.y;
+ cairo_matrix_transform_point (&user_to_backend, &x, &y);
+ x3_fixed = _cairo_fixed_from_double (x);
+ y3_fixed = _cairo_fixed_from_double (y);
+
+ status = _cairo_path_fixed_curve_to (cr->path,
+ x1_fixed, y1_fixed,
+ x2_fixed, y2_fixed,
+ x3_fixed, y3_fixed);
+ break;
+
+ case CAIRO_PATH_CLOSE_PATH:
+ if (unlikely (p->header.length < 1))
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+
+ status = _cairo_path_fixed_close_path (cr->path);
+ break;
+
+ default:
+ return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
+ }
+
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/gfx/cairo/cairo/src/cairo-pattern.c b/gfx/cairo/cairo/src/cairo-pattern.c
new file mode 100644
index 000000000..0c51804f8
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-pattern.c
@@ -0,0 +1,3228 @@
+/* -*- 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 <davidr@novell.com>
+ * Keith Packard <keithp@keithp.com>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-freed-pool-private.h"
+
+/**
+ * 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_<emphasis>type</emphasis>()
+ * or implicitly through
+ * cairo_set_source_<emphasis>type</emphasis>() functions.
+ */
+
+#if HAS_FREED_POOL
+static freed_pool_t freed_pattern_pool[4];
+#endif
+
+static const cairo_solid_pattern_t _cairo_pattern_nil = {
+ { CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_NO_MEMORY, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ CAIRO_FILTER_DEFAULT, /* filter */
+ CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
+};
+
+static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = {
+ { CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_NULL_POINTER, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ CAIRO_FILTER_DEFAULT, /* filter */
+ CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
+};
+
+const cairo_solid_pattern_t _cairo_pattern_black = {
+ { CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_SUCCESS, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ CAIRO_FILTER_DEFAULT, /* filter */
+ CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
+ { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */
+};
+
+const cairo_solid_pattern_t _cairo_pattern_clear = {
+ { CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_SUCCESS, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ CAIRO_FILTER_DEFAULT, /* filter */
+ CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
+ { 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */
+};
+
+const cairo_solid_pattern_t _cairo_pattern_white = {
+ { CAIRO_PATTERN_TYPE_SOLID, /* type */
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_SUCCESS, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ { 1., 0., 0., 1., 0., 0., }, /* matrix */
+ CAIRO_FILTER_DEFAULT, /* filter */
+ CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
+ { 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */
+};
+
+/**
+ * _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);
+}
+
+static void
+_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
+{
+#if HAVE_VALGRIND
+ switch (type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t));
+ break;
+ }
+#endif
+
+ pattern->type = type;
+ pattern->status = CAIRO_STATUS_SUCCESS;
+
+ /* 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)
+ pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT;
+ else
+ pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT;
+
+ pattern->filter = CAIRO_FILTER_DEFAULT;
+
+ pattern->has_component_alpha = FALSE;
+
+ cairo_matrix_init_identity (&pattern->matrix);
+}
+
+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;
+}
+
+cairo_status_t
+_cairo_pattern_init_copy (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other)
+{
+ 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;
+ cairo_status_t status;
+
+ if (other->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)));
+ } else {
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)));
+ }
+
+ status = _cairo_gradient_pattern_init_copy (dst, src);
+ if (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);
+
+ 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;
+ }
+
+ memcpy (pattern, other, size);
+
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
+ _cairo_user_data_array_init (&pattern->user_data);
+}
+
+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);
+
+ if (surface_pattern->surface->status)
+ return surface_pattern->surface->status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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;
+ }
+
+#if HAVE_VALGRIND
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_solid_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_surface_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_linear_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t));
+ break;
+ }
+#endif
+}
+
+cairo_status_t
+_cairo_pattern_create_copy (cairo_pattern_t **pattern_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 = malloc (sizeof (cairo_solid_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ pattern = malloc (sizeof (cairo_surface_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ pattern = malloc (sizeof (cairo_linear_pattern_t));
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ pattern = malloc (sizeof (cairo_radial_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;
+}
+
+void
+_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
+ double x0, double y0, double x1, double y1)
+{
+ _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR);
+
+ pattern->p1.x = _cairo_fixed_from_double (x0);
+ pattern->p1.y = _cairo_fixed_from_double (y0);
+ pattern->p2.x = _cairo_fixed_from_double (x1);
+ pattern->p2.y = _cairo_fixed_from_double (y1);
+}
+
+void
+_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
+ double cx0, double cy0, double radius0,
+ double cx1, double cy1, double radius1)
+{
+ _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL);
+
+ pattern->c1.x = _cairo_fixed_from_double (cx0);
+ pattern->c1.y = _cairo_fixed_from_double (cy0);
+ pattern->r1 = _cairo_fixed_from_double (fabs (radius0));
+ pattern->c2.x = _cairo_fixed_from_double (cx1);
+ pattern->c2.y = _cairo_fixed_from_double (cy1);
+ pattern->r2 = _cairo_fixed_from_double (fabs (radius1));
+}
+
+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 = 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().
+ **/
+cairo_pattern_t *
+cairo_pattern_create_rgb (double red, double green, double blue)
+{
+ cairo_color_t color;
+
+ red = _cairo_restrict_value (red, 0.0, 1.0);
+ green = _cairo_restrict_value (green, 0.0, 1.0);
+ blue = _cairo_restrict_value (blue, 0.0, 1.0);
+
+ _cairo_color_init_rgb (&color, red, green, blue);
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ return _cairo_pattern_create_solid (&color);
+}
+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().
+ **/
+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().
+ **/
+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 = 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().
+ **/
+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 = 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().
+ **/
+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 = 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;
+}
+
+/**
+ * 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.
+ *
+ * The number of references to a #cairo_pattern_t can be get using
+ * cairo_pattern_get_reference_count().
+ *
+ * Return value: the referenced #cairo_pattern_t.
+ **/
+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
+ *
+ * This function returns the type a pattern.
+ * 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, or
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
+ **/
+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().
+ **/
+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 */
+ _freed_pool_put (&freed_pattern_pool[type], 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);
+}
+
+/* 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_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.
+ **/
+void
+cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
+ double offset,
+ double red,
+ double green,
+ double blue)
+{
+ if (pattern->status)
+ return;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
+ pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ offset = _cairo_restrict_value (offset, 0.0, 1.0);
+ red = _cairo_restrict_value (red, 0.0, 1.0);
+ green = _cairo_restrict_value (green, 0.0, 1.0);
+ blue = _cairo_restrict_value (blue, 0.0, 1.0);
+
+ _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
+ offset, red, green, blue, 1.0);
+}
+
+/**
+ * cairo_pattern_add_color_stop_rgba:
+ * @pattern: 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.
+ */
+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);
+}
+
+/**
+ * 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:
+ *
+ * <informalexample><programlisting>
+ * cairo_matrix_init_scale (&amp;matrix, 0.5, 0.5);
+ * cairo_pattern_set_matrix (pattern, &amp;matrix);
+ * </programlisting></informalexample>
+ *
+ * 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().
+ **/
+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;
+
+ 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.
+ **/
+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:
+ *
+ * <informalexample><programlisting>
+ * cairo_set_source_surface (cr, image, x, y);
+ * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
+ * </programlisting></informalexample>
+ **/
+void
+cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
+{
+ if (pattern->status)
+ return;
+
+ pattern->filter = 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.
+ **/
+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.
+ **/
+void
+cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
+{
+ if (pattern->status)
+ return;
+
+ pattern->extend = 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.
+ **/
+cairo_extend_t
+cairo_pattern_get_extend (cairo_pattern_t *pattern)
+{
+ return pattern->extend;
+}
+slim_hidden_def (cairo_pattern_get_extend);
+
+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 void
+_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern,
+ double offset_x,
+ double offset_y,
+ int width,
+ int height,
+ cairo_bool_t *is_horizontal,
+ cairo_bool_t *is_vertical)
+{
+ cairo_point_double_t point0, point1;
+ double a, b, c, d, tx, ty;
+ double scale, start, dx, dy;
+ cairo_fixed_t factors[3];
+ int i;
+
+ /* To classify a pattern as horizontal or vertical, we first
+ * compute the (fixed point) factors at the corners of the
+ * pattern. We actually only need 3/4 corners, so we skip the
+ * fourth.
+ */
+ point0.x = _cairo_fixed_to_double (pattern->p1.x);
+ point0.y = _cairo_fixed_to_double (pattern->p1.y);
+ point1.x = _cairo_fixed_to_double (pattern->p2.x);
+ point1.y = _cairo_fixed_to_double (pattern->p2.y);
+
+ _cairo_matrix_get_affine (&pattern->base.base.matrix,
+ &a, &b, &c, &d, &tx, &ty);
+
+ dx = point1.x - point0.x;
+ dy = point1.y - point0.y;
+ scale = dx * dx + dy * dy;
+ scale = (scale) ? 1.0 / scale : 1.0;
+
+ start = dx * point0.x + dy * point0.y;
+
+ for (i = 0; i < 3; i++) {
+ double qx_device = (i % 2) * (width - 1) + offset_x;
+ double qy_device = (i / 2) * (height - 1) + offset_y;
+
+ /* transform fragment into pattern space */
+ double qx = a * qx_device + c * qy_device + tx;
+ double qy = b * qx_device + d * qy_device + ty;
+
+ factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale);
+ }
+
+ /* We consider a pattern to be vertical if the fixed point factor
+ * at the two upper corners is the same. We could accept a small
+ * change, but determining what change is acceptable would require
+ * sorting the stops in the pattern and looking at the differences.
+ *
+ * Horizontal works the same way with the two left corners.
+ */
+
+ *is_vertical = factors[1] == factors[0];
+ *is_horizontal = factors[2] == factors[0];
+}
+
+static cairo_int_status_t
+_cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ cairo_surface_t **out,
+ cairo_surface_attributes_t *attr)
+{
+ cairo_image_surface_t *image;
+ pixman_image_t *pixman_image;
+ pixman_transform_t pixman_transform;
+ cairo_status_t status;
+ cairo_bool_t repeat = FALSE;
+ cairo_bool_t opaque = TRUE;
+
+ pixman_gradient_stop_t pixman_stops_static[2];
+ pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
+ unsigned int i;
+ int clone_offset_x, clone_offset_y;
+ cairo_matrix_t matrix = pattern->base.matrix;
+
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
+ pixman_stops = _cairo_malloc_ab (pattern->n_stops,
+ sizeof(pixman_gradient_stop_t));
+ if (unlikely (pixman_stops == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < pattern->n_stops; i++) {
+ pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
+ pixman_stops[i].color.red = pattern->stops[i].color.red_short;
+ pixman_stops[i].color.green = pattern->stops[i].color.green_short;
+ pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
+ pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
+ if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (pixman_stops[i].color.alpha))
+ opaque = FALSE;
+ }
+
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
+ pixman_point_fixed_t p1, p2;
+ double x0, y0, x1, y1, maxabs;
+
+ /*
+ * Transform the matrix to avoid overflow when converting between
+ * cairo_fixed_t and pixman_fixed_t (without incurring performance
+ * loss when the transformation is unnecessary).
+ *
+ * Having a function to compute the required transformation to
+ * "normalize" a given bounding box would be generally useful -
+ * cf linear patterns, gradient patterns, surface patterns...
+ */
+ x0 = _cairo_fixed_to_double (linear->p1.x);
+ y0 = _cairo_fixed_to_double (linear->p1.y);
+ x1 = _cairo_fixed_to_double (linear->p2.x);
+ y1 = _cairo_fixed_to_double (linear->p2.y);
+ cairo_matrix_transform_point (&matrix, &x0, &y0);
+ cairo_matrix_transform_point (&matrix, &x1, &y1);
+ maxabs = MAX (MAX (fabs (x0), fabs (x1)), MAX (fabs (y0), fabs (y1)));
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+ if (maxabs > PIXMAN_MAX_INT)
+ {
+ double sf;
+ cairo_matrix_t scale;
+
+ sf = PIXMAN_MAX_INT / maxabs;
+
+ p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
+ p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
+ p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
+ p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
+
+ /* cairo_matrix_scale does a pre-scale, we want a post-scale */
+ cairo_matrix_init_scale (&scale, sf, sf);
+ cairo_matrix_multiply (&matrix, &matrix, &scale);
+ }
+ else
+ {
+ p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
+ p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
+ p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
+ p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
+ }
+
+ pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
+ pixman_stops,
+ pattern->n_stops);
+ }
+ else
+ {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
+ pixman_point_fixed_t c1, c2;
+ pixman_fixed_t r1, r2;
+
+ c1.x = _cairo_fixed_to_16_16 (radial->c1.x);
+ c1.y = _cairo_fixed_to_16_16 (radial->c1.y);
+ r1 = _cairo_fixed_to_16_16 (radial->r1);
+
+ c2.x = _cairo_fixed_to_16_16 (radial->c2.x);
+ c2.y = _cairo_fixed_to_16_16 (radial->c2.y);
+ r2 = _cairo_fixed_to_16_16 (radial->r2);
+
+ pixman_image = pixman_image_create_radial_gradient (&c1, &c2,
+ r1, r2,
+ pixman_stops,
+ pattern->n_stops);
+ }
+
+ if (pixman_stops != pixman_stops_static)
+ free (pixman_stops);
+
+ if (unlikely (pixman_image == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (_cairo_surface_is_image (dst))
+ {
+ image = (cairo_image_surface_t *)
+ _cairo_image_surface_create_for_pixman_image (pixman_image,
+ PIXMAN_a8r8g8b8);
+ if (image->base.status)
+ {
+ pixman_image_unref (pixman_image);
+ return image->base.status;
+ }
+
+ attr->x_offset = attr->y_offset = 0;
+ attr->matrix = matrix;
+ attr->extend = pattern->base.extend;
+ attr->filter = CAIRO_FILTER_NEAREST;
+ attr->has_component_alpha = pattern->base.has_component_alpha;
+
+ *out = &image->base;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_bool_t is_horizontal;
+ cairo_bool_t is_vertical;
+
+ _cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern,
+ x, y, width, height,
+ &is_horizontal, &is_vertical);
+ if (is_horizontal) {
+ height = 1;
+ repeat = TRUE;
+ }
+ /* width-1 repeating patterns are quite slow with scan-line based
+ * compositing code, so we use a wider strip and spend some extra
+ * expense in computing the gradient. It's possible that for narrow
+ * gradients we'd be better off using a 2 or 4 pixel strip; the
+ * wider the gradient, the more it's worth spending extra time
+ * computing a sample.
+ */
+ if (is_vertical && width > 8) {
+ width = 8;
+ repeat = TRUE;
+ }
+ }
+
+ if (! pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR,
+ NULL, 0))
+ {
+ pixman_image_unref (pixman_image);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ image = (cairo_image_surface_t *)
+ cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ if (image->base.status) {
+ pixman_image_unref (pixman_image);
+ return image->base.status;
+ }
+
+ _cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform,
+ width/2., height/2.);
+ if (!pixman_image_set_transform (pixman_image, &pixman_transform)) {
+ cairo_surface_destroy (&image->base);
+ pixman_image_unref (pixman_image);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ switch (pattern->base.extend) {
+ case CAIRO_EXTEND_NONE:
+ pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NONE);
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NORMAL);
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_REFLECT);
+ break;
+ case CAIRO_EXTEND_PAD:
+ pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_PAD);
+ break;
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ pixman_image,
+ NULL,
+ image->pixman_image,
+ x, y,
+ 0, 0,
+ 0, 0,
+ width, height);
+
+ pixman_image_unref (pixman_image);
+
+ _cairo_debug_check_image_surface_is_defined (&image->base);
+
+ status = _cairo_surface_clone_similar (dst, &image->base,
+ 0, 0, width, height,
+ &clone_offset_x,
+ &clone_offset_y,
+ out);
+
+ cairo_surface_destroy (&image->base);
+
+ attr->x_offset = -x;
+ attr->y_offset = -y;
+ cairo_matrix_init_identity (&attr->matrix);
+ attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE;
+ attr->filter = CAIRO_FILTER_NEAREST;
+ attr->has_component_alpha = pattern->base.has_component_alpha;
+
+ return status;
+}
+
+/* We maintain a small cache here, because we don't want to constantly
+ * recreate surfaces for simple solid colors. */
+#define MAX_SURFACE_CACHE_SIZE 16
+static struct {
+ struct _cairo_pattern_solid_surface_cache{
+ cairo_color_t color;
+ cairo_surface_t *surface;
+ } cache[MAX_SURFACE_CACHE_SIZE];
+ int size;
+} solid_surface_cache;
+
+static cairo_bool_t
+_cairo_pattern_solid_surface_matches (
+ const struct _cairo_pattern_solid_surface_cache *cache,
+ const cairo_solid_pattern_t *pattern,
+ cairo_surface_t *dst)
+{
+ if (cairo_surface_get_content (cache->surface) != _cairo_color_get_content (&pattern->color))
+ return FALSE;
+
+ if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1)
+ return FALSE;
+
+ if (! _cairo_surface_is_similar (cache->surface, dst))
+ return FALSE;
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_cairo_pattern_solid_surface_matches_color (
+ const struct _cairo_pattern_solid_surface_cache *cache,
+ const cairo_solid_pattern_t *pattern,
+ cairo_surface_t *dst)
+{
+ if (! _cairo_color_equal (&cache->color, &pattern->color))
+ return FALSE;
+
+ return _cairo_pattern_solid_surface_matches (cache, pattern, dst);
+}
+
+static cairo_int_status_t
+_cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ cairo_surface_t **out,
+ cairo_surface_attributes_t *attribs)
+{
+ static int i;
+
+ cairo_surface_t *surface, *to_destroy = NULL;
+ cairo_status_t status;
+
+ CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
+
+ /* Check cache first */
+ if (i < solid_surface_cache.size &&
+ _cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i],
+ pattern,
+ dst))
+ {
+ goto DONE;
+ }
+
+ for (i = 0 ; i < solid_surface_cache.size; i++) {
+ if (_cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i],
+ pattern,
+ dst))
+ {
+ goto DONE;
+ }
+ }
+
+ /* Choose a surface to repaint/evict */
+ surface = NULL;
+ if (solid_surface_cache.size == MAX_SURFACE_CACHE_SIZE) {
+ i = rand () % MAX_SURFACE_CACHE_SIZE;
+ surface = solid_surface_cache.cache[i].surface;
+
+ if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
+ pattern,
+ dst))
+ {
+ /* Reuse the surface instead of evicting */
+ status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern);
+ if (unlikely (status))
+ goto EVICT;
+
+ cairo_surface_reference (surface);
+ }
+ else
+ {
+ EVICT:
+ surface = NULL;
+ }
+ }
+
+ if (surface == NULL) {
+ /* Not cached, need to create new */
+ surface = _cairo_surface_create_solid_pattern_surface (dst, pattern);
+ if (surface == NULL) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto UNLOCK;
+ }
+ if (unlikely (surface->status)) {
+ status = surface->status;
+ goto UNLOCK;
+ }
+
+ if (unlikely (! _cairo_surface_is_similar (surface, dst)))
+ {
+ /* In the rare event of a substitute surface being returned,
+ * don't cache the fallback.
+ */
+ *out = surface;
+ goto NOCACHE;
+ }
+ }
+
+ if (i == solid_surface_cache.size)
+ solid_surface_cache.size++;
+
+ to_destroy = solid_surface_cache.cache[i].surface;
+ solid_surface_cache.cache[i].surface = surface;
+ solid_surface_cache.cache[i].color = pattern->color;
+
+DONE:
+ *out = cairo_surface_reference (solid_surface_cache.cache[i].surface);
+
+NOCACHE:
+ attribs->x_offset = attribs->y_offset = 0;
+ cairo_matrix_init_identity (&attribs->matrix);
+ attribs->extend = CAIRO_EXTEND_REPEAT;
+ attribs->filter = CAIRO_FILTER_NEAREST;
+ attribs->has_component_alpha = pattern->base.has_component_alpha;
+
+ status = CAIRO_STATUS_SUCCESS;
+
+UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
+
+ if (to_destroy)
+ cairo_surface_destroy (to_destroy);
+
+ return status;
+}
+
+static void
+_cairo_pattern_reset_solid_surface_cache (void)
+{
+ CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
+
+ /* remove surfaces starting from the end so that solid_surface_cache.cache
+ * is always in a consistent state when we release the mutex. */
+ while (solid_surface_cache.size) {
+ cairo_surface_t *surface;
+
+ solid_surface_cache.size--;
+ surface = solid_surface_cache.cache[solid_surface_cache.size].surface;
+ solid_surface_cache.cache[solid_surface_cache.size].surface = NULL;
+
+ /* release the lock to avoid the possibility of a recursive
+ * deadlock when the surface destroy closure gets called */
+ CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
+ cairo_surface_destroy (surface);
+ CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
+ }
+
+ CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
+}
+
+static void
+_extents_to_linear_parameter (const cairo_linear_pattern_t *linear,
+ const cairo_rectangle_int_t *extents,
+ double t[2])
+{
+ double t0, tdx, tdy;
+ double p1x, p1y, pdx, pdy, invsqnorm;
+
+ p1x = _cairo_fixed_to_double (linear->p1.x);
+ p1y = _cairo_fixed_to_double (linear->p1.y);
+ pdx = _cairo_fixed_to_double (linear->p2.x) - p1x;
+ pdy = _cairo_fixed_to_double (linear->p2.y) - p1y;
+ invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
+ pdx *= invsqnorm;
+ pdy *= invsqnorm;
+
+ t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy;
+ tdx = extents->width * pdx;
+ tdy = extents->height * pdy;
+
+ t[0] = t[1] = t0;
+ if (tdx < 0)
+ t[0] += tdx;
+ else
+ t[1] += tdx;
+
+ if (tdy < 0)
+ t[0] += tdy;
+ else
+ t[1] += tdy;
+}
+
+static cairo_bool_t
+_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
+{
+ return linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y;
+}
+
+static cairo_bool_t
+_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial)
+{
+ return radial->r1 == radial->r2 &&
+ (radial->r1 == 0 /* && radial->r2 == 0 */ ||
+ (radial->c1.x == radial->c2.x && radial->c1.y == radial->c2.y));
+}
+
+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;
+
+ /* Check if the extents intersect the drawn part of the pattern. */
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ /* EXTEND_NONE degenerate linear gradients are clear */
+ if (_linear_pattern_is_degenerate (linear))
+ return TRUE;
+
+ if (extents != NULL) {
+ double t[2];
+ _extents_to_linear_parameter (linear, extents, t);
+ if ((t[0] <= 0.0 && t[1] <= 0.0) || (t[0] >= 1.0 && t[1] >= 1.0))
+ return TRUE;
+ }
+ }
+ } else {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+ /* degenerate radial gradients are clear */
+ if (_radial_pattern_is_degenerate (radial) && FALSE)
+ return TRUE;
+ /* TODO: check actual intersection */
+ }
+
+ for (i = 0; i < gradient->n_stops; i++)
+ if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color))
+ return FALSE;
+
+ 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, degenerate linear */
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ 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;
+
+ _extents_to_linear_parameter (linear, extents, t);
+ if (t[0] < 0.0 || t[1] > 1.0)
+ 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_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 *r)
+{
+ if (pattern->surface->content & CAIRO_CONTENT_ALPHA)
+ return FALSE;
+
+ if (pattern->base.extend != CAIRO_EXTEND_NONE)
+ return TRUE;
+
+ if (r != NULL) {
+ cairo_rectangle_int_t extents;
+
+ if (! _cairo_surface_get_extents (pattern->surface, &extents))
+ return TRUE;
+
+ if (r->x >= extents.x &&
+ r->y >= extents.y &&
+ r->x + r->width <= extents.x + extents.width &&
+ r->y + r->height <= extents.y + extents.height)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+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
+_gradient_is_opaque (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 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 (extents == NULL)
+ return FALSE;
+
+ _extents_to_linear_parameter (linear, extents, t);
+ if (t[0] < 0.0 || t[1] > 1.0)
+ return FALSE;
+ }
+ }
+
+ 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 *extents)
+{
+ 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, extents);
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _gradient_is_opaque (&pattern->gradient.base, extents);
+ }
+
+ 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 (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_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _gradient_is_clear (&pattern->gradient.base, NULL);
+ }
+
+ ASSERT_NOT_REACHED;
+ return FALSE;
+}
+
+/**
+ * _cairo_pattern_analyze_filter:
+ * @pattern: surface pattern
+ * @pad_out: location to store necessary padding in the source image, or %NULL
+ * Returns: the optimized #cairo_filter_t to use with @pattern.
+ *
+ * Analyze the filter to determine how much extra needs to be sampled
+ * from the source image to account for the filter radius and whether
+ * we can optimize the filter to a simpler value.
+ *
+ * XXX: We don't actually have any way of querying the backend for
+ * the filter radius, so we just guess base on what we know that
+ * backends do currently (see bug #10508)
+ */
+cairo_filter_t
+_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern,
+ double *pad_out)
+{
+ double pad;
+ cairo_filter_t optimized_filter;
+
+ switch (pattern->filter) {
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ /* If source pixels map 1:1 onto destination pixels, we do
+ * not need to filter (and do not want to filter, since it
+ * will cause blurriness)
+ */
+ if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) {
+ pad = 0.;
+ optimized_filter = CAIRO_FILTER_NEAREST;
+ } else {
+ /* 0.5 is enough for a bilinear filter. It's possible we
+ * should defensively use more for CAIRO_FILTER_BEST, but
+ * without a single example, it's hard to know how much
+ * more would be defensive...
+ */
+ pad = 0.5;
+ optimized_filter = pattern->filter;
+ }
+ break;
+
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ case CAIRO_FILTER_GAUSSIAN:
+ default:
+ pad = 0.;
+ optimized_filter = pattern->filter;
+ break;
+ }
+
+ if (pad_out)
+ *pad_out = pad;
+
+ return optimized_filter;
+}
+
+
+static double
+_pixman_nearest_sample (double d)
+{
+ return ceil (d - .5);
+}
+
+static cairo_int_status_t
+_cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ unsigned int flags,
+ cairo_surface_t **out,
+ cairo_surface_attributes_t *attr)
+{
+ cairo_surface_t *surface;
+ cairo_rectangle_int_t extents;
+ cairo_rectangle_int_t sampled_area;
+ double x1, y1, x2, y2;
+ int tx, ty;
+ double pad;
+ cairo_bool_t is_identity;
+ cairo_bool_t is_empty;
+ cairo_bool_t is_bounded;
+ cairo_int_status_t status;
+
+ surface = cairo_surface_reference (pattern->surface);
+
+ is_identity = FALSE;
+ attr->matrix = pattern->base.matrix;
+ attr->extend = pattern->base.extend;
+ attr->filter = _cairo_pattern_analyze_filter (&pattern->base, &pad);
+ attr->has_component_alpha = pattern->base.has_component_alpha;
+
+ attr->x_offset = attr->y_offset = tx = ty = 0;
+ if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) {
+ cairo_matrix_init_identity (&attr->matrix);
+ attr->x_offset = tx;
+ attr->y_offset = ty;
+ is_identity = TRUE;
+ } else if (attr->filter == CAIRO_FILTER_NEAREST) {
+ /*
+ * For NEAREST, we can remove the fractional translation component
+ * from the transformation - this ensures that the pattern will always
+ * hit fast-paths in the backends for simple transformations that
+ * become (almost) identity, without loss of quality.
+ */
+ attr->matrix.x0 = 0;
+ attr->matrix.y0 = 0;
+ if (_cairo_matrix_is_pixel_exact (&attr->matrix)) {
+ /* The rounding here is rather peculiar as it needs to match the
+ * rounding performed on the sample coordinate used by pixman.
+ */
+ attr->matrix.x0 = _pixman_nearest_sample (pattern->base.matrix.x0);
+ attr->matrix.y0 = _pixman_nearest_sample (pattern->base.matrix.y0);
+ } else {
+ attr->matrix.x0 = pattern->base.matrix.x0;
+ attr->matrix.y0 = pattern->base.matrix.y0;
+ }
+
+ if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) {
+ cairo_matrix_init_identity (&attr->matrix);
+ attr->x_offset = tx;
+ attr->y_offset = ty;
+ is_identity = TRUE;
+ }
+ }
+
+ /* XXX: Hack:
+ *
+ * The way we currently support CAIRO_EXTEND_REFLECT is to create
+ * an image twice bigger on each side, and create a pattern of four
+ * images such that the new image, when repeated, has the same effect
+ * of reflecting the original pattern.
+ */
+ if (flags & CAIRO_PATTERN_ACQUIRE_NO_REFLECT &&
+ attr->extend == CAIRO_EXTEND_REFLECT)
+ {
+ cairo_t *cr;
+ cairo_surface_t *src;
+ int w, h;
+
+ is_bounded = _cairo_surface_get_extents (surface, &extents);
+ assert (is_bounded);
+
+ status = _cairo_surface_clone_similar (dst, surface,
+ extents.x, extents.y,
+ extents.width, extents.height,
+ &extents.x, &extents.y, &src);
+ if (unlikely (status))
+ goto BAIL;
+
+ w = 2 * extents.width;
+ h = 2 * extents.height;
+
+ if (is_identity) {
+ attr->x_offset = -x;
+ x += tx;
+ while (x <= -w)
+ x += w;
+ while (x >= w)
+ x -= w;
+ extents.x += x;
+ tx = x = 0;
+
+ attr->y_offset = -y;
+ y += ty;
+ while (y <= -h)
+ y += h;
+ while (y >= h)
+ y -= h;
+ extents.y += y;
+ ty = y = 0;
+ }
+
+ cairo_surface_destroy (surface);
+ surface = _cairo_surface_create_similar_solid (dst,
+ dst->content, w, h,
+ CAIRO_COLOR_TRANSPARENT,
+ FALSE);
+ if (surface == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (unlikely (surface->status)) {
+ cairo_surface_destroy (src);
+ return surface->status;
+ }
+
+ surface->device_transform = pattern->surface->device_transform;
+ surface->device_transform_inverse = pattern->surface->device_transform_inverse;
+
+ cr = cairo_create (surface);
+
+ cairo_set_source_surface (cr, src, -extents.x, -extents.y);
+ cairo_paint (cr);
+
+ cairo_scale (cr, -1, +1);
+ cairo_set_source_surface (cr, src, extents.x-w, -extents.y);
+ cairo_paint (cr);
+ cairo_set_source_surface (cr, src, extents.x, -extents.y);
+ cairo_paint (cr);
+
+ cairo_scale (cr, +1, -1);
+ cairo_set_source_surface (cr, src, extents.x-w, extents.y-h);
+ cairo_paint (cr);
+ cairo_set_source_surface (cr, src, extents.x, extents.y-h);
+ cairo_paint (cr);
+ cairo_set_source_surface (cr, src, extents.x-w, extents.y);
+ cairo_paint (cr);
+ cairo_set_source_surface (cr, src, extents.x, extents.y);
+ cairo_paint (cr);
+
+ cairo_scale (cr, -1, +1);
+ cairo_set_source_surface (cr, src, -extents.x, extents.y-h);
+ cairo_paint (cr);
+ cairo_set_source_surface (cr, src, -extents.x, extents.y);
+ cairo_paint (cr);
+
+ status = cairo_status (cr);
+ cairo_destroy (cr);
+
+ cairo_surface_destroy (src);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ attr->extend = CAIRO_EXTEND_REPEAT;
+ }
+
+ /* We first transform the rectangle to the coordinate space of the
+ * source surface so that we only need to clone that portion of the
+ * surface that will be read.
+ */
+ x1 = x;
+ y1 = y;
+ x2 = x + (int) width;
+ y2 = y + (int) height;
+ if (! is_identity) {
+ _cairo_matrix_transform_bounding_box (&attr->matrix,
+ &x1, &y1, &x2, &y2,
+ NULL);
+ }
+
+ sampled_area.x = floor (x1 - pad);
+ sampled_area.y = floor (y1 - pad);
+ sampled_area.width = ceil (x2 + pad) - sampled_area.x;
+ sampled_area.height = ceil (y2 + pad) - sampled_area.y;
+
+ sampled_area.x += tx;
+ sampled_area.y += ty;
+
+ if ( _cairo_surface_get_extents (surface, &extents)) {
+ if (attr->extend == CAIRO_EXTEND_NONE) {
+ /* Never acquire a larger area than the source itself */
+ is_empty = _cairo_rectangle_intersect (&extents, &sampled_area);
+ } else {
+ int trim = 0;
+
+ if (sampled_area.x >= extents.x &&
+ sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width)
+ {
+ /* source is horizontally contained within extents, trim */
+ extents.x = sampled_area.x;
+ extents.width = sampled_area.width;
+ trim |= 0x1;
+ }
+
+ if (sampled_area.y >= extents.y &&
+ sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height)
+ {
+ /* source is vertically contained within extents, trim */
+ extents.y = sampled_area.y;
+ extents.height = sampled_area.height;
+ trim |= 0x2;
+ }
+
+ if (trim == 0x3) {
+ /* source is wholly contained within extents, drop the REPEAT */
+ attr->extend = CAIRO_EXTEND_NONE;
+ }
+
+ is_empty = extents.width == 0 || extents.height == 0;
+ }
+ }
+
+ /* XXX can we use is_empty? */
+
+ status = _cairo_surface_clone_similar (dst, surface,
+ extents.x, extents.y,
+ extents.width, extents.height,
+ &x, &y, out);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (x != 0 || y != 0) {
+ if (is_identity) {
+ attr->x_offset -= x;
+ attr->y_offset -= y;
+ } else {
+ cairo_matrix_t m;
+
+ x -= attr->x_offset;
+ y -= attr->y_offset;
+ attr->x_offset = 0;
+ attr->y_offset = 0;
+
+ cairo_matrix_init_translate (&m, -x, -y);
+ cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m);
+ }
+ }
+
+ /* reduce likelihood of range overflow with large downscaling */
+ if (! is_identity) {
+ cairo_matrix_t m;
+ cairo_status_t invert_status;
+
+ m = attr->matrix;
+ invert_status = cairo_matrix_invert (&m);
+ assert (invert_status == CAIRO_STATUS_SUCCESS);
+
+ if (m.x0 != 0. || m.y0 != 0.) {
+ /* pixman also limits the [xy]_offset to 16 bits so evenly
+ * spread the bits between the two.
+ */
+ x = floor (m.x0 / 2);
+ y = floor (m.y0 / 2);
+ attr->x_offset -= x;
+ attr->y_offset -= y;
+ cairo_matrix_init_translate (&m, x, y);
+ cairo_matrix_multiply (&attr->matrix, &m, &attr->matrix);
+ }
+ }
+
+ BAIL:
+ cairo_surface_destroy (surface);
+ return status;
+}
+
+/**
+ * _cairo_pattern_acquire_surface:
+ * @pattern: a #cairo_pattern_t
+ * @dst: destination surface
+ * @x: X coordinate in source corresponding to left side of destination area
+ * @y: Y coordinate in source corresponding to top side of destination area
+ * @width: width of destination area
+ * @height: height of destination area
+ * @surface_out: location to store a pointer to a surface
+ * @attributes: surface attributes that destination backend should apply to
+ * the returned surface
+ *
+ * A convenience function to obtain a surface to use as the source for
+ * drawing on @dst.
+ *
+ * Note that this function is only suitable for use when the destination
+ * surface is pixel based and 1 device unit maps to one pixel.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out.
+ **/
+cairo_int_status_t
+_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ unsigned int flags,
+ cairo_surface_t **surface_out,
+ cairo_surface_attributes_t *attributes)
+{
+ if (unlikely (pattern->status)) {
+ *surface_out = NULL;
+ return pattern->status;
+ }
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return _cairo_pattern_acquire_surface_for_solid ((cairo_solid_pattern_t *) pattern,
+ dst, x, y, width, height,
+ surface_out,
+ attributes);
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return _cairo_pattern_acquire_surface_for_gradient ((cairo_gradient_pattern_t *) pattern,
+ dst, x, y, width, height,
+ surface_out,
+ attributes);
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return _cairo_pattern_acquire_surface_for_surface ((cairo_surface_pattern_t *) pattern,
+ dst, x, y, width, height,
+ flags,
+ surface_out,
+ attributes);
+
+ default:
+ ASSERT_NOT_REACHED;
+ return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ }
+}
+
+/**
+ * _cairo_pattern_release_surface:
+ * @pattern: a #cairo_pattern_t
+ * @surface: a surface obtained by _cairo_pattern_acquire_surface
+ * @attributes: attributes obtained by _cairo_pattern_acquire_surface
+ *
+ * Releases resources obtained by _cairo_pattern_acquire_surface.
+ **/
+void
+_cairo_pattern_release_surface (const cairo_pattern_t *pattern,
+ cairo_surface_t *surface,
+ cairo_surface_attributes_t *attributes)
+{
+ cairo_surface_destroy (surface);
+}
+
+cairo_int_status_t
+_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ cairo_surface_t *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ unsigned int width,
+ unsigned int height,
+ unsigned int flags,
+ cairo_surface_t **src_out,
+ cairo_surface_t **mask_out,
+ cairo_surface_attributes_t *src_attributes,
+ cairo_surface_attributes_t *mask_attributes)
+{
+ cairo_int_status_t status;
+ cairo_pattern_union_t src_tmp;
+
+ if (unlikely (src->status))
+ return src->status;
+ if (unlikely (mask != NULL && mask->status))
+ return mask->status;
+
+ /* If src and mask are both solid, then the mask alpha can be
+ * combined into src and mask can be ignored. */
+
+ if (src->type == CAIRO_PATTERN_TYPE_SOLID &&
+ mask &&
+ ! mask->has_component_alpha &&
+ mask->type == CAIRO_PATTERN_TYPE_SOLID)
+ {
+ cairo_color_t combined;
+ cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src;
+ cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask;
+
+ combined = src_solid->color;
+ _cairo_color_multiply_alpha (&combined, mask_solid->color.alpha);
+
+ _cairo_pattern_init_solid (&src_tmp.solid, &combined);
+
+ src = &src_tmp.base;
+ mask = NULL;
+ }
+
+ status = _cairo_pattern_acquire_surface (src, dst,
+ src_x, src_y,
+ width, height,
+ flags,
+ src_out, src_attributes);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (mask == NULL) {
+ *mask_out = NULL;
+ goto BAIL;
+ }
+
+ status = _cairo_pattern_acquire_surface (mask, dst,
+ mask_x, mask_y,
+ width, height,
+ flags,
+ mask_out, mask_attributes);
+ if (unlikely (status))
+ _cairo_pattern_release_surface (src, *src_out, src_attributes);
+
+ BAIL:
+ if (src == &src_tmp.base)
+ _cairo_pattern_fini (&src_tmp.base);
+
+ return status;
+}
+
+/**
+ * _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).
+ *
+ * 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)
+{
+ double x1, y1, x2, y2;
+ cairo_status_t status;
+
+ 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;
+ double pad;
+
+ 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;
+
+ /* The filter can effectively enlarge the extents of the
+ * pattern, so extend as necessary.
+ */
+ _cairo_pattern_analyze_filter (&surface_pattern->base, &pad);
+ x1 = surface_extents.x - pad;
+ y1 = surface_extents.y - pad;
+ x2 = surface_extents.x + (int) surface_extents.width + pad;
+ y2 = surface_extents.y + (int) surface_extents.height + pad;
+ }
+ 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 r, D;
+
+ if (radial->r1 == 0 && radial->r2 == 0)
+ goto EMPTY;
+
+ cx1 = _cairo_fixed_to_double (radial->c1.x);
+ cy1 = _cairo_fixed_to_double (radial->c1.y);
+ r = _cairo_fixed_to_double (radial->r1);
+ x1 = cx1 - r; x2 = cx1 + r;
+ y1 = cy1 - r; y2 = cy1 + r;
+
+ cx2 = _cairo_fixed_to_double (radial->c2.x);
+ cy2 = _cairo_fixed_to_double (radial->c2.y);
+ r = fabs (_cairo_fixed_to_double (radial->r2));
+
+ if (pattern->extend != CAIRO_EXTEND_NONE)
+ goto UNBOUNDED;
+
+ /* We need to be careful, as if the circles are not
+ * self-contained, then the solution is actually unbounded.
+ */
+ D = (cx1-cx2)*(cx1-cx2) + (cy1-cy2)*(cy1-cy2);
+ if (D > r*r - 1e-5)
+ goto UNBOUNDED;
+
+ if (cx2 - r < x1)
+ x1 = cx2 - r;
+ if (cx2 + r > x2)
+ x2 = cx2 + r;
+
+ if (cy2 - r < y1)
+ y1 = cy2 - r;
+ if (cy2 + r > y2)
+ y2 = cy2 + r;
+ }
+ 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->p1.x == linear->p2.x && linear->p1.y == linear->p2.y)
+ goto EMPTY;
+
+ if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.)
+ goto UNBOUNDED;
+
+ if (linear->p1.x == linear->p2.x) {
+ x1 = -HUGE_VAL;
+ x2 = HUGE_VAL;
+ y1 = _cairo_fixed_to_double (MIN (linear->p1.y, linear->p2.y));
+ y2 = _cairo_fixed_to_double (MAX (linear->p1.y, linear->p2.y));
+ } else if (linear->p1.y == linear->p2.y) {
+ x1 = _cairo_fixed_to_double (MIN (linear->p1.x, linear->p2.x));
+ x2 = _cairo_fixed_to_double (MAX (linear->p1.x, linear->p2.x));
+ y1 = -HUGE_VAL;
+ y2 = HUGE_VAL;
+ } else {
+ goto UNBOUNDED;
+ }
+ }
+ 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;
+
+ 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);
+ }
+
+ x1 = floor (x1);
+ if (x1 < CAIRO_RECT_INT_MIN)
+ x1 = CAIRO_RECT_INT_MIN;
+ y1 = floor (y1);
+ if (y1 < CAIRO_RECT_INT_MIN)
+ y1 = CAIRO_RECT_INT_MIN;
+
+ x2 = ceil (x2);
+ if (x2 > CAIRO_RECT_INT_MAX)
+ x2 = CAIRO_RECT_INT_MAX;
+ y2 = ceil (y2);
+ if (y2 > CAIRO_RECT_INT_MAX)
+ y2 = CAIRO_RECT_INT_MAX;
+
+ extents->x = x1; extents->width = x2 - x1;
+ extents->y = y1; extents->height = y2 - y1;
+ return;
+
+ UNBOUNDED:
+ /* unbounded patterns -> 'infinite' extents */
+ _cairo_unbounded_rectangle_init (extents);
+ return;
+
+ EMPTY:
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ return;
+}
+
+
+static unsigned long
+_cairo_solid_pattern_hash (unsigned long hash,
+ const cairo_pattern_t *pattern)
+{
+ const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+
+ hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color));
+
+ return hash;
+}
+
+static unsigned long
+_cairo_gradient_color_stops_hash (unsigned long hash,
+ const cairo_gradient_pattern_t *gradient)
+{
+ unsigned int n;
+
+ hash = _cairo_hash_bytes (hash,
+ &gradient->n_stops,
+ sizeof (gradient->n_stops));
+
+ for (n = 0; n < gradient->n_stops; n++) {
+ hash = _cairo_hash_bytes (hash,
+ &gradient->stops[n].offset,
+ sizeof (double));
+ hash = _cairo_hash_bytes (hash,
+ &gradient->stops[n].color,
+ sizeof (cairo_color_t));
+ }
+
+ return hash;
+}
+
+unsigned long
+_cairo_linear_pattern_hash (unsigned long hash,
+ const cairo_linear_pattern_t *linear)
+{
+ hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1));
+ hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2));
+
+ 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->c1, sizeof (radial->c1));
+ hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1));
+ hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2));
+ hash = _cairo_hash_bytes (hash, &radial->r2, sizeof (radial->r2));
+
+ return _cairo_gradient_color_stops_hash (hash, &radial->base);
+}
+
+static unsigned long
+_cairo_surface_pattern_hash (unsigned long hash,
+ const cairo_pattern_t *pattern)
+{
+ const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
+
+ hash ^= surface->surface->unique_id;
+
+ return hash;
+}
+
+unsigned long
+_cairo_pattern_hash (const cairo_pattern_t *pattern)
+{
+ unsigned long hash = _CAIRO_HASH_INIT_VALUE;
+
+ if (pattern->status)
+ return 0;
+
+ hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type));
+ 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, 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_SURFACE:
+ return _cairo_surface_pattern_hash (hash, pattern);
+ default:
+ ASSERT_NOT_REACHED;
+ return FALSE;
+ }
+}
+
+static unsigned long
+_cairo_gradient_pattern_color_stops_size (const cairo_pattern_t *pattern)
+{
+ cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+
+ return gradient->n_stops * (sizeof (double) + sizeof (cairo_color_t));
+}
+
+unsigned long
+_cairo_pattern_size (const cairo_pattern_t *pattern)
+{
+ if (pattern->status)
+ return 0;
+
+ /* XXX */
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ return sizeof (cairo_solid_pattern_t);
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ return sizeof (cairo_surface_pattern_t);
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ return sizeof (cairo_linear_pattern_t) +
+ _cairo_gradient_pattern_color_stops_size (pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ return sizeof (cairo_radial_pattern_t) +
+ _cairo_gradient_pattern_color_stops_size (pattern);
+ default:
+ ASSERT_NOT_REACHED;
+ return 0;
+ }
+}
+
+
+static cairo_bool_t
+_cairo_solid_pattern_equal (const cairo_pattern_t *A,
+ const cairo_pattern_t *B)
+{
+ const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A;
+ const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B;
+
+ 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->p1.x != b->p1.x)
+ return FALSE;
+
+ if (a->p1.y != b->p1.y)
+ return FALSE;
+
+ if (a->p2.x != b->p2.x)
+ return FALSE;
+
+ if (a->p2.y != b->p2.y)
+ return FALSE;
+
+ return _cairo_gradient_color_stops_equal (&a->base, &b->base);
+}
+
+cairo_bool_t
+_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
+ const cairo_radial_pattern_t *b)
+{
+ if (a->c1.x != b->c1.x)
+ return FALSE;
+
+ if (a->c1.y != b->c1.y)
+ return FALSE;
+
+ if (a->r1 != b->r1)
+ return FALSE;
+
+ if (a->c2.x != b->c2.x)
+ return FALSE;
+
+ if (a->c2.y != b->c2.y)
+ return FALSE;
+
+ if (a->r2 != b->r2)
+ return FALSE;
+
+ return _cairo_gradient_color_stops_equal (&a->base, &b->base);
+}
+
+static cairo_bool_t
+_cairo_surface_pattern_equal (const cairo_pattern_t *A,
+ const cairo_pattern_t *B)
+{
+ const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A;
+ const cairo_surface_pattern_t *b = (cairo_surface_pattern_t *) B;
+
+ return a->surface->unique_id == b->surface->unique_id;
+}
+
+cairo_bool_t
+_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
+{
+ if (a->status || b->status)
+ return FALSE;
+
+ if (a == 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 (a, 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_SURFACE:
+ return _cairo_surface_pattern_equal (a, 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_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 are 0 to 1 less than 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 = _cairo_fixed_to_double (linear->p1.x);
+ if (y0)
+ *y0 = _cairo_fixed_to_double (linear->p1.y);
+ if (x1)
+ *x1 = _cairo_fixed_to_double (linear->p2.x);
+ if (y1)
+ *y1 = _cairo_fixed_to_double (linear->p2.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 = _cairo_fixed_to_double (radial->c1.x);
+ if (y0)
+ *y0 = _cairo_fixed_to_double (radial->c1.y);
+ if (r0)
+ *r0 = _cairo_fixed_to_double (radial->r1);
+ if (x1)
+ *x1 = _cairo_fixed_to_double (radial->c2.x);
+ if (y1)
+ *y1 = _cairo_fixed_to_double (radial->c2.y);
+ if (r1)
+ *r1 = _cairo_fixed_to_double (radial->r2);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+void
+_cairo_pattern_reset_static_data (void)
+{
+#if HAS_FREED_POOL
+ int i;
+
+ for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++)
+ _freed_pool_reset (&freed_pattern_pool[i]);
+#endif
+
+ _cairo_pattern_reset_solid_surface_cache ();
+}
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 000000000..67d1cc233
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-pdf-operators-private.h
@@ -0,0 +1,171 @@
+/* -*- 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 <krh@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_PDF_OPERATORS_H
+#define CAIRO_PDF_OPERATORS_H
+
+#include "cairo-compiler-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_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 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;
+ 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_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,
+ 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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule);
+
+cairo_private cairo_int_status_t
+_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators,
+ 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);
+
+#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 000000000..d70756b3a
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-pdf-operators.c
@@ -0,0 +1,1470 @@
+/* -*- 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 <krh@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#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)
+{
+ pdf_operators->stream = stream;
+ pdf_operators->cairo_to_pdf = *cairo_to_pdf;
+ pdf_operators->font_subsets = font_subsets;
+ 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
+ * operatations.
+ *
+ * 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 struct _word_wrap_stream {
+ cairo_output_stream_t base;
+ cairo_output_stream_t *output;
+ int max_column;
+ int column;
+ cairo_bool_t last_write_was_space;
+ cairo_bool_t in_hexstring;
+ cairo_bool_t empty_hexstring;
+} word_wrap_stream_t;
+
+static int
+_count_word_up_to (const unsigned char *s, int length)
+{
+ int word = 0;
+
+ while (length--) {
+ if (! (_cairo_isspace (*s) || *s == '<')) {
+ s++;
+ word++;
+ } else {
+ return word;
+ }
+ }
+
+ return word;
+}
+
+
+/* Count up to either the end of the ASCII hexstring or the number
+ * of columns remaining.
+ */
+static int
+_count_hexstring_up_to (const unsigned char *s, int length, int columns)
+{
+ int word = 0;
+
+ while (length--) {
+ if (*s++ != '>')
+ word++;
+ else
+ return word;
+
+ columns--;
+ if (columns < 0 && word > 1)
+ return word;
+ }
+
+ return word;
+}
+
+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;
+ cairo_bool_t newline;
+ int word;
+
+ while (length) {
+ if (*data == '<') {
+ stream->in_hexstring = TRUE;
+ stream->empty_hexstring = TRUE;
+ stream->last_write_was_space = FALSE;
+ data++;
+ length--;
+ _cairo_output_stream_printf (stream->output, "<");
+ stream->column++;
+ } else if (*data == '>') {
+ stream->in_hexstring = FALSE;
+ stream->last_write_was_space = FALSE;
+ data++;
+ length--;
+ _cairo_output_stream_printf (stream->output, ">");
+ stream->column++;
+ } else if (_cairo_isspace (*data)) {
+ newline = (*data == '\n' || *data == '\r');
+ if (! newline && stream->column >= stream->max_column) {
+ _cairo_output_stream_printf (stream->output, "\n");
+ stream->column = 0;
+ }
+ _cairo_output_stream_write (stream->output, data, 1);
+ data++;
+ length--;
+ if (newline) {
+ stream->column = 0;
+ }
+ else
+ stream->column++;
+ stream->last_write_was_space = TRUE;
+ } else {
+ if (stream->in_hexstring) {
+ word = _count_hexstring_up_to (data, length,
+ MAX (stream->max_column - stream->column, 0));
+ } else {
+ word = _count_word_up_to (data, length);
+ }
+ /* Don't wrap if this word is a continuation of a non hex
+ * string word from a previous call to write. */
+ if (stream->column + word >= stream->max_column) {
+ if (stream->last_write_was_space ||
+ (stream->in_hexstring && !stream->empty_hexstring))
+ {
+ _cairo_output_stream_printf (stream->output, "\n");
+ stream->column = 0;
+ }
+ }
+ _cairo_output_stream_write (stream->output, data, word);
+ data += word;
+ length -= word;
+ stream->column += word;
+ stream->last_write_was_space = FALSE;
+ if (stream->in_hexstring)
+ stream->empty_hexstring = FALSE;
+ }
+ }
+
+ 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, int max_column)
+{
+ word_wrap_stream_t *stream;
+
+ if (output->status)
+ return _cairo_output_stream_create_in_error (output->status);
+
+ stream = malloc (sizeof (word_wrap_stream_t));
+ 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->column = 0;
+ stream->last_write_was_space = FALSE;
+ stream->in_hexstring = FALSE;
+ stream->empty_hexstring = TRUE;
+
+ 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,
+ 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, 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)) {
+ status = _cairo_pdf_path_rectangle (&info, &box);
+ } else {
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_pdf_path_move_to,
+ _cairo_pdf_path_line_to,
+ _cairo_pdf_path_curve_to,
+ _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,
+ 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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ const char *pdf_operator)
+{
+ cairo_status_t status;
+ cairo_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 %f %f %f %f %f %f cm\n",
+ m.xx, m.yx, m.xy, m.yy,
+ m.x0, m.y0);
+ } 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,
+ 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,
+ 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,
+ 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);
+}
+
+#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, "<");
+ for (i = 0; i < pdf_operators->num_glyphs; i++) {
+ _cairo_output_stream_printf (stream,
+ "%0*x",
+ pdf_operators->hex_width,
+ pdf_operators->glyphs[i].glyph_index);
+ pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
+ }
+ _cairo_output_stream_printf (stream, ">Tj\n");
+
+ 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, "[<");
+ 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 (rounded_delta != 0) {
+ _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_output_stream_printf (stream,
+ "%0*x",
+ pdf_operators->hex_width,
+ pdf_operators->glyphs[i].glyph_index);
+ pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
+ }
+ _cairo_output_stream_printf (stream, ">]TJ\n");
+
+ 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, 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_printf (pdf_operators->stream,
+ "%f %f %f %f %f %f Tm\n",
+ pdf_operators->text_matrix.xx,
+ pdf_operators->text_matrix.yx,
+ pdf_operators->text_matrix.xy,
+ pdf_operators->text_matrix.yy,
+ pdf_operators->text_matrix.x0,
+ pdf_operators->text_matrix.y0);
+
+ 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;
+
+ 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 <feff");
+ if (utf8_len) {
+ status = _cairo_utf8_to_utf16 (utf8, utf8_len, &utf16, &utf16_len);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < utf16_len; i++) {
+ _cairo_output_stream_printf (pdf_operators->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 becauses 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;
+ 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;
+ }
+ }
+
+ /* 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;
+
+ 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++;
+ }
+ 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 font space */
+ cairo_matrix_multiply (&text_matrix, &text_matrix, &invert_y_axis);
+
+ /* Invert y axis in device space */
+ cairo_matrix_multiply (&text_matrix, &invert_y_axis, &text_matrix);
+
+ 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);
+}
+
+#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 000000000..221418ec9
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-pdf-surface-private.h
@@ -0,0 +1,196 @@
+/* -*- 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 <krh@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#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"
+
+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 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;
+ cairo_bool_t interpolate;
+ cairo_pdf_resource_t surface_res;
+ int width;
+ int height;
+} cairo_pdf_source_surface_entry_t;
+
+typedef struct _cairo_pdf_source_surface {
+ cairo_surface_t *surface;
+ 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_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_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_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_matrix_t cairo_to_pdf;
+
+ 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_hash_table_t *all_surfaces;
+ cairo_array_t smask_groups;
+ cairo_array_t knockout_group;
+
+ 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_version_t pdf_version;
+ cairo_bool_t compress_content;
+
+ cairo_pdf_resource_t content;
+ cairo_pdf_resource_t content_resources;
+ cairo_pdf_group_resources_t resources;
+ cairo_bool_t has_fallback_images;
+ cairo_bool_t header_emitted;
+
+ struct {
+ cairo_bool_t active;
+ 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_bool_t is_knockout;
+ } group_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_surface_t *paginated_surface;
+};
+
+#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 000000000..09bd42ea0
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-pdf-surface.c
@@ -0,0 +1,6250 @@
+/* -*- 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 <krh@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#define _BSD_SOURCE /* for snprintf() */
+#include "cairoint.h"
+#include "cairo-pdf.h"
+#include "cairo-pdf-surface-private.h"
+#include "cairo-pdf-operators-private.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.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-subsurface-private.h"
+#include "cairo-type3-glyph-surface-private.h"
+
+#include <time.h>
+#include <zlib.h>
+
+/* Issues:
+ *
+ * - We embed an image in the stream each time it's composited. We
+ * could add generation counters to surfaces and remember the stream
+ * ID for a particular generation for a particular surface.
+ *
+ * - Backend specific meta data.
+ */
+
+/*
+ * Page Structure of the Generated PDF:
+ *
+ * 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
+ *
+ * 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.
+ */
+
+/**
+ * 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.
+ */
+
+/**
+ * CAIRO_HAS_PDF_SURFACE:
+ *
+ * Defined if the PDF surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+static const cairo_pdf_version_t _cairo_pdf_versions[] =
+{
+ CAIRO_PDF_VERSION_1_4,
+ CAIRO_PDF_VERSION_1_5
+};
+
+#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions)
+
+static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] =
+{
+ "PDF 1.4",
+ "PDF 1.5"
+};
+
+typedef struct _cairo_pdf_object {
+ long offset;
+} cairo_pdf_object_t;
+
+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 cairo_pdf_resource_t
+_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface);
+
+static void
+_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface);
+
+static void
+_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group);
+
+static cairo_status_t
+_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_status_t
+_cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t *resource,
+ cairo_bool_t compressed,
+ const char *fmt,
+ ...) CAIRO_PRINTF_FORMAT(4, 5);
+static cairo_status_t
+_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface);
+
+static cairo_status_t
+_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
+
+static void
+_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface);
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface);
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface);
+
+static long
+_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface);
+
+static cairo_status_t
+_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
+
+static cairo_status_t
+_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface);
+
+static cairo_bool_t
+_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b);
+
+static const cairo_surface_backend_t cairo_pdf_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend;
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_resource_t resource;
+ cairo_status_t status;
+ cairo_pdf_object_t object;
+
+ object.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;
+}
+
+static 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->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;
+ cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height);
+ _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+ &surface->cairo_to_pdf);
+}
+
+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;
+ _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 = 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);
+
+ surface->output = output;
+ surface->width = width;
+ surface->height = height;
+ cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height);
+
+ _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));
+ 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;
+ }
+
+ 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->pdf_version = CAIRO_PDF_VERSION_1_5;
+ surface->compress_content = 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->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);
+ _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);
+
+ 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 */
+
+ if (! _extract_pdf_surface (surface, &pdf_surface))
+ return;
+
+ _cairo_pdf_surface_set_size_internal (pdf_surface,
+ width_in_points,
+ height_in_points);
+}
+
+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);
+}
+
+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->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->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->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_status_t
+_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface,
+ double alpha,
+ int *index)
+{
+ int num_alphas, i;
+ double other;
+ cairo_status_t status;
+ cairo_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_status_t
+_cairo_pdf_surface_add_smask (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t smask)
+{
+ return _cairo_array_append (&(surface->resources.smasks), &smask);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t pattern)
+{
+ return _cairo_array_append (&(surface->resources.patterns), &pattern);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t xobject)
+{
+ return _cairo_array_append (&(surface->resources.xobjects), &xobject);
+}
+
+static cairo_status_t
+_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_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)
+{
+ int num_alphas, num_smasks, num_resources, i;
+ double alpha;
+ cairo_pdf_resource_t *smask, *pattern, *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");
+
+ 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->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)
+{
+ 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;
+
+ 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);
+ if (group->utf8)
+ free (group->utf8);
+ if (group->glyphs)
+ free (group->glyphs);
+ if (group->clusters)
+ free (group->clusters);
+ if (group->scaled_font)
+ cairo_scaled_font_destroy (group->scaled_font);
+ free (group);
+}
+
+static cairo_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;
+
+ return (a->id == b->id) && (a->interpolate == b->interpolate);
+}
+
+static void
+_cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key)
+{
+ key->base.hash = key->id;
+}
+
+static cairo_int_status_t
+_get_jpx_image_info (cairo_surface_t *source,
+ cairo_image_info_t *info,
+ const unsigned char **mime_data,
+ unsigned long *mime_data_length)
+{
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2,
+ mime_data, mime_data_length);
+ if (*mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length);
+}
+
+static cairo_int_status_t
+_get_jpeg_image_info (cairo_surface_t *source,
+ cairo_image_info_t *info,
+ const unsigned char **mime_data,
+ unsigned long *mime_data_length)
+{
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+ mime_data, mime_data_length);
+ if (*mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length);
+}
+
+static cairo_status_t
+_get_source_surface_size (cairo_surface_t *source,
+ int *width,
+ int *height)
+{
+ cairo_status_t status;
+ cairo_rectangle_int_t extents;
+ cairo_image_info_t info;
+ const unsigned char *mime_data;
+ unsigned long mime_data_length;
+
+ if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+
+ *width = sub->extents.width;
+ *height = sub->extents.height;
+
+ } else {
+ cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) source;
+ cairo_box_t bbox;
+
+ status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
+ if (unlikely (status))
+ return status;
+
+ _cairo_box_round_to_rectangle (&bbox, &extents);
+
+ *width = extents.width;
+ *height = extents.height;
+ }
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ *width = info.width;
+ *height = info.height;
+ return status;
+ }
+
+ status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ *width = info.width;
+ *height = info.height;
+ return status;
+ }
+
+ if (! _cairo_surface_get_extents (source, &extents))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ *width = extents.width;
+ *height = extents.height;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
+ cairo_surface_t *source,
+ cairo_filter_t filter,
+ cairo_pdf_resource_t *surface_res,
+ int *width,
+ int *height)
+{
+ cairo_pdf_source_surface_t src_surface;
+ cairo_pdf_source_surface_entry_t surface_key;
+ cairo_pdf_source_surface_entry_t *surface_entry;
+ cairo_status_t status;
+ cairo_bool_t 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;
+ }
+
+ surface_key.id = source->unique_id;
+ surface_key.interpolate = interpolate;
+ _cairo_pdf_source_surface_init_key (&surface_key);
+ surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base);
+ if (surface_entry) {
+ *surface_res = surface_entry->surface_res;
+ *width = surface_entry->width;
+ *height = surface_entry->height;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t));
+ if (surface_entry == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ surface_entry->id = surface_key.id;
+ surface_entry->interpolate = interpolate;
+ _cairo_pdf_source_surface_init_key (surface_entry);
+
+ src_surface.hash_entry = surface_entry;
+ src_surface.surface = cairo_surface_reference (source);
+ surface_entry->surface_res = _cairo_pdf_surface_new_object (surface);
+ if (surface_entry->surface_res.id == 0) {
+ cairo_surface_destroy (source);
+ free (surface_entry);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ status = _get_source_surface_size (source, &surface_entry->width,
+ &surface_entry->height);
+
+ status = _cairo_array_append (&surface->page_surfaces, &src_surface);
+ if (unlikely (status)) {
+ cairo_surface_destroy (source);
+ free (surface_entry);
+ return status;
+ }
+
+ status = _cairo_hash_table_insert (surface->all_surfaces,
+ &surface_entry->base);
+
+ *surface_res = surface_entry->surface_res;
+ *width = surface_entry->width;
+ *height = surface_entry->height;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents,
+ cairo_pdf_resource_t *pattern_res,
+ cairo_pdf_resource_t *gstate_res)
+{
+ cairo_pdf_pattern_t pdf_pattern;
+ cairo_status_t status;
+
+ /* Solid colors are emitted into the content stream */
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ pattern_res->id = 0;
+ gstate_res->id = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ 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)
+ {
+ if (_cairo_pattern_is_opaque (pattern, extents) == FALSE) {
+ 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;
+
+ status = _cairo_array_append (&surface->page_patterns, &pdf_pattern);
+ if (unlikely (status)) {
+ cairo_pattern_destroy (pdf_pattern.pattern);
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_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);
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface)
+{
+ cairo_status_t status;
+ long length;
+
+ if (! surface->pdf_stream.active)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+
+ if (surface->pdf_stream.compressed) {
+ cairo_status_t status2;
+
+ status2 = _cairo_output_stream_destroy (surface->output);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ status = status2;
+
+ surface->output = surface->pdf_stream.old_output;
+ _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
+ surface->pdf_stream.old_output = NULL;
+ }
+
+ 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_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)
+{
+ _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_content) {
+ _cairo_output_stream_printf (surface->output,
+ " /Filter /FlateDecode\n");
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " /Subtype /Form\n"
+ " /BBox [ 0 0 %f %f ]\n"
+ " /Group <<\n"
+ " /Type /Group\n"
+ " /S /Transparency\n"
+ " /CS /DeviceRGB\n",
+ surface->width,
+ surface->height);
+
+ 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);
+ _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_status_t
+_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t *resource)
+{
+ cairo_status_t status;
+
+ assert (surface->pdf_stream.active == FALSE);
+ assert (surface->group_stream.active == FALSE);
+
+ surface->group_stream.active = TRUE;
+ surface->current_pattern_is_solid_color = FALSE;
+ surface->current_operator = CAIRO_OPERATOR_OVER;
+ _cairo_pdf_operators_reset (&surface->pdf_operators);
+
+ surface->group_stream.mem_stream = _cairo_memory_stream_create ();
+
+ if (surface->compress_content) {
+ 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;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface)
+{
+ cairo_status_t status;
+
+ status = _cairo_pdf_surface_open_group (surface, NULL);
+ if (unlikely (status))
+ return status;
+
+ surface->group_stream.is_knockout = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t *group)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS, status2;
+
+ 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_content) {
+ 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);
+ if (group)
+ *group = surface->group_stream.resource;
+
+ status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ surface->group_stream.mem_stream = NULL;
+ surface->group_stream.stream = NULL;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t *resource,
+ cairo_bool_t is_form)
+{
+ cairo_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) {
+ status =
+ _cairo_pdf_surface_open_stream (surface,
+ resource,
+ surface->compress_content,
+ " /Type /XObject\n"
+ " /Subtype /Form\n"
+ " /BBox [ 0 0 %f %f ]\n"
+ " /Group <<\n"
+ " /Type /Group\n"
+ " /S /Transparency\n"
+ " /CS /DeviceRGB\n"
+ " >>\n"
+ " /Resources %d 0 R\n",
+ surface->width,
+ surface->height,
+ surface->content_resources.id);
+ } else {
+ status =
+ _cairo_pdf_surface_open_stream (surface,
+ resource,
+ surface->compress_content,
+ NULL);
+ }
+ if (unlikely (status))
+ return status;
+
+ surface->content = surface->pdf_stream.self;
+
+ _cairo_output_stream_printf (surface->output, "q\n");
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface)
+{
+ cairo_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);
+ _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);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_finish (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ long offset;
+ cairo_pdf_resource_t info, catalog;
+ cairo_status_t status, status2;
+
+ status = surface->base.status;
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_pdf_surface_emit_font_subsets (surface);
+
+ _cairo_pdf_surface_write_pages (surface);
+
+ info = _cairo_pdf_surface_write_info (surface);
+ if (info.id == 0 && status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ catalog = _cairo_pdf_surface_write_catalog (surface);
+ if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ 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,
+ info.id);
+
+ _cairo_output_stream_printf (surface->output,
+ "startxref\n"
+ "%ld\n"
+ "%%%%EOF\n",
+ offset);
+
+ /* 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_surface_clear (surface);
+ _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_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);
+
+ if (surface->font_subsets) {
+ _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+ surface->font_subsets = NULL;
+ }
+
+ _cairo_surface_clipper_reset (&surface->clipper);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_start_page (void *abstract_surface)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+
+ /* Document header */
+ if (! surface->header_emitted) {
+ const char *version;
+
+ switch (surface->pdf_version) {
+ case CAIRO_PDF_VERSION_1_4:
+ version = "1.4";
+ break;
+ default:
+ case CAIRO_PDF_VERSION_1_5:
+ version = "1.5";
+ break;
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ "%%PDF-%s\n", version);
+ _cairo_output_stream_printf (surface->output,
+ "%%%c%c%c%c\n", 181, 237, 174, 251);
+ surface->header_emitted = TRUE;
+ }
+
+ _cairo_pdf_group_resources_clear (&surface->resources);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_has_fallback_images (void *abstract_surface,
+ cairo_bool_t has_fallbacks)
+{
+ cairo_status_t status;
+ cairo_pdf_surface_t *surface = abstract_surface;
+
+ surface->has_fallback_images = has_fallbacks;
+ status = _cairo_pdf_surface_open_content_stream (surface, NULL, 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;
+}
+
+/* Emit alpha channel from the image into the given data, providing
+ * an id that can be used to reference the resulting SMask object.
+ *
+ * In the case that the alpha channel happens to be all opaque, then
+ * no SMask object will be emitted and *id_ret will be set to 0.
+ */
+static cairo_status_t
+_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface,
+ cairo_image_surface_t *image,
+ cairo_pdf_resource_t *stream_ret)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ char *alpha;
+ unsigned long alpha_size;
+ uint32_t *pixel32;
+ uint8_t *pixel8;
+ int i, x, y;
+ cairo_bool_t opaque;
+ uint8_t a;
+
+ /* This is the only image format we support, which simplifies things. */
+ assert (image->format == CAIRO_FORMAT_ARGB32 ||
+ image->format == CAIRO_FORMAT_A8 ||
+ image->format == CAIRO_FORMAT_A1 );
+
+ stream_ret->id = 0;
+
+ if (image->format == CAIRO_FORMAT_A1) {
+ 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;
+ }
+
+ opaque = TRUE;
+ i = 0;
+ for (y = 0; y < image->height; y++) {
+ if (image->format == CAIRO_FORMAT_ARGB32) {
+ pixel32 = (uint32_t *) (image->data + y * image->stride);
+
+ for (x = 0; x < image->width; x++, pixel32++) {
+ a = (*pixel32 & 0xff000000) >> 24;
+ alpha[i++] = a;
+ if (a != 0xff)
+ opaque = FALSE;
+ }
+ } else if (image->format == CAIRO_FORMAT_A8){
+ pixel8 = (uint8_t *) (image->data + y * image->stride);
+
+ for (x = 0; x < image->width; x++, pixel8++) {
+ a = *pixel8;
+ alpha[i++] = a;
+ if (a != 0xff)
+ opaque = FALSE;
+ }
+ } else { /* image->format == CAIRO_FORMAT_A1 */
+ pixel8 = (uint8_t *) (image->data + y * image->stride);
+
+ for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) {
+ a = *pixel8;
+ a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
+ alpha[i++] = a;
+ if (a != 0xff)
+ opaque = FALSE;
+ }
+ }
+ }
+
+ /* Bail out without emitting smask if it's all opaque. */
+ if (opaque)
+ goto CLEANUP_ALPHA;
+
+ status = _cairo_pdf_surface_open_stream (surface,
+ NULL,
+ TRUE,
+ " /Type /XObject\n"
+ " /Subtype /Image\n"
+ " /Width %d\n"
+ " /Height %d\n"
+ " /ColorSpace /DeviceGray\n"
+ " /BitsPerComponent %d\n",
+ image->width, image->height,
+ image->format == CAIRO_FORMAT_A1 ? 1 : 8);
+ if (unlikely (status))
+ goto CLEANUP_ALPHA;
+
+ *stream_ret = surface->pdf_stream.self;
+ _cairo_output_stream_write (surface->output, alpha, alpha_size);
+ status = _cairo_pdf_surface_close_stream (surface);
+
+ CLEANUP_ALPHA:
+ free (alpha);
+ CLEANUP:
+ return status;
+}
+
+/* Emit image data into the given surface, providing a resource that
+ * can be used to reference the data in image_ret. */
+static cairo_status_t
+_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface,
+ cairo_image_surface_t *image,
+ cairo_pdf_resource_t *image_res,
+ cairo_filter_t filter)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ char *rgb;
+ unsigned long rgb_size;
+ uint32_t *pixel;
+ int i, x, y;
+ cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */
+ cairo_bool_t need_smask;
+ const char *interpolate = "true";
+
+ /* These are the only image formats we currently support, (which
+ * makes things a lot simpler here). This is enforced through
+ * _cairo_pdf_surface_analyze_operation which only accept source surfaces of
+ * CONTENT_COLOR or CONTENT_COLOR_ALPHA.
+ */
+ assert (image->format == CAIRO_FORMAT_RGB24 ||
+ image->format == CAIRO_FORMAT_ARGB32 ||
+ image->format == CAIRO_FORMAT_A8 ||
+ image->format == CAIRO_FORMAT_A1);
+
+ rgb_size = image->height * image->width * 3;
+ rgb = _cairo_malloc_abc (image->width, image->height, 3);
+ if (unlikely (rgb == 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);
+
+ for (x = 0; x < image->width; x++, pixel++) {
+ /* 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) {
+ rgb[i++] = 0;
+ rgb[i++] = 0;
+ rgb[i++] = 0;
+ } else {
+ rgb[i++] = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
+ rgb[i++] = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a;
+ rgb[i++] = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a;
+ }
+ } else if (image->format == CAIRO_FORMAT_RGB24) {
+ rgb[i++] = (*pixel & 0x00ff0000) >> 16;
+ rgb[i++] = (*pixel & 0x0000ff00) >> 8;
+ rgb[i++] = (*pixel & 0x000000ff) >> 0;
+ } else {
+ rgb[i++] = 0;
+ rgb[i++] = 0;
+ rgb[i++] = 0;
+ }
+ }
+ }
+
+ need_smask = FALSE;
+ if (image->format == CAIRO_FORMAT_ARGB32 ||
+ image->format == CAIRO_FORMAT_A8 ||
+ image->format == CAIRO_FORMAT_A1) {
+ status = _cairo_pdf_surface_emit_smask (surface, image, &smask);
+ if (unlikely (status))
+ goto CLEANUP_RGB;
+
+ if (smask.id)
+ need_smask = TRUE;
+ }
+
+ switch (filter) {
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ interpolate = "true";
+ break;
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ case CAIRO_FILTER_GAUSSIAN:
+ interpolate = "false";
+ break;
+ }
+
+#define IMAGE_DICTIONARY " /Type /XObject\n" \
+ " /Subtype /Image\n" \
+ " /Width %d\n" \
+ " /Height %d\n" \
+ " /ColorSpace /DeviceRGB\n" \
+ " /Interpolate %s\n" \
+ " /BitsPerComponent 8\n"
+
+ if (need_smask)
+ status = _cairo_pdf_surface_open_stream (surface,
+ image_res,
+ TRUE,
+ IMAGE_DICTIONARY
+ " /SMask %d 0 R\n",
+ image->width, image->height,
+ interpolate,
+ smask.id);
+ else
+ status = _cairo_pdf_surface_open_stream (surface,
+ image_res,
+ TRUE,
+ IMAGE_DICTIONARY,
+ image->width, image->height,
+ interpolate);
+ if (unlikely (status))
+ goto CLEANUP_RGB;
+
+#undef IMAGE_DICTIONARY
+
+ _cairo_output_stream_write (surface->output, rgb, rgb_size);
+ status = _cairo_pdf_surface_close_stream (surface);
+
+CLEANUP_RGB:
+ free (rgb);
+CLEANUP:
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface,
+ cairo_surface_t *source,
+ cairo_pdf_resource_t res)
+{
+ cairo_status_t status;
+ const unsigned char *mime_data;
+ unsigned long mime_data_length;
+ cairo_image_info_t info;
+
+ if (surface->pdf_version < CAIRO_PDF_VERSION_1_5)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2,
+ &mime_data, &mime_data_length);
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length);
+ if (status)
+ return status;
+
+ status = _cairo_pdf_surface_open_stream (surface,
+ &res,
+ FALSE,
+ " /Type /XObject\n"
+ " /Subtype /Image\n"
+ " /Width %d\n"
+ " /Height %d\n"
+ " /Filter /JPXDecode\n",
+ info.width,
+ info.height);
+ 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_resource_t res)
+{
+ cairo_status_t status;
+ const unsigned char *mime_data;
+ unsigned long mime_data_length;
+ cairo_image_info_t info;
+
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+ &mime_data, &mime_data_length);
+ if (unlikely (source->status))
+ return source->status;
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length);
+ if (unlikely (status))
+ return status;
+
+ if (info.num_components != 1 && info.num_components != 3)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_pdf_surface_open_stream (surface,
+ &res,
+ FALSE,
+ " /Type /XObject\n"
+ " /Subtype /Image\n"
+ " /Width %d\n"
+ " /Height %d\n"
+ " /ColorSpace %s\n"
+ " /BitsPerComponent %d\n"
+ " /Filter /DCTDecode\n",
+ info.width,
+ info.height,
+ info.num_components == 1 ? "/DeviceGray" : "/DeviceRGB",
+ info.bits_per_component);
+ if (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_status_t
+_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface,
+ cairo_surface_t *source,
+ cairo_pdf_resource_t resource,
+ cairo_bool_t interpolate)
+{
+ cairo_image_surface_t *image;
+ void *image_extra;
+ cairo_status_t status;
+
+ status = _cairo_pdf_surface_emit_jpx_image (surface, source, resource);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_pdf_surface_emit_jpeg_image (surface, source, resource);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_surface_acquire_source_image (source, &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_emit_image (surface, image,
+ &resource, interpolate);
+ if (unlikely (status))
+ goto BAIL;
+
+BAIL:
+ _cairo_surface_release_source_image (source, image, image_extra);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface,
+ cairo_pdf_pattern_t *pdf_pattern,
+ cairo_pdf_resource_t *resource,
+ int *width,
+ int *height,
+ int *origin_x,
+ int *origin_y)
+{
+ cairo_image_surface_t *image;
+ cairo_surface_t *pad_image;
+ void *image_extra;
+ cairo_status_t status;
+ cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern;
+ int x = 0;
+ int y = 0;
+ cairo_bool_t interpolate;
+
+ status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ pad_image = &image->base;
+ if (pattern->base.extend == CAIRO_EXTEND_PAD) {
+ cairo_box_t box;
+ cairo_rectangle_int_t rect;
+ cairo_surface_pattern_t pad_pattern;
+
+ /* get the operation extents in pattern space */
+ _cairo_box_from_rectangle (&box, &pdf_pattern->extents);
+ _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL);
+ _cairo_box_round_to_rectangle (&box, &rect);
+ x = -rect.x;
+ y = -rect.y;
+
+ pad_image = _cairo_image_surface_create_with_content (pattern->surface->content,
+ rect.width,
+ rect.height);
+ if (pad_image->status) {
+ status = pad_image->status;
+ goto BAIL;
+ }
+
+ _cairo_pattern_init_for_surface (&pad_pattern, &image->base);
+ cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y);
+ pad_pattern.base.extend = CAIRO_EXTEND_PAD;
+ status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE,
+ &pad_pattern.base,
+ NULL,
+ pad_image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ rect.width,
+ rect.height,
+ NULL);
+ _cairo_pattern_fini (&pad_pattern.base);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ switch (pdf_pattern->pattern->filter) {
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ interpolate = TRUE;
+ break;
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ case CAIRO_FILTER_GAUSSIAN:
+ interpolate = FALSE;
+ break;
+ }
+
+ *resource = _cairo_pdf_surface_new_object (surface);
+ if (resource->id == 0) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image,
+ resource, interpolate);
+ if (unlikely (status))
+ goto BAIL;
+
+ *width = ((cairo_image_surface_t *)pad_image)->width;
+ *height = ((cairo_image_surface_t *)pad_image)->height;
+ *origin_x = x;
+ *origin_y = y;
+
+BAIL:
+ if (pad_image != &image->base)
+ cairo_surface_destroy (pad_image);
+
+ _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+ return status;
+}
+
+
+static cairo_status_t
+_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
+ cairo_surface_t *recording_surface,
+ cairo_pdf_resource_t resource)
+{
+ double old_width, old_height;
+ cairo_paginated_mode_t old_paginated_mode;
+ cairo_rectangle_int_t recording_extents;
+ cairo_bool_t is_bounded;
+ cairo_status_t status;
+ int alpha = 0;
+
+ is_bounded = _cairo_surface_get_extents (recording_surface, &recording_extents);
+ assert (is_bounded);
+
+ old_width = surface->width;
+ old_height = surface->height;
+ old_paginated_mode = surface->paginated_mode;
+
+ _cairo_pdf_surface_set_size_internal (surface,
+ recording_extents.width,
+ recording_extents.height);
+ /* Patterns are emitted after fallback images. The paginated mode
+ * needs to be set to _RENDER while the recording surface is replayed
+ * back to this surface.
+ */
+ surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER;
+ _cairo_pdf_group_resources_clear (&surface->resources);
+ status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE);
+ if (unlikely (status))
+ return status;
+
+ if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) {
+ status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n",
+ alpha,
+ surface->width,
+ surface->height);
+ }
+
+ status = _cairo_recording_surface_replay_region (recording_surface,
+ NULL,
+ &surface->base,
+ CAIRO_RECORDING_REGION_NATIVE);
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_close_content_stream (surface);
+
+ _cairo_pdf_surface_set_size_internal (surface,
+ old_width,
+ old_height);
+ surface->paginated_mode = old_paginated_mode;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_recording_subsurface (cairo_pdf_surface_t *surface,
+ cairo_surface_t *recording_surface,
+ const cairo_rectangle_int_t *extents,
+ cairo_pdf_resource_t resource)
+{
+ double old_width, old_height;
+ cairo_paginated_mode_t old_paginated_mode;
+ cairo_status_t status;
+ int alpha = 0;
+
+ old_width = surface->width;
+ old_height = surface->height;
+ old_paginated_mode = surface->paginated_mode;
+
+ _cairo_pdf_surface_set_size_internal (surface,
+ extents->width,
+ extents->height);
+ /* Patterns are emitted after fallback images. The paginated mode
+ * needs to be set to _RENDER while the recording surface is replayed
+ * back to this surface.
+ */
+ surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER;
+ _cairo_pdf_group_resources_clear (&surface->resources);
+ status = _cairo_pdf_surface_open_content_stream (surface, &resource, TRUE);
+ if (unlikely (status))
+ return status;
+
+ if (cairo_surface_get_content (recording_surface) == CAIRO_CONTENT_COLOR) {
+ status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n",
+ alpha,
+ surface->width,
+ surface->height);
+ }
+
+ status = _cairo_recording_surface_replay_region (recording_surface,
+ extents,
+ &surface->base,
+ CAIRO_RECORDING_REGION_NATIVE);
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_close_content_stream (surface);
+
+ _cairo_pdf_surface_set_size_internal (surface,
+ old_width,
+ old_height);
+ surface->paginated_mode = old_paginated_mode;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface,
+ cairo_pdf_source_surface_t *src_surface)
+{
+ if (src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (src_surface->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) src_surface->surface;
+ return _cairo_pdf_surface_emit_recording_subsurface (surface,
+ sub->target,
+ &sub->extents,
+ src_surface->hash_entry->surface_res);
+ } else {
+ return _cairo_pdf_surface_emit_recording_surface (surface,
+ src_surface->surface,
+ src_surface->hash_entry->surface_res);
+ }
+ } else {
+ return _cairo_pdf_surface_emit_image_surface (surface,
+ src_surface->surface,
+ src_surface->hash_entry->surface_res,
+ src_surface->hash_entry->interpolate);
+ }
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
+ cairo_pdf_pattern_t *pdf_pattern)
+{
+ cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern;
+ cairo_status_t status;
+ cairo_pdf_resource_t pattern_resource = {0};
+ cairo_matrix_t cairo_p2d, pdf_p2d;
+ cairo_extend_t extend = cairo_pattern_get_extend (&pattern->base);
+ double xstep, ystep;
+ int pattern_width = 0; /* squelch bogus compiler warning */
+ int pattern_height = 0; /* squelch bogus compiler warning */
+ int origin_x = 0; /* squelch bogus compiler warning */
+ int origin_y = 0; /* squelch bogus compiler warning */
+ int bbox_x, bbox_y;
+ char draw_surface[200];
+
+ if (pattern->base.extend == CAIRO_EXTEND_PAD &&
+ pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING)
+ {
+ status = _cairo_pdf_surface_emit_padded_image_surface (surface,
+ pdf_pattern,
+ &pattern_resource,
+ &pattern_width,
+ &pattern_height,
+ &origin_x,
+ &origin_y);
+ }
+ else
+ {
+ status = _cairo_pdf_surface_add_source_surface (surface,
+ pattern->surface,
+ pdf_pattern->pattern->filter,
+ &pattern_resource,
+ &pattern_width,
+ &pattern_height);
+ }
+ if (unlikely (status))
+ return status;
+
+ bbox_x = pattern_width;
+ bbox_y = pattern_height;
+ 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->width, y2 = surface->height;
+ _cairo_matrix_transform_bounding_box (&pattern->base.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_width + pattern_height);
+ }
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ xstep = pattern_width;
+ ystep = pattern_height;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ bbox_x = pattern_width*2;
+ bbox_y = pattern_height*2;
+ xstep = bbox_x;
+ ystep = bbox_y;
+ 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->base.matrix;
+ status = cairo_matrix_invert (&cairo_p2d);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf);
+ cairo_matrix_translate (&pdf_p2d, -origin_x, -origin_y);
+ cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height);
+ cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
+
+ _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+ status = _cairo_pdf_surface_open_stream (surface,
+ &pdf_pattern->pattern_res,
+ FALSE,
+ " /PatternType 1\n"
+ " /BBox [0 0 %d %d]\n"
+ " /XStep %f\n"
+ " /YStep %f\n"
+ " /TilingType 1\n"
+ " /PaintType 1\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Resources << /XObject << /x%d %d 0 R >> >>\n",
+ bbox_x, bbox_y,
+ xstep, ystep,
+ pdf_p2d.xx, pdf_p2d.yx,
+ pdf_p2d.xy, pdf_p2d.yy,
+ pdf_p2d.x0, pdf_p2d.y0,
+ pattern_resource.id,
+ pattern_resource.id);
+ if (unlikely (status))
+ return status;
+
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ snprintf(draw_surface,
+ sizeof (draw_surface),
+ "/x%d Do\n",
+ pattern_resource.id);
+ } else {
+ snprintf(draw_surface,
+ sizeof (draw_surface),
+ "q %d 0 0 %d 0 0 cm /x%d Do Q",
+ pattern_width,
+ pattern_height,
+ pattern_resource.id);
+ }
+
+ if (extend == CAIRO_EXTEND_REFLECT) {
+ _cairo_output_stream_printf (surface->output,
+ "q 0 0 %d %d re W n %s Q\n"
+ "q -1 0 0 1 %d 0 cm 0 0 %d %d re W n %s Q\n"
+ "q 1 0 0 -1 0 %d cm 0 0 %d %d re W n %s Q\n"
+ "q -1 0 0 -1 %d %d cm 0 0 %d %d re W n %s Q\n",
+ pattern_width, pattern_height,
+ draw_surface,
+ pattern_width*2, pattern_width, pattern_height,
+ draw_surface,
+ pattern_height*2, pattern_width, pattern_height,
+ draw_surface,
+ pattern_width*2, pattern_height*2, pattern_width, pattern_height,
+ draw_surface);
+ } 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_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_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_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_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_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_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_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_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 (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_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_status_t
+cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface,
+ cairo_pdf_resource_t gstate_resource,
+ cairo_pdf_resource_t gradient_mask)
+{
+ cairo_pdf_resource_t smask_resource;
+ cairo_status_t status;
+
+ status = _cairo_pdf_surface_open_stream (surface,
+ NULL,
+ surface->compress_content,
+ " /Type /XObject\n"
+ " /Subtype /Form\n"
+ " /FormType 1\n"
+ " /BBox [ 0 0 %f %f ]\n"
+ " /Resources\n"
+ " << /ExtGState\n"
+ " << /a0 << /ca 1 /CA 1 >>"
+ " >>\n"
+ " /Pattern\n"
+ " << /p%d %d 0 R >>\n"
+ " >>\n"
+ " /Group\n"
+ " << /Type /Group\n"
+ " /S /Transparency\n"
+ " /CS /DeviceGray\n"
+ " >>\n",
+ surface->width,
+ surface->height,
+ gradient_mask.id,
+ gradient_mask.id);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "q\n"
+ "/a0 gs\n"
+ "/Pattern cs /p%d scn\n"
+ "0 0 %f %f re\n"
+ "f\n"
+ "Q\n",
+ gradient_mask.id,
+ surface->width,
+ surface->height);
+
+ 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 cairo_status_t
+_cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t *surface,
+ cairo_pdf_pattern_t *pdf_pattern)
+{
+ cairo_linear_pattern_t *pattern = (cairo_linear_pattern_t *) pdf_pattern->pattern;
+ cairo_pdf_resource_t color_function, alpha_function;
+ double x1, y1, x2, y2;
+ double _x1, _y1, _x2, _y2;
+ cairo_matrix_t pat_to_pdf;
+ cairo_extend_t extend;
+ cairo_status_t status;
+ cairo_gradient_pattern_t *gradient = &pattern->base;
+ double first_stop, last_stop;
+ int repeat_begin = 0, repeat_end = 1;
+
+ assert (pattern->base.n_stops != 0);
+
+ extend = cairo_pattern_get_extend (pdf_pattern->pattern);
+
+ pat_to_pdf = pattern->base.base.matrix;
+ status = cairo_matrix_invert (&pat_to_pdf);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+ first_stop = gradient->stops[0].offset;
+ last_stop = gradient->stops[gradient->n_stops - 1].offset;
+
+ if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+ double dx, dy;
+ int x_rep = 0, y_rep = 0;
+
+ x1 = _cairo_fixed_to_double (pattern->p1.x);
+ y1 = _cairo_fixed_to_double (pattern->p1.y);
+ cairo_matrix_transform_point (&pat_to_pdf, &x1, &y1);
+
+ x2 = _cairo_fixed_to_double (pattern->p2.x);
+ y2 = _cairo_fixed_to_double (pattern->p2.y);
+ cairo_matrix_transform_point (&pat_to_pdf, &x2, &y2);
+
+ dx = fabs (x2 - x1);
+ dy = fabs (y2 - y1);
+ if (dx > 1e-6)
+ x_rep = ceil (surface->width/dx);
+ if (dy > 1e-6)
+ y_rep = ceil (surface->height/dy);
+
+ repeat_end = MAX (x_rep, y_rep);
+ repeat_begin = -repeat_end;
+ first_stop = repeat_begin;
+ last_stop = repeat_end;
+ }
+
+ /* PDF requires the first and last stop to be the same as the line
+ * coordinates. For repeating patterns this moves the line
+ * coordinates out to the begin/end of the repeating function. For
+ * non repeating patterns this may move the line coordinates in if
+ * there are not stops at offset 0 and 1. */
+ x1 = _cairo_fixed_to_double (pattern->p1.x);
+ y1 = _cairo_fixed_to_double (pattern->p1.y);
+ x2 = _cairo_fixed_to_double (pattern->p2.x);
+ y2 = _cairo_fixed_to_double (pattern->p2.y);
+
+ _x1 = x1 + (x2 - x1)*first_stop;
+ _y1 = y1 + (y2 - y1)*first_stop;
+ _x2 = x1 + (x2 - x1)*last_stop;
+ _y2 = y1 + (y2 - y1)*last_stop;
+
+ x1 = _x1;
+ x2 = _x2;
+ y1 = _y1;
+ y2 = _y2;
+
+ /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
+ * Type 2 function is used by itself without a stitching
+ * function. Type 2 functions always have the domain [0 1] */
+ if ((pattern->base.base.extend == CAIRO_EXTEND_NONE ||
+ pattern->base.base.extend == CAIRO_EXTEND_PAD) &&
+ gradient->n_stops == 2) {
+ first_stop = 0.0;
+ last_stop = 1.0;
+ }
+
+ status = _cairo_pdf_surface_emit_pattern_stops (surface,
+ &pattern->base,
+ &color_function,
+ &alpha_function);
+ if (unlikely (status))
+ return status;
+
+ if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+ status = _cairo_pdf_surface_emit_repeating_function (surface,
+ &pattern->base,
+ &color_function,
+ repeat_begin,
+ repeat_end);
+ if (unlikely (status))
+ return status;
+
+ if (alpha_function.id != 0) {
+ status = _cairo_pdf_surface_emit_repeating_function (surface,
+ &pattern->base,
+ &alpha_function,
+ repeat_begin,
+ repeat_end);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Pattern\n"
+ " /PatternType 2\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Shading\n"
+ " << /ShadingType 2\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /Coords [ %f %f %f %f ]\n"
+ " /Domain [ %f %f ]\n"
+ " /Function %d 0 R\n",
+ pdf_pattern->pattern_res.id,
+ pat_to_pdf.xx, pat_to_pdf.yx,
+ pat_to_pdf.xy, pat_to_pdf.yy,
+ pat_to_pdf.x0, pat_to_pdf.y0,
+ x1, y1, x2, y2,
+ first_stop, last_stop,
+ color_function.id);
+
+ if (extend == CAIRO_EXTEND_PAD) {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ false false ]\n");
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " >>\n"
+ ">>\n"
+ "endobj\n");
+
+ 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_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Pattern\n"
+ " /PatternType 2\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Shading\n"
+ " << /ShadingType 2\n"
+ " /ColorSpace /DeviceGray\n"
+ " /Coords [ %f %f %f %f ]\n"
+ " /Domain [ %f %f ]\n"
+ " /Function %d 0 R\n",
+ mask_resource.id,
+ pat_to_pdf.xx, pat_to_pdf.yx,
+ pat_to_pdf.xy, pat_to_pdf.yy,
+ pat_to_pdf.x0, pat_to_pdf.y0,
+ x1, y1, x2, y2,
+ first_stop, last_stop,
+ alpha_function.id);
+
+ if (extend == CAIRO_EXTEND_PAD) {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ false false ]\n");
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " >>\n"
+ ">>\n"
+ "endobj\n");
+ status = _cairo_pdf_surface_add_pattern (surface, mask_resource);
+ if (unlikely (status))
+ return status;
+
+ status = cairo_pdf_surface_emit_transparency_group (surface,
+ pdf_pattern->gstate_res,
+ mask_resource);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t *surface,
+ cairo_pdf_pattern_t *pdf_pattern)
+{
+ cairo_pdf_resource_t color_function, alpha_function;
+ double x1, y1, x2, y2, r1, r2;
+ cairo_matrix_t pat_to_pdf;
+ cairo_extend_t extend;
+ cairo_status_t status;
+ cairo_radial_pattern_t *pattern = (cairo_radial_pattern_t *) pdf_pattern->pattern;
+
+ assert (pattern->base.n_stops != 0);
+
+ extend = cairo_pattern_get_extend (pdf_pattern->pattern);
+
+ status = _cairo_pdf_surface_emit_pattern_stops (surface,
+ &pattern->base,
+ &color_function,
+ &alpha_function);
+ if (unlikely (status))
+ return status;
+
+ pat_to_pdf = pattern->base.base.matrix;
+ status = cairo_matrix_invert (&pat_to_pdf);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
+ x1 = _cairo_fixed_to_double (pattern->c1.x);
+ y1 = _cairo_fixed_to_double (pattern->c1.y);
+ r1 = _cairo_fixed_to_double (pattern->r1);
+ x2 = _cairo_fixed_to_double (pattern->c2.x);
+ y2 = _cairo_fixed_to_double (pattern->c2.y);
+ r2 = _cairo_fixed_to_double (pattern->r2);
+
+ _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Pattern\n"
+ " /PatternType 2\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Shading\n"
+ " << /ShadingType 3\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /Coords [ %f %f %f %f %f %f ]\n"
+ " /Function %d 0 R\n",
+ pdf_pattern->pattern_res.id,
+ pat_to_pdf.xx, pat_to_pdf.yx,
+ pat_to_pdf.xy, pat_to_pdf.yy,
+ pat_to_pdf.x0, pat_to_pdf.y0,
+ x1, y1, r1, x2, y2, r2,
+ color_function.id);
+
+ if (extend == CAIRO_EXTEND_PAD) {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ false false ]\n");
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " >>\n"
+ ">>\n"
+ "endobj\n");
+
+ 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_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Pattern\n"
+ " /PatternType 2\n"
+ " /Matrix [ %f %f %f %f %f %f ]\n"
+ " /Shading\n"
+ " << /ShadingType 3\n"
+ " /ColorSpace /DeviceGray\n"
+ " /Coords [ %f %f %f %f %f %f ]\n"
+ " /Function %d 0 R\n",
+ mask_resource.id,
+ pat_to_pdf.xx, pat_to_pdf.yx,
+ pat_to_pdf.xy, pat_to_pdf.yy,
+ pat_to_pdf.x0, pat_to_pdf.y0,
+ x1, y1, r1, x2, y2, r2,
+ alpha_function.id);
+
+ if (extend == CAIRO_EXTEND_PAD) {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->output,
+ " /Extend [ false false ]\n");
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " >>\n"
+ ">>\n"
+ "endobj\n");
+
+ status = cairo_pdf_surface_emit_transparency_group (surface,
+ pdf_pattern->gstate_res,
+ mask_resource);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern)
+{
+ double old_width, old_height;
+ cairo_status_t status;
+
+ old_width = surface->width;
+ old_height = surface->height;
+ _cairo_pdf_surface_set_size_internal (surface,
+ pdf_pattern->width,
+ pdf_pattern->height);
+
+ 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:
+ status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern);
+ break;
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ status = _cairo_pdf_surface_emit_linear_pattern (surface, pdf_pattern);
+ break;
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ status = _cairo_pdf_surface_emit_radial_pattern (surface, pdf_pattern);
+ break;
+
+ default:
+ ASSERT_NOT_REACHED;
+ status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ break;
+ }
+
+ _cairo_pdf_surface_set_size_internal (surface,
+ old_width,
+ old_height);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface,
+ cairo_surface_pattern_t *source)
+{
+ cairo_pdf_resource_t surface_res;
+ int width, height;
+ cairo_matrix_t cairo_p2d, pdf_p2d;
+ cairo_status_t status;
+ int alpha;
+
+ status = _cairo_pdf_surface_add_source_surface (surface,
+ source->surface,
+ source->base.filter,
+ &surface_res,
+ &width,
+ &height);
+ if (unlikely (status))
+ return status;
+
+ cairo_p2d = source->base.matrix;
+ status = cairo_matrix_invert (&cairo_p2d);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ pdf_p2d = surface->cairo_to_pdf;
+ cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d);
+ cairo_matrix_translate (&pdf_p2d, 0.0, height);
+ cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
+ if (source->surface->type != CAIRO_SURFACE_TYPE_RECORDING)
+ cairo_matrix_scale (&pdf_p2d, width, height);
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_matrix_is_identity (&pdf_p2d)) {
+ _cairo_output_stream_printf (surface->output,
+ "%f %f %f %f %f %f cm\n",
+ pdf_p2d.xx, pdf_p2d.yx,
+ pdf_p2d.xy, pdf_p2d.yy,
+ pdf_p2d.x0, pdf_p2d.y0);
+ }
+
+ status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "/a%d gs /x%d Do\n",
+ alpha,
+ surface_res.id);
+
+ return _cairo_pdf_surface_add_xobject (surface, surface_res);
+}
+
+static cairo_status_t
+_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface,
+ cairo_operator_t op)
+{
+ cairo_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_status_t
+_cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
+ const cairo_pattern_t *pattern,
+ cairo_pdf_resource_t pattern_res,
+ cairo_bool_t is_stroke)
+{
+ cairo_status_t status;
+ 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->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_pdf_surface_close_content_stream (surface);
+ if (unlikely (status))
+ return status;
+
+ 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;
+
+ 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 TRUE;
+}
+
+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);
+}
+
+static cairo_pdf_resource_t
+_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_resource_t info;
+
+ info = _cairo_pdf_surface_new_object (surface);
+ if (info.id == 0)
+ return info;
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Creator (cairo %s (http://cairographics.org))\n"
+ " /Producer (cairo %s (http://cairographics.org))\n"
+ ">>\n"
+ "endobj\n",
+ info.id,
+ cairo_version_string (),
+ cairo_version_string ());
+
+ return info;
+}
+
+static void
+_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_resource_t page;
+ int num_pages, i;
+
+ _cairo_pdf_surface_update_object (surface, surface->pages_resource);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Pages\n"
+ " /Kids [ ",
+ surface->pages_resource.id);
+
+ 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->output, "%d 0 R ", page.id);
+ }
+
+ _cairo_output_stream_printf (surface->output, "]\n");
+ _cairo_output_stream_printf (surface->output, " /Count %d\n", num_pages);
+
+
+ /* TODO: Figure out which other defaults to be inherited by /Page
+ * objects. */
+ _cairo_output_stream_printf (surface->output,
+ ">>\n"
+ "endobj\n");
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface,
+ const char *utf8)
+{
+ uint16_t *utf16 = NULL;
+ int utf16_len = 0;
+ cairo_status_t status;
+ int i;
+
+ if (utf8 && *utf8) {
+ status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
+ 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, ">");
+
+ if (utf16)
+ 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);
+ case 10: c+= ((uint32_t) data[9] << 16);
+ case 9 : c+= ((uint32_t) data[8] << 8);
+ case 8 : b+= ((uint32_t) data[7] << 24);
+ case 7 : b+= ((uint32_t) data[6] << 16);
+ case 6 : b+= ((uint32_t) data[5] << 8);
+ case 5 : b+= data[4];
+ case 4 : a+= ((uint32_t) data[3] << 24);
+ case 3 : a+= ((uint32_t) data[2] << 16);
+ case 2 : a+= ((uint32_t) data[1] << 8);
+ case 1 : a+= data[0];
+ }
+ HASH_MIX (a,b,c);
+
+ return c;
+}
+
+static void
+_create_font_subset_tag (cairo_scaled_font_subset_t *font_subset,
+ const char *font_name,
+ char *tag)
+{
+ uint32_t hash;
+ int i;
+ long numerator;
+ ldiv_t d;
+
+ hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0);
+ hash = _hash_data ((unsigned char *) (font_subset->glyphs),
+ font_subset->num_glyphs * sizeof(unsigned long), hash);
+
+ numerator = abs (hash);
+ for (i = 0; i < 6; i++) {
+ d = ldiv (numerator, 26);
+ numerator = d.quot;
+ tag[i] = 'A' + d.rem;
+ }
+ tag[i] = 0;
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset,
+ cairo_bool_t is_composite,
+ 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_content,
+ 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 (is_composite) {
+ _cairo_output_stream_printf (surface->output,
+ "<0000> <ffff>\n");
+ } else {
+ _cairo_output_stream_printf (surface->output,
+ "<00> <ff>\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 (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_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;
+ cairo_status_t status;
+ char tag[10];
+
+ _create_font_subset_tag (font_subset, subset->ps_name, tag);
+
+ subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+ font_subset->font_id,
+ font_subset->subset_id);
+ if (subset_resource.id == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_pdf_surface_open_stream (surface,
+ NULL,
+ TRUE,
+ " /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, TRUE,
+ &to_unicode_stream);
+ if (_cairo_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->font_name) {
+ _cairo_output_stream_printf (surface->output,
+ " /FontFamily (%s)\n",
+ subset->font_name);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " /Flags 4\n"
+ " /FontBBox [ %ld %ld %ld %ld ]\n"
+ " /ItalicAngle 0\n"
+ " /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);
+
+ cidfont_dict = _cairo_pdf_surface_new_object (surface);
+ if (cidfont_dict.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /CIDFontType0\n"
+ " /BaseFont /%s+%s\n"
+ " /CIDSystemInfo\n"
+ " << /Registry (Adobe)\n"
+ " /Ordering (Identity)\n"
+ " /Supplement 0\n"
+ " >>\n"
+ " /FontDescriptor %d 0 R\n"
+ " /W [0 [",
+ cidfont_dict.id,
+ tag,
+ subset->ps_name,
+ descriptor.id);
+
+ for (i = 0; i < font_subset->num_glyphs; i++)
+ _cairo_output_stream_printf (surface->output,
+ " %ld",
+ (long)(subset->widths[i]*PDF_UNITS_PER_EM));
+
+ _cairo_output_stream_printf (surface->output,
+ " ]]\n"
+ ">>\n"
+ "endobj\n");
+
+ _cairo_pdf_surface_update_object (surface, subset_resource);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /Type0\n"
+ " /BaseFont /%s+%s\n"
+ " /Encoding /Identity-H\n"
+ " /DescendantFonts [ %d 0 R]\n",
+ subset_resource.id,
+ tag,
+ subset->ps_name,
+ cidfont_dict.id);
+
+ if (to_unicode_stream.id != 0)
+ _cairo_output_stream_printf (surface->output,
+ " /ToUnicode %d 0 R\n",
+ to_unicode_stream.id);
+
+ _cairo_output_stream_printf (surface->output,
+ ">>\n"
+ "endobj\n");
+
+ font.font_id = font_subset->font_id;
+ font.subset_id = font_subset->subset_id;
+ font.subset_resource = subset_resource;
+ status = _cairo_array_append (&surface->fonts, &font);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_status_t status;
+ cairo_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_status_t
+_cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_status_t status;
+ cairo_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_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_status_t
+_cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset,
+ cairo_type1_subset_t *subset)
+{
+ cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream;
+ cairo_pdf_font_t font;
+ cairo_status_t status;
+ unsigned long length;
+ unsigned int i;
+ char tag[10];
+
+ _create_font_subset_tag (font_subset, subset->base_font, tag);
+
+ subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+ font_subset->font_id,
+ 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, FALSE,
+ &to_unicode_stream);
+ if (_cairo_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"
+ " /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 0\n"
+ " /LastChar %d\n"
+ " /FontDescriptor %d 0 R\n"
+ " /Widths [",
+ subset_resource.id,
+ tag,
+ subset->base_font,
+ font_subset->num_glyphs - 1,
+ descriptor.id);
+
+ for (i = 0; i < font_subset->num_glyphs; i++)
+ _cairo_output_stream_printf (surface->output,
+ " %ld",
+ (long)(subset->widths[i]*PDF_UNITS_PER_EM));
+
+ _cairo_output_stream_printf (surface->output,
+ " ]\n");
+
+ 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);
+}
+
+#if CAIRO_HAS_FT_FONT
+static cairo_status_t
+_cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_status_t status;
+ cairo_type1_subset_t subset;
+ char name[64];
+
+ 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;
+}
+#endif
+
+static cairo_status_t
+_cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_status_t status;
+ cairo_type1_subset_t subset;
+ char name[64];
+
+ 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_status_t
+_cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_pdf_resource_t stream, descriptor, cidfont_dict;
+ cairo_pdf_resource_t subset_resource, to_unicode_stream;
+ cairo_status_t status;
+ cairo_pdf_font_t font;
+ cairo_truetype_subset_t subset;
+ unsigned int i;
+ char tag[10];
+
+ subset_resource = _cairo_pdf_surface_get_font_resource (surface,
+ font_subset->font_id,
+ font_subset->subset_id);
+ if (subset_resource.id == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_truetype_subset_init (&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, TRUE,
+ &to_unicode_stream);
+ if (_cairo_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.font_name) {
+ _cairo_output_stream_printf (surface->output,
+ " /FontFamily (%s)\n",
+ subset.font_name);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ " /Flags 4\n"
+ " /FontBBox [ %ld %ld %ld %ld ]\n"
+ " /ItalicAngle 0\n"
+ " /Ascent %ld\n"
+ " /Descent %ld\n"
+ " /CapHeight %ld\n"
+ " /StemV 80\n"
+ " /StemH 80\n"
+ " /FontFile2 %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);
+
+ cidfont_dict = _cairo_pdf_surface_new_object (surface);
+ if (cidfont_dict.id == 0) {
+ _cairo_truetype_subset_fini (&subset);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /CIDFontType2\n"
+ " /BaseFont /%s+%s\n"
+ " /CIDSystemInfo\n"
+ " << /Registry (Adobe)\n"
+ " /Ordering (Identity)\n"
+ " /Supplement 0\n"
+ " >>\n"
+ " /FontDescriptor %d 0 R\n"
+ " /W [0 [",
+ cidfont_dict.id,
+ tag,
+ subset.ps_name,
+ descriptor.id);
+
+ for (i = 0; i < font_subset->num_glyphs; i++)
+ _cairo_output_stream_printf (surface->output,
+ " %ld",
+ (long)(subset.widths[i]*PDF_UNITS_PER_EM));
+
+ _cairo_output_stream_printf (surface->output,
+ " ]]\n"
+ ">>\n"
+ "endobj\n");
+
+ _cairo_pdf_surface_update_object (surface, subset_resource);
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Font\n"
+ " /Subtype /Type0\n"
+ " /BaseFont /%s+%s\n"
+ " /Encoding /Identity-H\n"
+ " /DescendantFonts [ %d 0 R]\n",
+ subset_resource.id,
+ tag,
+ subset.ps_name,
+ cidfont_dict.id);
+
+ if (to_unicode_stream.id != 0)
+ _cairo_output_stream_printf (surface->output,
+ " /ToUnicode %d 0 R\n",
+ to_unicode_stream.id);
+
+ _cairo_output_stream_printf (surface->output,
+ ">>\n"
+ "endobj\n");
+
+ 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_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_status_t
+_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_pdf_surface_t *surface = closure;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_status_t status2;
+ 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);
+ 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_STATUS_SUCCESS)
+ status = status2;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_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);
+ 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_content,
+ 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, FALSE,
+ &to_unicode_stream);
+ if (_cairo_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.p2.y),
+ _cairo_fixed_to_double (font_bbox.p2.x),
+ - _cairo_fixed_to_double (font_bbox.p1.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);
+
+ 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_status_t
+_cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_pdf_surface_t *surface = closure;
+ cairo_status_t status;
+
+ if (font_subset->is_composite) {
+ status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ } else {
+#if CAIRO_HAS_FT_FONT
+ status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+#endif
+
+ status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ }
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_pdf_surface_t *surface = closure;
+ cairo_status_t status;
+
+ status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ ASSERT_NOT_REACHED;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface)
+{
+ cairo_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_pdf_resource_t
+_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_resource_t catalog;
+
+ catalog = _cairo_pdf_surface_new_object (surface);
+ if (catalog.id == 0)
+ return catalog;
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Catalog\n"
+ " /Pages %d 0 R\n"
+ ">>\n"
+ "endobj\n",
+ catalog.id,
+ surface->pages_resource.id);
+
+ return catalog;
+}
+
+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->offset);
+ _cairo_output_stream_printf (surface->output,
+ "%s 00000 n \n", buffer);
+ }
+
+ return offset;
+}
+
+static cairo_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_status_t status;
+
+ /* Create mask group */
+ status = _cairo_pdf_surface_open_group (surface, NULL);
+ if (unlikely (status))
+ return status;
+
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL,
+ &pattern_res, &gstate_res);
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ smask_group = _cairo_pdf_surface_create_smask_group (surface);
+ if (unlikely (smask_group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ smask_group->operation = PDF_PAINT;
+ smask_group->source = cairo_pattern_reference (group->mask);
+ smask_group->source_res = pattern_res;
+ status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (smask_group);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (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,
+ "0 0 %f %f re f\n",
+ surface->width, surface->height);
+
+ 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, &group->source_res);
+ if (unlikely (status))
+ return status;
+
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL,
+ &pattern_res, &gstate_res);
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ smask_group = _cairo_pdf_surface_create_smask_group (surface);
+ if (unlikely (smask_group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ smask_group->operation = PDF_PAINT;
+ smask_group->source = cairo_pattern_reference (group->source);
+ smask_group->source_res = pattern_res;
+ status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (smask_group);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (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,
+ "0 0 %f %f re f\n",
+ surface->width, surface->height);
+
+ 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_status_t
+_cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface,
+ cairo_pdf_smask_group_t *group)
+{
+ double old_width, old_height;
+ cairo_status_t status;
+
+ old_width = surface->width;
+ old_height = surface->height;
+ _cairo_pdf_surface_set_size_internal (surface,
+ group->width,
+ group->height);
+ /* _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;
+ }
+
+ status = _cairo_pdf_surface_open_group (surface, &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:
+ _cairo_pdf_surface_set_size_internal (surface,
+ old_width,
+ old_height);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_pattern_t pattern;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_source_surface_t src_surface;
+ int pattern_index, group_index, surface_index;
+ cairo_status_t status;
+
+ /* 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;
+ 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)))
+ {
+ 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);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface)
+{
+ cairo_pdf_resource_t page, knockout, res;
+ cairo_status_t status;
+ int i, len;
+
+ _cairo_pdf_group_resources_clear (&surface->resources);
+ if (surface->has_fallback_images) {
+ status = _cairo_pdf_surface_open_knockout_group (surface);
+ 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, 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);
+ if (unlikely (status))
+ return status;
+ }
+
+ page = _cairo_pdf_surface_new_object (surface);
+ if (page.id == 0)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_output_stream_printf (surface->output,
+ "%d 0 obj\n"
+ "<< /Type /Page\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"
+ " /CS /DeviceRGB\n"
+ " >>\n"
+ " /Resources %d 0 R\n"
+ ">>\n"
+ "endobj\n",
+ page.id,
+ surface->pages_resource.id,
+ surface->width,
+ surface->height,
+ surface->content.id,
+ surface->content_resources.id);
+
+ status = _cairo_array_append (&surface->pages, &page);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface);
+ if (unlikely (status))
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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...
+ content = cairo_surface_get_content (pattern->surface);
+ if (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
+_gradient_pattern_supported (const cairo_pattern_t *pattern)
+{
+ cairo_extend_t extend;
+
+ extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern);
+
+
+ /* Radial gradients are currently only supported with EXTEND_NONE
+ * and EXTEND_PAD and when one circle is inside the other. */
+ if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ double x1, y1, x2, y2, r1, r2, d;
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
+
+ if (extend == CAIRO_EXTEND_REPEAT ||
+ extend == CAIRO_EXTEND_REFLECT) {
+ return FALSE;
+ }
+
+ x1 = _cairo_fixed_to_double (radial->c1.x);
+ y1 = _cairo_fixed_to_double (radial->c1.y);
+ r1 = _cairo_fixed_to_double (radial->r1);
+ x2 = _cairo_fixed_to_double (radial->c2.x);
+ y2 = _cairo_fixed_to_double (radial->c2.y);
+ r2 = _cairo_fixed_to_double (radial->r2);
+
+ d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+ if (d > fabs(r2 - r1)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static cairo_bool_t
+_pattern_supported (const cairo_pattern_t *pattern)
+{
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+ return TRUE;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+ return _gradient_pattern_supported (pattern);
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
+ return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
+
+ 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)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ else
+ 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_status_t status;
+
+ status = _cairo_pdf_surface_close_content_stream (surface);
+ 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);
+ return _cairo_pdf_surface_open_content_stream (surface, NULL, TRUE);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_resource_t pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ &rect,
+ op, source, clip);
+ if (unlikely (status)) {
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ return CAIRO_STATUS_SUCCESS;
+
+ return status;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+ } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+ status = _cairo_pdf_surface_start_fallback (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ source->extend == CAIRO_EXTEND_NONE)
+ {
+ _cairo_output_stream_printf (surface->output, "q\n");
+ status = _cairo_pdf_surface_paint_surface_pattern (surface,
+ (cairo_surface_pattern_t *) source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output, "Q\n");
+ 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,
+ &extents.bounded,
+ &pattern_res, &gstate_res);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ group = _cairo_pdf_surface_create_smask_group (surface);
+ if (unlikely (group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ group->operation = PDF_PAINT;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+ group->source_res = pattern_res;
+ status = _cairo_pdf_surface_add_smask_group (surface, group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_add_xobject (surface, 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,
+ group->group_res.id);
+ } else {
+ status = _cairo_pdf_surface_select_pattern (surface, source,
+ pattern_res, FALSE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "0 0 %f %f re f\n",
+ surface->width, surface->height);
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+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,
+ cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_smask_group_t *group;
+ cairo_status_t status;
+ cairo_composite_rectangles_t extents;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ &rect,
+ op, source, mask, clip);
+ if (unlikely (status)) {
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ return CAIRO_STATUS_SUCCESS;
+
+ return status;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ cairo_status_t source_status, mask_status;
+
+ source_status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+ if (_cairo_status_is_error (source_status))
+ return source_status;
+
+ if (mask->has_component_alpha) {
+ mask_status = CAIRO_INT_STATUS_UNSUPPORTED;
+ } else {
+ mask_status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded);
+ if (_cairo_status_is_error (mask_status))
+ return mask_status;
+ }
+
+ return _cairo_analysis_surface_merge_status (source_status,
+ mask_status);
+ } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+ status = _cairo_pdf_surface_start_fallback (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+ assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded));
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ group = _cairo_pdf_surface_create_smask_group (surface);
+ if (unlikely (group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ group->operation = PDF_MASK;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+ status = _cairo_pattern_create_copy (&group->mask, mask);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+ group->source_res = _cairo_pdf_surface_new_object (surface);
+ if (group->source_res.id == 0) {
+ _cairo_pdf_smask_group_destroy (group);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ status = _cairo_pdf_surface_add_smask_group (surface, group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, group->group_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->source_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output,
+ "q /s%d gs /x%d Do Q\n",
+ group->group_res.id,
+ group->source_res.id);
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_resource_t pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &rect,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status)) {
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ return CAIRO_STATUS_SUCCESS;
+
+ return status;
+ }
+
+ /* use the more accurate extents */
+ if (extents.is_bounded) {
+ status = _cairo_path_fixed_stroke_extents (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &extents.mask);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+ &extents.bounded,
+ &pattern_res, &gstate_res);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ group = _cairo_pdf_surface_create_smask_group (surface);
+ if (unlikely (group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ group->operation = PDF_STROKE;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+ group->source_res = pattern_res;
+ status = _cairo_path_fixed_init_copy (&group->path, path);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+
+ 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);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+ 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 /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))
+ return status;
+
+ status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
+ path,
+ style,
+ ctm,
+ ctm_inverse);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+static cairo_int_status_t
+_cairo_pdf_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_resource_t pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ &rect,
+ op, source, path,
+ clip);
+ if (unlikely (status)) {
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ return CAIRO_STATUS_SUCCESS;
+
+ return status;
+ }
+
+ /* use the more accurate extents */
+ if (extents.is_bounded) {
+ _cairo_path_fixed_fill_extents (path,
+ fill_rule,
+ tolerance,
+ &extents.mask);
+
+ if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
+ return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+ } else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+ status = _cairo_pdf_surface_start_fallback (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ return status;
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ source->extend == CAIRO_EXTEND_NONE)
+ {
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output, "q\n");
+ status = _cairo_pdf_operators_clip (&surface->pdf_operators,
+ path,
+ fill_rule);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_paint_surface_pattern (surface,
+ (cairo_surface_pattern_t *) source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->output, "Q\n");
+ 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,
+ &extents.bounded,
+ &pattern_res, &gstate_res);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ group = _cairo_pdf_surface_create_smask_group (surface);
+ if (unlikely (group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ group->operation = PDF_FILL;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+ group->source_res = pattern_res;
+ status = _cairo_path_fixed_init_copy (&group->path, path);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+
+ group->fill_rule = fill_rule;
+ status = _cairo_pdf_surface_add_smask_group (surface, group);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+ 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 /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))
+ return status;
+
+ status = _cairo_pdf_operators_fill (&surface->pdf_operators,
+ path,
+ fill_rule);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+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,
+ cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res;
+ cairo_rectangle_int_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;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_select_operator (surface, fill_op);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_surface_fill_extents (&surface->base,
+ fill_op, fill_source, path, fill_rule,
+ fill_tolerance, fill_antialias,
+ clip, &extents);
+ if (unlikely (status))
+ return status;
+
+
+ fill_pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source,
+ &extents,
+ &fill_pattern_res,
+ &gstate_res);
+ if (unlikely (status))
+ return status;
+
+ assert (gstate_res.id == 0);
+
+ status = _cairo_surface_stroke_extents (&surface->base,
+ stroke_op, stroke_source, path,
+ stroke_style, stroke_ctm, stroke_ctm_inverse,
+ stroke_tolerance, stroke_antialias,
+ clip, &extents);
+ if (unlikely (status))
+ return status;
+
+ stroke_pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface,
+ stroke_source,
+ &extents,
+ &stroke_pattern_res,
+ &gstate_res);
+ if (unlikely (status))
+ return status;
+
+ 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))
+ return status;
+
+ status = _cairo_pdf_surface_select_pattern (surface, stroke_source,
+ stroke_pattern_res, TRUE);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators,
+ path,
+ fill_rule,
+ stroke_style,
+ stroke_ctm,
+ stroke_ctm_inverse);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+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,
+ cairo_clip_t *clip)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+ cairo_pdf_smask_group_t *group;
+ cairo_pdf_resource_t pattern_res, gstate_res;
+ cairo_composite_rectangles_t extents;
+ cairo_bool_t overlap;
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ &rect,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status)) {
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ return CAIRO_STATUS_SUCCESS;
+
+ return status;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
+
+ assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ pattern_res.id = 0;
+ gstate_res.id = 0;
+ status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
+ &extents.bounded,
+ &pattern_res, &gstate_res);
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_select_operator (surface, op);
+ if (unlikely (status))
+ return status;
+
+ if (gstate_res.id != 0) {
+ group = _cairo_pdf_surface_create_smask_group (surface);
+ if (unlikely (group == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ group->operation = PDF_SHOW_GLYPHS;
+ status = _cairo_pattern_create_copy (&group->source, source);
+ if (unlikely (status)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return status;
+ }
+ group->source_res = pattern_res;
+
+ if (utf8_len) {
+ group->utf8 = malloc (utf8_len);
+ if (unlikely (group->utf8 == NULL)) {
+ _cairo_pdf_smask_group_destroy (group);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ 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);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ 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);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ 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);
+ return status;
+ }
+
+ status = _cairo_pdf_surface_add_smask (surface, gstate_res);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
+ 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 /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))
+ return status;
+
+ /* 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))
+ return status;
+ }
+
+ 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))
+ return status;
+
+ status = _cairo_pdf_surface_unselect_pattern (surface);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _cairo_output_stream_get_status (surface->output);
+}
+
+
+static void
+_cairo_pdf_surface_set_paginated_mode (void *abstract_surface,
+ cairo_paginated_mode_t paginated_mode)
+{
+ cairo_pdf_surface_t *surface = abstract_surface;
+
+ surface->paginated_mode = paginated_mode;
+}
+
+static const cairo_surface_backend_t cairo_pdf_surface_backend = {
+ CAIRO_SURFACE_TYPE_PDF,
+ NULL, /* create similar: handled by wrapper */
+ _cairo_pdf_surface_finish,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* _cairo_pdf_surface_copy_page */
+ _cairo_pdf_surface_show_page,
+ _cairo_pdf_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_pdf_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ /* Here are the drawing functions */
+
+ _cairo_pdf_surface_paint,
+ _cairo_pdf_surface_mask,
+ _cairo_pdf_surface_stroke,
+ _cairo_pdf_surface_fill,
+ NULL, /* show_glyphs */
+ NULL, /* snapshot */
+
+ NULL, /* is_compatible */
+ _cairo_pdf_surface_fill_stroke,
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ _cairo_pdf_surface_has_show_text_glyphs,
+ _cairo_pdf_surface_show_text_glyphs,
+};
+
+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,
+};
diff --git a/gfx/cairo/cairo/src/cairo-pdf.h b/gfx/cairo/cairo/src/cairo-pdf.h
new file mode 100644
index 000000000..50460ccdf
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-pdf.h
@@ -0,0 +1,94 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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.
+ * @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification.
+ *
+ * #cairo_pdf_version_t is used to describe the version number of the PDF
+ * specification that a generated PDF file will conform to.
+ *
+ * Since 1.10
+ */
+typedef enum _cairo_pdf_version {
+ CAIRO_PDF_VERSION_1_4,
+ CAIRO_PDF_VERSION_1_5
+} cairo_pdf_version_t;
+
+cairo_public cairo_surface_t *
+cairo_pdf_surface_create (const char *filename,
+ double width_in_points,
+ 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_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 000000000..e71f7b561
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-pen.c
@@ -0,0 +1,398 @@
+/* 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-slope-private.h"
+
+static int
+_cairo_pen_vertices_needed (double tolerance,
+ double radius,
+ const cairo_matrix_t *matrix);
+
+static void
+_cairo_pen_compute_slopes (cairo_pen_t *pen);
+
+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++) {
+ double theta = 2 * M_PI * i / (double) pen->num_vertices;
+ double dx = radius * cos (reflect ? -theta : theta);
+ double dy = radius * sin (reflect ? -theta : theta);
+ cairo_pen_vertex_t *v = &pen->vertices[i];
+ 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_NOACCESS (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.
+*/
+
+static 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);
+
+ /*
+ * compute number of vertices needed
+ */
+ int num_vertices;
+
+ /* Where tolerance / M is > 1, we use 4 points */
+ if (tolerance >= major_axis) {
+ num_vertices = 4;
+ } else {
+ double delta = acos (1 - tolerance / major_axis);
+ num_vertices = ceil (M_PI / delta);
+
+ /* 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;
+}
diff --git a/gfx/cairo/cairo/src/cairo-platform.h b/gfx/cairo/cairo/src/cairo-platform.h
new file mode 100644
index 000000000..bfec29f67
--- /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 <stuart@mozilla.com>
+ */
+
+#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 000000000..41a33d753
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-png.c
@@ -0,0 +1,798 @@
+/* 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 <cworth@cworth.org>
+ * Kristian Høgsberg <krh@redhat.com>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <png.h>
+
+/**
+ * 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.
+ */
+
+/**
+ * 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.
+ */
+
+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;
+ }
+ }
+}
+
+/* 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_NO_MEMORY);
+
+#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)
+{
+ cairo_status_t *error = png_get_error_ptr (png);
+
+ /* default to the most likely error */
+ if (*error == CAIRO_STATUS_SUCCESS)
+ *error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* png does not expect to abort and will try to tidy up after a warning */
+}
+
+
+/* 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_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 depth;
+
+ 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;
+ }
+
+ /* 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;
+ }
+
+ for (i = 0; i < clone->height; i++)
+ rows[i] = (png_byte *) clone->data + i * clone->stride;
+
+ png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status,
+ png_simple_error_callback,
+ png_simple_warning_callback);
+ if (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:
+ depth = 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_RGB24:
+ depth = 8;
+ png_color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ case CAIRO_FORMAT_A8:
+ depth = 8;
+ png_color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ case CAIRO_FORMAT_A1:
+ depth = 1;
+ png_color_type = PNG_COLOR_TYPE_GRAY;
+#ifndef WORDS_BIGENDIAN
+ png_set_packswap (png);
+#endif
+ 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, depth,
+ png_color_type,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ white.gray = (1 << depth) - 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) {
+ png_set_write_user_transform_fn (png, unpremultiply_data);
+ } else if (png_color_type == PNG_COLOR_TYPE_RGB) {
+ png_set_write_user_transform_fn (png, convert_data_to_bytes);
+ 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);
+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
+ *
+ * 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.
+ **/
+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);
+
+ fp = fopen (filename, "wb");
+ 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.
+ **/
+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 = (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 = (0xff << 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 *surface;
+ png_struct *png = NULL;
+ png_info *info;
+ png_byte *data = NULL;
+ png_byte **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 == 16)
+ png_set_strip_16 (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 ||
+ ! (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:
+ format = CAIRO_FORMAT_ARGB32;
+ png_set_read_user_transform_fn (png, premultiply_data);
+ break;
+
+ case PNG_COLOR_TYPE_RGB:
+ format = CAIRO_FORMAT_RGB24;
+ png_set_read_user_transform_fn (png, convert_bytes_to_data);
+ 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 * 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;
+ }
+
+ 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:
+ if (row_pointers != NULL)
+ free (row_pointers);
+ if (data != NULL)
+ 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
+ *
+ * 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
+ *
+ * Alternatively, you can allow errors to propagate through the drawing
+ * operations and check the status on the context upon completion
+ * using cairo_status().
+ **/
+cairo_surface_t *
+cairo_image_surface_create_from_png (const char *filename)
+{
+ struct png_read_closure_t png_closure;
+ cairo_surface_t *surface;
+
+ png_closure.closure = fopen (filename, "rb");
+ if (png_closure.closure == NULL) {
+ cairo_status_t status;
+ switch (errno) {
+ case ENOMEM:
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ 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
+ *
+ * Alternatively, you can allow errors to propagate through the drawing
+ * operations and check the status on the context upon completion
+ * using cairo_status().
+ **/
+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.c b/gfx/cairo/cairo/src/cairo-polygon.c
new file mode 100644
index 000000000..1b5fab02b
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-polygon.c
@@ -0,0 +1,492 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-slope-private.h"
+
+void
+_cairo_polygon_init (cairo_polygon_t *polygon)
+{
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
+
+ polygon->status = CAIRO_STATUS_SUCCESS;
+
+ polygon->num_edges = 0;
+
+ polygon->edges = polygon->edges_embedded;
+ polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
+
+ polygon->has_current_point = FALSE;
+ polygon->has_current_edge = FALSE;
+ polygon->num_limits = 0;
+
+ polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
+ polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
+}
+
+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_fini (cairo_polygon_t *polygon)
+{
+ if (polygon->edges != polygon->edges_embedded)
+ free (polygon->edges);
+
+ VG (VALGRIND_MAKE_MEM_NOACCESS (polygon, sizeof (cairo_polygon_t)));
+}
+
+/* make room for at least one more edge */
+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 p[2];
+ int top_y, bot_y;
+ int n;
+
+ for (n = 0; n < polygon->num_limits; n++) {
+ const cairo_box_t *limits = &polygon->limits[n];
+
+ if (top >= limits->p2.y)
+ continue;
+ if (bottom <= limits->p1.y)
+ continue;
+
+ if (p1->x >= limits->p1.x && p2->x >= limits->p1.x &&
+ p1->x <= limits->p2.x && p2->x <= limits->p2.x)
+ {
+ top_y = top;
+ if (top_y < limits->p1.y)
+ top_y = limits->p1.y;
+
+ bot_y = bottom;
+ if (bot_y > limits->p2.y)
+ bot_y = limits->p2.y;
+
+ _add_edge (polygon, p1, p2, top_y, bot_y, dir);
+ }
+ else if (p1->x <= limits->p1.x && p2->x <= limits->p1.x)
+ {
+ p[0].x = limits->p1.x;
+ p[0].y = limits->p1.y;
+ top_y = top;
+ if (top_y < p[0].y)
+ top_y = p[0].y;
+
+ p[1].x = limits->p1.x;
+ p[1].y = limits->p2.y;
+ bot_y = bottom;
+ if (bot_y > p[1].y)
+ bot_y = p[1].y;
+
+ _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+ }
+ else if (p1->x >= limits->p2.x && p2->x >= limits->p2.x)
+ {
+ p[0].x = limits->p2.x;
+ p[0].y = limits->p1.y;
+ top_y = top;
+ if (top_y < p[0].y)
+ top_y = p[0].y;
+
+ p[1].x = limits->p2.x;
+ p[1].y = limits->p2.y;
+ bot_y = bottom;
+ if (bot_y > p[1].y)
+ bot_y = p[1].y;
+
+ _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+ }
+ else
+ {
+ int left_y, right_y;
+ int p1_y, p2_y;
+
+ left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+ limits->p1.x);
+ right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
+ limits->p2.x);
+
+ p1_y = top;
+ p2_y = bottom;
+
+ if (p1->x < p2->x) {
+ if (p1->x < limits->p1.x && left_y > limits->p1.y) {
+ p[0].x = limits->p1.x;
+ p[0].y = limits->p1.y;
+ top_y = p1_y;
+ if (top_y < p[0].y)
+ top_y = p[0].y;
+
+ p[1].x = limits->p1.x;
+ p[1].y = limits->p2.y;
+ bot_y = left_y;
+ if (bot_y > p[1].y)
+ bot_y = p[1].y;
+
+ if (bot_y > top_y)
+ _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+ p1_y = bot_y;
+ }
+
+ if (p2->x > limits->p2.x && right_y < limits->p2.y) {
+ p[0].x = limits->p2.x;
+ p[0].y = limits->p1.y;
+ top_y = right_y;
+ if (top_y < p[0].y)
+ top_y = p[0].y;
+
+ p[1].x = limits->p2.x;
+ p[1].y = limits->p2.y;
+ bot_y = p2_y;
+ if (bot_y > p[1].y)
+ bot_y = p[1].y;
+
+ if (bot_y > top_y)
+ _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+ p2_y = top_y;
+ }
+ } else {
+ if (p1->x > limits->p2.x && right_y > limits->p1.y) {
+ p[0].x = limits->p2.x;
+ p[0].y = limits->p1.y;
+ top_y = p1_y;
+ if (top_y < p[0].y)
+ top_y = p[0].y;
+
+ p[1].x = limits->p2.x;
+ p[1].y = limits->p2.y;
+ bot_y = right_y;
+ if (bot_y > p[1].y)
+ bot_y = p[1].y;
+
+ if (bot_y > top_y)
+ _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+ p1_y = bot_y;
+ }
+
+ if (p2->x < limits->p1.x && left_y < limits->p2.y) {
+ p[0].x = limits->p1.x;
+ p[0].y = limits->p1.y;
+ top_y = left_y;
+ if (top_y < p[0].y)
+ top_y = p[0].y;
+
+ p[1].x = limits->p1.x;
+ p[1].y = limits->p2.y;
+ bot_y = p2_y;
+ if (bot_y > p[1].y)
+ bot_y = p[1].y;
+
+ if (bot_y > top_y)
+ _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
+ p2_y = top_y;
+ }
+ }
+
+ if (p1_y < limits->p1.y)
+ p1_y = limits->p1.y;
+ if (p2_y > limits->p2.y)
+ p2_y = limits->p2.y;
+ if (p2_y > p1_y)
+ _add_edge (polygon, p1, p2, p1_y, p2_y, dir);
+ }
+ }
+}
+
+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) {
+ dir = 1;
+ } else {
+ const cairo_point_t *t;
+ t = p1, p1 = p2, p2 = t;
+ dir = -1;
+ }
+
+ 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);
+ 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;
+}
+
+/* flattened path operations */
+
+cairo_status_t
+_cairo_polygon_move_to (cairo_polygon_t *polygon,
+ const cairo_point_t *point)
+{
+ if (polygon->has_current_edge) {
+ _cairo_polygon_add_edge (polygon,
+ &polygon->last_point,
+ &polygon->current_point);
+ polygon->has_current_edge = FALSE;
+ }
+
+ if (! polygon->has_current_point) {
+ polygon->first_point = *point;
+ polygon->has_current_point = TRUE;
+ }
+
+ polygon->current_point = *point;
+ return polygon->status;
+}
+
+cairo_status_t
+_cairo_polygon_line_to (cairo_polygon_t *polygon,
+ const cairo_point_t *point)
+{
+ /* squash collinear edges */
+ if (polygon->has_current_edge) {
+ if (polygon->current_point.x != point->x ||
+ polygon->current_point.y != point->y)
+ {
+ cairo_slope_t this;
+
+ _cairo_slope_init (&this, &polygon->current_point, point);
+ if (_cairo_slope_equal (&polygon->current_edge, &this)) {
+ polygon->current_point = *point;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ _cairo_polygon_add_edge (polygon,
+ &polygon->last_point,
+ &polygon->current_point);
+
+ polygon->last_point = polygon->current_point;
+ polygon->current_edge = this;
+ }
+ } else if (polygon->has_current_point) {
+ if (polygon->current_point.x != point->x ||
+ polygon->current_point.y != point->y)
+ {
+ polygon->last_point = polygon->current_point;
+ _cairo_slope_init (&polygon->current_edge,
+ &polygon->last_point,
+ point);
+ polygon->has_current_edge = TRUE;
+ }
+ } else {
+ polygon->first_point = *point;
+ polygon->has_current_point = TRUE;
+ }
+
+ polygon->current_point = *point;
+ return polygon->status;
+}
+
+cairo_status_t
+_cairo_polygon_close (cairo_polygon_t *polygon)
+{
+ cairo_status_t status;
+
+ if (polygon->has_current_point) {
+ status = _cairo_polygon_line_to (polygon, &polygon->first_point);
+ polygon->has_current_point = FALSE;
+ }
+
+ if (polygon->has_current_edge) {
+ _cairo_polygon_add_edge (polygon,
+ &polygon->last_point,
+ &polygon->current_point);
+ polygon->has_current_edge = FALSE;
+ }
+
+ return polygon->status;
+}
diff --git a/gfx/cairo/cairo/src/cairo-private.h b/gfx/cairo/cairo/src/cairo-private.h
new file mode 100644
index 000000000..901a69a31
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-private.h
@@ -0,0 +1,57 @@
+/* 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 <cworth@redhat.com>
+ */
+
+#ifndef CAIRO_PRIVATE_H
+#define CAIRO_PRIVATE_H
+
+#include "cairo-reference-count-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-path-fixed-private.h"
+
+struct _cairo {
+ cairo_reference_count_t ref_count;
+
+ cairo_status_t status;
+
+ cairo_user_data_array_t user_data;
+
+ cairo_gstate_t *gstate;
+ cairo_gstate_t gstate_tail[2];
+ cairo_gstate_t *gstate_freelist;
+
+ cairo_path_fixed_t path[1];
+};
+
+#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 000000000..a5a8cd0da
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-ps-surface-private.h
@@ -0,0 +1,109 @@
+/* -*- 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 <cworth@cworth.org>
+ * Kristian Høgsberg <krh@redhat.com>
+ * Keith Packard <keithp@keithp.com>
+ */
+
+#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 <time.h>
+
+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_content_t content;
+ double width;
+ double height;
+ cairo_rectangle_int_t page_bbox;
+ int bbox_x1, bbox_y1, bbox_x2, bbox_y2;
+ cairo_matrix_t cairo_to_ps;
+
+ /* XXX These 3 are used as temporary storage whilst emitting patterns */
+ cairo_image_surface_t *image;
+ cairo_image_surface_t *acquired_image;
+ void *image_extra;
+
+ cairo_bool_t use_string_datasource;
+
+ cairo_bool_t current_pattern_is_solid_color;
+ 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 *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_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 000000000..4e7fb132b
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-ps-surface.c
@@ -0,0 +1,3929 @@
+/* -*- 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 <cworth@cworth.org>
+ * Kristian Høgsberg <krh@redhat.com>
+ * Keith Packard <keithp@keithp.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+
+/*
+ * 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 _BSD_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-composite-rectangles-private.h"
+#include "cairo-error-private.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-subsurface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-type3-glyph-surface-private.h"
+#include "cairo-image-info-private.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+#include <zlib.h>
+#include <errno.h>
+
+#define DEBUG_PS 0
+
+#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.
+ */
+
+/**
+ * CAIRO_HAS_PS_SURFACE:
+ *
+ * Defined if the PostScript surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+static const cairo_surface_backend_t cairo_ps_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend;
+
+static void
+_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface,
+ cairo_surface_pattern_t *pattern);
+
+static const cairo_ps_level_t _cairo_ps_levels[] =
+{
+ CAIRO_PS_LEVEL_2,
+ 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"
+};
+
+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_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 = "";
+
+ 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 (http://cairographics.org)\n"
+ "%%%%CreationDate: %s"
+ "%%%%Pages: %d\n"
+ "%%%%BoundingBox: %d %d %d %d\n",
+ eps_header,
+ cairo_version_string (),
+ ctime_r (&now, ctime_buf),
+ surface->num_pages,
+ surface->bbox_x1,
+ surface->bbox_y1,
+ surface->bbox_x2,
+ surface->bbox_y2);
+
+ _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);
+ }
+ }
+
+ 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]);
+ free (comments[i]);
+ comments[i] = NULL;
+ }
+
+ _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,
+ "/cairo_eps_state save def\n"
+ "/dict_count countdictstack def\n"
+ "/op_count count 1 sub def\n"
+ "userdict 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"
+ "/pdfmark where { pop globaldict /?pdfmark /exec load put }\n"
+ " { globaldict begin /?pdfmark /pop load def /pdfmark\n"
+ " /cleartomark load def end } ifelse\n"
+ "/BDC { mark 3 1 roll /BDC pdfmark } bind def\n"
+ "/EMC { mark /EMC pdfmark } bind def\n"
+ "/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n"
+ "/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_output_stream_printf (surface->final_stream,
+ "%%%%EndProlog\n");
+
+ num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments);
+ if (num_comments) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "%%%%BeginSetup\n");
+
+ comments = _cairo_array_index (&surface->dsc_setup_comments, 0);
+ for (i = 0; i < num_comments; i++) {
+ _cairo_output_stream_printf (surface->final_stream,
+ "%s\n", comments[i]);
+ free (comments[i]);
+ comments[i] = NULL;
+ }
+
+ _cairo_output_stream_printf (surface->final_stream,
+ "%%%%EndSetup\n");
+ }
+}
+
+#if CAIRO_HAS_FT_FONT
+static cairo_status_t
+_cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface,
+ cairo_scaled_font_subset_t *font_subset)
+
+
+{
+ 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
+
+ length = subset.header_length + subset.data_length + subset.trailer_length;
+ _cairo_output_stream_write (surface->final_stream, subset.data, length);
+
+ _cairo_type1_subset_fini (&subset);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+static cairo_status_t
+_cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface,
+ 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;
+
+ /* FIXME: Figure out document structure convention for fonts */
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->final_stream,
+ "%% _cairo_ps_surface_emit_type1_font_fallback\n");
+#endif
+
+ length = subset.header_length + subset.data_length + subset.trailer_length;
+ _cairo_output_stream_write (surface->final_stream, subset.data, length);
+
+ _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 (&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,
+ "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 */
+
+ 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_truetype_subset_fini (&subset);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_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_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);
+
+ 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,
+ "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);
+ 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);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_ps_surface_t *surface = closure;
+ cairo_status_t status;
+
+
+ status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
+ if (_cairo_status_is_error (status))
+ return status;
+
+#if CAIRO_HAS_FT_FONT
+ status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+#endif
+
+ status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ 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_status_t
+_cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_ps_surface_t *surface = closure;
+ cairo_status_t status;
+
+ status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
+ if (_cairo_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_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_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,
+ "count op_count sub {pop} repeat\n"
+ "countdictstack dict_count sub {end} repeat\n"
+ "cairo_eps_state restore\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->page_bbox)) {
+ if (rect.x == surface->page_bbox.x &&
+ rect.width == surface->page_bbox.width &&
+ rect.y == surface->page_bbox.y &&
+ rect.height == surface->page_bbox.height)
+ {
+ 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 = 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 = 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);
+
+ 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;
+ }
+
+ surface->has_creation_date = FALSE;
+ surface->eps = FALSE;
+ surface->ps_level = CAIRO_PS_LEVEL_3;
+ surface->ps_level_used = CAIRO_PS_LEVEL_2;
+ surface->width = width;
+ surface->height = height;
+ cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height);
+ surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
+ surface->force_fallbacks = FALSE;
+ surface->content = CAIRO_CONTENT_COLOR_ALPHA;
+ surface->use_string_datasource = FALSE;
+ surface->current_pattern_is_solid_color = FALSE;
+
+ surface->page_bbox.x = 0;
+ surface->page_bbox.y = 0;
+ surface->page_bbox.width = width;
+ surface->page_bbox.height = height;
+
+ _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);
+ 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 *));
+
+ 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;
+ }
+
+ _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;
+ cairo_status_t status_ignored;
+
+ if (surface->status)
+ return FALSE;
+ if (surface->finished) {
+ if (set_error_on_failure)
+ status_ignored = _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return FALSE;
+ }
+
+ if (! _cairo_surface_is_paginated (surface)) {
+ if (set_error_on_failure)
+ status_ignored = _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return FALSE;
+ }
+
+ target = _cairo_paginated_surface_get_target (surface);
+ if (target->status) {
+ if (set_error_on_failure)
+ status_ignored = _cairo_surface_set_error (surface, target->status);
+ return FALSE;
+ }
+ if (target->finished) {
+ if (set_error_on_failure)
+ status_ignored = _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return FALSE;
+ }
+
+ if (! _cairo_surface_is_ps (target)) {
+ if (set_error_on_failure)
+ status_ignored = _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ 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;
+
+ if (! _extract_ps_surface (surface, TRUE, &ps_surface))
+ return;
+
+ ps_surface->width = width_in_points;
+ ps_surface->height = height_in_points;
+ cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points);
+ _cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators,
+ &ps_surface->cairo_to_ps);
+}
+
+/**
+ * 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_begin_setup().
+ *
+ * For comments to appear in the Setup section, this function should
+ * be called after a call to cairo_ps_surface_begin_setup() but before
+ * a call to cairo_ps_surface_begin_page_setup().
+ *
+ * For comments to appear in the PageSetup section, this function
+ * should be called after a call to cairo_ps_surface_begin_page_setup().
+ *
+ * Note that it is only necessary to call cairo_ps_surface_begin_page_setup()
+ * for the first page of any surface. After a call to
+ * cairo_show_page() or cairo_copy_page() comments are unambiguously
+ * directed to the PageSetup section of the current page. But it
+ * doesn't hurt to call this function at the beginning of every page
+ * as that consistency may make the calling code simpler.
+ *
+ * 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:
+ *
+ * <informalexample><programlisting>
+ * #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");
+ * ...
+ * </programlisting></informalexample>
+ *
+ * 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);
+
+ status = _cairo_ps_surface_emit_font_subsets (surface);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_ps_surface_emit_body (surface);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ _cairo_ps_surface_emit_footer (surface);
+
+CLEANUP:
+ _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_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_end_page (cairo_ps_surface_t *surface)
+{
+ cairo_int_status_t status;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ if (surface->clipper.clip.path != NULL) {
+ _cairo_output_stream_printf (surface->stream, "Q Q\n");
+ _cairo_surface_clipper_reset (&surface->clipper);
+ } else
+ _cairo_output_stream_printf (surface->stream, "Q\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_show_page (void *abstract_surface)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+
+ status = _cairo_ps_surface_end_page (surface);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->stream, "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);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_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);
+ 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 (pattern->surface, image, image_extra);
+
+ 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)
+{
+ const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *) pattern;
+ uint16_t alpha;
+ cairo_extend_t extend;
+ unsigned int i;
+
+ if (surface->ps_level == CAIRO_PS_LEVEL_2)
+ return FALSE;
+
+ if (gradient->n_stops == 0)
+ return TRUE;
+
+ /* Alpha gradients are only supported (by flattening the alpha)
+ * if there is no variation in the alpha across the gradient. */
+ alpha = gradient->stops[0].color.alpha_short;
+ for (i = 0; i < gradient->n_stops; i++) {
+ if (gradient->stops[i].color.alpha_short != alpha)
+ return FALSE;
+ }
+
+ extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern);
+
+ /* Radial gradients are currently only supported when one circle
+ * is inside the other. */
+ if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ double x1, y1, x2, y2, r1, r2, d;
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
+
+ if (extend == CAIRO_EXTEND_REPEAT ||
+ extend == CAIRO_EXTEND_REFLECT) {
+ return FALSE;
+ }
+
+ x1 = _cairo_fixed_to_double (radial->c1.x);
+ y1 = _cairo_fixed_to_double (radial->c1.y);
+ r1 = _cairo_fixed_to_double (radial->r1);
+ x2 = _cairo_fixed_to_double (radial->c2.x);
+ y2 = _cairo_fixed_to_double (radial->c2.y);
+ r2 = _cairo_fixed_to_double (radial->r2);
+
+ d = sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+ if (d > fabs(r2 - r1)) {
+ return FALSE;
+ }
+ }
+
+ 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)
+{
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+ return TRUE;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+ return _gradient_pattern_supported (surface, pattern);
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
+ return surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
+
+ 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_rectangle_int_t *extents)
+{
+ 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;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+ if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (pattern->extend == CAIRO_EXTEND_PAD)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ else
+ return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+ }
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE)
+ 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) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+ return _cairo_ps_surface_analyze_surface_pattern_transparency (surface,
+ surface_pattern);
+ }
+
+ if (_cairo_pattern_is_opaque (pattern, extents))
+ 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_rectangle_int_t *extents)
+{
+ return _cairo_ps_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+/* The "standard" implementation limit for PostScript string sizes is
+ * 65535 characters (see PostScript Language Reference, Appendix
+ * B). We go one short of that because we sometimes need two
+ * characters in a string to represent a single ASCII85 byte, (for the
+ * escape sequences "\\", "\(", and "\)") and we must not split these
+ * across two strings. So we'd be in trouble if we went right to the
+ * limit and one of these escape sequences just happened to land at
+ * the end.
+ */
+#define STRING_ARRAY_MAX_STRING_SIZE (65535-1)
+#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;
+ cairo_bool_t use_strings;
+} string_array_stream_t;
+
+static cairo_status_t
+_string_array_stream_write (cairo_output_stream_t *base,
+ const unsigned char *data,
+ unsigned int length)
+{
+ string_array_stream_t *stream = (string_array_stream_t *) base;
+ unsigned char c;
+ const unsigned char backslash = '\\';
+
+ if (length == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ while (length--) {
+ if (stream->string_size == 0 && stream->use_strings) {
+ _cairo_output_stream_printf (stream->output, "(");
+ stream->column++;
+ }
+
+ c = *data++;
+ if (stream->use_strings) {
+ switch (c) {
+ case '\\':
+ case '(':
+ case ')':
+ _cairo_output_stream_write (stream->output, &backslash, 1);
+ stream->column++;
+ stream->string_size++;
+ break;
+ }
+ }
+ /* Have to be careful to never split the final ~> sequence. */
+ if (c == '~') {
+ _cairo_output_stream_write (stream->output, &c, 1);
+ stream->column++;
+ stream->string_size++;
+
+ if (length-- == 0)
+ break;
+
+ c = *data++;
+ }
+ _cairo_output_stream_write (stream->output, &c, 1);
+ stream->column++;
+ stream->string_size++;
+
+ if (stream->use_strings &&
+ stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE)
+ {
+ _cairo_output_stream_printf (stream->output, ")\n");
+ stream->string_size = 0;
+ stream->column = 0;
+ }
+ if (stream->column >= STRING_ARRAY_MAX_COLUMN) {
+ _cairo_output_stream_printf (stream->output, "\n ");
+ stream->string_size += 2;
+ stream->column = 1;
+ }
+ }
+
+ return _cairo_output_stream_get_status (stream->output);
+}
+
+static cairo_status_t
+_string_array_stream_close (cairo_output_stream_t *base)
+{
+ cairo_status_t status;
+ string_array_stream_t *stream = (string_array_stream_t *) base;
+
+ if (stream->use_strings)
+ _cairo_output_stream_printf (stream->output, ")\n");
+
+ status = _cairo_output_stream_get_status (stream->output);
+
+ return status;
+}
+
+/* A string_array_stream wraps an existing output stream. It takes the
+ * data provided to it and output one or more consecutive string
+ * objects, each within the standard PostScript implementation limit
+ * of 65k characters.
+ *
+ * The strings are each separated by a space character for easy
+ * inclusion within an array object, (but the array delimiters are not
+ * added by the string_array_stream).
+ *
+ * The string array stream is also careful to wrap the output within
+ * STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds
+ * necessary escaping for special characters within a string,
+ * (specifically '\', '(', and ')').
+ */
+static cairo_output_stream_t *
+_string_array_stream_create (cairo_output_stream_t *output)
+{
+ string_array_stream_t *stream;
+
+ stream = malloc (sizeof (string_array_stream_t));
+ if (unlikely (stream == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+ }
+
+ _cairo_output_stream_init (&stream->base,
+ _string_array_stream_write,
+ NULL,
+ _string_array_stream_close);
+ stream->output = output;
+ stream->column = 0;
+ stream->string_size = 0;
+ stream->use_strings = TRUE;
+
+ return &stream->base;
+}
+
+/* A base85_array_stream wraps an existing output stream. It wraps the
+ * output within STRING_ARRAY_MAX_COLUMN columns (+/- 1). The output
+ * is not enclosed in strings like string_array_stream.
+ */
+static cairo_output_stream_t *
+_base85_array_stream_create (cairo_output_stream_t *output)
+{
+ string_array_stream_t *stream;
+
+ stream = malloc (sizeof (string_array_stream_t));
+ if (unlikely (stream == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_output_stream_t *) &_cairo_output_stream_nil;
+ }
+
+ _cairo_output_stream_init (&stream->base,
+ _string_array_stream_write,
+ NULL,
+ _string_array_stream_close);
+ stream->output = output;
+ stream->column = 0;
+ stream->string_size = 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_bool_t use_strings)
+{
+ cairo_output_stream_t *base85_stream, *string_array_stream;
+ cairo_status_t status, status2;
+
+ if (use_strings)
+ string_array_stream = _string_array_stream_create (surface->stream);
+ else
+ string_array_stream = _base85_array_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);
+ }
+
+ _cairo_output_stream_write (base85_stream, data, length);
+
+ status = _cairo_output_stream_destroy (base85_stream);
+
+ /* Mark end of base85 data */
+ _cairo_output_stream_printf (string_array_stream, "~>");
+ status2 = _cairo_output_stream_destroy (string_array_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
+ cairo_image_surface_t *image,
+ cairo_operator_t op,
+ cairo_filter_t filter)
+{
+ cairo_status_t status;
+ unsigned char *data, *data_compressed;
+ unsigned long data_size, data_compressed_size;
+ cairo_image_surface_t *opaque_image = NULL;
+ int x, y, i;
+ cairo_image_transparency_t transparency;
+ cairo_bool_t use_mask;
+ uint32_t *pixel;
+ int bit;
+ const char *interpolate;
+
+ if (image->base.status)
+ return image->base.status;
+
+ switch (filter) {
+ default:
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_BILINEAR:
+ interpolate = "true";
+ break;
+ case CAIRO_FILTER_FAST:
+ case CAIRO_FILTER_NEAREST:
+ case CAIRO_FILTER_GAUSSIAN:
+ interpolate = "false";
+ break;
+ }
+
+ 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 (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,
+ &opaque_image);
+ if (unlikely (status))
+ return status;
+
+ use_mask = FALSE;
+ } else if (transparency == CAIRO_IMAGE_IS_OPAQUE) {
+ opaque_image = image;
+ use_mask = FALSE;
+ } else {
+ use_mask = TRUE;
+ }
+
+ if (use_mask) {
+ /* Type 2 (mask and image interleaved) has the mask and image
+ * samples interleaved by row. The mask row is first, one bit
+ * per pixel with (bit 7 first). The row is padded to byte
+ * boundaries. The image data is 3 bytes per pixel RGB
+ * format. */
+ data_size = image->height * ((image->width + 7)/8 + 3*image->width);
+ } else {
+ data_size = image->height * image->width * 3;
+ }
+ data = malloc (data_size);
+ if (unlikely (data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail1;
+ }
+
+ if (use_mask) {
+ i = 0;
+ for (y = 0; y < image->height; y++) {
+ /* mask row */
+ pixel = (uint32_t *) (image->data + y * image->stride);
+ bit = 7;
+ for (x = 0; x < image->width; x++, pixel++) {
+ if (bit == 7)
+ data[i] = 0;
+ if (((*pixel & 0xff000000) >> 24) > 0x80)
+ data[i] |= (1 << bit);
+ bit--;
+ if (bit < 0) {
+ bit = 7;
+ i++;
+ }
+ }
+ if (bit != 7)
+ i++;
+
+ /* image row*/
+ pixel = (uint32_t *) (image->data + y * image->stride);
+ for (x = 0; x < image->width; x++, pixel++) {
+ data[i++] = (*pixel & 0x00ff0000) >> 16;
+ data[i++] = (*pixel & 0x0000ff00) >> 8;
+ data[i++] = (*pixel & 0x000000ff) >> 0;
+ }
+ }
+ } else {
+ i = 0;
+ for (y = 0; y < opaque_image->height; y++) {
+ pixel = (uint32_t *) (opaque_image->data + y * opaque_image->stride);
+ for (x = 0; x < opaque_image->width; x++, pixel++) {
+ data[i++] = (*pixel & 0x00ff0000) >> 16;
+ data[i++] = (*pixel & 0x0000ff00) >> 8;
+ data[i++] = (*pixel & 0x000000ff) >> 0;
+ }
+ }
+ }
+
+ /* XXX: Should fix cairo-lzw to provide a stream-based interface
+ * instead. */
+ data_compressed_size = data_size;
+ data_compressed = _cairo_lzw_compress (data, &data_compressed_size);
+ if (unlikely (data_compressed == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail2;
+ }
+
+ if (surface->use_string_datasource) {
+ /* Emit the image data as a base85-encoded string which will
+ * be used as the data source for the image operator later. */
+ _cairo_output_stream_printf (surface->stream,
+ "/CairoImageData [\n");
+
+ status = _cairo_ps_surface_emit_base85_string (surface,
+ data_compressed,
+ data_compressed_size,
+ TRUE);
+ if (unlikely (status))
+ goto bail3;
+
+ _cairo_output_stream_printf (surface->stream,
+ "] def\n");
+ _cairo_output_stream_printf (surface->stream,
+ "/CairoImageDataIndex 0 def\n");
+ }
+
+ if (use_mask) {
+ _cairo_output_stream_printf (surface->stream,
+ "/DeviceRGB setcolorspace\n"
+ "5 dict dup begin\n"
+ " /ImageType 3 def\n"
+ " /InterleaveType 2 def\n"
+ " /DataDict 8 dict def\n"
+ " DataDict begin\n"
+ " /ImageType 1 def\n"
+ " /Width %d def\n"
+ " /Height %d def\n"
+ " /Interpolate %s def\n"
+ " /BitsPerComponent 8 def\n"
+ " /Decode [ 0 1 0 1 0 1 ] def\n",
+ image->width,
+ image->height,
+ interpolate);
+
+ if (surface->use_string_datasource) {
+ _cairo_output_stream_printf (surface->stream,
+ " /DataSource {\n"
+ " CairoImageData CairoImageDataIndex get\n"
+ " /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
+ " CairoImageDataIndex CairoImageData length 1 sub gt\n"
+ " { /CairoImageDataIndex 0 def } if\n"
+ " } /ASCII85Decode filter /LZWDecode filter def\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /DataSource currentfile /ASCII85Decode filter /LZWDecode filter def\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+ " end\n"
+ " /MaskDict 8 dict def\n"
+ " MaskDict begin\n"
+ " /ImageType 1 def\n"
+ " /Width %d def\n"
+ " /Height %d def\n"
+ " /Interpolate %s def\n"
+ " /BitsPerComponent 1 def\n"
+ " /Decode [ 1 0 ] def\n"
+ " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+ " end\n"
+ "end\n"
+ "image\n",
+ image->height,
+ image->width,
+ image->height,
+ interpolate,
+ image->height);
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ "/DeviceRGB setcolorspace\n"
+ "8 dict dup begin\n"
+ " /ImageType 1 def\n"
+ " /Width %d def\n"
+ " /Height %d def\n"
+ " /BitsPerComponent 8 def\n"
+ " /Decode [ 0 1 0 1 0 1 ] def\n",
+ opaque_image->width,
+ opaque_image->height);
+ if (surface->use_string_datasource) {
+ _cairo_output_stream_printf (surface->stream,
+ " /DataSource {\n"
+ " CairoImageData CairoImageDataIndex get\n"
+ " /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
+ " CairoImageDataIndex CairoImageData length 1 sub gt\n"
+ " { /CairoImageDataIndex 0 def } if\n"
+ " } /ASCII85Decode filter /LZWDecode filter def\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /DataSource currentfile /ASCII85Decode filter /LZWDecode filter def\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " /Interpolate %s def\n"
+ " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+ "end\n"
+ "image\n",
+ interpolate,
+ opaque_image->height);
+ }
+
+ if (!surface->use_string_datasource) {
+ /* Emit the image data as a base85-encoded string which will
+ * be used as the data source for the image operator. */
+ status = _cairo_ps_surface_emit_base85_string (surface,
+ data_compressed,
+ data_compressed_size,
+ FALSE);
+ _cairo_output_stream_printf (surface->stream, "\n");
+ } else {
+ status = CAIRO_STATUS_SUCCESS;
+ }
+
+bail3:
+ free (data_compressed);
+
+bail2:
+ free (data);
+
+bail1:
+ if (!use_mask && opaque_image != image)
+ cairo_surface_destroy (&opaque_image->base);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface,
+ cairo_surface_t *source,
+ int width,
+ int height)
+{
+ cairo_status_t status;
+ const unsigned char *mime_data;
+ unsigned long mime_data_length;
+ cairo_image_info_t info;
+
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+ &mime_data, &mime_data_length);
+ if (unlikely (source->status))
+ return source->status;
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length);
+ if (unlikely (status))
+ return status;
+
+ if (info.num_components != 1 && info.num_components != 3)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (surface->use_string_datasource) {
+ /* Emit the image data as a base85-encoded string which will
+ * be used as the data source for the image operator later. */
+ _cairo_output_stream_printf (surface->stream,
+ "/CairoImageData [\n");
+
+ status = _cairo_ps_surface_emit_base85_string (surface,
+ mime_data,
+ mime_data_length,
+ TRUE);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->stream,
+ "] def\n");
+ _cairo_output_stream_printf (surface->stream,
+ "/CairoImageDataIndex 0 def\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ "/%s setcolorspace\n"
+ "8 dict dup begin\n"
+ " /ImageType 1 def\n"
+ " /Width %d def\n"
+ " /Height %d def\n"
+ " /BitsPerComponent %d def\n"
+ " /Decode [ 0 1 0 1 0 1 ] def\n",
+ info.num_components == 1 ? "DeviceGray" : "DeviceRGB",
+ info.width,
+ info.height,
+ info.bits_per_component);
+
+ if (surface->use_string_datasource) {
+ _cairo_output_stream_printf (surface->stream,
+ " /DataSource {\n"
+ " CairoImageData CairoImageDataIndex get\n"
+ " /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
+ " CairoImageDataIndex CairoImageData length 1 sub gt\n"
+ " { /CairoImageDataIndex 0 def } if\n"
+ " } /ASCII85Decode filter /DCTDecode filter def\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
+ "end\n"
+ "image\n",
+ info.height);
+
+ if (!surface->use_string_datasource) {
+ /* Emit the image data as a base85-encoded string which will
+ * be used as the data source for the image operator. */
+ status = _cairo_ps_surface_emit_base85_string (surface,
+ mime_data,
+ mime_data_length,
+ FALSE);
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface,
+ cairo_surface_t *recording_surface)
+{
+ double old_width, old_height;
+ cairo_matrix_t old_cairo_to_ps;
+ cairo_content_t old_content;
+ cairo_rectangle_int_t old_page_bbox;
+ cairo_box_t bbox;
+ cairo_status_t status;
+
+ old_content = surface->content;
+ old_width = surface->width;
+ old_height = surface->height;
+ old_page_bbox = surface->page_bbox;
+ old_cairo_to_ps = surface->cairo_to_ps;
+
+ status =
+ _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+ &bbox,
+ NULL);
+ if (unlikely (status))
+ return status;
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->stream,
+ "%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n",
+ _cairo_fixed_to_double (bbox.p1.x),
+ _cairo_fixed_to_double (bbox.p1.y),
+ _cairo_fixed_to_double (bbox.p2.x),
+ _cairo_fixed_to_double (bbox.p2.y));
+#endif
+
+ surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x);
+ surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y);
+ _cairo_box_round_to_rectangle (&bbox, &surface->page_bbox);
+
+ surface->current_pattern_is_solid_color = FALSE;
+ _cairo_pdf_operators_reset (&surface->pdf_operators);
+ cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height);
+ _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+ &surface->cairo_to_ps);
+ _cairo_output_stream_printf (surface->stream, " q\n");
+
+ if (recording_surface->content == CAIRO_CONTENT_COLOR) {
+ surface->content = CAIRO_CONTENT_COLOR;
+ _cairo_output_stream_printf (surface->stream,
+ " 0 g %d %d %d %d rectfill\n",
+ surface->page_bbox.x,
+ surface->page_bbox.y,
+ surface->page_bbox.width,
+ surface->page_bbox.height);
+ }
+
+ status = _cairo_recording_surface_replay_region (recording_surface,
+ NULL,
+ &surface->base,
+ CAIRO_RECORDING_REGION_NATIVE);
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->stream, " Q\n");
+ surface->content = old_content;
+ surface->width = old_width;
+ surface->height = old_height;
+ surface->page_bbox = old_page_bbox;
+ surface->current_pattern_is_solid_color = FALSE;
+ _cairo_pdf_operators_reset (&surface->pdf_operators);
+ surface->cairo_to_ps = old_cairo_to_ps;
+
+ _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+ &surface->cairo_to_ps);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface,
+ cairo_surface_t *recording_surface,
+ const cairo_rectangle_int_t *extents)
+{
+ double old_width, old_height;
+ cairo_matrix_t old_cairo_to_ps;
+ cairo_content_t old_content;
+ cairo_rectangle_int_t old_page_bbox;
+ cairo_status_t status;
+
+ old_content = surface->content;
+ old_width = surface->width;
+ old_height = surface->height;
+ old_page_bbox = surface->page_bbox;
+ old_cairo_to_ps = surface->cairo_to_ps;
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->stream,
+ "%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n",
+ extents->x, extents->y,
+ extents->width, extents->height);
+#endif
+
+ surface->page_bbox.x = surface->page_bbox.y = 0;
+ surface->page_bbox.width = surface->width = extents->width;
+ surface->page_bbox.height = surface->height = extents->height;
+
+ surface->current_pattern_is_solid_color = FALSE;
+ _cairo_pdf_operators_reset (&surface->pdf_operators);
+ cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height);
+ _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+ &surface->cairo_to_ps);
+ _cairo_output_stream_printf (surface->stream, " q\n");
+
+ if (recording_surface->content == CAIRO_CONTENT_COLOR) {
+ surface->content = CAIRO_CONTENT_COLOR;
+ _cairo_output_stream_printf (surface->stream,
+ " 0 g %d %d %d %d rectfill\n",
+ surface->page_bbox.x,
+ surface->page_bbox.y,
+ surface->page_bbox.width,
+ surface->page_bbox.height);
+ }
+
+ status = _cairo_recording_surface_replay_region (recording_surface,
+ extents,
+ &surface->base,
+ CAIRO_RECORDING_REGION_NATIVE);
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->stream, " Q\n");
+ surface->content = old_content;
+ surface->width = old_width;
+ surface->height = old_height;
+ surface->page_bbox = old_page_bbox;
+ surface->current_pattern_is_solid_color = FALSE;
+ _cairo_pdf_operators_reset (&surface->pdf_operators);
+ surface->cairo_to_ps = old_cairo_to_ps;
+
+ _cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
+ &surface->cairo_to_ps);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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);
+}
+
+static cairo_status_t
+_cairo_ps_surface_acquire_surface (cairo_ps_surface_t *surface,
+ cairo_surface_pattern_t *pattern,
+ cairo_rectangle_int_t *extents,
+ int *width,
+ int *height,
+ int *origin_x,
+ int *origin_y)
+{
+ cairo_status_t status;
+ cairo_surface_t *pad_image;
+ int x = 0;
+ int y = 0;
+
+ surface->acquired_image = NULL;
+ surface->image = NULL;
+
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ if (pattern->surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) pattern->surface;
+
+ *width = sub->extents.width;
+ *height = sub->extents.height;
+ } else {
+ cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface;
+ cairo_box_t bbox;
+ cairo_rectangle_int_t extents;
+
+ status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
+ if (unlikely (status))
+ return status;
+
+ _cairo_box_round_to_rectangle (&bbox, &extents);
+ *width = extents.width;
+ *height = extents.height;
+ }
+ return CAIRO_STATUS_SUCCESS;
+ } else {
+ status = _cairo_surface_acquire_source_image (pattern->surface,
+ &surface->acquired_image,
+ &surface->image_extra);
+ if (unlikely (status))
+ return status;
+
+ pad_image = &surface->acquired_image->base;
+ if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) {
+ cairo_box_t box;
+ cairo_rectangle_int_t rect;
+ cairo_surface_pattern_t pad_pattern;
+
+ /* get the operation extents in pattern space */
+ _cairo_box_from_rectangle (&box, extents);
+ _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL);
+ _cairo_box_round_to_rectangle (&box, &rect);
+ x = -rect.x;
+ y = -rect.y;
+
+ pad_image =
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ surface->acquired_image->pixman_format,
+ rect.width, rect.height,
+ 0);
+ if (pad_image->status) {
+ status = pad_image->status;
+ goto BAIL;
+ }
+
+ _cairo_pattern_init_for_surface (&pad_pattern, &surface->acquired_image->base);
+ cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y);
+ pad_pattern.base.extend = CAIRO_EXTEND_PAD;
+ status = _cairo_surface_paint (pad_image,
+ CAIRO_OPERATOR_SOURCE,
+ &pad_pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pad_pattern.base);
+ if (unlikely (status)) {
+ if (pad_image != &surface->acquired_image->base)
+ cairo_surface_destroy (pad_image);
+
+ goto BAIL;
+ }
+ }
+
+ surface->image = (cairo_image_surface_t *) pad_image;
+ *width = surface->image->width;
+ *height = surface->image->height;
+ *origin_x = x;
+ *origin_y = y;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+BAIL:
+ _cairo_ps_surface_release_surface (surface, pattern);
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
+ cairo_surface_pattern_t *pattern,
+ cairo_operator_t op,
+ int width,
+ int height)
+{
+ cairo_status_t status;
+
+ if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ cairo_surface_t *source = pattern->surface;
+
+ if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
+ status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents);
+ } else {
+ status = _cairo_ps_surface_emit_recording_surface (surface, source);
+ }
+ } else {
+ if (pattern->base.extend != CAIRO_EXTEND_PAD) {
+ status = _cairo_ps_surface_emit_jpeg_image (surface, pattern->surface,
+ width, height);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ status = _cairo_ps_surface_emit_image (surface, surface->image,
+ op, pattern->base.filter);
+ }
+
+ return status;
+}
+
+static void
+_cairo_ps_surface_release_surface (cairo_ps_surface_t *surface,
+ cairo_surface_pattern_t *pattern)
+{
+ if (surface->image != surface->acquired_image)
+ cairo_surface_destroy (&surface->image->base);
+
+ if (pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING) {
+ _cairo_surface_release_source_image (pattern->surface,
+ surface->acquired_image,
+ surface->image_extra);
+ }
+
+ surface->acquired_image = NULL;
+ surface->image = NULL;
+}
+
+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,
+ cairo_surface_pattern_t *pattern,
+ cairo_rectangle_int_t *extents,
+ cairo_operator_t op)
+{
+ cairo_status_t status;
+ int width, height;
+ cairo_matrix_t cairo_p2d, ps_p2d;
+ cairo_path_fixed_t path;
+ int origin_x = 0;
+ int origin_y = 0;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_ps_surface_acquire_surface (surface,
+ pattern,
+ extents,
+ &width, &height,
+ &origin_x, &origin_y);
+ if (unlikely (status))
+ return status;
+
+ _path_fixed_init_rectangle (&path, extents);
+ status = _cairo_pdf_operators_clip (&surface->pdf_operators,
+ &path,
+ CAIRO_FILL_RULE_WINDING);
+ _cairo_path_fixed_fini (&path);
+ if (unlikely (status))
+ return status;
+
+ cairo_p2d = pattern->base.matrix;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
+ double scale = cairo_p2d.xx;
+
+ _cairo_output_stream_printf (surface->stream,
+ "%% Fallback Image: x=%f, y=%f, w=%d, h=%d res=%fdpi size=%ld\n",
+ -cairo_p2d.x0/scale,
+ -cairo_p2d.y0/scale,
+ (int)(width/scale),
+ (int)(height/scale),
+ scale*72,
+ (long)width*height*3);
+ } else {
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ _cairo_output_stream_printf (surface->stream,
+ "%d g 0 0 %f %f rectfill\n",
+ surface->content == CAIRO_CONTENT_COLOR ? 0 : 1,
+ surface->width,
+ surface->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, -origin_x, -origin_y);
+ cairo_matrix_translate (&ps_p2d, 0.0, height);
+ cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
+
+ if (! _cairo_matrix_is_identity (&ps_p2d)) {
+ _cairo_output_stream_printf (surface->stream,
+ "[ %f %f %f %f %f %f ] concat\n",
+ ps_p2d.xx, ps_p2d.yx,
+ ps_p2d.xy, ps_p2d.yy,
+ ps_p2d.x0, ps_p2d.y0);
+ }
+
+ status = _cairo_ps_surface_emit_surface (surface, pattern, op, width, height);
+ _cairo_ps_surface_release_surface (surface, pattern);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface,
+ cairo_surface_pattern_t *pattern,
+ cairo_rectangle_int_t *extents,
+ cairo_operator_t op)
+{
+ cairo_status_t status;
+ int pattern_width = 0; /* squelch bogus compiler warning */
+ int pattern_height = 0; /* squelch bogus compiler warning */
+ double xstep, ystep;
+ cairo_matrix_t cairo_p2d, ps_p2d;
+ cairo_bool_t old_use_string_datasource;
+ int origin_x = 0;
+ int origin_y = 0;
+
+ cairo_p2d = pattern->base.matrix;
+ status = cairo_matrix_invert (&cairo_p2d);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ status = _cairo_ps_surface_acquire_surface (surface,
+ pattern,
+ extents,
+ &pattern_width, &pattern_height,
+ &origin_x, &origin_y);
+ if (unlikely (status))
+ return status;
+
+ switch (pattern->base.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->width, y2 = surface->height;
+ _cairo_matrix_transform_bounding_box (&pattern->base.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_width + pattern_height);
+ break;
+ }
+ case CAIRO_EXTEND_REPEAT:
+ xstep = pattern_width;
+ ystep = pattern_height;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ xstep = pattern_width*2;
+ ystep = pattern_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");
+
+ old_use_string_datasource = surface->use_string_datasource;
+ surface->use_string_datasource = TRUE;
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ _cairo_output_stream_printf (surface->stream,
+ "%d g 0 0 %f %f rectfill\n",
+ surface->content == CAIRO_CONTENT_COLOR ? 0 : 1,
+ xstep, ystep);
+ }
+ status = _cairo_ps_surface_emit_surface (surface, pattern, op,
+ pattern_width, pattern_height);
+ if (unlikely (status))
+ return status;
+
+ surface->use_string_datasource = old_use_string_datasource;
+ _cairo_output_stream_printf (surface->stream,
+ "} 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 (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+ _cairo_output_stream_printf (surface->stream,
+ " /BBox [0 0 %d %d]\n"
+ " /PaintProc {\n"
+ " CairoPattern\n"
+ " [-1 0 0 1 %d 0] concat CairoPattern\n"
+ " [ 1 0 0 -1 0 %d] concat CairoPattern\n"
+ " [-1 0 0 1 %d 0] concat CairoPattern\n"
+ " CairoPattern\n"
+ " } bind\n",
+ pattern_width*2, pattern_height*2,
+ pattern_width*2,
+ pattern_height*2,
+ pattern_width*2);
+ } 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 [0 0 %d %d]\n",
+ pattern_width, pattern_height);
+ }
+ _cairo_output_stream_printf (surface->stream,
+ " /PaintProc { CairoPattern }\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ ">>\n");
+
+ cairo_p2d = pattern->base.matrix;
+ status = cairo_matrix_invert (&cairo_p2d);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_init_identity (&ps_p2d);
+ cairo_matrix_translate (&ps_p2d, 0.0, surface->height);
+ cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
+ cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d);
+ cairo_matrix_translate (&ps_p2d, 0.0, pattern_height);
+ cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
+
+ _cairo_output_stream_printf (surface->stream,
+ "[ %f %f %f %f %f %f ]\n",
+ ps_p2d.xx, ps_p2d.yx,
+ ps_p2d.xy, ps_p2d.yy,
+ ps_p2d.x0, ps_p2d.y0);
+ _cairo_output_stream_printf (surface->stream,
+ "makepattern setpattern\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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 (n_stops == 1) {
+ /* work around single stop gradients */
+ _cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[0]);
+ } 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_linear_pattern (cairo_ps_surface_t *surface,
+ cairo_linear_pattern_t *pattern)
+{
+ double x1, y1, x2, y2;
+ double _x1, _y1, _x2, _y2;
+ cairo_matrix_t pat_to_ps;
+ cairo_extend_t extend;
+ cairo_status_t status;
+ cairo_gradient_pattern_t *gradient = &pattern->base;
+ double first_stop, last_stop;
+ int repeat_begin = 0, repeat_end = 1;
+
+ extend = cairo_pattern_get_extend (&pattern->base.base);
+
+ pat_to_ps = pattern->base.base.matrix;
+ status = cairo_matrix_invert (&pat_to_ps);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
+ first_stop = gradient->stops[0].offset;
+ last_stop = gradient->stops[gradient->n_stops - 1].offset;
+
+ if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+ double dx, dy;
+ int x_rep = 0, y_rep = 0;
+
+ x1 = _cairo_fixed_to_double (pattern->p1.x);
+ y1 = _cairo_fixed_to_double (pattern->p1.y);
+ cairo_matrix_transform_point (&pat_to_ps, &x1, &y1);
+
+ x2 = _cairo_fixed_to_double (pattern->p2.x);
+ y2 = _cairo_fixed_to_double (pattern->p2.y);
+ cairo_matrix_transform_point (&pat_to_ps, &x2, &y2);
+
+ dx = fabs (x2 - x1);
+ dy = fabs (y2 - y1);
+ if (dx > 1e-6)
+ x_rep = ceil (surface->width/dx);
+ if (dy > 1e-6)
+ y_rep = ceil (surface->height/dy);
+
+ repeat_end = MAX (x_rep, y_rep);
+ repeat_begin = -repeat_end;
+ first_stop = repeat_begin;
+ last_stop = repeat_end;
+ }
+
+ /* PS requires the first and last stop to be the same as the line
+ * coordinates. For repeating patterns this moves the line
+ * coordinates out to the begin/end of the repeating function. For
+ * non repeating patterns this may move the line coordinates in if
+ * there are not stops at offset 0 and 1. */
+ x1 = _cairo_fixed_to_double (pattern->p1.x);
+ y1 = _cairo_fixed_to_double (pattern->p1.y);
+ x2 = _cairo_fixed_to_double (pattern->p2.x);
+ y2 = _cairo_fixed_to_double (pattern->p2.y);
+
+ _x1 = x1 + (x2 - x1)*first_stop;
+ _y1 = y1 + (y2 - y1)*first_stop;
+ _x2 = x1 + (x2 - x1)*last_stop;
+ _y2 = y1 + (y2 - y1)*last_stop;
+
+ x1 = _x1;
+ x2 = _x2;
+ y1 = _y1;
+ y2 = _y2;
+
+ /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
+ * Type 2 function is used by itself without a stitching
+ * function. Type 2 functions always have the domain [0 1] */
+ if ((pattern->base.base.extend == CAIRO_EXTEND_NONE ||
+ pattern->base.base.extend == CAIRO_EXTEND_PAD) &&
+ gradient->n_stops == 2) {
+ first_stop = 0.0;
+ last_stop = 1.0;
+ }
+
+ status = _cairo_ps_surface_emit_pattern_stops (surface,
+ &pattern->base);
+ if (unlikely (status))
+ return status;
+
+ if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT ||
+ pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+ status = _cairo_ps_surface_emit_repeating_function (surface,
+ &pattern->base,
+ repeat_begin,
+ repeat_end);
+ if (unlikely (status))
+ return status;
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ "<< /PatternType 2\n"
+ " /Shading\n"
+ " << /ShadingType 2\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /Coords [ %f %f %f %f ]\n"
+ " /Domain [ %f %f ]\n"
+ " /Function CairoFunction\n",
+ x1, y1, x2, y2,
+ first_stop, last_stop);
+
+ if (extend == CAIRO_EXTEND_PAD) {
+ _cairo_output_stream_printf (surface->stream,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /Extend [ false false ]\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " >>\n"
+ ">>\n");
+ _cairo_output_stream_printf (surface->stream,
+ "[ %f %f %f %f %f %f ]\n",
+ pat_to_ps.xx, pat_to_ps.yx,
+ pat_to_ps.xy, pat_to_ps.yy,
+ pat_to_ps.x0, pat_to_ps.y0);
+ _cairo_output_stream_printf (surface->stream,
+ "makepattern setpattern\n");
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t *surface,
+ cairo_radial_pattern_t *pattern)
+{
+ double x1, y1, x2, y2, r1, r2;
+ cairo_matrix_t pat_to_ps;
+ cairo_extend_t extend;
+ cairo_status_t status;
+
+ extend = cairo_pattern_get_extend (&pattern->base.base);
+
+ pat_to_ps = pattern->base.base.matrix;
+ status = cairo_matrix_invert (&pat_to_ps);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
+ x1 = _cairo_fixed_to_double (pattern->c1.x);
+ y1 = _cairo_fixed_to_double (pattern->c1.y);
+ r1 = _cairo_fixed_to_double (pattern->r1);
+ x2 = _cairo_fixed_to_double (pattern->c2.x);
+ y2 = _cairo_fixed_to_double (pattern->c2.y);
+ r2 = _cairo_fixed_to_double (pattern->r2);
+
+ status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->stream,
+ "<< /PatternType 2\n"
+ " /Shading\n"
+ " << /ShadingType 3\n"
+ " /ColorSpace /DeviceRGB\n"
+ " /Coords [ %f %f %f %f %f %f ]\n"
+ " /Function CairoFunction\n",
+ x1, y1, r1, x2, y2, r2);
+
+ if (extend == CAIRO_EXTEND_PAD) {
+ _cairo_output_stream_printf (surface->stream,
+ " /Extend [ true true ]\n");
+ } else {
+ _cairo_output_stream_printf (surface->stream,
+ " /Extend [ false false ]\n");
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ " >>\n"
+ ">>\n");
+
+ _cairo_output_stream_printf (surface->stream,
+ "[ %f %f %f %f %f %f ]\n",
+ pat_to_ps.xx, pat_to_ps.yx,
+ pat_to_ps.xy, pat_to_ps.yy,
+ pat_to_ps.x0, pat_to_ps.y0);
+ _cairo_output_stream_printf (surface->stream,
+ "makepattern setpattern\n");
+
+ 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;
+
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+
+ 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:
+ status = _cairo_ps_surface_emit_surface_pattern (surface,
+ (cairo_surface_pattern_t *) pattern,
+ extents,
+ op);
+ if (unlikely (status))
+ return status;
+ break;
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ status = _cairo_ps_surface_emit_linear_pattern (surface,
+ (cairo_linear_pattern_t *) pattern);
+ if (unlikely (status))
+ return status;
+ break;
+
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ status = _cairo_ps_surface_emit_radial_pattern (surface,
+ (cairo_radial_pattern_t *) pattern);
+ if (unlikely (status))
+ return status;
+ break;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_ps_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+
+ rectangle->x = 0;
+ rectangle->y = 0;
+
+ /* XXX: The conversion to integers here is pretty bogus, (not to
+ * mention the aribitray limitation of width to a short(!). We
+ * may need to come up with a better interface for get_extents.
+ */
+ rectangle->width = ceil (surface->width);
+ rectangle->height = ceil (surface->height);
+
+ return TRUE;
+}
+
+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);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_output_stream_t *stream = surface->stream;
+ cairo_composite_rectangles_t extents;
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ &rect,
+ op, source, clip);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded);
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (stream,
+ "%% _cairo_ps_surface_paint\n");
+#endif
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ (source->extend == CAIRO_EXTEND_NONE ||
+ source->extend == CAIRO_EXTEND_PAD))
+ {
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (stream, "q\n");
+ status = _cairo_ps_surface_paint_surface (surface,
+ (cairo_surface_pattern_t *) source,
+ &extents.bounded, op);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (stream, "Q\n");
+ } else {
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (stream, "%d %d %d %d rectfill\n",
+ extents.bounded.x, extents.bounded.y,
+ extents.bounded.width, extents.bounded.height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &rect,
+ op, source,
+ path, style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox))
+ return CAIRO_STATUS_SUCCESS;
+
+ /* use the more accurate extents */
+ if (extents.is_bounded) {
+ status = _cairo_path_fixed_stroke_extents (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &extents.mask);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded);
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->stream,
+ "%% _cairo_ps_surface_stroke\n");
+#endif
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_pdf_operators_stroke (&surface->pdf_operators,
+ path,
+ style,
+ ctm,
+ ctm_inverse);
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_int_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ &rect,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (! _cairo_rectangle_intersect (&extents.bounded, &surface->page_bbox))
+ return CAIRO_STATUS_SUCCESS;
+
+ /* use the more accurate extents */
+ if (extents.is_bounded) {
+ _cairo_path_fixed_fill_extents (path,
+ fill_rule,
+ tolerance,
+ &extents.mask);
+
+ if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded);
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded));
+
+#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))
+ return status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ (source->extend == CAIRO_EXTEND_NONE ||
+ source->extend == CAIRO_EXTEND_PAD))
+ {
+ _cairo_output_stream_printf (surface->stream, "q\n");
+
+ status = _cairo_pdf_operators_clip (&surface->pdf_operators,
+ path,
+ fill_rule);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_ps_surface_paint_surface (surface,
+ (cairo_surface_pattern_t *) source,
+ &extents.bounded, op);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->stream, "Q\n");
+ _cairo_pdf_operators_reset (&surface->pdf_operators);
+ } else {
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_pdf_operators_fill (&surface->pdf_operators,
+ path,
+ fill_rule);
+ }
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_show_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ cairo_composite_rectangles_t extents;
+ cairo_bool_t overlap;
+ cairo_status_t status;
+
+ cairo_rectangle_int_t rect;
+ rect.x = rect.y = 0;
+ rect.width = surface->width;
+ rect.height = surface->height;
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ &rect,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ &overlap);
+ if (unlikely (status))
+ return status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_ps_surface_analyze_operation (surface, op, source, &extents.bounded);
+
+ assert (_cairo_ps_surface_operation_supported (surface, op, source, &extents.bounded));
+
+#if DEBUG_PS
+ _cairo_output_stream_printf (surface->stream,
+ "%% _cairo_ps_surface_show_glyphs\n");
+#endif
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
+ if (unlikely (status))
+ return status;
+
+ return _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0,
+ FALSE,
+ scaled_font);
+}
+
+static void
+_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 (surface->clipper.clip.path != NULL) {
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+
+ _cairo_output_stream_printf (surface->stream, "Q q\n");
+ _cairo_surface_clipper_reset (&surface->clipper);
+ }
+}
+
+static cairo_int_status_t
+_cairo_ps_surface_set_bounding_box (void *abstract_surface,
+ cairo_box_t *bbox)
+{
+ cairo_ps_surface_t *surface = abstract_surface;
+ int i, num_comments;
+ char **comments;
+ int x1, y1, x2, y2;
+ cairo_bool_t has_page_media;
+ const char *page_media;
+
+ if (surface->eps) {
+ x1 = floor (_cairo_fixed_to_double (bbox->p1.x));
+ y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y));
+ x2 = ceil (_cairo_fixed_to_double (bbox->p2.x));
+ y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y));
+ } else {
+ x1 = 0;
+ y1 = 0;
+ x2 = ceil (surface->width);
+ y2 = ceil (surface->height);
+ }
+
+ surface->page_bbox.x = x1;
+ surface->page_bbox.y = y1;
+ surface->page_bbox.width = x2 - x1;
+ surface->page_bbox.height = y2 - y1;
+
+ _cairo_output_stream_printf (surface->stream,
+ "%%%%Page: %d %d\n",
+ surface->num_pages,
+ surface->num_pages);
+
+ _cairo_output_stream_printf (surface->stream,
+ "%%%%BeginPageSetup\n");
+
+ has_page_media = 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;
+ 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);
+ }
+
+ _cairo_output_stream_printf (surface->stream,
+ "%%%%PageBoundingBox: %d %d %d %d\n",
+ x1, y1, x2, y2);
+
+ _cairo_output_stream_printf (surface->stream,
+ "%%%%EndPageSetup\n"
+ "q %d %d %d %d rectclip q\n",
+ surface->page_bbox.x,
+ surface->page_bbox.y,
+ surface->page_bbox.width,
+ surface->page_bbox.height);
+
+ if (surface->num_pages == 1) {
+ surface->bbox_x1 = x1;
+ surface->bbox_y1 = y1;
+ surface->bbox_x2 = x2;
+ surface->bbox_y2 = y2;
+ } else {
+ if (x1 < surface->bbox_x1)
+ surface->bbox_x1 = x1;
+ if (y1 < surface->bbox_y1)
+ surface->bbox_y1 = y1;
+ if (x2 > surface->bbox_x2)
+ surface->bbox_x2 = x2;
+ if (y2 > surface->bbox_y2)
+ surface->bbox_y2 = y2;
+ }
+ surface->current_pattern_is_solid_color = FALSE;
+ _cairo_pdf_operators_reset (&surface->pdf_operators);
+
+ 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,
+ NULL, /* create similar: handled by wrapper */
+ _cairo_ps_surface_finish,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* cairo_ps_surface_copy_page */
+ _cairo_ps_surface_show_page,
+ _cairo_ps_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_ps_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ /* Here are the drawing functions */
+
+ _cairo_ps_surface_paint, /* paint */
+ NULL, /* mask */
+ _cairo_ps_surface_stroke,
+ _cairo_ps_surface_fill,
+ _cairo_ps_surface_show_glyphs,
+ NULL, /* snapshot */
+};
+
+static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = {
+ _cairo_ps_surface_start_page,
+ _cairo_ps_surface_set_paginated_mode,
+ _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 000000000..fd1d21deb
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-ps.h
@@ -0,0 +1,114 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_PS_H
+#define CAIRO_PS_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_PS_SURFACE
+
+#include <stdio.h>
+
+CAIRO_BEGIN_DECLS
+
+/* PS-surface functions */
+
+/**
+ * cairo_ps_level_t:
+ * @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification.
+ * @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification.
+ *
+ * #cairo_ps_level_t is used to describe the language level of the
+ * PostScript Language Reference that a generated PostScript file will
+ * conform to.
+ */
+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 000000000..6311c7100
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-qt-surface.cpp
@@ -0,0 +1,1748 @@
+/* -*- 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ * Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+/* Get INT16_MIN etc. as per C99 */
+#define __STDC_LIMIT_MACROS
+
+#include "cairoint.h"
+#include "cairo-types-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-region-private.h"
+
+#include "cairo-ft.h"
+#include "cairo-qt.h"
+#include "cairo-error-private.h"
+
+#include <memory>
+
+#include <QtGui/QPainter>
+#include <QtGui/QPaintEngine>
+#include <QtGui/QPaintDevice>
+#include <QtGui/QImage>
+#include <QtGui/QPixmap>
+#include <QtGui/QBrush>
+#include <QtGui/QPen>
+#include <QtCore/QVarLengthArray>
+
+#include <sys/time.h>
+
+/* 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;
+ }
+ return QPainter::CompositionMode_Source;
+}
+
+static bool
+_op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op)
+{
+ 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_ARGB32:
+ return QImage::Format_ARGB32_Premultiplied;
+ case CAIRO_FORMAT_RGB24:
+ return QImage::Format_RGB32;
+ 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
+ }
+
+ 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 <qpainter_path_data *> (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 <qpainter_path_data *> (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 <qpainter_path_data *> (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 <qpainter_path_data *> (closure);
+
+ pdata->path.closeSubpath();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static inline QPainterPath
+path_to_qt (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_DIRECTION_FORWARD,
+ _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 (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 if (qs->p)
+ 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);
+}
+
+static cairo_status_t
+_cairo_qt_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ QImage *qimg = NULL;
+
+ D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface));
+
+ *image_extra = NULL;
+
+ if (qs->image_equiv) {
+ *image_out = (cairo_image_surface_t*)
+ cairo_surface_reference (qs->image_equiv);
+
+ image_rect->x = qs->window.x();
+ image_rect->y = qs->window.y();
+ image_rect->width = qs->window.width();
+ image_rect->height = qs->window.height();
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ 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_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());
+ }
+ }
+
+ if (qimg == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ *image_out = (cairo_image_surface_t*)
+ cairo_image_surface_create_for_data (qimg->bits(),
+ _cairo_format_from_qimage_format (qimg->format()),
+ qimg->width(), qimg->height(),
+ qimg->bytesPerLine());
+ *image_extra = qimg;
+
+ image_rect->x = qs->window.x() + offset.x();
+ image_rect->y = qs->window.y() + offset.y();
+ image_rect->width = qs->window.width() - offset.x();
+ image_rect->height = qs->window.height() - offset.y();
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_qt_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface));
+
+ cairo_surface_destroy (&image->base);
+
+ if (image_extra) {
+ QImage *qimg = (QImage*) image_extra;
+
+ // XXX should I be using setBackgroundMode here instead of setCompositionMode?
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_Source);
+
+ qs->p->drawImage (image_rect->x, image_rect->y, *qimg);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+
+ delete qimg;
+ }
+}
+
+static cairo_status_t
+_cairo_qt_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ if (src->backend == qs->base.backend) {
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return (cairo_status_t) CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+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,
+ 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.united(r);
+ }
+
+ qs->p->setClipRegion (qr, Qt::IntersectClip);
+ }
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_set_clip (cairo_qt_surface_t *qs,
+ cairo_clip_t *clip)
+{
+ cairo_int_status_t status;
+
+ D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", qs, clip ? "(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) :
+ 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 (_cairo_fixed_to_double (lpat->p1.x),
+ _cairo_fixed_to_double (lpat->p1.y),
+ _cairo_fixed_to_double (lpat->p2.x),
+ _cairo_fixed_to_double (lpat->p2.y));
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern;
+
+ /* Based on the SVG surface code */
+
+ cairo_point_t *c0, *c1;
+ cairo_fixed_t radius0, radius1;
+
+ if (rpat->r1 < rpat->r2) {
+ c0 = &rpat->c1;
+ c1 = &rpat->c2;
+ radius0 = rpat->r1;
+ radius1 = rpat->r2;
+ reverse_stops = FALSE;
+ } else {
+ c0 = &rpat->c2;
+ c1 = &rpat->c1;
+ radius0 = rpat->r2;
+ radius1 = rpat->r1;
+ reverse_stops = TRUE;
+ }
+
+ double x0 = _cairo_fixed_to_double (c0->x);
+ double y0 = _cairo_fixed_to_double (c0->y);
+ double r0 = _cairo_fixed_to_double (radius0);
+ double x1 = _cairo_fixed_to_double (c1->x);
+ double y1 = _cairo_fixed_to_double (c1->y);
+ double r1 = _cairo_fixed_to_double (radius1);
+
+ if (rpat->r1 == rpat->r2) {
+ 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 () {
+ 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<qreal> 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,
+ 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<QImage> 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;
+
+ _cairo_clip_init_copy (&clip, &qs->clipper.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,
+ cairo_clip_t *clip)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+ cairo_int_status_t status;
+
+ D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op)));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _op_is_supported (qs, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ 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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op)));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _op_is_supported (qs, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ 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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op)));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _op_is_supported (qs, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ 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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+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,
+ cairo_clip_t *clip)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op)));
+
+ if (!qs->p)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ 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_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_qt_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_pattern_t *mask_pattern,
+ void *abstract_surface,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+ if (mask_pattern)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _op_is_supported (qs, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ _cairo_qt_surface_set_clip_region (qs, clip_region);
+
+ D(fprintf(stderr, "q[%p] composite op:%s src:%p [%d %d] dst [%d %d] dim [%d %d]\n",
+ abstract_surface, _opstr(op), (void*)pattern,
+ src_x, src_y, dst_x, dst_y, width, height));
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
+
+ QColor color;
+ color.setRgbF(solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ qs->p->fillRect (dst_x, dst_y, width, height, color);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern;
+ cairo_surface_t *surface = spattern->surface;
+
+ QImage *qimg = NULL;
+ QPixmap *qpixmap = NULL;
+ std::auto_ptr<QImage> qimg_d;
+
+ if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
+ cairo_image_surface_t *isurf = (cairo_image_surface_t*) surface;
+ qimg = new QImage ((const uchar *) isurf->data,
+ isurf->width,
+ isurf->height,
+ isurf->stride,
+ _qimage_format_from_cairo_format (isurf->format));
+ qimg_d.reset(qimg);
+ }
+
+ if (surface->type == CAIRO_SURFACE_TYPE_QT) {
+ cairo_qt_surface_t *qsrc = (cairo_qt_surface_t*) surface;
+
+ if (qsrc->image)
+ qimg = qsrc->image;
+ else if (qsrc->pixmap)
+ qpixmap = qsrc->pixmap;
+ }
+
+ if (!qimg && !qpixmap)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ QMatrix savedMatrix = qs->p->worldMatrix();
+ if (! _cairo_matrix_is_identity (&pattern->matrix)) {
+ cairo_matrix_t pm = pattern->matrix;
+ cairo_status_t status;
+
+ status = cairo_matrix_invert (&pm);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ qs->p->setWorldMatrix(_qmatrix_from_cairo_matrix (pm), true);
+ }
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
+
+ if (qimg)
+ qs->p->drawImage (dst_x, dst_y, *qimg, src_x, src_y, width, height);
+ else if (qpixmap)
+ qs->p->drawPixmap (dst_x, dst_y, *qpixmap, src_x, src_y, width, height);
+
+ if (qs->supports_porter_duff)
+ qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ return CAIRO_INT_STATUS_SUCCESS;
+}
+
+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_create_similar,
+ _cairo_qt_surface_finish,
+ _cairo_qt_surface_acquire_source_image,
+ _cairo_qt_surface_release_source_image,
+ _cairo_qt_surface_acquire_dest_image,
+ _cairo_qt_surface_release_dest_image,
+ _cairo_qt_surface_clone_similar,
+
+ _cairo_qt_surface_composite,
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_qt_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ _cairo_qt_surface_mark_dirty,
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ _cairo_qt_surface_paint,
+ _cairo_qt_surface_mask,
+ _cairo_qt_surface_stroke,
+ _cairo_qt_surface_fill,
+ _cairo_qt_surface_show_glyphs,
+
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ NULL, /* has_show_text_glyphs */
+ NULL, /* show_text_glyphs */
+};
+
+cairo_surface_t *
+cairo_qt_surface_create (QPainter *painter)
+{
+ cairo_qt_surface_t *qs;
+
+ qs = (cairo_qt_surface_t *) 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,
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ _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 *) 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,
+ _cairo_content_from_format (format));
+
+ _cairo_surface_clipper_init (&qs->clipper,
+ _cairo_qt_surface_clipper_intersect_clip_path);
+
+ if (CAIRO_FORMAT_A8 == format) {
+ qs->image = NULL;
+ qs->image_equiv = cairo_image_surface_create(format,
+ width, height);
+ qs->p = NULL;
+ qs->supports_porter_duff = false;
+ qs->window = QRect(0, 0, width, height);
+ return &qs->base;
+ }
+
+ QImage *image = new QImage (width, height,
+ _qimage_format_from_cairo_format (format));
+
+ 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 *) 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, content);
+
+ _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;
+}
+
+QPainter *
+cairo_qt_surface_get_qpainter (cairo_surface_t *surface)
+{
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
+
+ if (surface->type != CAIRO_SURFACE_TYPE_QT)
+ 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;
+
+ if (surface->type != CAIRO_SURFACE_TYPE_QT)
+ 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;
+
+ if (surface->type != CAIRO_SURFACE_TYPE_QT)
+ return NULL;
+
+ 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 ExtendMode::REPEAT
+ * - implement EXTEND_NONE (?? probably need to clip to the extents of the source)
+ * - implement ExtendMode::REFLECT (create temporary and copy 4x, then ExtendMode::REPEAT that)
+ *
+ * - 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 000000000..c20bbb18d
--- /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 <vladimir@mozilla.com>
+ */
+
+#ifndef CAIRO_QT_H
+#define CAIRO_QT_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_QT_SURFACE
+
+#include <QtGui/QImage>
+#include <QtGui/QPainter>
+
+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 000000000..2a17e77fa
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-quartz-font.c
@@ -0,0 +1,843 @@
+/* -*- 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 <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include <dlfcn.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.
+ */
+
+/* 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;
+
+/* 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;
+
+/* CTFontCreateWithGraphicsFont is not public until 10.5. */
+typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
+static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
+
+static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
+static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
+
+static void
+quartz_font_ensure_symbols(void)
+{
+ if (_cairo_quartz_font_symbol_lookup_done)
+ return;
+
+ /* 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");
+
+ 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");
+
+ CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
+
+ if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
+ CGFontGetGlyphBBoxesPtr &&
+ CGFontGetGlyphsForUnicharsPtr &&
+ CGFontGetUnitsPerEmPtr &&
+ CGFontGetGlyphAdvancesPtr &&
+ (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 = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc.
+ /* handle CSS-ish faces */
+ if (!strcmp(family, "serif") || !strcmp(family, "Times Roman"))
+ family = "Times";
+ else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans"))
+ family = "Helvetica";
+ else if (!strcmp(family, "cursive"))
+ family = "Apple Chancery";
+ else if (!strcmp(family, "fantasy"))
+ family = "Papyrus";
+ else if (!strcmp(family, "monospace") || !strcmp(family, "mono"))
+ family = "Courier";
+
+ /* Try to build up the full name, e.g. "Helvetica Bold Oblique" first,
+ * then drop the bold, then drop the slant, then drop both.. finally
+ * just use "Helvetica". And if Helvetica doesn't exist, give up.
+ */
+ for (loop = 0; loop < 5; loop++) {
+ if (loop == 4)
+ family = "Helvetica";
+
+ strcpy (full_name, family);
+
+ if (loop < 3 && (loop & 1) == 0) {
+ if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD)
+ strcat (full_name, " Bold");
+ }
+
+ if (loop < 3 && (loop & 2) == 0) {
+ if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC)
+ strcat (full_name, " Italic");
+ else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE)
+ strcat (full_name, " Oblique");
+ }
+
+ if (CGFontCreateWithFontNamePtr) {
+ cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
+ cgFont = CGFontCreateWithFontNamePtr (cgFontName);
+ CFRelease (cgFontName);
+ } else {
+ cgFont = CGFontCreateWithNamePtr (full_name);
+ }
+
+ if (cgFont)
+ break;
+ }
+
+ if (!cgFont) {
+ /* Give up */
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ *font_face = cairo_quartz_font_face_create_for_cgfont (cgFont);
+ CGFontRelease (cgFont);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_quartz_font_face_destroy (void *abstract_face)
+{
+ 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);
+}
+
+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 = 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 = 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)
+{
+}
+
+#define INVALID_GLYPH 0x00
+
+static inline CGGlyph
+_cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) {
+ unsigned long index = _cairo_scaled_glyph_index (scaled_glyph);
+ if (index > 0xffff)
+ return INVALID_GLYPH;
+ return (CGGlyph) index;
+}
+
+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 emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
+ double xscale, yscale;
+ double xmin, ymin, xmax, ymax;
+
+ if (glyph == INVALID_GLYPH)
+ goto FAIL;
+
+ if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
+ !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
+ goto FAIL;
+
+ /* 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;
+ }
+
+ status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale,
+ &xscale, &yscale, 1);
+ if (status)
+ goto FAIL;
+
+ bbox = CGRectMake (bbox.origin.x / emscale,
+ bbox.origin.y / emscale,
+ bbox.size.width / emscale,
+ bbox.size.height / emscale);
+
+ /* 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
+
+#if 0
+ fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph,
+ bbox.origin.x / emscale, bbox.origin.y / emscale,
+ bbox.size.width / emscale, bbox.size.height / emscale);
+#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;
+
+ extents.x_advance = (double) advance / emscale;
+ extents.y_advance = 0.0;
+
+#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;
+ CTFontRef ctFont;
+ cairo_path_fixed_t *path;
+
+ if (glyph == INVALID_GLYPH) {
+ _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);
+
+ ctFont = CTFontCreateWithGraphicsFont (font_face->cgFont, 1.0, NULL, NULL);
+ glyphPath = CTFontCreatePathForGlyph (ctFont, glyph, &textMatrix);
+ CFRelease (ctFont);
+ 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 xscale, yscale;
+ 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 (glyph == INVALID_GLYPH) {
+ 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;
+ }
+
+ status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale,
+ &xscale, &yscale, 1);
+ if (status)
+ return status;
+
+ /* scale(1,-1) * font->base.scale * scale(1,-1) */
+ textMatrix = CGAffineTransformMake (font->base.scale.xx,
+ -font->base.scale.yx,
+ -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:
+ 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:
+ 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);
+ UniChar u = (UniChar) ucs4;
+ CGGlyph glyph;
+
+ CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1);
+
+ return glyph;
+}
+
+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,
+ NULL, /* show_glyphs */
+ NULL, /* 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;
+}
+
+#if !defined(__LP64__) && !TARGET_OS_IPHONE
+/*
+ * compat with old ATSUI backend
+ */
+
+/**
+ * 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)
+{
+ ATSFontRef atsFont = FMGetATSFontRefFromFont (font_id);
+ CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
+ cairo_font_face_t *ff;
+
+ ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
+
+ CGFontRelease (cgFont);
+
+ return ff;
+}
+
+/* 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
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 000000000..9a18dd46e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-quartz-image-surface.c
@@ -0,0 +1,290 @@
+/* -*- 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 <vladimir@mozilla.com>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-quartz-image.h"
+#include "cairo-quartz-private.h"
+
+#include "cairo-error-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 *info, const void *data, size_t size)
+{
+ cairo_surface_t *surface = (cairo_surface_t *) info;
+ cairo_surface_destroy (surface);
+}
+
+static cairo_surface_t *
+_cairo_quartz_image_surface_create_similar (void *asurface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_surface_t *result;
+ cairo_surface_t *isurf =
+ _cairo_image_surface_create_with_content (content, width, height);
+ if (cairo_surface_status(isurf))
+ return isurf;
+
+ 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;
+
+ /* the imageSurface will be destroyed by the data provider's release callback */
+ CGImageRelease (surface->image);
+
+ 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_status_t
+_cairo_quartz_image_surface_acquire_dest_image (void *asurface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+
+ *image_out = surface->imageSurface;
+ *image_rect = surface->extents;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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 = surface->extents;
+ 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)
+{
+ cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
+ CGImageRef oldImage = surface->image;
+ CGImageRef newImage = NULL;
+
+ /* To be released by the ReleaseCallback */
+ cairo_surface_reference ((cairo_surface_t*) surface->imageSurface);
+
+ newImage = _cairo_quartz_create_cgimage (surface->imageSurface->format,
+ surface->imageSurface->width,
+ surface->imageSurface->height,
+ surface->imageSurface->stride,
+ surface->imageSurface->data,
+ TRUE,
+ NULL,
+ DataProviderReleaseCallback,
+ surface->imageSurface);
+
+ surface->image = newImage;
+ CGImageRelease (oldImage);
+
+ surface->base.is_clear = surface->imageSurface->base.is_clear;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_quartz_image_surface_backend = {
+ CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
+ _cairo_quartz_image_surface_create_similar,
+ _cairo_quartz_image_surface_finish,
+ _cairo_quartz_image_surface_acquire_source_image,
+ NULL, /* release_source_image */
+ _cairo_quartz_image_surface_acquire_dest_image,
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_quartz_image_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ _cairo_quartz_image_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ NULL, /* paint */
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+ NULL, /* surface_show_glyphs */
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL /* fill_stroke */
+
+};
+
+/**
+ * cairo_quartz_image_surface_create
+ * @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;
+ unsigned char *data;
+
+ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE)
+ 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;
+ data = image_surface->data;
+
+ 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 = malloc(sizeof(cairo_quartz_image_surface_t));
+ if (qisurf == NULL)
+ return SURFACE_ERROR_NO_MEMORY;
+
+ memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t));
+
+ /* In case the create_cgimage fails, this ref will
+ * be released via the callback (which will be called in
+ * case of failure.)
+ */
+ cairo_surface_reference (surface);
+
+ image = _cairo_quartz_create_cgimage (format,
+ width, height,
+ stride,
+ data,
+ TRUE,
+ NULL,
+ DataProviderReleaseCallback,
+ image_surface);
+
+ 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));
+
+ qisurf->extents.x = qisurf->extents.y = 0;
+ qisurf->extents.width = width;
+ qisurf->extents.height = height;
+
+ qisurf->image = image;
+ qisurf->imageSurface = image_surface;
+
+ qisurf->base.is_clear = image_surface->base.is_clear;
+
+ 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;
+
+ if (cairo_surface_get_type(asurface) != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE)
+ return NULL;
+
+ 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 000000000..2d6e8fb52
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-quartz-image.h
@@ -0,0 +1,64 @@
+/* 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 <vladimir@mozilla.com>
+ */
+
+#ifndef CAIRO_QUARTZ_IMAGE_H
+#define CAIRO_QUARTZ_IMAGE_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_QUARTZ_IMAGE_SURFACE
+#include "TargetConditionals.h"
+
+#if !TARGET_OS_IPHONE
+#include <Carbon/Carbon.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#endif
+
+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 000000000..1c8d496af
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-quartz-private.h
@@ -0,0 +1,119 @@
+/* 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 <calumr@mac.com>
+ * Vladimir Vukicevic <vladimir@mozilla.com>
+ */
+
+#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"
+
+#ifdef CGFLOAT_DEFINED
+typedef CGFloat cairo_quartz_float_t;
+#else
+typedef float cairo_quartz_float_t;
+#endif
+
+/* 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 a CGImage representing the contents of the surface.
+ * We clear this out before any painting into the surface, so that we
+ * don't force a copy to be created.
+ */
+ CGImageRef bitmapContextImage;
+
+ /**
+ * If non-null, this is the CGLayer for the surface.
+ */
+ CGLayerRef cgLayer;
+
+ cairo_rectangle_int_t extents;
+
+ cairo_bool_t ownsData;
+} cairo_quartz_surface_t;
+
+typedef struct cairo_quartz_image_surface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t extents;
+
+ CGImageRef image;
+ cairo_image_surface_t *imageSurface;
+} cairo_quartz_image_surface_t;
+
+cairo_bool_t
+_cairo_quartz_verify_surface_size(int width, int height);
+
+CGImageRef
+_cairo_quartz_create_cgimage (cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride,
+ void *data,
+ cairo_bool_t interpolate,
+ CGColorSpaceRef colorSpaceOverride,
+ CGDataProviderReleaseDataCallback releaseCallback,
+ void *releaseInfo);
+
+CGFontRef
+_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
+
+CTFontRef
+_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
+
+#else
+
+# error Cairo was not compiled with support for the quartz backend
+
+#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 000000000..434a28983
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
@@ -0,0 +1,3800 @@
+/* -*- 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 <vladimir@mozilla.com>
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* required for RTLD_DEFAULT */
+#endif
+#include "cairoint.h"
+
+#include "cairo-quartz-private.h"
+
+#include "cairo-error-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-private.h"
+
+#include <dlfcn.h>
+
+#ifndef RTLD_DEFAULT
+#define RTLD_DEFAULT ((void *) 0)
+#endif
+
+#include <limits.h>
+
+#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.
+ */
+
+/* Here are some of the differences between cairo and CoreGraphics
+ - cairo has only a single source active at once vs. CoreGraphics having
+ separate sources for stroke and fill
+*/
+
+/* 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);
+CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
+
+/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9
+ * has all the stuff we care about, just some of it isn't exported in the SDK.
+ */
+#ifndef kCGBitmapByteOrder32Host
+#define USE_10_3_WORKAROUNDS
+#define kCGBitmapAlphaInfoMask 0x1F
+#define kCGBitmapByteOrderMask 0x7000
+#define kCGBitmapByteOrder32Host 0
+
+typedef uint32_t CGBitmapInfo;
+
+/* public in 10.4, present in 10.3.9 */
+CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef);
+CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
+#endif
+
+/* Some of these are present in earlier versions of the OS than where
+ * they are public; others are not public at all (CGContextCopyPath,
+ * CGContextReplacePathWithClipPath, many of the getters, etc.)
+ */
+static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
+static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
+static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
+static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
+
+/* CTFontDrawGlyphs is not available until 10.7 */
+static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
+
+static SInt32 _cairo_quartz_osx_version = 0x0;
+
+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);
+
+static cairo_bool_t
+_cairo_surface_is_quartz (const cairo_surface_t *surface);
+
+/* Load all extra symbols */
+static void quartz_ensure_symbols(void)
+{
+ if (_cairo_quartz_symbol_lookup_done)
+ return;
+
+ CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask");
+ CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
+ CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
+ CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
+ CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+ CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
+
+ CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
+
+#if !TARGET_OS_IPHONE
+ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
+ // assume 10.5
+ _cairo_quartz_osx_version = 0x1050;
+ }
+#else
+ //TODO: this is not great
+ _cairo_quartz_osx_version = 0x1050;
+#endif
+
+ _cairo_quartz_symbol_lookup_done = TRUE;
+}
+
+CGImageRef
+_cairo_quartz_create_cgimage (cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride,
+ void *data,
+ cairo_bool_t interpolate,
+ CGColorSpaceRef colorSpaceOverride,
+ CGDataProviderReleaseDataCallback releaseCallback,
+ void *releaseInfo)
+{
+ 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_RGB16_565:
+ case CAIRO_FORMAT_INVALID:
+ default:
+ return NULL;
+ }
+
+ dataProvider = CGDataProviderCreateWithData (releaseInfo,
+ data,
+ height * stride,
+ releaseCallback);
+
+ if (!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 (cgc == NULL)
+ return FALSE;
+
+ if (CGContextGetTypePtr) {
+ /* 4 is the type value of a bitmap context */
+ if (CGContextGetTypePtr(cgc) == 4)
+ return TRUE;
+ return FALSE;
+ }
+
+ /* 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 (cairo_path_fixed_t *path,
+ CGContextRef closure)
+{
+ cairo_status_t status;
+
+ CGContextBeginPath (closure);
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_path_to_quartz_context_move_to,
+ _cairo_path_to_quartz_context_line_to,
+ _cairo_path_to_quartz_context_curve_to,
+ _cairo_path_to_quartz_context_close_path,
+ closure);
+
+ assert (status == CAIRO_STATUS_SUCCESS);
+}
+
+/*
+ * Misc helpers/callbacks
+ */
+
+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 (0);
+ return kPrivateCGCompositeClear;
+ }
+}
+
+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));
+
+ if (surface->base.content == CAIRO_CONTENT_ALPHA) {
+ /* For some weird reason, some compositing operators are
+ swapped when operating on masks */
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_ADD:
+ CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op));
+ return CAIRO_STATUS_SUCCESS;
+
+ case CAIRO_OPERATOR_IN:
+ CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationAtop);
+ return CAIRO_STATUS_SUCCESS;
+
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceOver);
+ return CAIRO_STATUS_SUCCESS;
+
+ case CAIRO_OPERATOR_DEST_ATOP:
+ CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceIn);
+ return CAIRO_STATUS_SUCCESS;
+
+ case CAIRO_OPERATOR_SATURATE:
+ CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositePlusLighter);
+ return CAIRO_STATUS_SUCCESS;
+
+
+ case CAIRO_OPERATOR_ATOP:
+ /*
+ CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationOver);
+ return CAIRO_STATUS_SUCCESS;
+ */
+ case CAIRO_OPERATOR_DEST:
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_XOR:
+ default:
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ } else {
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+ CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op));
+ return CAIRO_STATUS_SUCCESS;
+
+ case CAIRO_OPERATOR_DEST:
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ case CAIRO_OPERATOR_SATURATE:
+ /* TODO: the following are mostly supported by CGContextSetBlendMode*/
+ case CAIRO_OPERATOR_MULTIPLY:
+ case CAIRO_OPERATOR_SCREEN:
+ case CAIRO_OPERATOR_OVERLAY:
+ case CAIRO_OPERATOR_DARKEN:
+ case CAIRO_OPERATOR_LIGHTEN:
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ case CAIRO_OPERATOR_COLOR_BURN:
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ case CAIRO_OPERATOR_DIFFERENCE:
+ case CAIRO_OPERATOR_EXCLUSION:
+ case CAIRO_OPERATOR_HSL_HUE:
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ case CAIRO_OPERATOR_HSL_COLOR:
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ default:
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+}
+
+static inline CGLineCap
+_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
+{
+ switch (ccap) {
+ case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break;
+ case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break;
+ case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break;
+ }
+
+ return kCGLineCapButt;
+}
+
+static inline CGLineJoin
+_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
+{
+ switch (cjoin) {
+ case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break;
+ case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break;
+ case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break;
+ }
+
+ return kCGLineJoinMiter;
+}
+
+static inline CGInterpolationQuality
+_cairo_quartz_filter_to_quartz (cairo_filter_t filter)
+{
+ switch (filter) {
+ case CAIRO_FILTER_NEAREST:
+ return kCGInterpolationNone;
+
+ case CAIRO_FILTER_FAST:
+ return kCGInterpolationLow;
+
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return kCGInterpolationDefault;
+ }
+
+ 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;
+}
+
+typedef struct {
+ bool isClipping;
+ CGGlyph *cg_glyphs;
+ union {
+ CGSize *cg_advances;
+ CGPoint *cg_positions;
+ } u;
+ size_t nglyphs;
+ CGAffineTransform textTransform;
+ cairo_scaled_font_t *scaled_font;
+ CGPoint origin;
+} unbounded_show_glyphs_t;
+
+typedef struct {
+ CGPathRef cgPath;
+ cairo_fill_rule_t fill_rule;
+} unbounded_stroke_fill_t;
+
+typedef struct {
+ CGImageRef mask;
+ CGAffineTransform maskTransform;
+} unbounded_mask_t;
+
+typedef enum {
+ UNBOUNDED_STROKE_FILL,
+ UNBOUNDED_SHOW_GLYPHS,
+ UNBOUNDED_MASK
+} unbounded_op_t;
+
+typedef struct {
+ unbounded_op_t op;
+ union {
+ unbounded_stroke_fill_t stroke_fill;
+ unbounded_show_glyphs_t show_glyphs;
+ unbounded_mask_t mask;
+ } u;
+} unbounded_op_data_t;
+
+static void
+_cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface,
+ unbounded_op_data_t *op,
+ cairo_antialias_t antialias)
+{
+ CGRect clipBox, clipBoxRound;
+ CGContextRef cgc;
+ CGImageRef maskImage;
+
+ /* TODO: handle failure */
+ if (!CGContextClipToMaskPtr)
+ return;
+
+ clipBox = CGContextGetClipBoundingBox (surface->cgContext);
+ clipBoxRound = CGRectIntegral (clipBox);
+
+ cgc = CGBitmapContextCreate (NULL,
+ clipBoxRound.size.width,
+ clipBoxRound.size.height,
+ 8,
+ (((size_t) clipBoxRound.size.width) + 15) & (~15),
+ NULL,
+ kCGImageAlphaOnly);
+
+ if (!cgc)
+ return;
+
+ CGContextSetCompositeOperation (cgc, kPrivateCGCompositeCopy);
+ /* We want to mask out whatever we just rendered, so we fill the
+ * surface opaque, and then we'll render transparent.
+ */
+ CGContextSetAlpha (cgc, 1.0f);
+ CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height));
+
+ CGContextSetCompositeOperation (cgc, kPrivateCGCompositeClear);
+ CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE));
+
+ CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y);
+
+ /* We need to either render the path that was given to us, or the glyph op */
+ if (op->op == UNBOUNDED_STROKE_FILL) {
+ CGContextBeginPath (cgc);
+ CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
+
+ if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextFillPath (cgc);
+ else
+ CGContextEOFillPath (cgc);
+ } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
+ if (op->u.show_glyphs.isClipping) {
+ /* Note that the comment in show_glyphs about kCGTextClip
+ * and the text transform still applies here; however, the
+ * cg_advances we have were already transformed, so we
+ * don't have to do anything. */
+ CGContextSetTextDrawingMode (cgc, kCGTextClip);
+ CGContextSaveGState (cgc);
+ }
+ CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
+ CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
+ if (CTFontDrawGlyphsPtr) {
+ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font),
+ op->u.show_glyphs.cg_glyphs,
+ op->u.show_glyphs.u.cg_positions,
+ op->u.show_glyphs.nglyphs,
+ cgc);
+ } else {
+ CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font));
+ CGContextSetFontSize (cgc, 1.0);
+ CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+
+ CGContextShowGlyphsWithAdvances (cgc,
+ op->u.show_glyphs.cg_glyphs,
+ op->u.show_glyphs.u.cg_advances,
+ op->u.show_glyphs.nglyphs);
+
+ }
+ if (op->u.show_glyphs.isClipping) {
+ CGContextClearRect (cgc, clipBoxRound);
+ CGContextRestoreGState (cgc);
+ }
+ } else if (op->op == UNBOUNDED_MASK) {
+ CGAffineTransform ctm = CGContextGetCTM (cgc);
+ CGContextSaveGState (cgc);
+ CGContextConcatCTM (cgc, op->u.mask.maskTransform);
+ CGContextClipToMask (cgc, CGRectMake (0.0f, 0.0f,
+ CGImageGetWidth(op->u.mask.mask), CGImageGetHeight(op->u.mask.mask)),
+ op->u.mask.mask);
+ CGContextSetCTM (cgc, ctm);
+ CGContextClearRect (cgc, clipBoxRound);
+ CGContextRestoreGState (cgc);
+ }
+
+ /* Also mask out the portion of the clipbox that we rounded out, if any */
+ if (!CGRectEqualToRect (clipBox, clipBoxRound)) {
+ CGContextBeginPath (cgc);
+ CGContextAddRect (cgc, clipBoxRound);
+ CGContextAddRect (cgc, clipBox);
+ CGContextEOFillPath (cgc);
+ }
+
+ maskImage = CGBitmapContextCreateImage (cgc);
+ CGContextRelease (cgc);
+
+ if (!maskImage)
+ return;
+
+ /* Then render with the mask */
+ CGContextSaveGState (surface->cgContext);
+
+ CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
+ CGContextClipToMaskPtr (surface->cgContext, clipBoxRound, maskImage);
+ CGImageRelease (maskImage);
+
+ /* Finally, clear out the entire clipping region through our mask */
+ CGContextClearRect (surface->cgContext, clipBoxRound);
+
+ CGContextRestoreGState (surface->cgContext);
+}
+
+/*
+ * Source -> Quartz setup and finish functions
+ */
+
+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 will clamp input values to the input range.
+
+ Our stops are all in the range 0.0 to 1.0. However, the color before the
+ beginning of the gradient line is obtained by Quartz computing a negative
+ position on the gradient line, clamping it to the input range we specified
+ for our color function, and then calling our color function (actually it
+ pre-samples the color function into an array, but that doesn't matter just
+ here). Therefore if we set the lower bound to 0.0, a negative position
+ on the gradient line will pass 0.0 to ComputeGradientValue, which will
+ select the last color stop with position 0, although it should select
+ the first color stop (this matters when there are multiple color stops with
+ position 0).
+
+ Therefore we pass a small negative number as the lower bound of the input
+ range, so this value gets passed into ComputeGradientValue, which will
+ return the color of the first stop. The number should be small because
+ as far as I can tell, Quartz pre-samples the entire input range of the color
+ function into an array of fixed size, so if the input range is larger
+ than needed, the resolution of the gradient will be unnecessarily low.
+*/
+static const cairo_quartz_float_t nonrepeating_gradient_input_value_range[2] = { -0.001f, 1.f };
+
+static CGFunctionRef
+CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
+{
+ cairo_pattern_t *pat;
+
+ if (_cairo_pattern_create_copy (&pat, &gpat->base))
+ /* quartz doesn't deal very well with malloc failing, so there's
+ * not much point in us trying either */
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+ nonrepeating_gradient_input_value_range,
+ 4,
+ gradient_output_value_ranges,
+ &gradient_callbacks);
+}
+
+static void
+UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start,
+ double dx, double dy,
+ double x, double y)
+{
+ /* Compute a parameter t such that a line perpendicular to the (dx,dy)
+ vector, passing through (start->x + dx*t, start->y + dy*t), also
+ passes through (x,y).
+
+ Let px = x - start->x, py = y - start->y.
+ t is given by
+ (px - dx*t)*dx + (py - dy*t)*dy = 0
+
+ Solving for t we get
+ numerator = dx*px + dy*py
+ denominator = dx^2 + dy^2
+ t = numerator/denominator
+
+ In CreateRepeatingLinearGradientFunction we know the length of (dx,dy)
+ is not zero. (This is checked in _cairo_quartz_setup_linear_source.)
+ */
+ double px = x - start->x;
+ double py = y - start->y;
+ double numerator = dx*px + dy*py;
+ double denominator = dx*dx + dy*dy;
+ double t = numerator/denominator;
+
+ if (*min_t > t) {
+ *min_t = t;
+ }
+ if (*max_t < t) {
+ *max_t = t;
+ }
+}
+
+static CGFunctionRef
+CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
+ const cairo_gradient_pattern_t *gpat,
+ CGPoint *start, CGPoint *end,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_pattern_t *pat;
+ cairo_quartz_float_t input_value_range[2];
+ double t_min = 0.;
+ double t_max = 0.;
+ double dx = end->x - start->x;
+ double dy = end->y - start->y;
+ double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+
+ if (!extents) {
+ extents = &surface->extents;
+ }
+ bounds_x1 = extents->x;
+ bounds_y1 = extents->y;
+ bounds_x2 = extents->x + extents->width;
+ bounds_y2 = extents->y + extents->height;
+ _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
+ &bounds_x1, &bounds_y1,
+ &bounds_x2, &bounds_y2,
+ NULL);
+
+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
+ bounds_x1, bounds_y1);
+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
+ bounds_x2, bounds_y1);
+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
+ bounds_x2, bounds_y2);
+ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
+ bounds_x1, bounds_y2);
+
+ /* Move t_min and t_max to the nearest usable integer to try to avoid
+ subtle variations due to numerical instability, especially accidentally
+ cutting off a pixel. Extending the gradient repetitions is always safe. */
+ t_min = floor (t_min);
+ t_max = ceil (t_max);
+ end->x = start->x + dx*t_max;
+ end->y = start->y + dy*t_max;
+ start->x = start->x + dx*t_min;
+ start->y = start->y + dy*t_min;
+
+ // set the input range for the function -- the function knows how to
+ // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT.
+ input_value_range[0] = t_min;
+ input_value_range[1] = t_max;
+
+ if (_cairo_pattern_create_copy (&pat, &gpat->base))
+ /* quartz doesn't deal very well with malloc failing, so there's
+ * not much point in us trying either */
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+ gradient_output_value_ranges,
+ &gradient_callbacks);
+}
+
+static void
+UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center,
+ double dr, double dx, double dy,
+ double x, double y)
+{
+ /* Compute a parameter t such that a circle centered at
+ (center->x + dx*t, center->y + dy*t) with radius dr*t contains the
+ point (x,y).
+
+ Let px = x - center->x, py = y - center->y.
+ Parameter values for which t is on the circle are given by
+ (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2
+
+ Solving for t using the quadratic formula, and simplifying, we get
+ numerator = dx*px + dy*py +-
+ sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
+ denominator = dx^2 + dy^2 - dr^2
+ t = numerator/denominator
+
+ In CreateRepeatingRadialGradientFunction we know the outer circle
+ contains the inner circle. Therefore the distance between the circle
+ centers plus the radius of the inner circle is less than the radius of
+ the outer circle. (This is checked in _cairo_quartz_setup_radial_source.)
+ Therefore
+ dx^2 + dy^2 < dr^2
+ So the denominator is negative and the larger solution for t is given by
+ numerator = dx*px + dy*py -
+ sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
+ denominator = dx^2 + dy^2 - dr^2
+ t = numerator/denominator
+ dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive.
+ */
+ double px = x - center->x;
+ double py = y - center->y;
+ double dx_py_minus_dy_px = dx*py - dy*px;
+ double numerator = dx*px + dy*py -
+ sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px);
+ double denominator = dx*dx + dy*dy - dr*dr;
+ double t = numerator/denominator;
+
+ if (*max_t < t) {
+ *max_t = t;
+ }
+}
+
+/* This must only be called when one of the circles properly contains the other */
+static CGFunctionRef
+CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
+ const cairo_gradient_pattern_t *gpat,
+ CGPoint *start, double *start_radius,
+ CGPoint *end, double *end_radius,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_pattern_t *pat;
+ cairo_quartz_float_t input_value_range[2];
+ CGPoint *inner;
+ double *inner_radius;
+ CGPoint *outer;
+ double *outer_radius;
+ /* minimum and maximum t-parameter values that will make our gradient
+ cover the clipBox */
+ double t_min, t_max, t_temp;
+ /* outer minus inner */
+ double dr, dx, dy;
+ double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+
+ if (!extents) {
+ extents = &surface->extents;
+ }
+ bounds_x1 = extents->x;
+ bounds_y1 = extents->y;
+ bounds_x2 = extents->x + extents->width;
+ bounds_y2 = extents->y + extents->height;
+ _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
+ &bounds_x1, &bounds_y1,
+ &bounds_x2, &bounds_y2,
+ NULL);
+
+ if (*start_radius < *end_radius) {
+ /* end circle contains start circle */
+ inner = start;
+ outer = end;
+ inner_radius = start_radius;
+ outer_radius = end_radius;
+ } else {
+ /* start circle contains end circle */
+ inner = end;
+ outer = start;
+ inner_radius = end_radius;
+ outer_radius = start_radius;
+ }
+
+ dr = *outer_radius - *inner_radius;
+ dx = outer->x - inner->x;
+ dy = outer->y - inner->y;
+
+ /* We can't round or fudge t_min here, it has to be as accurate as possible. */
+ t_min = -(*inner_radius/dr);
+ inner->x += t_min*dx;
+ inner->y += t_min*dy;
+ *inner_radius = 0.;
+
+ t_temp = 0.;
+ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
+ bounds_x1, bounds_y1);
+ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
+ bounds_x2, bounds_y1);
+ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
+ bounds_x2, bounds_y2);
+ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
+ bounds_x1, bounds_y2);
+ /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
+ But for the parameter values we use with Quartz, t_min means radius 0.
+ Since the circles are alway expanding and contain the earlier circles,
+ it's safe to extend t_max/t_temp as much as we want, so round t_temp up
+ to the nearest integer. This may help us give stable results. */
+ t_temp = ceil (t_temp);
+ t_max = t_min + t_temp;
+ outer->x = inner->x + t_temp*dx;
+ outer->y = inner->y + t_temp*dy;
+ *outer_radius = t_temp*dr;
+
+ /* set the input range for the function -- the function knows how to
+ map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */
+ if (*start_radius < *end_radius) {
+ input_value_range[0] = t_min;
+ input_value_range[1] = t_max;
+ } else {
+ input_value_range[0] = 1 - t_max;
+ input_value_range[1] = 1 - t_min;
+ }
+
+ if (_cairo_pattern_create_copy (&pat, &gpat->base))
+ /* quartz doesn't deal very well with malloc failing, so there's
+ * not much point in us trying either */
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+ gradient_output_value_ranges,
+ &gradient_callbacks);
+}
+
+/* Obtain a CGImageRef from a #cairo_surface_t * */
+
+typedef struct {
+ cairo_surface_t *surface;
+ cairo_image_surface_t *image_out;
+ void *image_extra;
+} quartz_source_image_t;
+
+static void
+DataProviderReleaseCallback (void *info, const void *data, size_t size)
+{
+ quartz_source_image_t *source_img = info;
+ _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
+ cairo_surface_destroy (source_img->surface);
+ free (source_img);
+}
+
+static cairo_status_t
+_cairo_surface_to_cgimage (cairo_surface_t *source,
+ CGImageRef *image_out)
+{
+ cairo_status_t status;
+ quartz_source_image_t *source_img;
+
+ 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_STATUS_SUCCESS;
+ }
+
+ if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
+ if (!surface->bitmapContextImage) {
+ surface->bitmapContextImage =
+ CGBitmapContextCreateImage (surface->cgContext);
+ }
+ if (surface->bitmapContextImage) {
+ *image_out = CGImageRetain (surface->bitmapContextImage);
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+ }
+
+ source_img = malloc (sizeof (quartz_source_image_t));
+ if (source_img == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ source_img->surface = cairo_surface_reference(source);
+
+ status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
+ if (status) {
+ cairo_surface_destroy (source_img->surface);
+ free (source_img);
+ return status;
+ }
+
+ if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
+ *image_out = NULL;
+ DataProviderReleaseCallback (source_img,
+ source_img->image_out->data,
+ source_img->image_out->height * source_img->image_out->stride);
+ } else {
+ *image_out = _cairo_quartz_create_cgimage (source_img->image_out->format,
+ source_img->image_out->width,
+ source_img->image_out->height,
+ source_img->image_out->stride,
+ source_img->image_out->data,
+ TRUE,
+ NULL,
+ DataProviderReleaseCallback,
+ source_img);
+
+ /* TODO: differentiate memory error and unsupported surface type */
+ if (*image_out == NULL)
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ 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,
+ CGPatternRef *cgpat)
+{
+ cairo_surface_pattern_t *spattern;
+ cairo_surface_t *pat_surf;
+ cairo_rectangle_int_t extents;
+
+ 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 */
+ if (apattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ spattern = (cairo_surface_pattern_t *) apattern;
+ pat_surf = spattern->surface;
+
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+ status = _cairo_surface_to_cgimage (pat_surf, &image);
+ if (status)
+ return status;
+ if (image == NULL)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ info = malloc(sizeof(SurfacePatternDrawInfo));
+ if (!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;
+
+ m = spattern->base.matrix;
+ 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;
+}
+
+typedef enum {
+ DO_SOLID,
+ DO_SHADING,
+ DO_PATTERN,
+ DO_IMAGE,
+ DO_TILED_IMAGE,
+ DO_LAYER,
+ DO_UNSUPPORTED,
+ DO_NOTHING
+} cairo_quartz_action_t;
+
+/* State used during a drawing operation. */
+typedef struct {
+ CGContextRef context;
+ cairo_quartz_action_t action;
+
+ // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
+ CGAffineTransform transform;
+
+ // Used with DO_IMAGE and DO_TILED_IMAGE
+ CGImageRef image;
+ cairo_surface_t *imageSurface;
+
+ // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
+ CGRect imageRect;
+
+ // Used with DO_LAYER
+ CGLayerRef layer;
+
+ // Used with DO_SHADING
+ CGShadingRef shading;
+
+ // Used with DO_PATTERN
+ CGPatternRef pattern;
+} cairo_quartz_drawing_state_t;
+
+static void
+_cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
+ const cairo_pattern_t *source,
+ cairo_quartz_drawing_state_t *state)
+{
+ CGRect clipBox = CGContextGetClipBoundingBox (state->context);
+ double x0, y0, w, h;
+
+ cairo_surface_t *fallback;
+ CGImageRef img;
+
+ cairo_status_t status;
+
+ if (clipBox.size.width == 0.0f ||
+ clipBox.size.height == 0.0f) {
+ state->action = DO_NOTHING;
+ return;
+ }
+
+ x0 = floor(clipBox.origin.x);
+ y0 = floor(clipBox.origin.y);
+ w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
+ h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
+
+ /* Create a temporary the size of the clip surface, and position
+ * it so that the device origin coincides with the original surface */
+ fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h);
+ cairo_surface_set_device_offset (fallback, -x0, -y0);
+
+#if 0
+ {
+ cairo_t *fallback_cr;
+ cairo_pattern_t *source_copy;
+
+ /* Paint the source onto our temporary */
+ fallback_cr = cairo_create (fallback);
+ cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Use a copy of the pattern because it is const and could be allocated
+ * on the stack */
+ status = _cairo_pattern_create_copy (&source_copy, source);
+ cairo_set_source (fallback_cr, source_copy);
+ cairo_pattern_destroy (source_copy);
+
+ cairo_paint (fallback_cr);
+ cairo_destroy (fallback_cr);
+ }
+#else
+ {
+ cairo_pattern_union_t pattern;
+
+ _cairo_pattern_init_static_copy (&pattern.base, source);
+ _cairo_pattern_transform (&pattern.base,
+ &fallback->device_transform_inverse);
+ status = _cairo_surface_paint (fallback,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL);
+ }
+#endif
+
+ status = _cairo_surface_to_cgimage (fallback, &img);
+ if (status) {
+ state->action = DO_UNSUPPORTED;
+ return;
+ }
+ if (img == NULL) {
+ state->action = DO_NOTHING;
+ return;
+ }
+
+ state->imageRect = CGRectMake (0.0, 0.0, w, h);
+ state->image = img;
+ state->imageSurface = fallback;
+ state->transform = CGAffineTransformMakeTranslation (x0, y0);
+ state->action = DO_IMAGE;
+}
+
+/*
+Quartz does not support repeating radients. We handle repeating gradients
+by manually extending the gradient and repeating color stops. We need to
+minimize the number of repetitions since Quartz seems to sample our color
+function across the entire range, even if part of that range is not needed
+for the visible area of the gradient, and it samples with some fixed resolution,
+so if the gradient range is too large it samples with very low resolution and
+the gradient is very coarse. CreateRepeatingLinearGradientFunction and
+CreateRepeatingRadialGradientFunction compute the number of repetitions needed
+based on the extents of the object (the clip region cannot be used here since
+we don't want the rasterization of the entire gradient to depend on the
+clip region).
+*/
+static void
+_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
+ const cairo_linear_pattern_t *lpat,
+ cairo_rectangle_int_t *extents,
+ cairo_quartz_drawing_state_t *state)
+{
+ const cairo_pattern_t *abspat = &lpat->base.base;
+ cairo_matrix_t mat;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+
+ if (lpat->base.n_stops == 0) {
+ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
+ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
+ state->action = DO_SOLID;
+ return;
+ }
+
+ if (lpat->p1.x == lpat->p2.x &&
+ lpat->p1.y == lpat->p2.y) {
+ /* Quartz handles cases where the vector has no length very
+ * differently from pixman.
+ * Whatever the correct behaviour is, let's at least have only pixman's
+ * implementation to worry about.
+ */
+ _cairo_quartz_setup_fallback_source (surface, abspat, state);
+ return;
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+ start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
+ _cairo_fixed_to_double (lpat->p1.y));
+ end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
+ _cairo_fixed_to_double (lpat->p2.y));
+
+ if (abspat->extend == CAIRO_EXTEND_NONE ||
+ abspat->extend == CAIRO_EXTEND_PAD)
+ {
+ gradFunc = CreateGradientFunction (&lpat->base);
+ } else {
+ gradFunc = CreateRepeatingLinearGradientFunction (surface,
+ &lpat->base,
+ &start, &end,
+ extents);
+ }
+
+ state->shading = CGShadingCreateAxial (rgb,
+ start, end,
+ gradFunc,
+ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+ state->action = DO_SHADING;
+}
+
+static void
+_cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
+ const cairo_radial_pattern_t *rpat,
+ cairo_rectangle_int_t *extents,
+ cairo_quartz_drawing_state_t *state)
+{
+ const cairo_pattern_t *abspat = &rpat->base.base;
+ cairo_matrix_t mat;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+ double c1x = _cairo_fixed_to_double (rpat->c1.x);
+ double c1y = _cairo_fixed_to_double (rpat->c1.y);
+ double c2x = _cairo_fixed_to_double (rpat->c2.x);
+ double c2y = _cairo_fixed_to_double (rpat->c2.y);
+ double r1 = _cairo_fixed_to_double (rpat->r1);
+ double r2 = _cairo_fixed_to_double (rpat->r2);
+ double dx = c1x - c2x;
+ double dy = c1y - c2y;
+ double centerDistance = sqrt (dx*dx + dy*dy);
+
+ if (rpat->base.n_stops == 0) {
+ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
+ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
+ state->action = DO_SOLID;
+ return;
+ }
+
+ if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
+ r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
+ /* Quartz handles cases where neither circle contains the other very
+ * differently from pixman.
+ * Whatever the correct behaviour is, let's at least have only pixman's
+ * implementation to worry about.
+ * Note that this also catches the cases where r1 == r2.
+ */
+ _cairo_quartz_setup_fallback_source (surface, abspat, state);
+ return;
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+ start = CGPointMake (c1x, c1y);
+ end = CGPointMake (c2x, c2y);
+
+ if (abspat->extend == CAIRO_EXTEND_NONE ||
+ abspat->extend == CAIRO_EXTEND_PAD)
+ {
+ gradFunc = CreateGradientFunction (&rpat->base);
+ } else {
+ gradFunc = CreateRepeatingRadialGradientFunction (surface,
+ &rpat->base,
+ &start, &r1,
+ &end, &r2,
+ extents);
+ }
+
+ state->shading = CGShadingCreateRadial (rgb,
+ start,
+ r1,
+ end,
+ r2,
+ gradFunc,
+ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+ state->action = DO_SHADING;
+}
+
+static void
+_cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface,
+ const cairo_surface_pattern_t *spat,
+ cairo_rectangle_int_t *extents,
+ cairo_quartz_drawing_state_t *state)
+{
+ const cairo_pattern_t *source = &spat->base;
+ CGContextRef context = state->context;
+
+ if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD ||
+ (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
+ {
+ 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;
+ cairo_bool_t repeat = source->extend == CAIRO_EXTEND_REPEAT;
+ cairo_status_t status;
+
+ cairo_matrix_invert(&m);
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
+
+ /* Draw nonrepeating CGLayer surface using DO_LAYER */
+ if (!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) {
+ state->imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
+ state->layer = quartz_surf->cgLayer;
+ state->action = DO_LAYER;
+ return;
+ }
+ }
+
+ status = _cairo_surface_to_cgimage (pat_surf, &img);
+ if (status) {
+ state->action = DO_UNSUPPORTED;
+ return;
+ }
+ if (img == NULL) {
+ state->action = DO_NOTHING;
+ return;
+ }
+
+ /* XXXroc what is this for? */
+ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+
+ state->image = img;
+
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+ if (!repeat) {
+ state->imageRect = CGRectMake (0, 0, extents.width, extents.height);
+ state->action = DO_IMAGE;
+ return;
+ }
+
+ /* 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 (context),
+ state->transform);
+
+ srcRect = CGRectMake (0, 0, extents.width, extents.height);
+ 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->imageRect = srcRect;
+ state->action = DO_TILED_IMAGE;
+ return;
+ }
+
+ /* Fall through to generic SURFACE case */
+ }
+
+ CGFloat patternAlpha = 1.0f;
+ CGColorSpaceRef patternSpace;
+ CGPatternRef pattern;
+ cairo_int_status_t status;
+
+ status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ state->action = DO_NOTHING;
+ return;
+ }
+ if (status) {
+ state->action = DO_UNSUPPORTED;
+ return;
+ }
+
+ patternSpace = CGColorSpaceCreatePattern (NULL);
+ CGContextSetFillColorSpace (context, patternSpace);
+ CGContextSetFillPattern (context, pattern, &patternAlpha);
+ CGContextSetStrokeColorSpace (context, patternSpace);
+ CGContextSetStrokePattern (context, pattern, &patternAlpha);
+ CGColorSpaceRelease (patternSpace);
+
+ /* Quartz likes to munge the pattern phase (as yet unexplained
+ * why); force it to 0,0 as we've already baked in the correct
+ * pattern translation into the pattern matrix
+ */
+ CGContextSetPatternPhase (context, CGSizeMake(0,0));
+
+ state->pattern = pattern;
+ state->action = DO_PATTERN;
+ return;
+}
+
+/**
+ * Call this before any operation that can modify the contents of a
+ * cairo_quartz_surface_t.
+ */
+static void
+_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface)
+{
+ if (surface->bitmapContextImage) {
+ CGImageRelease (surface->bitmapContextImage);
+ surface->bitmapContextImage = NULL;
+ }
+}
+
+/**
+ * Sets up internal state to be used to draw the source mask, stored in
+ * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on
+ * surface->cgContext.
+ */
+static cairo_quartz_drawing_state_t
+_cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
+ const cairo_pattern_t *source,
+ cairo_operator_t op,
+ cairo_rectangle_int_t *extents)
+{
+ CGContextRef context = surface->cgContext;
+ cairo_quartz_drawing_state_t state;
+ cairo_status_t status;
+
+ state.context = context;
+ state.image = NULL;
+ state.imageSurface = NULL;
+ state.layer = NULL;
+ state.shading = NULL;
+ state.pattern = NULL;
+
+ _cairo_quartz_surface_will_change (surface);
+
+ // Save before we change the pattern, colorspace, etc. so that
+ // we can restore and make sure that quartz releases our
+ // pattern (which may be stack allocated)
+ CGContextSaveGState(context);
+
+ CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
+
+ status = _cairo_quartz_surface_set_cairo_operator (surface, op);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ state.action = DO_NOTHING;
+ return state;
+ }
+ if (status) {
+ state.action = DO_UNSUPPORTED;
+ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+
+ CGContextSetRGBStrokeColor (context,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+ CGContextSetRGBFillColor (context,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+ state.action = DO_SOLID;
+ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
+ _cairo_quartz_setup_linear_source (surface, lpat, extents, &state);
+ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
+ _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
+ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (source, NULL) &&
+ CGContextGetAlphaPtr &&
+ CGContextGetAlphaPtr (surface->cgContext) == 1.0) {
+ // Quartz won't touch pixels outside the bounds of the
+ // source surface, so we can just go ahead and use Copy here
+ // to accelerate things.
+ // Quartz won't necessarily be able to do this optimization internally;
+ // for CGLayer surfaces, we can know all the pixels are opaque
+ // (because it's CONTENT_COLOR), but Quartz won't know.
+ CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy);
+ }
+
+ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ _cairo_quartz_setup_surface_source (surface, spat, extents, &state);
+ return state;
+ }
+
+ state.action = DO_UNSUPPORTED;
+ return state;
+}
+
+/**
+ * 1) Tears down internal state used to draw the source
+ * 2) Does CGContextRestoreGState(state->context)
+ */
+static void
+_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
+{
+ if (state->image) {
+ CGImageRelease(state->image);
+ }
+
+ if (state->imageSurface) {
+ cairo_surface_destroy(state->imageSurface);
+ }
+
+ if (state->shading) {
+ CGShadingRelease(state->shading);
+ }
+
+ if (state->pattern) {
+ CGPatternRelease(state->pattern);
+ }
+
+ CGContextRestoreGState(state->context);
+}
+
+
+static void
+_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
+{
+ assert (state &&
+ ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) ||
+ (state->layer && state->action == DO_LAYER)));
+
+ CGContextConcatCTM (state->context, state->transform);
+ CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
+ CGContextScaleCTM (state->context, 1, -1);
+
+ if (state->action == DO_TILED_IMAGE) {
+ CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
+ /* no need to worry about unbounded operators, since tiled images
+ fill the entire clip region */
+ } else {
+ if (state->action == DO_LAYER) {
+ /* Note that according to Apple docs it's completely legal
+ * to draw a CGLayer to any CGContext, even one it wasn't
+ * created for.
+ */
+ CGContextDrawLayerAtPoint (state->context, state->imageRect.origin,
+ state->layer);
+ } else {
+ CGContextDrawImage (state->context, state->imageRect, state->image);
+ }
+
+ /* disable this EXTEND_NONE correctness code because we use this path
+ * for both EXTEND_NONE and EXTEND_PAD */
+ if (0 && !_cairo_operator_bounded_by_source (op)) {
+ CGContextBeginPath (state->context);
+ CGContextAddRect (state->context, state->imageRect);
+ CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
+ CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
+ CGContextEOFillPath (state->context);
+ }
+ }
+}
+
+
+/*
+ * 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->bitmapContextImage) {
+ CGImageRelease (surface->bitmapContextImage);
+ surface->bitmapContextImage = NULL;
+ }
+
+ if (surface->imageSurfaceEquiv) {
+ if (surface->ownsData)
+ _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv);
+ cairo_surface_destroy (surface->imageSurfaceEquiv);
+ surface->imageSurfaceEquiv = NULL;
+ } else if (surface->imageData && surface->ownsData) {
+ free (surface->imageData);
+ }
+
+ surface->imageData = NULL;
+
+ if (surface->cgLayer) {
+ CGLayerRelease (surface->cgLayer);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_quartz_surface_acquire_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_int_status_t status;
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+ *image_extra = NULL;
+
+ /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */
+
+ status = _cairo_quartz_get_image (surface, image_out);
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) {
+ /* copy the layer into a Quartz bitmap context so we can get the data */
+ cairo_surface_t *tmp =
+ cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32,
+ surface->extents.width,
+ surface->extents.height);
+ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp;
+
+ /* if surface creation failed, we won't have a Quartz surface here */
+ if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ &&
+ tmp_surface->imageSurfaceEquiv) {
+ CGContextSaveGState (tmp_surface->cgContext);
+ CGContextTranslateCTM (tmp_surface->cgContext, 0, surface->extents.height);
+ CGContextScaleCTM (tmp_surface->cgContext, 1, -1);
+ /* Note that according to Apple docs it's completely legal
+ * to draw a CGLayer to any CGContext, even one it wasn't
+ * created for.
+ */
+ CGContextDrawLayerAtPoint (tmp_surface->cgContext,
+ CGPointMake (0.0, 0.0),
+ surface->cgLayer);
+ CGContextRestoreGState (tmp_surface->cgContext);
+
+ *image_out = (cairo_image_surface_t*)
+ cairo_surface_reference(tmp_surface->imageSurfaceEquiv);
+ *image_extra = tmp;
+ status = CAIRO_STATUS_SUCCESS;
+ } else {
+ cairo_surface_destroy (tmp);
+ }
+ }
+
+ if (status)
+ 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_surface_destroy ((cairo_surface_t *) image);
+
+ if (image_extra) {
+ cairo_surface_destroy ((cairo_surface_t *) image_extra);
+ }
+}
+
+
+static cairo_status_t
+_cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+ ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
+
+ *image_rect = surface->extents;
+ *image_extra = NULL;
+
+ _cairo_quartz_surface_will_change (surface);
+
+ return _cairo_quartz_surface_acquire_image (abstract_surface,
+ image_out, image_extra);
+}
+
+static void
+_cairo_quartz_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */
+
+ cairo_surface_destroy ((cairo_surface_t *) image);
+
+ if (image_extra) {
+ /* we need to write the data from the temp surface back to the layer */
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra;
+ CGImageRef img;
+ cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img);
+ if (status) {
+ cairo_surface_destroy (&tmp_surface->base);
+ return;
+ }
+
+ CGContextSaveGState (surface->cgContext);
+ CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height);
+ CGContextScaleCTM (surface->cgContext, 1, -1);
+ CGContextDrawImage (surface->cgContext,
+ CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height),
+ img);
+ CGContextRestoreGState (surface->cgContext);
+
+ cairo_surface_destroy (&tmp_surface->base);
+ }
+}
+
+static cairo_surface_t *
+_cairo_quartz_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_format_t format;
+
+ 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));
+ }
+
+ return cairo_quartz_surface_create (format, width, height);
+}
+
+static cairo_status_t
+_cairo_quartz_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_quartz_surface_t *new_surface = NULL;
+ cairo_format_t new_format;
+ CGImageRef quartz_image = NULL;
+ cairo_status_t status;
+
+ *clone_out = NULL;
+
+ // verify width and height of surface
+ if (!_cairo_quartz_verify_surface_size(width, height)) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (width == 0 || height == 0) {
+ *clone_out = (cairo_surface_t*)
+ _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
+ width, height);
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (_cairo_surface_is_quartz (src)) {
+ cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src;
+
+ if (IS_EMPTY(qsurf)) {
+ *clone_out = (cairo_surface_t*)
+ _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
+ qsurf->extents.width, qsurf->extents.height);
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ status = _cairo_surface_to_cgimage (src, &quartz_image);
+ if (status)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ new_format = CAIRO_FORMAT_ARGB32; /* assumed */
+ if (_cairo_surface_is_image (src)) {
+ new_format = ((cairo_image_surface_t *) src)->format;
+ }
+
+ new_surface = (cairo_quartz_surface_t *)
+ cairo_quartz_surface_create (new_format, width, height);
+
+ if (quartz_image == NULL)
+ goto FINISH;
+
+ if (!new_surface || new_surface->base.status) {
+ CGImageRelease (quartz_image);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ CGContextSaveGState (new_surface->cgContext);
+
+ CGContextSetCompositeOperation (new_surface->cgContext,
+ kPrivateCGCompositeCopy);
+
+ CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y);
+ CGContextDrawImage (new_surface->cgContext,
+ CGRectMake (0, 0, CGImageGetWidth(quartz_image), CGImageGetHeight(quartz_image)),
+ quartz_image);
+
+ CGContextRestoreGState (new_surface->cgContext);
+
+ CGImageRelease (quartz_image);
+
+FINISH:
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+ *clone_out = (cairo_surface_t*) new_surface;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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_surface_paint_cg (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+ cairo_quartz_drawing_state_t state;
+
+ ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextFillRect (state.context, CGRectMake(surface->extents.x,
+ surface->extents.y,
+ surface->extents.width,
+ surface->extents.height));
+ } else if (state.action == DO_SHADING) {
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ _cairo_quartz_teardown_state (&state);
+
+ ND((stderr, "-- paint\n"));
+ return rv;
+}
+
+static cairo_bool_t
+_cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
+{
+ /* For repeating gradients we need to manually extend the gradient and
+ repeat stops, since Quartz doesn't support repeating gradients natively.
+ We need to minimze the number of repeated stops, and since rasterization
+ depends on the number of repetitions we use (even if some of the
+ repetitions go beyond the extents of the object or outside the clip
+ region), it's important to use the same number of repetitions when
+ rendering an object no matter what the clip region is. So the
+ computation of the repetition count cannot depended on the clip region,
+ and should only depend on the object extents, so we need to compute
+ the object extents for repeating gradients. */
+ return (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ source->type == CAIRO_PATTERN_TYPE_RADIAL) &&
+ (source->extend == CAIRO_EXTEND_REPEAT ||
+ source->extend == CAIRO_EXTEND_REFLECT);
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv;
+ cairo_image_surface_t *image;
+
+ rv = _cairo_quartz_surface_paint_cg (abstract_surface,
+ op,
+ source,
+ clip);
+
+ if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
+ return rv;
+
+ rv = _cairo_quartz_get_image (surface, &image);
+ if (rv == CAIRO_STATUS_SUCCESS) {
+ rv = _cairo_surface_paint (&image->base, op, source, clip);
+ cairo_surface_destroy (&image->base);
+ }
+
+ return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_fill_cg (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+ cairo_quartz_drawing_state_t state;
+ CGPathRef path_for_unbounded = NULL;
+
+ ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+ if (_cairo_quartz_source_needs_extents (source))
+ {
+ /* We don't need precise extents since these are only used to
+ compute the number of gradient reptitions needed to cover the
+ object. */
+ cairo_rectangle_int_t path_extents;
+ _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
+ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
+ } else {
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
+
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
+
+ if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
+ path_for_unbounded = CGContextCopyPathPtr (state.context);
+
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextFillPath (state.context);
+ else
+ CGContextEOFillPath (state.context);
+ } else if (state.action == DO_SHADING) {
+
+ // we have to clip and then paint the shading; we can't fill
+ // with the shading
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextClip (state.context);
+ else
+ CGContextEOClip (state.context);
+
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextClip (state.context);
+ else
+ CGContextEOClip (state.context);
+
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ _cairo_quartz_teardown_state (&state);
+
+ if (path_for_unbounded) {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_STROKE_FILL;
+ ub.u.stroke_fill.cgPath = path_for_unbounded;
+ ub.u.stroke_fill.fill_rule = fill_rule;
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
+ CGPathRelease (path_for_unbounded);
+ }
+
+ ND((stderr, "-- fill\n"));
+ return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv;
+ cairo_image_surface_t *image;
+
+ rv = _cairo_quartz_surface_fill_cg (abstract_surface,
+ op,
+ source,
+ path,
+ fill_rule,
+ tolerance,
+ antialias,
+ clip);
+
+ if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
+ return rv;
+
+ rv = _cairo_quartz_get_image (surface, &image);
+ if (rv == CAIRO_STATUS_SUCCESS) {
+ rv = _cairo_surface_fill (&image->base, op, source,
+ path, fill_rule, tolerance, antialias,
+ clip);
+ cairo_surface_destroy (&image->base);
+ }
+
+ return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_stroke_cg (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+ cairo_quartz_drawing_state_t state;
+ CGAffineTransform origCTM, strokeTransform;
+ CGPathRef path_for_unbounded = NULL;
+
+ ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+ rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+ if (unlikely (rv))
+ return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+
+ if (_cairo_quartz_source_needs_extents (source))
+ {
+ cairo_rectangle_int_t path_extents;
+ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
+ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
+ } else {
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+ // Turning antialiasing off used to cause misrendering with
+ // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
+ // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
+ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
+ CGContextSetLineWidth (state.context, style->line_width);
+ CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
+ CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
+ CGContextSetMiterLimit (state.context, style->miter_limit);
+
+ origCTM = CGContextGetCTM (state.context);
+
+ if (style->dash && style->num_dashes) {
+#define STATIC_DASH 32
+ cairo_quartz_float_t sdash[STATIC_DASH];
+ cairo_quartz_float_t *fdash = sdash;
+ unsigned int max_dashes = style->num_dashes;
+ unsigned int k;
+
+ bool set_line_dash = false;
+ if (style->num_dashes % 2 == 0) {
+ for (k = 1; k < max_dashes; k++) {
+ if (style->dash[k]) {
+ set_line_dash = true;
+ break;
+ }
+ }
+ } else
+ set_line_dash = true;
+
+ if (set_line_dash) {
+ if (style->num_dashes%2)
+ max_dashes *= 2;
+ if (max_dashes > STATIC_DASH)
+ fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
+ if (fdash == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (k = 0; k < max_dashes; k++)
+ fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
+
+ CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
+ if (fdash != sdash)
+ free (fdash);
+ } else
+ CGContextSetLineDash (state.context, 0, NULL, 0);
+ } else
+ CGContextSetLineDash (state.context, 0, NULL, 0);
+
+
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
+
+ _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+ CGContextConcatCTM (state.context, strokeTransform);
+
+ if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
+ path_for_unbounded = CGContextCopyPathPtr (state.context);
+
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextStrokePath (state.context);
+ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
+ CGContextReplacePathWithStrokedPath (state.context);
+ CGContextClip (state.context);
+
+ CGContextSetCTM (state.context, origCTM);
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action == DO_SHADING) {
+ CGContextReplacePathWithStrokedPath (state.context);
+ CGContextClip (state.context);
+
+ CGContextSetCTM (state.context, origCTM);
+
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto BAIL;
+ }
+
+ if (path_for_unbounded) {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_STROKE_FILL;
+ ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING;
+
+ CGContextBeginPath (state.context);
+ CGContextAddPath (state.context, path_for_unbounded);
+ CGPathRelease (path_for_unbounded);
+
+ CGContextSaveGState (state.context);
+ CGContextConcatCTM (state.context, strokeTransform);
+ CGContextReplacePathWithStrokedPath (state.context);
+ CGContextRestoreGState (state.context);
+
+ ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (state.context);
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
+ CGPathRelease (ub.u.stroke_fill.cgPath);
+ }
+
+ BAIL:
+ _cairo_quartz_teardown_state (&state);
+
+ ND((stderr, "-- stroke\n"));
+ return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv;
+ cairo_image_surface_t *image;
+
+ rv = _cairo_quartz_surface_stroke_cg (abstract_surface, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+
+ if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
+ return rv;
+
+ rv = _cairo_quartz_get_image (surface, &image);
+ if (rv == CAIRO_STATUS_SUCCESS) {
+ rv = _cairo_surface_stroke (&image->base, op, source,
+ path, style, ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ cairo_surface_destroy (&image->base);
+ }
+
+ return rv;
+}
+
+#if CAIRO_HAS_QUARTZ_FONT
+static cairo_int_status_t
+_cairo_quartz_surface_show_glyphs_cg (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ CGAffineTransform textTransform, ctm, invTextTransform;
+#define STATIC_BUF_SIZE 64
+ CGGlyph glyphs_static[STATIC_BUF_SIZE];
+ CGSize cg_advances_static[STATIC_BUF_SIZE];
+ CGGlyph *cg_glyphs = &glyphs_static[0];
+ /* We'll use the cg_advances array for either advances or positions,
+ depending which API we're using to actually draw. The types involved
+ have the same size, so this is safe. */
+ CGSize *cg_advances = &cg_advances_static[0];
+
+ cairo_rectangle_int_t glyph_extents;
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+ cairo_quartz_drawing_state_t state;
+ cairo_quartz_float_t xprev, yprev;
+ int i;
+ CGFontRef cgfref = NULL;
+
+ cairo_bool_t isClipping = FALSE;
+ cairo_bool_t didForceFontSmoothing = FALSE;
+ cairo_antialias_t effective_antialiasing;
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (num_glyphs <= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+ if (_cairo_quartz_source_needs_extents (source) &&
+ !_cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
+ &glyph_extents, NULL))
+ {
+ state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
+ } else {
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextSetTextDrawingMode (state.context, kCGTextFill);
+ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_SHADING || state.action == DO_LAYER) {
+ CGContextSetTextDrawingMode (state.context, kCGTextClip);
+ isClipping = TRUE;
+ } else {
+ if (state.action != DO_NOTHING)
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto BAIL;
+ }
+
+ /* this doesn't addref */
+ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
+ CGContextSetFont (state.context, cgfref);
+ CGContextSetFontSize (state.context, 1.0);
+
+ effective_antialiasing = scaled_font->options.antialias;
+ if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL &&
+ !surface->base.permit_subpixel_antialiasing) {
+ effective_antialiasing = CAIRO_ANTIALIAS_GRAY;
+ }
+
+ switch (scaled_font->options.antialias) {
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ CGContextSetShouldAntialias (state.context, TRUE);
+ CGContextSetShouldSmoothFonts (state.context, TRUE);
+ if (CGContextSetAllowsFontSmoothingPtr &&
+ !CGContextGetAllowsFontSmoothingPtr (state.context))
+ {
+ didForceFontSmoothing = TRUE;
+ CGContextSetAllowsFontSmoothingPtr (state.context, TRUE);
+ }
+ break;
+ case CAIRO_ANTIALIAS_NONE:
+ CGContextSetShouldAntialias (state.context, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+ CGContextSetShouldAntialias (state.context, TRUE);
+ CGContextSetShouldSmoothFonts (state.context, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_DEFAULT:
+ /* Don't do anything */
+ break;
+ }
+
+ if (num_glyphs > STATIC_BUF_SIZE) {
+ cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
+ if (cg_glyphs == NULL) {
+ rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof(CGSize));
+ if (cg_advances == NULL) {
+ rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ }
+
+ /* 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);
+
+ /* 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);
+
+ CGContextSetTextMatrix (state.context, CGAffineTransformIdentity);
+
+ /* Translate to the first glyph's position before drawing */
+ ctm = CGContextGetCTM (state.context);
+ CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
+ CGContextConcatCTM (state.context, textTransform);
+
+ if (CTFontDrawGlyphsPtr) {
+ /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use
+ * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap
+ * fonts like Apple Color Emoji will render properly.
+ * For this, we need to convert our glyph positions to Core Graphics's CGPoint.
+ * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */
+
+ CGPoint *cg_positions = (CGPoint*) cg_advances;
+ cairo_quartz_float_t origin_x = glyphs[0].x;
+ cairo_quartz_float_t origin_y = glyphs[0].y;
+
+ for (i = 0; i < num_glyphs; i++) {
+ CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y);
+ cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform);
+ cg_glyphs[i] = glyphs[i].index;
+ }
+
+ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
+ cg_glyphs, cg_positions, num_glyphs, state.context);
+ } else {
+ /* Convert our glyph positions to glyph advances. We need n-1 advances,
+ * since the advance at index 0 is applied after glyph 0. */
+ xprev = glyphs[0].x;
+ yprev = glyphs[0].y;
+
+ cg_glyphs[0] = glyphs[0].index;
+
+ for (i = 1; i < num_glyphs; i++) {
+ cairo_quartz_float_t xf = glyphs[i].x;
+ cairo_quartz_float_t yf = glyphs[i].y;
+ cg_glyphs[i] = glyphs[i].index;
+ cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
+ xprev = xf;
+ yprev = yf;
+ }
+
+ CGContextShowGlyphsWithAdvances (state.context,
+ cg_glyphs,
+ cg_advances,
+ num_glyphs);
+ }
+
+ CGContextSetCTM (state.context, ctm);
+
+ if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action == DO_SHADING) {
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+ }
+
+BAIL:
+ if (didForceFontSmoothing)
+ CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
+
+ _cairo_quartz_teardown_state (&state);
+
+ if (rv == CAIRO_STATUS_SUCCESS &&
+ cgfref &&
+ !_cairo_operator_bounded_by_mask (op))
+ {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_SHOW_GLYPHS;
+
+ ub.u.show_glyphs.isClipping = isClipping;
+ ub.u.show_glyphs.cg_glyphs = cg_glyphs;
+ if (CTFontDrawGlyphsPtr) {
+ /* we're using Core Text API: the cg_advances array was
+ reused (above) for glyph positions */
+ CGPoint *cg_positions = (CGPoint*) cg_advances;
+ ub.u.show_glyphs.u.cg_positions = cg_positions;
+ } else {
+ ub.u.show_glyphs.u.cg_advances = cg_advances;
+ }
+ ub.u.show_glyphs.nglyphs = num_glyphs;
+ ub.u.show_glyphs.textTransform = textTransform;
+ ub.u.show_glyphs.scaled_font = scaled_font;
+ ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
+ }
+
+
+ if (cg_advances != &cg_advances_static[0]) {
+ free (cg_advances);
+ }
+
+ if (cg_glyphs != &glyphs_static[0]) {
+ free (cg_glyphs);
+ }
+
+ return rv;
+}
+#endif /* CAIRO_HAS_QUARTZ_FONT */
+
+static cairo_int_status_t
+_cairo_quartz_surface_show_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ cairo_image_surface_t *image;
+
+#if CAIRO_HAS_QUARTZ_FONT
+ rv = _cairo_quartz_surface_show_glyphs_cg (abstract_surface, op, source,
+ glyphs, num_glyphs,
+ scaled_font, clip, remaining_glyphs);
+
+ if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
+ return rv;
+
+#endif
+
+ rv = _cairo_quartz_get_image (surface, &image);
+ if (rv == CAIRO_STATUS_SUCCESS) {
+ rv = _cairo_surface_show_text_glyphs (&image->base, op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font, clip);
+ cairo_surface_destroy (&image->base);
+ }
+
+ return rv;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_surface_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ CGRect rect;
+ CGImageRef img;
+ cairo_surface_t *pat_surf = mask->surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ CGAffineTransform ctm, mask_matrix;
+
+ status = _cairo_surface_to_cgimage (pat_surf, &img);
+ if (status)
+ return status;
+ if (img == NULL) {
+ if (!_cairo_operator_bounded_by_mask (op))
+ CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ rect = CGRectMake (0.0f, 0.0f, CGImageGetWidth (img) , CGImageGetHeight (img));
+
+ CGContextSaveGState (surface->cgContext);
+
+ /* ClipToMask is essentially drawing an image, so we need to flip the CTM
+ * to get the image to appear oriented the right way */
+ ctm = CGContextGetCTM (surface->cgContext);
+
+ _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix);
+ mask_matrix = CGAffineTransformInvert(mask_matrix);
+ mask_matrix = CGAffineTransformTranslate (mask_matrix, 0.0, CGImageGetHeight (img));
+ mask_matrix = CGAffineTransformScale (mask_matrix, 1.0, -1.0);
+
+ CGContextConcatCTM (surface->cgContext, mask_matrix);
+ CGContextClipToMaskPtr (surface->cgContext, rect, img);
+
+ CGContextSetCTM (surface->cgContext, ctm);
+
+ status = _cairo_quartz_surface_paint_cg (surface, op, source, clip);
+
+ CGContextRestoreGState (surface->cgContext);
+
+ if (!_cairo_operator_bounded_by_mask (op)) {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_MASK;
+ ub.u.mask.mask = img;
+ ub.u.mask.maskTransform = mask_matrix;
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE);
+ }
+
+ CGImageRelease (img);
+
+ return status;
+}
+
+/* This is somewhat less than ideal, but it gets the job done;
+ * it would be better to avoid calling back into cairo. This
+ * creates a temporary surface to use as the mask.
+ */
+static cairo_int_status_t
+_cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ int width = surface->extents.width;
+ int height = surface->extents.height;
+
+ cairo_surface_t *gradient_surf = NULL;
+ cairo_surface_pattern_t surface_pattern;
+ cairo_int_status_t status;
+
+ /* Render the gradient to a surface */
+ gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_A8,
+ width,
+ height);
+
+ status = _cairo_quartz_surface_paint (gradient_surf, CAIRO_OPERATOR_SOURCE, mask, NULL);
+ if (status)
+ goto BAIL;
+
+ _cairo_pattern_init_for_surface (&surface_pattern, gradient_surf);
+
+ status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, clip);
+
+ _cairo_pattern_fini (&surface_pattern.base);
+
+ BAIL:
+ if (gradient_surf)
+ cairo_surface_destroy (gradient_surf);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_mask_cg (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+
+ ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+ /* Using CGContextSetAlpha to implement mask alpha doesn't work for all operators. */
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
+ op == CAIRO_OPERATOR_OVER) {
+ /* This is easy; we just need to paint with the alpha. */
+ cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
+
+ CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
+ rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip);
+ CGContextSetAlpha (surface->cgContext, 1.0);
+
+ return rv;
+ }
+
+ /* If we have CGContextClipToMask, we can do more complex masks */
+ if (CGContextClipToMaskPtr) {
+ /* For these, we can skip creating a temporary surface, since we already have one */
+ /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */
+ if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ mask->extend == CAIRO_EXTEND_NONE) {
+ return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip);
+ }
+
+ return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip);
+ }
+
+ /* So, CGContextClipToMask is not present in 10.3.9, so we're
+ * doomed; if we have imageData, we can do fallback, otherwise
+ * just pretend success.
+ */
+ if (surface->imageData)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_quartz_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv;
+ cairo_image_surface_t *image;
+
+ rv = _cairo_quartz_surface_mask_cg (abstract_surface,
+ op,
+ source,
+ mask,
+ clip);
+
+ if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED))
+ return rv;
+
+ rv = _cairo_quartz_get_image (surface, &image);
+ if (rv == CAIRO_STATUS_SUCCESS) {
+ rv = _cairo_surface_mask (&image->base, op, source, mask, clip);
+ cairo_surface_destroy (&image->base);
+ }
+
+ return rv;
+}
+
+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_status_t
+_cairo_quartz_surface_mark_dirty_rectangle (void *abstract_surface,
+ int x, int y,
+ int width, int height)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ _cairo_quartz_surface_will_change (surface);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+// XXXtodo implement show_page; need to figure out how to handle begin/end
+
+static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
+ CAIRO_SURFACE_TYPE_QUARTZ,
+ _cairo_quartz_surface_create_similar,
+ _cairo_quartz_surface_finish,
+ _cairo_quartz_surface_acquire_image,
+ _cairo_quartz_surface_release_source_image,
+ _cairo_quartz_surface_acquire_dest_image,
+ _cairo_quartz_surface_release_dest_image,
+ _cairo_quartz_surface_clone_similar,
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_quartz_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ _cairo_quartz_surface_mark_dirty_rectangle,
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ _cairo_quartz_surface_paint,
+ _cairo_quartz_surface_mask,
+ _cairo_quartz_surface_stroke,
+ _cairo_quartz_surface_fill,
+ _cairo_quartz_surface_show_glyphs,
+
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL /* fill_stroke */
+};
+
+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 = malloc(sizeof(cairo_quartz_surface_t));
+ if (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);
+
+ _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;
+
+ if (IS_EMPTY(surface)) {
+ surface->cgContext = NULL;
+ surface->cgContextBaseCTM = CGAffineTransformIdentity;
+ surface->imageData = NULL;
+ 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->imageData = NULL;
+ surface->imageSurfaceEquiv = NULL;
+ surface->bitmapContextImage = NULL;
+ surface->cgLayer = NULL;
+ 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:
+ *
+ * <informalexample><programlisting>
+ * CGContextTranslateCTM (cgContext, 0.0, height);
+ * CGContextScaleCTM (cgContext, 1.0, -1.0);
+ * </programlisting></informalexample>
+ *
+ * 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.4
+ **/
+
+cairo_surface_t *
+cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_quartz_surface_t *surf;
+
+ CGContextRetain (cgContext);
+
+ surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
+ width, height);
+ if (surf->base.status) {
+ CGContextRelease (cgContext);
+ // create_internal will have set an error
+ return (cairo_surface_t*) surf;
+ }
+
+ return (cairo_surface_t *) surf;
+}
+
+/**
+ * cairo_quartz_cglayer_surface_create_similar
+ * @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.4
+ **/
+cairo_surface_t *
+cairo_quartz_surface_create (cairo_format_t format,
+ unsigned int width,
+ unsigned int height)
+{
+ int stride;
+ unsigned char *data;
+
+ 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)
+ {
+ stride = width * 4;
+ } else if (format == CAIRO_FORMAT_A8) {
+ stride = width;
+ } 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;
+
+ data = _cairo_malloc_ab (height, stride);
+ if (!data) {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ /* zero the memory to match the image surface behaviour */
+ memset (data, 0, height * stride);
+
+ cairo_quartz_surface_t *surf;
+ surf = (cairo_quartz_surface_t *) cairo_quartz_surface_create_for_data
+ (data, format, width, height, stride);
+ if (surf->base.status) {
+ free (data);
+ return (cairo_surface_t *) surf;
+ }
+
+ // We created this data, so we can delete it.
+ surf->ownsData = TRUE;
+
+ return (cairo_surface_t *) surf;
+}
+
+/**
+ * cairo_quartz_surface_create_for_data
+ * @data: a pointer to a buffer supplied by the application in which
+ * to write contents. This pointer must be suitably aligned for any
+ * kind of variable, (for example, a pointer returned by malloc).
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a Quartz surface backed by a CGBitmap. The surface is
+ * created using the Device RGB (or Device Gray, for A8) color space.
+ * All Cairo operations, including those that require software
+ * rendering, will succeed on this surface.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_quartz_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride)
+{
+ cairo_quartz_surface_t *surf;
+ CGContextRef cgc;
+ CGColorSpaceRef cgColorspace;
+ CGBitmapInfo bitinfo;
+ void *imageData = data;
+ int bitsPerComponent;
+ unsigned int i;
+
+ // verify width and height of surface
+ if (!_cairo_quartz_verify_surface_size(width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ if (width == 0 || height == 0) {
+ return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
+ width, height);
+ }
+
+ if (format == CAIRO_FORMAT_ARGB32 ||
+ format == CAIRO_FORMAT_RGB24)
+ {
+ cgColorspace = CGColorSpaceCreateDeviceRGB();
+ bitinfo = kCGBitmapByteOrder32Host;
+ if (format == CAIRO_FORMAT_ARGB32)
+ bitinfo |= kCGImageAlphaPremultipliedFirst;
+ else
+ bitinfo |= kCGImageAlphaNoneSkipFirst;
+ bitsPerComponent = 8;
+ } else if (format == CAIRO_FORMAT_A8) {
+ cgColorspace = NULL;
+ bitinfo = kCGImageAlphaOnly;
+ bitsPerComponent = 8;
+ } else if (format == CAIRO_FORMAT_A1) {
+ /* I don't think we can usefully support this, as defined by
+ * cairo_format_t -- these are 1-bit pixels stored in 32-bit
+ * quantities.
+ */
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ } else {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ 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
+ *
+ * Returns the CGContextRef that the given Quartz surface is backed
+ * by.
+ *
+ * Return value: the CGContextRef for the given surface.
+ *
+ * Since: 1.4
+ **/
+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;
+}
+
+static cairo_bool_t
+_cairo_surface_is_quartz (const cairo_surface_t *surface)
+{
+ return surface->backend == &cairo_quartz_surface_backend;
+}
+
+CGContextRef
+cairo_quartz_get_cg_context_with_clip (cairo_t *cr)
+{
+
+ cairo_surface_t *surface = cr->gstate->target;
+ cairo_clip_t *clip = &cr->gstate->clip;
+ cairo_status_t status;
+
+ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
+
+ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
+ return NULL;
+
+ if (!clip->path) {
+ if (clip->all_clipped) {
+ /* Save the state before we set an empty clip rect so that
+ * our previous clip will be restored */
+
+ /* _cairo_surface_clipper_set_clip doesn't deal with
+ * clip->all_clipped because drawing is normally discarded earlier */
+ CGRect empty = {{0,0}, {0,0}};
+ CGContextClipToRect (quartz->cgContext, empty);
+ CGContextSaveGState (quartz->cgContext);
+
+ return quartz->cgContext;
+ }
+
+ /* an empty clip is represented by NULL */
+ clip = NULL;
+ }
+
+ status = _cairo_surface_clipper_set_clip (&quartz->clipper, clip);
+
+ /* Save the state after we set the clip so that it persists
+ * after we restore */
+ CGContextSaveGState (quartz->cgContext);
+
+ if (unlikely (status))
+ return NULL;
+
+ return quartz->cgContext;
+}
+
+void
+cairo_quartz_finish_cg_context_with_clip (cairo_t *cr)
+{
+ cairo_surface_t *surface = cr->gstate->target;
+
+ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
+
+ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
+ return;
+
+ CGContextRestoreGState (quartz->cgContext);
+}
+
+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 <Movies.h>
+
+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 000000000..2941a0339
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-quartz.h
@@ -0,0 +1,112 @@
+/* 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 <vladimir@mozilla.com>
+ */
+
+#ifndef CAIRO_QUARTZ_H
+#define CAIRO_QUARTZ_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_QUARTZ_SURFACE
+#include "TargetConditionals.h"
+
+#if !TARGET_OS_IPHONE
+#include <ApplicationServices/ApplicationServices.h>
+#else
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreText/CoreText.h>
+#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_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,
+ 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 CGContextRef
+cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+
+cairo_public CGContextRef
+cairo_quartz_get_cg_context_with_clip (cairo_t *cr);
+
+cairo_public void
+cairo_quartz_finish_cg_context_with_clip (cairo_t *cr);
+
+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 !defined(__LP64__) && !TARGET_OS_IPHONE
+cairo_public cairo_font_face_t *
+cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
+#endif
+
+#endif /* CAIRO_HAS_QUARTZ_FONT */
+
+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-recording-surface-private.h b/gfx/cairo/cairo/src/cairo-recording-surface-private.h
new file mode 100644
index 000000000..4ec5f88b4
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-recording-surface-private.h
@@ -0,0 +1,171 @@
+/* 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 <krh@redhat.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_RECORDING_SURFACE_H
+#define CAIRO_RECORDING_SURFACE_H
+
+#include "cairoint.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-clip-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_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_clip_t clip;
+} 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 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_t;
+
+typedef struct _cairo_recording_surface {
+ cairo_surface_t base;
+
+ cairo_content_t content;
+
+ /* A recording-surface is logically unbounded, but when used as a
+ * source we need to render it to an image, so we need a size at
+ * which to create that image. */
+ cairo_rectangle_t extents_pixels;
+ cairo_rectangle_int_t extents;
+ cairo_bool_t unbounded;
+
+ cairo_clip_t clip;
+
+ cairo_array_t commands;
+
+ int replay_start_idx;
+} 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 (cairo_surface_t *surface,
+ cairo_surface_t *target);
+
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_analyze_recording_pattern (cairo_surface_t *surface,
+ cairo_surface_t *target);
+
+cairo_private cairo_status_t
+_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
+ cairo_surface_t *target);
+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_bool_t
+_cairo_surface_is_recording (const cairo_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 000000000..0e955b83f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-recording-surface.c
@@ -0,0 +1,1134 @@
+/* -*- 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 <krh@redhat.com>
+ * Carl Worth <cworth@cworth.org>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+/**
+ * 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:
+ * <informalexample><programlisting>
+ * cairo_t *cr;
+ *
+ * cr = cairo_create (target);
+ * cairo_set_source_surface (cr, recording_surface, 0.0, 0.0);
+ * cairo_paint (cr);
+ * cairo_destroy (cr);
+ * </programlisting></informalexample>
+ *
+ * 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-analysis-surface-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-wrapper-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).
+ */
+
+/**
+ * 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 *recording_surface;
+ cairo_status_t status;
+
+ recording_surface = malloc (sizeof (cairo_recording_surface_t));
+ if (unlikely (recording_surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&recording_surface->base,
+ &cairo_recording_surface_backend,
+ NULL, /* device */
+ content);
+
+ recording_surface->content = content;
+
+ recording_surface->unbounded = TRUE;
+ _cairo_clip_init (&recording_surface->clip);
+
+ /* unbounded -> 'infinite' extents */
+ if (extents != NULL) {
+ recording_surface->extents_pixels = *extents;
+
+ /* XXX check for overflow */
+ recording_surface->extents.x = floor (extents->x);
+ recording_surface->extents.y = floor (extents->y);
+ recording_surface->extents.width = ceil (extents->x + extents->width) - recording_surface->extents.x;
+ recording_surface->extents.height = ceil (extents->y + extents->height) - recording_surface->extents.y;
+
+ status = _cairo_clip_rectangle (&recording_surface->clip,
+ &recording_surface->extents);
+ if (unlikely (status)) {
+ free (recording_surface);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ recording_surface->unbounded = FALSE;
+ }
+
+ _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *));
+
+ recording_surface->replay_start_idx = 0;
+ recording_surface->base.is_clear = TRUE;
+
+ return &recording_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 *recording_surface = abstract_surface;
+ cairo_command_t **elements;
+ int i, num_elements;
+
+ num_elements = recording_surface->commands.num_elements;
+ elements = _cairo_array_index (&recording_surface->commands, 0);
+ 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;
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ _cairo_clip_fini (&command->header.clip);
+ free (command);
+ }
+
+ _cairo_array_fini (&recording_surface->commands);
+ _cairo_clip_fini (&recording_surface->clip);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_recording_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *surface = abstract_surface;
+ cairo_surface_t *image;
+
+ image = _cairo_surface_has_snapshot (&surface->base,
+ &_cairo_image_surface_backend);
+ if (image != NULL) {
+ *image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ image = _cairo_image_surface_create_with_content (surface->content,
+ surface->extents.width,
+ surface->extents.height);
+ if (unlikely (image->status))
+ return image->status;
+
+ cairo_surface_set_device_offset (image,
+ -surface->extents.x,
+ -surface->extents.y);
+
+ status = _cairo_recording_surface_replay (&surface->base, image);
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+ cairo_surface_attach_snapshot (&surface->base, image, NULL);
+
+ *image_out = (cairo_image_surface_t *) image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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 *recording_surface,
+ cairo_command_header_t *command,
+ cairo_command_type_t type,
+ cairo_operator_t op,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ command->type = type;
+ command->op = op;
+ command->region = CAIRO_RECORDING_REGION_ALL;
+ _cairo_clip_init_copy (&command->clip, clip);
+ if (recording_surface->clip.path != NULL)
+ status = _cairo_clip_apply_clip (&command->clip, &recording_surface->clip);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *recording_surface = abstract_surface;
+ cairo_command_paint_t *command;
+
+ command = malloc (sizeof (cairo_command_paint_t));
+ if (unlikely (command == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _command_init (recording_surface,
+ &command->header, CAIRO_COMMAND_PAINT, op, clip);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_pattern_init_snapshot (&command->source.base, source);
+ if (unlikely (status))
+ goto CLEANUP_COMMAND;
+
+ status = _cairo_array_append (&recording_surface->commands, &command);
+ if (unlikely (status))
+ goto CLEANUP_SOURCE;
+
+ /* An optimisation that takes care to not replay what was done
+ * before surface is cleared. We don't erase recorded commands
+ * since we may have earlier snapshots of this surface. */
+ if (op == CAIRO_OPERATOR_CLEAR && clip == NULL)
+ recording_surface->replay_start_idx = recording_surface->commands.num_elements;
+
+ return CAIRO_STATUS_SUCCESS;
+
+ CLEANUP_SOURCE:
+ _cairo_pattern_fini (&command->source.base);
+ CLEANUP_COMMAND:
+ _cairo_clip_fini (&command->header.clip);
+ free (command);
+ 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,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *recording_surface = abstract_surface;
+ cairo_command_mask_t *command;
+
+ command = malloc (sizeof (cairo_command_mask_t));
+ if (unlikely (command == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _command_init (recording_surface,
+ &command->header, CAIRO_COMMAND_MASK, op, clip);
+ 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_array_append (&recording_surface->commands, &command);
+ if (unlikely (status))
+ goto CLEANUP_MASK;
+
+ return CAIRO_STATUS_SUCCESS;
+
+ CLEANUP_MASK:
+ _cairo_pattern_fini (&command->mask.base);
+ CLEANUP_SOURCE:
+ _cairo_pattern_fini (&command->source.base);
+ CLEANUP_COMMAND:
+ _cairo_clip_fini (&command->header.clip);
+ free (command);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *recording_surface = abstract_surface;
+ cairo_command_stroke_t *command;
+
+ command = malloc (sizeof (cairo_command_stroke_t));
+ if (unlikely (command == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _command_init (recording_surface,
+ &command->header, CAIRO_COMMAND_STROKE, op, clip);
+ 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_array_append (&recording_surface->commands, &command);
+ if (unlikely (status))
+ goto CLEANUP_STYLE;
+
+ 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_fini (&command->header.clip);
+ free (command);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_recording_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *recording_surface = abstract_surface;
+ cairo_command_fill_t *command;
+
+ command = malloc (sizeof (cairo_command_fill_t));
+ if (unlikely (command == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status =_command_init (recording_surface,
+ &command->header, CAIRO_COMMAND_FILL, op, clip);
+ 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_array_append (&recording_surface->commands, &command);
+ if (unlikely (status))
+ goto CLEANUP_PATH;
+
+ return CAIRO_STATUS_SUCCESS;
+
+ CLEANUP_PATH:
+ _cairo_path_fixed_fini (&command->path);
+ CLEANUP_SOURCE:
+ _cairo_pattern_fini (&command->source.base);
+ CLEANUP_COMMAND:
+ _cairo_clip_fini (&command->header.clip);
+ free (command);
+ 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,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_recording_surface_t *recording_surface = abstract_surface;
+ cairo_command_show_text_glyphs_t *command;
+
+ command = malloc (sizeof (cairo_command_show_text_glyphs_t));
+ if (unlikely (command == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _command_init (recording_surface,
+ &command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS,
+ op, clip);
+ 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 = 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_array_append (&recording_surface->commands, &command);
+ if (unlikely (status))
+ goto CLEANUP_SCALED_FONT;
+
+ 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_fini (&command->header.clip);
+ free (command);
+ return status;
+}
+
+/**
+ * _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 *recording_surface;
+ cairo_status_t status;
+
+ recording_surface = malloc (sizeof (cairo_recording_surface_t));
+ if (unlikely (recording_surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&recording_surface->base,
+ &cairo_recording_surface_backend,
+ NULL, /* device */
+ other->base.content);
+
+ recording_surface->extents_pixels = other->extents_pixels;
+ recording_surface->extents = other->extents;
+ recording_surface->unbounded = other->unbounded;
+ recording_surface->content = other->content;
+
+ _cairo_clip_init_copy (&recording_surface->clip, &other->clip);
+
+ /* XXX We should in theory be able to reuse the original array, but we
+ * need to handle reference cycles during subsurface and self-copy.
+ */
+ recording_surface->replay_start_idx = 0;
+ recording_surface->base.is_clear = TRUE;
+
+ _cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *));
+ status = _cairo_recording_surface_replay (&other->base, &recording_surface->base);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&recording_surface->base);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return &recording_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;
+}
+
+/**
+ * _cairo_surface_is_recording:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a #cairo_recording_surface_t
+ *
+ * Return value: %TRUE if the surface is a recording surface
+ **/
+cairo_bool_t
+_cairo_surface_is_recording (const cairo_surface_t *surface)
+{
+ return surface->backend == &cairo_recording_surface_backend;
+}
+
+static const cairo_surface_backend_t cairo_recording_surface_backend = {
+ CAIRO_SURFACE_TYPE_RECORDING,
+ _cairo_recording_surface_create_similar,
+ _cairo_recording_surface_finish,
+ _cairo_recording_surface_acquire_source_image,
+ _cairo_recording_surface_release_source_image,
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_recording_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ /* Here are the 5 basic drawing operations, (which are in some
+ * sense the only things that cairo_recording_surface should need to
+ * 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,
+
+ _cairo_recording_surface_snapshot,
+
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+
+ _cairo_recording_surface_has_show_text_glyphs,
+ _cairo_recording_surface_show_text_glyphs
+};
+
+cairo_int_status_t
+_cairo_recording_surface_get_path (cairo_surface_t *surface,
+ cairo_path_fixed_t *path)
+{
+ cairo_recording_surface_t *recording_surface;
+ cairo_command_t **elements;
+ int i, num_elements;
+ cairo_int_status_t status;
+
+ if (surface->status)
+ return surface->status;
+
+ recording_surface = (cairo_recording_surface_t *) surface;
+ status = CAIRO_STATUS_SUCCESS;
+
+ num_elements = recording_surface->commands.num_elements;
+ elements = _cairo_array_index (&recording_surface->commands, 0);
+ for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
+ 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_to_traps (&command->stroke.path,
+ &command->stroke.style,
+ &command->stroke.ctm,
+ &command->stroke.ctm_inverse,
+ command->stroke.tolerance,
+ &traps);
+
+ if (status == CAIRO_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, CAIRO_DIRECTION_FORWARD,
+ 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;
+ }
+
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ if (unlikely (status))
+ break;
+ }
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+#define _clip(c) ((c)->header.clip.path ? &(c)->header.clip : NULL)
+static cairo_status_t
+_cairo_recording_surface_replay_internal (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *surface_extents,
+ cairo_surface_t *target,
+ cairo_recording_replay_type_t type,
+ cairo_recording_region_type_t region)
+{
+ cairo_recording_surface_t *recording_surface;
+ cairo_command_t **elements;
+ int i, num_elements;
+ cairo_int_status_t status;
+ cairo_surface_wrapper_t wrapper;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (unlikely (surface->finished))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ if (surface->is_clear)
+ return CAIRO_STATUS_SUCCESS;
+
+ assert (_cairo_surface_is_recording (surface));
+
+ _cairo_surface_wrapper_init (&wrapper, target);
+ _cairo_surface_wrapper_set_extents (&wrapper, surface_extents);
+
+ recording_surface = (cairo_recording_surface_t *) surface;
+ status = CAIRO_STATUS_SUCCESS;
+
+ num_elements = recording_surface->commands.num_elements;
+ elements = _cairo_array_index (&recording_surface->commands, 0);
+
+ for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
+ cairo_command_t *command = elements[i];
+
+ if (type == CAIRO_RECORDING_REPLAY && region != CAIRO_RECORDING_REGION_ALL) {
+ if (command->header.region != region)
+ continue;
+ }
+
+ switch (command->header.type) {
+ case CAIRO_COMMAND_PAINT:
+ status = _cairo_surface_wrapper_paint (&wrapper,
+ command->header.op,
+ &command->paint.source.base,
+ _clip (command));
+ break;
+
+ case CAIRO_COMMAND_MASK:
+ status = _cairo_surface_wrapper_mask (&wrapper,
+ command->header.op,
+ &command->mask.source.base,
+ &command->mask.mask.base,
+ _clip (command));
+ 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,
+ _clip (command));
+ break;
+ }
+ case CAIRO_COMMAND_FILL:
+ {
+ 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_is_equal (&command->fill.path,
+ &stroke_command->stroke.path))
+ {
+ status = _cairo_surface_wrapper_fill_stroke (&wrapper,
+ command->header.op,
+ &command->fill.source.base,
+ command->fill.fill_rule,
+ command->fill.tolerance,
+ command->fill.antialias,
+ &command->fill.path,
+ stroke_command->header.op,
+ &stroke_command->stroke.source.base,
+ &stroke_command->stroke.style,
+ &stroke_command->stroke.ctm,
+ &stroke_command->stroke.ctm_inverse,
+ stroke_command->stroke.tolerance,
+ stroke_command->stroke.antialias,
+ _clip (command));
+ i++;
+ }
+ else
+ {
+ 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,
+ _clip (command));
+ }
+ break;
+ }
+ case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
+ {
+ cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs;
+ cairo_glyph_t *glyphs_copy;
+ int num_glyphs = command->show_text_glyphs.num_glyphs;
+
+ /* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
+ * to modify the glyph array that's passed in. We must always
+ * copy the array before handing it to the backend.
+ */
+ glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unlikely (glyphs_copy == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ break;
+ }
+
+ memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+
+ status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
+ command->header.op,
+ &command->show_text_glyphs.source.base,
+ command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
+ glyphs_copy, num_glyphs,
+ command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
+ command->show_text_glyphs.cluster_flags,
+ command->show_text_glyphs.scaled_font,
+ _clip (command));
+ free (glyphs_copy);
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ if (type == CAIRO_RECORDING_CREATE_REGIONS) {
+ if (status == CAIRO_STATUS_SUCCESS) {
+ command->header.region = CAIRO_RECORDING_REGION_NATIVE;
+ } else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
+ command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK;
+ status = CAIRO_STATUS_SUCCESS;
+ } else {
+ assert (_cairo_status_is_error (status));
+ }
+ }
+
+ if (unlikely (status))
+ break;
+ }
+
+ /* free up any caches */
+ for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
+ cairo_command_t *command = elements[i];
+
+ _cairo_clip_drop_cache (&command->header.clip);
+ }
+
+ _cairo_surface_wrapper_fini (&wrapper);
+
+ return _cairo_surface_set_error (surface, 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 (surface, NULL,
+ target,
+ 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,
+ cairo_surface_t *target)
+{
+ return _cairo_recording_surface_replay_internal (surface, NULL,
+ target,
+ 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 (surface, surface_extents,
+ target,
+ 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->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 (! _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);
+}
diff --git a/gfx/cairo/cairo/src/cairo-rectangle.c b/gfx/cairo/cairo/src/cairo-rectangle.c
new file mode 100644
index 000000000..608da53ea
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-rectangle.c
@@ -0,0 +1,266 @@
+/* -*- 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+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)
+{
+ int n;
+
+ assert (num_boxes > 0);
+ *extents = *boxes;
+
+ for (n = 1; n < num_boxes; n++) {
+ if (boxes[n].p1.x < extents->p1.x)
+ extents->p1.x = boxes[n].p1.x;
+ if (boxes[n].p2.x > extents->p2.x)
+ extents->p2.x = boxes[n].p2.x;
+
+ if (boxes[n].p1.y < extents->p1.y)
+ extents->p1.y = boxes[n].p1.y;
+ if (boxes[n].p2.y > extents->p2.y)
+ extents->p2.y = boxes[n].p2.y;
+ }
+}
+
+/* This function will return 'true' if the containing_rectangle contains the
+ * contained_rectangle, and false otherwise.
+ */
+cairo_bool_t
+_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle,
+ const cairo_rectangle_int_t *contained_rectangle)
+{
+ if (containing_rectangle->x > contained_rectangle->x ||
+ containing_rectangle->y > contained_rectangle->y)
+ return FALSE;
+
+ if (containing_rectangle->x + containing_rectangle->width <
+ contained_rectangle->x + contained_rectangle->width ||
+ containing_rectangle->y + containing_rectangle->height <
+ contained_rectangle->y + contained_rectangle->height)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* 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;
+ }
+}
+
+#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 (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 > xlen || t2 < 0)
+ 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 > ylen || t4 < 0)
+ 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;
+}
+
+cairo_bool_t
+_cairo_box_contains_point (cairo_box_t *box, const cairo_point_t *point)
+{
+ if (point->x < box->p1.x || point->x > box->p2.x ||
+ point->y < box->p1.y || point->y > box->p2.y)
+ return FALSE;
+ return TRUE;
+}
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 000000000..dab2c151f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-rectangular-scan-converter.c
@@ -0,0 +1,723 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-combsort-private.h"
+#include "cairo-error-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.h"
+#include "cairo-spans-private.h"
+
+#include <setjmp.h>
+
+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++;
+ sweep->spans[n].x = prev_x;
+ sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
+ sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8;
+ prev_coverage = coverage;
+ }
+
+ coverage += cell->covered;
+ if (coverage != prev_coverage) {
+ int n = sweep->num_spans++;
+ sweep->spans[n].x = cell->x;
+ sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
+ sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8;
+ 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++;
+ sweep->spans[n].x = prev_x;
+ sweep->spans[n].coverage = coverage;
+ }
+
+ if (coverage && prev_x < sweep->xmax) {
+ int n = sweep->num_spans++;
+ sweep->spans[n].x = sweep->xmax;
+ 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 = self->xmin;
+ sweep_line.xmax = self->xmax;
+ sweep_line.start = rectangles;
+ if ((status = setjmp (sweep_line.jmpbuf)))
+ goto BAIL;
+
+ sweep_line.current_y = self->ymin;
+ 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;
+ }
+
+ sweep_line.current_y++;
+
+ do {
+ 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);
+
+ sweep_line.current_y++;
+ } while (TRUE);
+
+ out:
+ status = renderer->render_rows (renderer,
+ sweep_line.current_y,
+ self->ymax - sweep_line.current_y,
+ NULL, 0);
+
+ BAIL:
+ sweep_line_fini (&sweep_line);
+
+ return status;
+}
+
+static 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,
+ self->ymin, self->ymax - self->ymin,
+ NULL, 0);
+ }
+
+ 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->left = box->p1.x;
+ rectangle->right = box->p2.x;
+ rectangle->dir = dir;
+
+ rectangle->top = box->p1.y;
+ rectangle->top_y = _cairo_fixed_integer_floor (box->p1.y);
+ rectangle->bottom = box->p2.y;
+ rectangle->bottom_y = _cairo_fixed_integer_floor (box->p2.y);
+ assert (rectangle->bottom_y >= rectangle->top_y);
+
+ self->num_rectangles++;
+
+ 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.add_edge = NULL;
+ self->base.add_polygon = NULL;
+ self->base.generate = _cairo_rectangular_scan_converter_generate;
+
+ self->xmin = extents->x;
+ self->xmax = extents->x + extents->width;
+ self->ymin = extents->y;
+ self->ymax = extents->y + extents->height;
+
+ 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 000000000..0d91488ee
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-reference-count-private.h
@@ -0,0 +1,61 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#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_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count)
+
+#define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE))
+
+#define CAIRO_REFERENCE_COUNT_GET_VALUE(RC) _cairo_atomic_int_get (&(RC)->ref_count)
+
+#define CAIRO_REFERENCE_COUNT_INVALID_VALUE (-1)
+#define CAIRO_REFERENCE_COUNT_INVALID {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 000000000..11070ba76
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-region-private.h
@@ -0,0 +1,71 @@
+/* -*- 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 <otaylor@redhat.com>
+ * Vladimir Vukicevic <vladimir@pobox.com>
+ * Søren Sandmann <sandmann@daimi.au.dk>
+ */
+
+#ifndef CAIRO_REGION_PRIVATE_H
+#define CAIRO_REGION_PRIVATE_H
+
+#include "cairo-types-private.h"
+#include "cairo-reference-count-private.h"
+
+#include <pixman.h>
+
+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_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 000000000..112b1d824
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-region.c
@@ -0,0 +1,905 @@
+/* -*- 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 <otaylor@redhat.com>
+ * Vladimir Vukicevic <vladimir@pobox.com>
+ * Søren Sandmann <sandmann@daimi.au.dk>
+ */
+
+#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:
+ 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 (! _cairo_status_is_error (status))
+ return status;
+
+ /* Don't overwrite an existing error. This preserves the first
+ * error, which is the most significant. */
+ _cairo_status_set_error (&region->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 (&region->ref_count, 0);
+ pixman_region32_init (&region->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 (&region->ref_count, 0);
+ pixman_region32_init_rect (&region->rgn,
+ rectangle->x, rectangle->y,
+ rectangle->width, rectangle->height);
+}
+
+void
+_cairo_region_fini (cairo_region_t *region)
+{
+ assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&region->ref_count));
+ pixman_region32_fini (&region->rgn);
+ VG (VALGRIND_MAKE_MEM_NOACCESS (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 (&region->ref_count, 1);
+
+ pixman_region32_init (&region->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));
+
+ 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 (&region->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));
+ }
+
+ CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
+ region->status = CAIRO_STATUS_SUCCESS;
+ return region;
+}
+slim_hidden_def (cairo_region_create_rectangles);
+
+/**
+ * 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 (&region->ref_count, 1);
+
+ pixman_region32_init_rect (&region->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 (&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 (&region->ref_count))
+ return NULL;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&region->ref_count));
+
+ _cairo_reference_count_inc (&region->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 (&region->ref_count))
+ return;
+
+ assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&region->ref_count));
+
+ if (! _cairo_reference_count_dec_and_test (&region->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 &region->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 &region->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 &region->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 occured 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 (&region,
+ rectangle->x, rectangle->y,
+ rectangle->width, rectangle->height);
+
+ if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, &region))
+ status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+ pixman_region32_fini (&region);
+
+ 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 (&region,
+ rectangle->x, rectangle->y,
+ rectangle->width, rectangle->height);
+
+ if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, &region))
+ status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+ pixman_region32_fini (&region);
+
+ 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 (&region,
+ rectangle->x, rectangle->y,
+ rectangle->width, rectangle->height);
+
+ if (! pixman_region32_union (&dst->rgn, &dst->rgn, &region))
+ status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY);
+
+ pixman_region32_fini (&region);
+
+ 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 (&region,
+ rectangle->x, rectangle->y,
+ rectangle->width, rectangle->height);
+ pixman_region32_init (&tmp);
+
+ /* XXX: get an xor function into pixman */
+ if (! pixman_region32_subtract (&tmp, &region, &dst->rgn) ||
+ ! pixman_region32_subtract (&dst->rgn, &dst->rgn, &region) ||
+ ! 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 (&region);
+
+ 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 &region->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 (&region->rgn, dx, dy);
+}
+slim_hidden_def (cairo_region_translate);
+
+/**
+ * cairo_region_overlap_t:
+ * @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region
+ * @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region
+ * @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and
+ * partially outside the region.
+ *
+ * Used as the return value for cairo_region_contains_rectangle().
+ */
+
+/**
+ * cairo_region_contains_rectangle:
+ * @region: a #cairo_region_t
+ * @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 &region->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 &region->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 000000000..db20352ea
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-rename.h
@@ -0,0 +1,411 @@
+#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_allow_manual_show_glyphs _moz_cairo_dwrite_scaled_font_allow_manual_show_glyphs
+#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_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_create_for_data _moz_cairo_quartz_surface_create_for_data
+#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_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_alpha _moz_cairo_win32_surface_create_with_alpha
+#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_height _moz_cairo_win32_surface_get_height
+#define cairo_win32_surface_get_image _moz_cairo_win32_surface_get_image
+#define cairo_win32_surface_get_width _moz_cairo_win32_surface_get_width
+#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 000000000..191c85871
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-rtree-private.h
@@ -0,0 +1,134 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ */
+
+#ifndef CAIRO_RTREE_PRIVATE_H
+#define CAIRO_RTREE_PRIVATE_H
+
+#include "cairo-compiler-private.h"
+#include "cairo-types-private.h"
+
+#include "cairo-freelist-private.h"
+#include "cairo-list-private.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;
+ void **owner;
+ 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;
+ 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);
+
+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);
+
+static inline void *
+_cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+ 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 000000000..d6e57916a
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-rtree.c
@@ -0,0 +1,385 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ *
+ */
+
+#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->owner = NULL;
+ 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) {
+ if (node->owner != NULL)
+ *node->owner = NULL;
+ } 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);
+
+ 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_rtree_node_t *node, *next;
+ int i, cnt;
+
+ /* propagate pinned from children to root */
+ cairo_list_foreach_entry_safe (node, next, cairo_rtree_node_t,
+ &rtree->pinned, link)
+ {
+ if (node->parent != NULL)
+ _cairo_rtree_pin (rtree, node->parent);
+ }
+
+ 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)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ 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) {
+ if (node->owner != NULL)
+ *node->owner = NULL;
+ } 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;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+void
+_cairo_rtree_unpin (cairo_rtree_t *rtree)
+{
+ cairo_rtree_node_t *node, *next;
+ cairo_list_t can_collapse;
+
+ if (cairo_list_is_empty (&rtree->pinned))
+ return;
+
+ cairo_list_init (&can_collapse);
+
+ cairo_list_foreach_entry_safe (node, next,
+ cairo_rtree_node_t,
+ &rtree->pinned,
+ link)
+ {
+ node->pinned = FALSE;
+ if (node->state == CAIRO_RTREE_NODE_OCCUPIED && node->owner == NULL) {
+ cairo_bool_t all_available;
+ int i;
+
+ node->state = CAIRO_RTREE_NODE_AVAILABLE;
+ cairo_list_move (&node->link, &rtree->available);
+
+ all_available = TRUE;
+ node = node->parent;
+ for (i = 0; i < 4 && node->children[i] != NULL && all_available; i++)
+ all_available &= node->children[i]->state == CAIRO_RTREE_NODE_AVAILABLE;
+
+ if (all_available) {
+ cairo_list_move (&node->link, &can_collapse);
+ for (i = 0; i < 4 && node->children[i] != NULL; i++)
+ cairo_list_del (&node->children[i]->link);
+ }
+ }
+ else
+ {
+ cairo_list_move (&node->link, &rtree->evictable);
+ }
+ }
+
+ cairo_list_foreach_entry_safe (node, next,
+ cairo_rtree_node_t,
+ &can_collapse,
+ link)
+ {
+ _cairo_rtree_node_collapse (rtree, node);
+ }
+}
+
+void
+_cairo_rtree_init (cairo_rtree_t *rtree,
+ int width,
+ int height,
+ int min_size,
+ int node_size)
+{
+ 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;
+
+ 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) {
+ if (rtree->root.owner != NULL)
+ *rtree->root.owner = NULL;
+ } 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);
+}
+
+void
+_cairo_rtree_fini (cairo_rtree_t *rtree)
+{
+ int i;
+
+ if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
+ if (rtree->root.owner != NULL)
+ *rtree->root.owner = NULL;
+ } 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 000000000..029377b17
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-scaled-font-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 <cworth@cworth.org>
+ */
+
+#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"
+
+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;
+
+ /*
+ * One surface backend may store data in each glyph.
+ * Whichever surface manages to store its pointer here
+ * first gets to store data in each glyph
+ */
+ const cairo_surface_backend_t *surface_backend;
+ void *surface_private;
+
+ /* font backend managing this scaled font */
+ const cairo_scaled_font_backend_t *backend;
+ cairo_list_t link;
+};
+
+cairo_private void
+_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font);
+
+#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 000000000..b165d9aca
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-scaled-font-subsets-private.h
@@ -0,0 +1,668 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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;
+ 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_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_status_t
+(*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t *font_subset,
+ void *closure);
+
+/**
+ * _cairo_scaled_font_subsets_foreach:
+ * @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 *font_name;
+ 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_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 *font_name;
+ 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:
+ * @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.
+ *
+ * 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 (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);
+
+
+
+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;
+
+
+#if CAIRO_HAS_FT_FONT
+
+/**
+ * _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);
+
+#endif /* CAIRO_HAS_FT_FONT */
+
+
+/**
+ * _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_hexencode:
+ * @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_create_glyph_to_unicode_map:
+ * @font_subset: the #cairo_scaled_font_subset_t to initialize from
+ *
+ * If possible (depending on the format of the underlying
+ * #cairo_scaled_font_t and the font backend in use) assign
+ * the unicode character of each glyph in font_subset to
+ * fontsubset->to_unicode.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if successful,
+ * %CAIRO_INT_STATUS_UNSUPPORTED if the unicode encoding of
+ * the glyphs is not available. Possible errors include
+ * %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_private cairo_int_status_t
+_cairo_truetype_create_glyph_to_unicode_map (cairo_scaled_font_subset_t *font_subset);
+
+/**
+ * _cairo_truetype_index_to_ucs4:
+ * @scaled_font: the #cairo_scaled_font_t
+ * @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);
+
+#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 000000000..01bc05bfb
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c
@@ -0,0 +1,1091 @@
+/* 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 <cworth@cworth.org>
+ * Kristian Høgsberg <krh@redhat.com>
+ * Keith Packard <keithp@keithp.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#define _BSD_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_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 max_glyphs_per_subset;
+
+ 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;
+
+ 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_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;
+ 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_bool_t
+_cairo_sub_font_glyphs_equal (const void *key_a, const void *key_b)
+{
+ const cairo_sub_font_glyph_t *sub_font_glyph_a = key_a;
+ const cairo_sub_font_glyph_t *sub_font_glyph_b = key_b;
+
+ return sub_font_glyph_a->base.hash == sub_font_glyph_b->base.hash;
+}
+
+static cairo_sub_font_glyph_t *
+_cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index,
+ unsigned int subset_id,
+ unsigned int subset_glyph_index,
+ double x_advance,
+ double y_advance)
+{
+ cairo_sub_font_glyph_t *sub_font_glyph;
+
+ sub_font_glyph = 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_mapped = FALSE;
+ sub_font_glyph->unicode = -1;
+ sub_font_glyph->utf8 = NULL;
+ sub_font_glyph->utf8_len = 0;
+
+ return sub_font_glyph;
+}
+
+static void
+_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph)
+{
+ if (sub_font_glyph->utf8 != NULL)
+ free (sub_font_glyph->utf8);
+
+ free (sub_font_glyph);
+}
+
+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;
+ 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;
+ cairo_status_t status;
+ cairo_scaled_font_subsets_glyph_t subset_glyph;
+
+ sub_font = 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);
+ _cairo_sub_font_init_key (sub_font, scaled_font);
+
+ sub_font->parent = parent;
+ sub_font->scaled_font = scaled_font;
+ sub_font->font_id = font_id;
+
+ sub_font->current_subset = 0;
+ sub_font->num_glyphs_in_current_subset = 0;
+ sub_font->max_glyphs_per_subset = max_glyphs_per_subset;
+
+ sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal);
+ if (unlikely (sub_font->sub_font_glyphs == NULL)) {
+ free (sub_font);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+ sub_font->next = NULL;
+
+ /* Reserve first glyph in subset for the .notdef glyph except for
+ * Type 3 fonts */
+ if (! is_scaled) {
+ status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph);
+ if (unlikely (status)) {
+ _cairo_hash_table_destroy (sub_font->sub_font_glyphs);
+ free (sub_font);
+ return status;
+ }
+ }
+
+ *sub_font_out = sub_font;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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);
+}
+
+static cairo_status_t
+_cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
+ cairo_scaled_font_t *scaled_font,
+ unsigned long scaled_font_glyph_index)
+{
+ 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;
+ }
+
+ sub_font_glyph->unicode = unicode;
+ sub_font_glyph->utf8 = NULL;
+ sub_font_glyph->utf8_len = 0;
+ if (unicode != (uint32_t) -1) {
+ len = _cairo_ucs4_to_utf8 (unicode, buf);
+ if (len > 0) {
+ sub_font_glyph->utf8 = malloc (len + 1);
+ if (unlikely (sub_font_glyph->utf8 == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (sub_font_glyph->utf8, buf, len);
+ sub_font_glyph->utf8[len] = 0;
+ sub_font_glyph->utf8_len = len;
+ }
+ }
+
+ 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 = 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;
+ 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->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_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)
+{
+ 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) {
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ if (sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset)
+ {
+ cairo_scaled_font_subsets_glyph_t tmp_subset_glyph;
+
+ sub_font->current_subset++;
+ sub_font->num_glyphs_in_current_subset = 0;
+
+ /* Reserve first glyph in subset for the .notdef glyph
+ * except for Type 3 fonts */
+ if (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) {
+ status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ _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;
+ }
+
+ sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index,
+ sub_font->current_subset,
+ sub_font->num_glyphs_in_current_subset,
+ scaled_glyph->metrics.x_advance,
+ scaled_glyph->metrics.y_advance);
+ _cairo_scaled_font_thaw_cache (sub_font->scaled_font);
+
+ if (unlikely (sub_font_glyph == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph,
+ sub_font->scaled_font,
+ scaled_font_glyph_index);
+ if (unlikely (status)) {
+ _cairo_sub_font_glyph_destroy (sub_font_glyph);
+ return status;
+ }
+
+ status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base);
+ if (unlikely (status)) {
+ _cairo_sub_font_glyph_destroy (sub_font_glyph);
+ return status;
+ }
+
+ sub_font->num_glyphs_in_current_subset++;
+
+ if (sub_font->is_scaled) {
+ if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_scaled_subset_used)
+ sub_font->parent->max_glyphs_per_scaled_subset_used = sub_font->num_glyphs_in_current_subset;
+ } else {
+ if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_unscaled_subset_used)
+ sub_font->parent->max_glyphs_per_unscaled_subset_used = sub_font->num_glyphs_in_current_subset;
+ }
+ }
+
+ subset_glyph->font_id = sub_font->font_id;
+ subset_glyph->subset_id = sub_font_glyph->subset_id;
+ subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
+ subset_glyph->is_scaled = sub_font->is_scaled;
+ subset_glyph->is_composite = sub_font->is_composite;
+ subset_glyph->x_advance = sub_font_glyph->x_advance;
+ subset_glyph->y_advance = sub_font_glyph->y_advance;
+ 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;
+}
+
+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;
+
+ _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;
+
+ /* 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;
+ /* No need to check for out of memory here. If to_unicode is NULL, the PDF
+ * surface does not emit an ToUnicode stream */
+ subset.to_unicode = _cairo_malloc_ab (collection->num_glyphs, sizeof (unsigned long));
+ if (subset.to_unicode) {
+ for (j = 0; j < collection->num_glyphs; j++) {
+ /* default unicode character required when mapping fails */
+ subset.to_unicode[j] = 0xfffd;
+ }
+ }
+ collection->status = (collection->font_subset_callback) (&subset,
+ collection->font_subset_callback_closure);
+
+ if (subset.to_unicode != NULL)
+ free (subset.to_unicode);
+
+ if (subset.glyph_names != NULL) {
+ for (j = 0; j < collection->num_glyphs; j++)
+ free (subset.glyph_names[j]);
+ 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 = malloc (sizeof (cairo_scaled_font_subsets_t));
+ if (unlikely (subsets == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return NULL;
+ }
+
+ subsets->type = type;
+ 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);
+}
+
+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_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_status_is_error (status))
+ return status;
+
+ if (status == CAIRO_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 = FALSE;
+#if CAIRO_HAS_FT_FONT
+ type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font);
+#endif
+ if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) {
+ max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT;
+ subset_glyph->is_composite = TRUE;
+ } 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 *));
+ if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL)) {
+ if (collection.glyphs != NULL)
+ free (collection.glyphs);
+ if (collection.utf8 != NULL)
+ free (collection.utf8);
+
+ 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);
+
+ 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;
+}
+
+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 = 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 (unlikely (status))
+ goto CLEANUP_HASH;
+ }
+
+ if (utf16_len == 1) {
+ 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);
+ }
+ if (utf16)
+ 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++) {
+ if (subset->glyph_names[i] != NULL)
+ free (subset->glyph_names[i]);
+ }
+
+ free (subset->glyph_names);
+ subset->glyph_names = NULL;
+ }
+
+ 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 100644
index 000000000..95db65a99
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-scaled-font.c
@@ -0,0 +1,2990 @@
+/* -*- 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 <keithp@keithp.com>
+ * Carl D. Worth <cworth@cworth.org>
+ * Graydon Hoare <graydon@redhat.com>
+ * Owen Taylor <otaylor@redhat.com>
+ * Behdad Esfahbod <behdad@behdad.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+#include "cairo-error-private.h"
+#include "cairo-scaled-font-private.h"
+
+#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
+#define ISFINITE(x) isfinite (x)
+#else
+#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */
+#endif
+
+/**
+ * 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.
+ */
+
+/* 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 256
+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_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)
+{
+ const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend;
+
+ if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL)
+ surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font);
+
+ if (scaled_glyph->surface != NULL)
+ cairo_surface_destroy (&scaled_glyph->surface->base);
+
+ if (scaled_glyph->path != NULL)
+ _cairo_path_fixed_destroy (scaled_glyph->path);
+
+ if (scaled_glyph->recording_surface != NULL) {
+ cairo_surface_finish (scaled_glyph->recording_surface);
+ cairo_surface_destroy (scaled_glyph->recording_surface);
+ }
+}
+
+#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, /* surface_backend */
+ NULL, /* surface_private */
+ 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.
+ **/
+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 = 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 (void *closure)
+{
+ cairo_scaled_glyph_page_t *page = closure;
+ cairo_scaled_font_t *scaled_font;
+ unsigned int n;
+
+ scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
+ for (n = 0; n < page->num_glyphs; n++) {
+ _cairo_hash_table_remove (scaled_font->glyphs,
+ &page->glyphs[n].hash_entry);
+ _cairo_scaled_glyph_fini (scaled_font, &page->glyphs[n]);
+ }
+
+ cairo_list_del (&page->link);
+
+ free (page);
+}
+
+/* If a scaled font wants to unlock the font map while still being
+ * created (needed for user-fonts), we need to take extra care not
+ * 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 = 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;
+
+ 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);
+
+ 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 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)
+{
+ uint32_t hash = FNV1_32_INIT;
+
+ scaled_font->status = CAIRO_STATUS_SUCCESS;
+ scaled_font->placeholder = FALSE;
+ scaled_font->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);
+
+ /* We do a bytewise hash on the font matrices */
+ hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash);
+ hash = _hash_matrix_fnv (&scaled_font->ctm, hash);
+ hash = _hash_mix_bits (hash);
+
+ hash ^= (uintptr_t) scaled_font->font_face;
+ hash ^= cairo_font_options_hash (&scaled_font->options);
+
+ /* final mixing of bits */
+ hash = _hash_mix_bits (hash);
+
+ assert (hash != ZOMBIE);
+ scaled_font->hash_entry.hash = hash;
+}
+
+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;
+
+ if (key_a->hash_entry.hash != key_b->hash_entry.hash)
+ return FALSE;
+
+ return key_a->font_face == key_b->font_face &&
+ memcmp ((unsigned char *)(&key_a->font_matrix.xx),
+ (unsigned char *)(&key_b->font_matrix.xx),
+ sizeof(cairo_matrix_t)) == 0 &&
+ memcmp ((unsigned char *)(&key_a->ctm.xx),
+ (unsigned char *)(&key_b->ctm.xx),
+ sizeof(cairo_matrix_t)) == 0 &&
+ cairo_font_options_equal (&key_a->options, &key_b->options);
+}
+
+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);
+}
+
+static cairo_bool_t
+_cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b)
+{
+ const cairo_scaled_glyph_t *a = abstract_a;
+ const cairo_scaled_glyph_t *b = abstract_b;
+
+ return a->hash_entry.hash == b->hash_entry.hash;
+}
+
+/*
+ * Basic #cairo_scaled_font_t object management
+ */
+
+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;
+
+ _cairo_scaled_font_init_key (scaled_font, font_face,
+ font_matrix, ctm, 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 (_cairo_scaled_glyphs_equal);
+ 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);
+
+ scaled_font->surface_backend = NULL;
+ scaled_font->surface_private = NULL;
+
+ 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)
+{
+ scaled_font->cache_frozen = FALSE;
+
+ if (scaled_font->global_cache_frozen) {
+ CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+ _cairo_cache_thaw (&cairo_scaled_glyph_page_cache);
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+
+ scaled_font->global_cache_frozen = FALSE;
+ }
+
+ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+}
+
+void
+_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
+{
+ assert (! scaled_font->cache_frozen);
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+ while (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
+ _cairo_cache_remove (&cairo_scaled_glyph_page_cache,
+ &cairo_list_first_entry (&scaled_font->glyph_pages,
+ cairo_scaled_glyph_page_t,
+ link)->cache_entry);
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_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)
+{
+ 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);
+
+ if (scaled_font->surface_backend != NULL &&
+ scaled_font->surface_backend->scaled_font_fini != NULL)
+ scaled_font->surface_backend->scaled_font_fini (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);
+}
+
+/* XXX: allow multiple backends to share the font */
+void
+_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font)
+{
+ if (scaled_font->surface_backend == NULL)
+ return;
+
+ _cairo_scaled_font_reset_cache (scaled_font);
+
+ if (scaled_font->surface_backend->scaled_font_fini != NULL)
+ scaled_font->surface_backend->scaled_font_fini (scaled_font);
+
+ scaled_font->surface_backend = NULL;
+ scaled_font->surface_private = NULL;
+}
+
+void
+_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+ /* 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);
+}
+
+/**
+ * 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()
+ **/
+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;
+
+ if (font_face->backend->get_implementation != NULL) {
+ font_face = font_face->backend->get_implementation (font_face,
+ font_matrix,
+ ctm,
+ options);
+ if (unlikely (font_face->status)) {
+ _cairo_scaled_font_map_unlock ();
+ cairo_scaled_font_destroy (scaled_font);
+ return _cairo_scaled_font_create_in_error (font_face->status);
+ }
+ }
+
+ _cairo_scaled_font_init_key (&key, font_face,
+ font_matrix, ctm, options);
+ }
+ else
+ {
+ 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);
+ }
+ }
+
+ _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);
+ }
+
+ /* Return existing scaled_font if it exists in the hash table. */
+ if (scaled_font != NULL) {
+ /* If the original reference count is 0, then this font must have
+ * been found in font_map->holdovers, (which means this caching is
+ * actually working). So now we remove it from the holdovers
+ * array, unless we caught the font in the middle of destruction.
+ */
+ if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
+ if (scaled_font->holdover) {
+ int i;
+
+ for (i = 0; i < font_map->num_holdovers; i++) {
+ if (font_map->holdovers[i] == scaled_font) {
+ font_map->num_holdovers--;
+ memmove (&font_map->holdovers[i],
+ &font_map->holdovers[i+1],
+ (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*));
+ break;
+ }
+ }
+
+ 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. */
+ 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);
+
+ scaled_font->original_font_face =
+ cairo_font_face_reference (original_font_face);
+
+ 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 = 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++)
+ {
+ if (_cairo_scaled_font_nil_objects[status] != NULL) {
+ free (_cairo_scaled_font_nil_objects[status]);
+ _cairo_scaled_font_nil_objects[status] = NULL;
+ }
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
+
+ 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.
+ *
+ * The number of references to a #cairo_scaled_font_t can be get using
+ * cairo_scaled_font_get_reference_count().
+ *
+ * Returns: the referenced #cairo_scaled_font_t
+ **/
+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().
+ **/
+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));
+
+ if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count))
+ return;
+
+ font_map = _cairo_scaled_font_map_lock ();
+ assert (font_map != NULL);
+
+ /* 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.
+ **/
+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).
+ **/
+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)) {
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+ 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:
+ * <informalexample><programlisting>
+ * 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,
+ * &amp;glyphs, &amp;num_glyphs,
+ * &amp;clusters, &amp;num_clusters, &amp;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);
+ * }
+ * </programlisting></informalexample>
+ *
+ * If no cluster mapping is needed:
+ * <informalexample><programlisting>
+ * 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,
+ * &amp;glyphs, &amp;num_glyphs,
+ * NULL, NULL,
+ * NULL);
+ *
+ * if (status == CAIRO_STATUS_SUCCESS) {
+ * cairo_show_glyphs (cr, glyphs, num_glyphs);
+ * cairo_glyph_free (glyphs);
+ * }
+ * </programlisting></informalexample>
+ *
+ * If stack-based glyph and cluster arrays are to be used for small
+ * arrays:
+ * <informalexample><programlisting>
+ * 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,
+ * &amp;glyphs, &amp;num_glyphs,
+ * &amp;clusters, &amp;num_clusters, &amp;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);
+ * }
+ * </programlisting></informalexample>
+ *
+ * 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_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_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 && *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 && *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)
+{
+ return right > extents->p1.x &&
+ left < extents->p2.x &&
+ bottom > extents->p1.y &&
+ top < extents->p2.y;
+}
+
+/*
+ * 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;
+
+ _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;
+}
+
+void
+_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font,
+ const cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_rectangle_int_t *extents)
+{
+ double x0 = HUGE_VAL, x1 = -HUGE_VAL;
+ double y0 = HUGE_VAL, y1 = -HUGE_VAL;
+ int i;
+
+ for (i = 0; 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;
+ }
+
+ if (x0 <= x1 && y0 <= y1) {
+ extents->x = floor (x0 - scaled_font->extents.max_x_advance);
+ extents->width = ceil (x1 + scaled_font->extents.max_x_advance);
+ extents->width -= extents->x;
+
+ extents->y = floor (y0 - scaled_font->extents.ascent);
+ extents->height = ceil (y1 + scaled_font->extents.descent);
+ extents->height -= extents->y;
+ } else {
+ extents->x = extents->y = 0;
+ extents->width = extents->height = 0;
+ }
+}
+
+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_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_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_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);
+}
+
+/* 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_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_STATUS_SUCCESS) {
+ status = _cairo_path_fixed_append (path,
+ scaled_glyph->path, CAIRO_DIRECTION_FORWARD,
+ _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;
+}
+
+static cairo_bool_t
+_cairo_scaled_glyph_page_can_remove (const void *closure)
+{
+ const cairo_scaled_glyph_page_t *page = closure;
+ const cairo_scaled_font_t *scaled_font;
+
+ scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
+ return scaled_font->cache_frozen == 0;
+}
+
+static cairo_status_t
+_cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t **scaled_glyph)
+{
+ cairo_scaled_glyph_page_t *page;
+ cairo_status_t status;
+
+ /* only the first page in the list may contain available slots */
+ 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 = malloc (sizeof (cairo_scaled_glyph_page_t));
+ if (unlikely (page == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ page->cache_entry.hash = (uintptr_t) scaled_font;
+ page->cache_entry.size = 1; /* XXX occupancy weighting? */
+ page->num_glyphs = 0;
+
+ CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+ if (scaled_font->global_cache_frozen == FALSE) {
+ if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) {
+ status = _cairo_cache_init (&cairo_scaled_glyph_page_cache,
+ NULL,
+ _cairo_scaled_glyph_page_can_remove,
+ _cairo_scaled_glyph_page_destroy,
+ MAX_GLYPH_PAGES_CACHED);
+ if (unlikely (status)) {
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+ free (page);
+ return status;
+ }
+ }
+
+ _cairo_cache_freeze (&cairo_scaled_glyph_page_cache);
+ scaled_font->global_cache_frozen = TRUE;
+ }
+
+ status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache,
+ &page->cache_entry);
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+ if (unlikely (status)) {
+ free (page);
+ return status;
+ }
+
+ 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 (! 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_cache_remove (&cairo_scaled_glyph_page_cache,
+ &page->cache_entry);
+ }
+}
+
+/**
+ * _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_status_t status = CAIRO_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;
+
+ 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);
+
+ /* 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 is the
+ * font face passed to cairo_scaled_font_create().
+ *
+ * Return value: The #cairo_font_face_t with which @scaled_font was
+ * created.
+ *
+ * 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);
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 000000000..2cb427bcb
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-script-surface.c
@@ -0,0 +1,3622 @@
+/* -*- 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 <chris@chris-wilson.co.uk>
+ */
+
+/* The script surface is one that records all operations performed on
+ * it in the form of a procedural script, similar in fashion to
+ * PostScript but using Cairo's imaging model. In essence, this is
+ * equivalent to the 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.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-script.h"
+
+#include "cairo-analysis-surface-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-list-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-surface-snapshot-private.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 <ctype.h>
+
+#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_surface_font_private cairo_script_surface_font_private_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;
+
+ 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_surface_font_private {
+ cairo_script_context_t *ctx;
+ 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,
+ double width,
+ double height,
+ cairo_surface_t *passthrough);
+
+static void
+_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+static void
+_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr);
+
+static void
+_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; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) {
+ if ((b->map[n] & bit) == 0) {
+ b->map[n] |= bit;
+ b->count++;
+ *id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+ }
+ }
+ min += sizeof (b->map) * CHAR_BIT;
+
+ prev = &b->next;
+ b = b->next;
+ } while (b != NULL);
+
+ bb = malloc (sizeof (struct _bitmap));
+ if (unlikely (bb == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ *prev = bb;
+ bb->next = b;
+ bb->min = min;
+ bb->count = 1;
+ bb->map[0] = 0x1;
+ memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0]));
+ *id = min;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static 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 */
+ "ExtendMode::REPEAT", /* CAIRO_EXTEND_REPEAT */
+ "ExtendMode::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 */
+ "SamplingFilter::GOOD", /* CAIRO_FILTER_GOOD */
+ "FILTER_BEST", /* CAIRO_FILTER_BEST */
+ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */
+ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */
+ "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */
+ };
+ assert (filter < ARRAY_LENGTH (names));
+ return names[filter];
+}
+
+static const char *
+_fill_rule_to_string (cairo_fill_rule_t rule)
+{
+ static const char *names[] = {
+ "WINDING", /* CAIRO_FILL_RULE_WINDING */
+ "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */
+ };
+ assert (rule < ARRAY_LENGTH (names));
+ return names[rule];
+}
+
+static const char *
+_antialias_to_string (cairo_antialias_t antialias)
+{
+ static const char *names[] = {
+ "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */
+ "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */
+ "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */
+ "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */
+ };
+ assert (antialias < ARRAY_LENGTH (names));
+ return names[antialias];
+}
+
+static const char *
+_line_cap_to_string (cairo_line_cap_t line_cap)
+{
+ static const char *names[] = {
+ "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */
+ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */
+ "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */
+ };
+ assert (line_cap < ARRAY_LENGTH (names));
+ return names[line_cap];
+}
+
+static const char *
+_line_join_to_string (cairo_line_join_t line_join)
+{
+ static const char *names[] = {
+ "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */
+ "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */
+ "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */
+ };
+ assert (line_join < ARRAY_LENGTH (names));
+ return names[line_join];
+}
+
+static 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 (surface->defined) {
+ _cairo_output_stream_printf (ctx->stream, "s%u ",
+ surface->base.unique_id);
+ } else {
+ assert (! cairo_list_is_empty (&surface->operand.link));
+ if (! target_is_active (surface)) {
+ int depth = target_depth (surface);
+ if (ctx->active) {
+ _cairo_output_stream_printf (ctx->stream, "%d index ", depth);
+ _cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
+ } else {
+ if (depth == 1) {
+ _cairo_output_stream_puts (ctx->stream,
+ "exch\n");
+ } else {
+ _cairo_output_stream_printf (ctx->stream,
+ "%d -1 roll\n",
+ depth);
+ }
+ _cairo_output_stream_puts (ctx->stream, "/target get ");
+ }
+ } else {
+ _cairo_output_stream_puts (ctx->stream, "/target get ");
+ }
+ }
+}
+
+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 {
+ if (surface->cr.current_style.dash != NULL) {
+ free (surface->cr.current_style.dash);
+ surface->cr.current_style.dash = NULL;
+ }
+ }
+
+ surface->cr.current_style.num_dashes = num_dashes;
+ surface->cr.current_style.dash_offset = offset;
+
+ _cairo_output_stream_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_ARGB32: return "ARGB32";
+ 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",
+ _cairo_fixed_to_double (linear->p1.x),
+ _cairo_fixed_to_double (linear->p1.y),
+ _cairo_fixed_to_double (linear->p2.x),
+ _cairo_fixed_to_double (linear->p2.y));
+ return _emit_gradient_color_stops (&linear->base, 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",
+ _cairo_fixed_to_double (radial->c1.x),
+ _cairo_fixed_to_double (radial->c1.y),
+ _cairo_fixed_to_double (radial->r1),
+ _cairo_fixed_to_double (radial->c2.x),
+ _cairo_fixed_to_double (radial->c2.y),
+ _cairo_fixed_to_double (radial->r2));
+ return _emit_gradient_color_stops (&radial->base, ctx->stream);
+}
+
+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_surface_t *similar;
+ cairo_status_t status;
+ cairo_box_t bbox;
+ cairo_rectangle_int_t rect;
+
+ /* first measure the extents */
+ status = _cairo_recording_surface_get_bbox (source, &bbox, NULL);
+ if (unlikely (status))
+ return status;
+
+ /* convert to extents so that it matches the public api */
+ _cairo_box_round_to_rectangle (&bbox, &rect);
+
+ similar = _cairo_script_surface_create_internal (to_context (surface),
+ source->content,
+ rect.width,
+ rect.height,
+ NULL);
+ if (unlikely (similar->base.status))
+ return similar->base.status;
+
+ cairo_surface_set_device_offset (&similar->base, -rect.x, -rect.y);
+ similar->base.is_clear = TRUE;
+
+ _get_target (surface);
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "%d %d //%s similar dup context\n",
+ rect.width, rect.height,
+ _content_to_string (source->content));
+ 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 (to_context (surface)->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 stride, row, width;
+ uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE];
+ uint8_t *rowdata;
+ uint8_t *data;
+
+ stride = image->stride;
+ width = image->width;
+ data = image->data;
+#if WORDS_BIGENDIAN
+ switch (image->format) {
+ case CAIRO_FORMAT_A1:
+ for (row = image->height; row--; ) {
+ _cairo_output_stream_write (output, data, (width+7)/8);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_A8:
+ for (row = image->height; row--; ) {
+ _cairo_output_stream_write (output, data, width);
+ data += stride;
+ }
+ break;
+ case CAIRO_FORMAT_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 = 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_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_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;
+}
+
+struct def {
+ cairo_script_context_t *ctx;
+ cairo_user_data_array_t *user_data;
+ unsigned int tag;
+ cairo_list_t link;
+};
+
+static void
+_undef (void *data)
+{
+ struct def *def = data;
+
+ cairo_list_del (&def->link);
+ _cairo_output_stream_printf (def->ctx->stream, "/s%u undef\n", def->tag);
+ free (def);
+}
+
+static cairo_status_t
+_emit_image_surface (cairo_script_surface_t *surface,
+ cairo_image_surface_t *image)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_output_stream_t *base85_stream;
+ cairo_output_stream_t *zlib_stream;
+ cairo_status_t status, status2;
+ const uint8_t *mime_data;
+ unsigned long mime_data_length;
+ struct def *tag;
+
+ if (_cairo_user_data_array_get_data (&image->base.user_data,
+ (cairo_user_data_key_t *) ctx))
+ {
+ _cairo_output_stream_printf (ctx->stream,
+ "s%u ",
+ image->base.unique_id);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _emit_png_surface (surface, image);
+ if (_cairo_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_ARGB32:
+ len = clone->width * 4;
+ break;
+ case CAIRO_FORMAT_INVALID:
+ ASSERT_NOT_REACHED;
+ 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_STATUS_SUCCESS)
+ status = status2;
+ status2 = _cairo_output_stream_destroy (base85_stream);
+ if (status == CAIRO_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_STATUS_SUCCESS)
+ status = status2;
+ if (unlikely (status))
+ return status;
+ }
+ _cairo_output_stream_puts (ctx->stream, "~> >> image ");
+
+ cairo_surface_destroy (&clone->base);
+ }
+
+ tag = malloc (sizeof (*tag));
+ if (unlikely (tag == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ tag->ctx = ctx;
+ tag->tag = image->base.unique_id;
+ tag->user_data = &image->base.user_data;
+ cairo_list_add (&tag->link, &ctx->defines);
+ status = _cairo_user_data_array_set_data (&image->base.user_data,
+ (cairo_user_data_key_t *) ctx,
+ tag, _undef);
+ if (unlikely (status)) {
+ free (tag);
+ return status;
+ }
+
+ _cairo_output_stream_printf (ctx->stream,
+ "dup /s%u exch def ",
+ image->base.unique_id);
+
+ cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG,
+ &mime_data, &mime_data_length);
+ if (mime_data != NULL) {
+ _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_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_image_surface_pattern (cairo_script_surface_t *surface,
+ cairo_surface_t *source)
+{
+ cairo_surface_t *snapshot;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ void *extra;
+
+ /* XXX keeping a copy is nasty, but we want to hook into the surface's
+ * lifetime. Using a snapshot is a convenient method.
+ */
+ snapshot = _cairo_surface_snapshot (source);
+ status = _cairo_surface_acquire_source_image (snapshot, &image, &extra);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _emit_image_surface (surface, image);
+ _cairo_surface_release_source_image (snapshot, image, extra);
+ }
+ cairo_surface_destroy (snapshot);
+
+ return status;
+}
+
+static cairo_status_t
+_emit_subsurface_pattern (cairo_script_surface_t *surface,
+ cairo_surface_subsurface_t *sub)
+{
+ cairo_surface_t *source = sub->target;
+ cairo_status_t status;
+
+ 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_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_surface_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_surface_pattern_t *surface_pattern;
+ cairo_surface_t *source;
+ cairo_status_t status;
+
+ surface_pattern = (cairo_surface_pattern_t *) pattern;
+ source = surface_pattern->surface;
+
+ if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
+ source = ((cairo_surface_snapshot_t *) source)->target;
+
+ 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;
+ }
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_puts (to_context (surface)->stream, "pattern");
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_pattern (cairo_script_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_status_t status;
+ cairo_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_SURFACE:
+ status = _emit_surface_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_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_identity (cairo_script_surface_t *surface,
+ cairo_bool_t *matrix_updated)
+{
+ assert (target_is_active (surface));
+
+ if (_cairo_matrix_is_identity (&surface->cr.current_ctm))
+ return CAIRO_STATUS_SUCCESS;
+
+ _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_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_source (cairo_script_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source)
+{
+ cairo_bool_t matrix_updated = FALSE;
+ cairo_status_t status;
+
+ assert (target_is_active (surface));
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ /* the source is ignored, so don't change it */
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (_cairo_pattern_equal (&surface->cr.current_source.base, source))
+ return CAIRO_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_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_move_to (void *closure,
+ const cairo_point_t *point)
+{
+ _cairo_output_stream_printf (closure,
+ " %f %f m",
+ _cairo_fixed_to_double (point->x),
+ _cairo_fixed_to_double (point->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_line_to (void *closure,
+ const cairo_point_t *point)
+{
+ _cairo_output_stream_printf (closure,
+ " %f %f l",
+ _cairo_fixed_to_double (point->x),
+ _cairo_fixed_to_double (point->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_curve_to (void *closure,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ const cairo_point_t *p3)
+{
+ _cairo_output_stream_printf (closure,
+ " %f %f %f %f %f %f c",
+ _cairo_fixed_to_double (p1->x),
+ _cairo_fixed_to_double (p1->y),
+ _cairo_fixed_to_double (p2->x),
+ _cairo_fixed_to_double (p2->y),
+ _cairo_fixed_to_double (p3->x),
+ _cairo_fixed_to_double (p3->y));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_path_close (void *closure)
+{
+ _cairo_output_stream_printf (closure,
+ " h");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_path (cairo_script_surface_t *surface,
+ cairo_path_fixed_t *path)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_box_t box;
+ cairo_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);
+ } else if (_cairo_path_fixed_is_box (path, &box)) {
+ double x1 = _cairo_fixed_to_double (box.p1.x);
+ double y1 = _cairo_fixed_to_double (box.p1.y);
+ double x2 = _cairo_fixed_to_double (box.p2.x);
+ double y2 = _cairo_fixed_to_double (box.p2.y);
+
+ status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (ctx->stream,
+ " %f %f %f %f rectangle",
+ x1, y1, x2 - x1, y2 - y1);
+ } else {
+ cairo_status_t status;
+
+ status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _path_move_to,
+ _path_line_to,
+ _path_curve_to,
+ _path_close,
+ ctx->stream);
+ if (unlikely (status))
+ return status;
+ }
+
+ _cairo_output_stream_puts (ctx->stream, "\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+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_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;
+ }
+ }
+
+ surface = _cairo_script_surface_create_internal (ctx,
+ content,
+ width, height,
+ 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 void
+_device_flush (void *abstract_device)
+{
+ cairo_script_context_t *ctx = abstract_device;
+ cairo_status_t status;
+
+ status = _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_surface_font_private_t *font;
+
+ font = cairo_list_first_entry (&ctx->fonts,
+ cairo_script_surface_font_private_t,
+ link);
+ cairo_list_del (&font->link);
+ if (font->parent->surface_private == font)
+ font->parent->surface_private = NULL;
+ free (font);
+ }
+
+ while (! cairo_list_is_empty (&ctx->defines)) {
+ struct def *def = cairo_list_first_entry (&ctx->defines,
+ struct def, link);
+
+ status = _cairo_user_data_array_set_data (def->user_data,
+ (cairo_user_data_key_t *) ctx,
+ NULL, NULL);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ }
+
+ _bitmap_fini (ctx->surface_id.next);
+ _bitmap_fini (ctx->font_id.next);
+
+ status = _cairo_output_stream_destroy (ctx->stream);
+
+ free (ctx);
+}
+
+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);
+
+ if (surface->cr.current_style.dash != NULL) {
+ free (surface->cr.current_style.dash);
+ surface->cr.current_style.dash = NULL;
+ }
+ _cairo_pattern_fini (&surface->cr.current_source.base);
+ _cairo_path_fixed_fini (&surface->cr.current_path);
+ _cairo_surface_clipper_reset (&surface->clipper);
+
+ 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 = 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 - 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;
+ }
+ }
+
+ 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->is_rectilinear) {
+ status = _emit_tolerance (surface, tolerance, matrix_updated);
+ if (unlikely (status))
+ return status;
+ }
+
+ if (! path->maybe_fill_region) {
+ status = _emit_antialias (surface, antialias);
+ if (unlikely (status))
+ return status;
+ }
+
+ status = _emit_path (surface, path);
+ 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,
+ 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,
+ 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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_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);
+ 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;
+
+ if (! path->is_rectilinear) {
+ 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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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->is_rectilinear) {
+ status = _emit_tolerance (surface, tolerance, matrix_updated);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ if (! path->maybe_fill_region) {
+ status = _emit_antialias (surface, antialias);
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ status = _emit_path (surface, path);
+ 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_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+ cairo_script_surface_font_private_t *font_private;
+
+ font_private = scaled_font->surface_private;
+ if (font_private != NULL) {
+ cairo_status_t status;
+ cairo_device_t *device;
+
+ status = cairo_device_acquire (device = &font_private->ctx->base);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ _cairo_output_stream_printf (font_private->ctx->stream,
+ "/f%lu undef /sf%lu undef\n",
+ font_private->id,
+ font_private->id);
+
+ _bitmap_release_id (&font_private->ctx->font_id, font_private->id);
+ cairo_list_del (&font_private->link);
+ free (font_private);
+
+ cairo_device_release (device);
+ }
+
+ scaled_font->surface_private = NULL;
+ }
+}
+
+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_script_surface_font_private_t *font_private;
+ cairo_output_stream_t *base85_stream;
+ cairo_output_stream_t *zlib_stream;
+ cairo_status_t status, status2;
+ unsigned long size;
+ unsigned int load_flags;
+ 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 = 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;
+
+ font_private = scaled_font->surface_private;
+ _cairo_output_stream_printf (ctx->stream,
+ "~> >> font dup /f%lu exch def set-font-face",
+ font_private->id);
+
+ return status;
+}
+
+static cairo_status_t
+_emit_scaled_font_init (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_surface_font_private_t *font_private;
+ cairo_status_t status;
+
+ font_private = malloc (sizeof (cairo_script_surface_font_private_t));
+ if (unlikely (font_private == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font_private->ctx = ctx;
+ 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;
+ }
+
+ scaled_font->surface_private = font_private;
+ scaled_font->surface_backend = &_cairo_script_surface_backend;
+
+ status = _emit_context (surface);
+ if (unlikely (status))
+ return status;
+
+ status = _emit_type42_font (surface, scaled_font);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ font_private->has_sfnt = FALSE;
+ _cairo_output_stream_printf (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);
+
+ 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_surface_font_private_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;
+
+ if (! (scaled_font->surface_backend == NULL ||
+ scaled_font->surface_backend == &_cairo_script_surface_backend))
+ {
+ _cairo_scaled_font_revoke_ownership (scaled_font);
+ }
+
+ font_private = scaled_font->surface_private;
+ 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);
+ if (unlikely (status))
+ return status;
+
+ font_private = scaled_font->surface_private;
+ assert (font_private != NULL);
+
+ assert (target_is_active (surface));
+ _cairo_output_stream_printf (ctx->stream,
+ " /scaled-font get /sf%lu exch def\n",
+ 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_scaled_glyph_t *scaled_glyph)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_surface_font_private_t *font_private;
+ cairo_script_implicit_context_t old_cr;
+ cairo_status_t status;
+ unsigned long index;
+
+ font_private = scaled_font->surface_private;
+ index = ++font_private->subset_glyph_index;
+ scaled_glyph->surface_private = (void *) index;
+
+ _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_scaled_glyph_t *scaled_glyph)
+{
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_surface_font_private_t *font_private;
+ cairo_status_t status;
+ unsigned long index;
+
+ font_private = scaled_font->surface_private;
+ index = ++font_private->subset_glyph_index;
+ scaled_glyph->surface_private = (void *) index;
+
+ _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_surface_font_private_t *font_private;
+
+ assert (scaled_font->surface_backend == &_cairo_script_surface_backend);
+
+ font_private = scaled_font->surface_private;
+
+ _cairo_output_stream_printf (to_context (surface)->stream,
+ "f%lu /glyphs get\n",
+ font_private->id);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_emit_scaled_glyphs (cairo_script_surface_t *surface,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ unsigned int num_glyphs)
+{
+ cairo_script_surface_font_private_t *font_private;
+ cairo_status_t status;
+ unsigned int n;
+ cairo_bool_t have_glyph_prologue = FALSE;
+
+ if (num_glyphs == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ font_private = scaled_font->surface_private;
+ if (font_private->has_sfnt)
+ return CAIRO_STATUS_SUCCESS;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ for (n = 0; n < num_glyphs; n++) {
+ cairo_scaled_glyph_t *scaled_glyph;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[n].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status))
+ break;
+
+ if (scaled_glyph->surface_private != NULL)
+ continue;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[n].index,
+ CAIRO_SCALED_GLYPH_INFO_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,
+ scaled_glyph);
+ if (unlikely (status))
+ break;
+
+ continue;
+ }
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[n].index,
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ &scaled_glyph);
+ if (_cairo_status_is_error (status))
+ break;
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ if (! have_glyph_prologue) {
+ status = _emit_scaled_glyph_prologue (surface, scaled_font);
+ if (unlikely (status))
+ break;
+
+ have_glyph_prologue = TRUE;
+ }
+
+ status = _emit_scaled_glyph_bitmap (surface,
+ scaled_font,
+ scaled_glyph);
+ if (unlikely (status))
+ break;
+
+ continue;
+ }
+ }
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ if (have_glyph_prologue) {
+ _cairo_output_stream_puts (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,
+ cairo_clip_t *clip)
+{
+ cairo_script_surface_t *surface = abstract_surface;
+ cairo_script_context_t *ctx = to_context (surface);
+ cairo_script_surface_font_private_t *font_private;
+ cairo_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 = scaled_font->surface_private;
+
+ _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->surface_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))
+ 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->surface_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->surface_private);
+ }
+
+ dx = scaled_glyph->metrics.x_advance;
+ dy = scaled_glyph->metrics.y_advance;
+ cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy);
+ x += dx;
+ y += dy;
+ }
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ if (base85_stream != NULL) {
+ cairo_status_t status2;
+
+ status2 = _cairo_output_stream_destroy (base85_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ _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_create_similar,
+ _cairo_script_surface_finish,
+ _cairo_script_surface_acquire_source_image,
+ _cairo_script_surface_release_source_image,
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ _cairo_script_surface_copy_page,
+ _cairo_script_surface_show_page,
+ _cairo_script_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ _cairo_script_surface_scaled_font_fini,
+ NULL, /* scaled_glyph_fini */
+
+ /* The 5 high level operations */
+ _cairo_script_surface_paint,
+ _cairo_script_surface_mask,
+ _cairo_script_surface_stroke,
+ _cairo_script_surface_fill,
+ NULL,
+
+ _cairo_script_surface_snapshot,
+
+ NULL, /* is_similar */
+ /* XXX need fill-stroke for passthrough */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+
+ /* The alternate high-level text operation */
+ _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)
+{
+ if (cr->current_style.dash != NULL) {
+ free (cr->current_style.dash);
+ cr->current_style.dash = NULL;
+ }
+ _cairo_pattern_fini (&cr->current_source.base);
+ _cairo_path_fixed_fini (&cr->current_path);
+
+ _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,
+ double width,
+ double height,
+ 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 = 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);
+
+ _cairo_surface_wrapper_init (&surface->wrapper, passthrough);
+
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_script_surface_clipper_intersect_clip_path);
+
+ surface->width = width;
+ surface->height = height;
+
+ surface->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
+};
+
+static cairo_device_t *
+_cairo_script_context_create_internal (cairo_output_stream_t *stream)
+{
+ cairo_script_context_t *ctx;
+
+ ctx = malloc (sizeof (cairo_script_context_t));
+ 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);
+
+ _cairo_output_stream_puts (ctx->stream, "%!CairoScript\n");
+
+ return &ctx->base;
+}
+
+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_internal (stream);
+}
+
+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_internal (stream);
+}
+
+void
+cairo_script_write_comment (cairo_device_t *device,
+ const char *comment,
+ int len)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) device;
+
+ 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");
+}
+
+void
+cairo_script_set_mode (cairo_device_t *device,
+ cairo_script_mode_t mode)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) device;
+
+ context->mode = mode;
+}
+
+cairo_script_mode_t
+cairo_script_get_mode (cairo_device_t *device)
+{
+ cairo_script_context_t *context = (cairo_script_context_t *) device;
+
+ return context->mode;
+}
+
+cairo_surface_t *
+cairo_script_surface_create (cairo_device_t *device,
+ cairo_content_t content,
+ double width,
+ double height)
+{
+ if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (device->status))
+ return _cairo_surface_create_in_error (device->status);
+
+ return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
+ content,
+ width, height,
+ NULL)->base;
+}
+
+cairo_surface_t *
+cairo_script_surface_create_for_target (cairo_device_t *device,
+ cairo_surface_t *target)
+{
+ cairo_rectangle_int_t extents;
+
+ if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (device->status))
+ return _cairo_surface_create_in_error (device->status);
+
+ if (unlikely (target->status))
+ return _cairo_surface_create_in_error (target->status);
+
+ if (! _cairo_surface_get_extents (target, &extents))
+ extents.width = extents.height = -1;
+
+ return &_cairo_script_surface_create_internal ((cairo_script_context_t *) device,
+ target->content,
+ extents.width,
+ extents.height,
+ target)->base;
+}
+
+cairo_status_t
+cairo_script_from_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_status_t status;
+
+ if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
+ return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (device->status))
+ return _cairo_error (device->status);
+
+ if (unlikely (recording_surface->status))
+ return recording_surface->status;
+
+ 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_script_surface_create_internal ((cairo_script_context_t *) device,
+ recording_surface->content,
+ extents.width,
+ extents.height,
+ NULL)->base;
+ if (unlikely (surface->status))
+ return surface->status;
+
+ cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
+ status = _cairo_recording_surface_replay (recording_surface, surface);
+ cairo_surface_destroy (surface);
+
+ 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 000000000..b82230f2f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-script.h
@@ -0,0 +1,89 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SCRIPT_H
+#define CAIRO_SCRIPT_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_SCRIPT_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+typedef enum {
+ CAIRO_SCRIPT_MODE_BINARY,
+ CAIRO_SCRIPT_MODE_ASCII
+} 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-skia.h b/gfx/cairo/cairo/src/cairo-skia.h
new file mode 100644
index 000000000..f62823522
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-skia.h
@@ -0,0 +1,84 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SKIA_H
+#define CAIRO_SKIA_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_SKIA_SURFACE
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_skia_surface_create (cairo_format_t format,
+ int width,
+ int height);
+
+cairo_public cairo_surface_t *
+cairo_skia_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ int width,
+ int height,
+ int stride);
+
+cairo_public unsigned char *
+cairo_skia_surface_get_data (cairo_surface_t *surface);
+
+cairo_public cairo_format_t
+cairo_skia_surface_get_format (cairo_surface_t *surface);
+
+cairo_public int
+cairo_skia_surface_get_width (cairo_surface_t *surface);
+
+cairo_public int
+cairo_skia_surface_get_height (cairo_surface_t *surface);
+
+cairo_public int
+cairo_skia_surface_get_stride (cairo_surface_t *surface);
+
+cairo_public cairo_surface_t *
+cairo_skia_surface_get_image (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else
+
+# error Cairo was not compiled with support for the Skia backend
+
+#endif
+
+#endif
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 000000000..6a58c9f45
--- /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 <cworth@cworth.org>
+ */
+
+#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 000000000..827037f76
--- /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 <cworth@cworth.org>
+ */
+
+#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-private.h b/gfx/cairo/cairo/src/cairo-spans-private.h
new file mode 100644
index 000000000..00a4df868
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-spans-private.h
@@ -0,0 +1,189 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright (c) 2008 M Joonas Pihlaja
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef CAIRO_SPANS_PRIVATE_H
+#define CAIRO_SPANS_PRIVATE_H
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+/* Number of bits of precision used for alpha. */
+#define CAIRO_SPANS_UNIT_COVERAGE_BITS 8
+#define CAIRO_SPANS_UNIT_COVERAGE ((1 << CAIRO_SPANS_UNIT_COVERAGE_BITS)-1)
+
+/* A structure representing an open-ended horizontal span of constant
+ * pixel coverage. */
+typedef struct _cairo_half_open_span {
+ /* The inclusive x-coordinate of the start of the span. */
+ int x;
+
+ /* The pixel coverage for the pixels to the right. */
+ int coverage;
+} cairo_half_open_span_t;
+
+/* Span renderer interface. Instances of renderers are provided by
+ * surfaces if they want to composite spans instead of trapezoids. */
+typedef struct _cairo_span_renderer cairo_span_renderer_t;
+struct _cairo_span_renderer {
+ /* 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_warn 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;
+
+ /* Add a single edge to the converter. */
+ cairo_status_t (*add_edge) (void *abstract_converter,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ int top, int bottom,
+ int dir);
+
+ /* Add a polygon (set of edges) to the converter. */
+ cairo_status_t (*add_polygon) (void *abstract_converter,
+ const cairo_polygon_t *polygon);
+
+ /* Generates coverage spans for rows for the added edges and calls
+ * the renderer function for each row. After generating spans the
+ * only valid thing to do with the converter is to destroy it. */
+ 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);
+
+typedef struct _cairo_rectangular_scan_converter {
+ cairo_scan_converter_t base;
+
+ int xmin, xmax;
+ int ymin, ymax;
+
+ 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-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 000000000..a187b8998
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-spans.c
@@ -0,0 +1,323 @@
+/* -*- 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-fixed-private.h"
+
+static cairo_scan_converter_t *
+_create_scan_converter (cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects)
+{
+ if (antialias == CAIRO_ANTIALIAS_NONE) {
+ ASSERT_NOT_REACHED;
+ return NULL;
+ }
+
+ return _cairo_tor_scan_converter_create (rects->bounded.x,
+ rects->bounded.y,
+ rects->bounded.x + rects->bounded.width,
+ rects->bounded.y + rects->bounded.height,
+ fill_rule);
+}
+
+/* XXX Add me to the compositor interface. Ok, first create the compositor
+ * interface, and then add this with associated fallback!
+ */
+cairo_status_t
+_cairo_surface_composite_polygon (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_fill_rule_t fill_rule,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects,
+ cairo_polygon_t *polygon,
+ cairo_region_t *clip_region)
+{
+ cairo_span_renderer_t *renderer;
+ cairo_scan_converter_t *converter;
+ cairo_status_t status;
+
+ converter = _create_scan_converter (fill_rule, antialias, rects);
+ status = converter->add_polygon (converter, polygon);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+
+ renderer = _cairo_surface_create_span_renderer (op, pattern, surface,
+ antialias, rects,
+ clip_region);
+ status = converter->generate (converter, renderer);
+ if (unlikely (status))
+ goto CLEANUP_RENDERER;
+
+ status = renderer->finish (renderer);
+
+ CLEANUP_RENDERER:
+ renderer->destroy (renderer);
+ CLEANUP_CONVERTER:
+ converter->destroy (converter);
+ return status;
+}
+
+static void
+_cairo_nil_destroy (void *abstract)
+{
+ (void) abstract;
+}
+
+static cairo_status_t
+_cairo_nil_scan_converter_add_polygon (void *abstract_converter,
+ const cairo_polygon_t *polygon)
+{
+ (void) abstract_converter;
+ (void) polygon;
+ return _cairo_scan_converter_status (abstract_converter);
+}
+
+static cairo_status_t
+_cairo_nil_scan_converter_add_edge (void *abstract_converter,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ int top, int bottom,
+ int dir)
+{
+ (void) abstract_converter;
+ (void) p1;
+ (void) p2;
+ (void) top;
+ (void) bottom;
+ (void) dir;
+ return _cairo_scan_converter_status (abstract_converter);
+}
+
+static cairo_status_t
+_cairo_nil_scan_converter_generate (void *abstract_converter,
+ cairo_span_renderer_t *renderer)
+{
+ (void) abstract_converter;
+ (void) renderer;
+ return _cairo_scan_converter_status (abstract_converter);
+}
+
+cairo_status_t
+_cairo_scan_converter_status (void *abstract_converter)
+{
+ cairo_scan_converter_t *converter = abstract_converter;
+ return converter->status;
+}
+
+cairo_status_t
+_cairo_scan_converter_set_error (void *abstract_converter,
+ cairo_status_t error)
+{
+ cairo_scan_converter_t *converter = abstract_converter;
+ if (error == CAIRO_STATUS_SUCCESS)
+ ASSERT_NOT_REACHED;
+ if (converter->status == CAIRO_STATUS_SUCCESS) {
+ converter->add_polygon = _cairo_nil_scan_converter_add_polygon;
+ converter->add_edge = _cairo_nil_scan_converter_add_edge;
+ converter->generate = _cairo_nil_scan_converter_generate;
+ converter->status = error;
+ }
+ 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;
+ 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;
+ 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 000000000..ca2e2dc64
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-spline.c
@@ -0,0 +1,371 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-slope-private.h"
+
+cairo_bool_t
+_cairo_spline_init (cairo_spline_t *spline,
+ cairo_spline_add_point_func_t add_point_func,
+ void *closure,
+ const cairo_point_t *a, const cairo_point_t *b,
+ const cairo_point_t *c, const cairo_point_t *d)
+{
+ spline->add_point_func = add_point_func;
+ spline->closure = closure;
+
+ spline->knots.a = *a;
+ spline->knots.b = *b;
+ spline->knots.c = *c;
+ 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
+ _cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d);
+
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point)
+{
+ cairo_point_t *prev;
+
+ prev = &spline->last_point;
+ if (prev->x == point->x && prev->y == point->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ spline->last_point = *point;
+ return spline->add_point_func (spline->closure, point);
+}
+
+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);
+ }
+
+ _de_casteljau (s1, &s2);
+
+ status = _cairo_spline_decompose_into (s1, tolerance_squared, result);
+ if (unlikely (status)) {
+ return status;
+ }
+
+ status = _cairo_spline_decompose_into (&s2, tolerance_squared, result);
+ return status;
+}
+
+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 _cairo_spline_add_point (spline, &spline->knots.d);
+}
+
+/* Note: this function is only good for computing bounds in device space. */
+cairo_status_t
+_cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
+ void *closure,
+ const cairo_point_t *p0, const cairo_point_t *p1,
+ const cairo_point_t *p2, const cairo_point_t *p3)
+{
+ double x0, x1, x2, x3;
+ double y0, y1, y2, y3;
+ double a, b, c;
+ double t[4];
+ int t_num = 0, i;
+ cairo_status_t status;
+
+ x0 = _cairo_fixed_to_double (p0->x);
+ y0 = _cairo_fixed_to_double (p0->y);
+ x1 = _cairo_fixed_to_double (p1->x);
+ y1 = _cairo_fixed_to_double (p1->y);
+ x2 = _cairo_fixed_to_double (p2->x);
+ y2 = _cairo_fixed_to_double (p2->y);
+ x3 = _cairo_fixed_to_double (p3->x);
+ y3 = _cairo_fixed_to_double (p3->y);
+
+ /* The spline can be written as a polynomial of the four points:
+ *
+ * (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
+ *
+ * for 0≤t≤1. Now, the X and Y components of the spline follow the
+ * same polynomial but with x and y replaced for p. To find the
+ * bounds of the spline, we just need to find the X and Y bounds.
+ * To find the bound, we take the derivative and equal it to zero,
+ * and solve to find the t's that give the extreme points.
+ *
+ * Here is the derivative of the curve, sorted on t:
+ *
+ * 3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
+ *
+ * Let:
+ *
+ * a = -p0+3p1-3p2+p3
+ * b = p0-2p1+p2
+ * c = -p0+p1
+ *
+ * Gives:
+ *
+ * a.t² + 2b.t + c = 0
+ *
+ * With:
+ *
+ * delta = b*b - a*c
+ *
+ * the extreme points are at -c/2b if a is zero, at (-b±√delta)/a if
+ * delta is positive, and at -b/a if delta is zero.
+ */
+
+#define ADD(t0) \
+ { \
+ double _t0 = (t0); \
+ if (0 < _t0 && _t0 < 1) \
+ t[t_num++] = _t0; \
+ }
+
+#define FIND_EXTREMES(a,b,c) \
+ { \
+ if (a == 0) { \
+ if (b != 0) \
+ ADD (-c / (2*b)); \
+ } else { \
+ double b2 = b * b; \
+ double delta = b2 - a * c; \
+ if (delta > 0) { \
+ cairo_bool_t feasible; \
+ double _2ab = 2 * a * b; \
+ /* We are only interested in solutions t that satisfy 0<t<1 \
+ * here. We do some checks to avoid sqrt if the solutions \
+ * are not in that range. The checks can be derived from: \
+ * \
+ * 0 < (-b±√delta)/a < 1 \
+ */ \
+ if (_2ab >= 0) \
+ feasible = delta > b2 && delta < a*a + b2 + _2ab; \
+ else if (-b / a >= 1) \
+ feasible = delta < b2 && delta > a*a + b2 + _2ab; \
+ else \
+ feasible = delta < b2 || delta < a*a + b2 + _2ab; \
+ \
+ if (unlikely (feasible)) { \
+ double sqrt_delta = sqrt (delta); \
+ ADD ((-b - sqrt_delta) / a); \
+ ADD ((-b + sqrt_delta) / a); \
+ } \
+ } else if (delta == 0) { \
+ ADD (-b / a); \
+ } \
+ } \
+ }
+
+ /* Find X extremes */
+ a = -x0 + 3*x1 - 3*x2 + x3;
+ b = x0 - 2*x1 + x2;
+ c = -x0 + x1;
+ FIND_EXTREMES (a, b, c);
+
+ /* Find Y extremes */
+ a = -y0 + 3*y1 - 3*y2 + y3;
+ b = y0 - 2*y1 + y2;
+ c = -y0 + y1;
+ FIND_EXTREMES (a, b, c);
+
+ status = add_point_func (closure, p0);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < t_num; i++) {
+ cairo_point_t p;
+ double x, y;
+ double t_1_0, t_0_1;
+ double t_2_0, t_0_2;
+ double t_3_0, t_2_1_3, t_1_2_3, t_0_3;
+
+ t_1_0 = t[i]; /* t */
+ t_0_1 = 1 - t_1_0; /* (1 - t) */
+
+ t_2_0 = t_1_0 * t_1_0; /* t * t */
+ t_0_2 = t_0_1 * t_0_1; /* (1 - t) * (1 - t) */
+
+ t_3_0 = t_2_0 * t_1_0; /* t * t * t */
+ t_2_1_3 = t_2_0 * t_0_1 * 3; /* t * t * (1 - t) * 3 */
+ t_1_2_3 = t_1_0 * t_0_2 * 3; /* t * (1 - t) * (1 - t) * 3 */
+ t_0_3 = t_0_1 * t_0_2; /* (1 - t) * (1 - t) * (1 - t) */
+
+ /* Bezier polynomial */
+ x = x0 * t_0_3
+ + x1 * t_1_2_3
+ + x2 * t_2_1_3
+ + x3 * t_3_0;
+ y = y0 * t_0_3
+ + y1 * t_1_2_3
+ + y2 * t_2_1_3
+ + y3 * t_3_0;
+
+ p.x = _cairo_fixed_from_double (x);
+ p.y = _cairo_fixed_from_double (y);
+ status = add_point_func (closure, &p);
+ if (unlikely (status))
+ return status;
+ }
+
+ return add_point_func (closure, p3);
+}
diff --git a/gfx/cairo/cairo/src/cairo-stroke-style.c b/gfx/cairo/cairo/src/cairo-stroke-style.c
new file mode 100644
index 000000000..1513d1f35
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-stroke-style.c
@@ -0,0 +1,310 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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)
+{
+ if (style->dash) {
+ free (style->dash);
+ style->dash = NULL;
+ }
+ style->num_dashes = 0;
+
+ VG (VALGRIND_MAKE_MEM_NOACCESS (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_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 &&
+ style_expansion < M_SQRT2 * style->miter_limit)
+ {
+ style_expansion = M_SQRT2 * style->miter_limit;
+ }
+
+ style_expansion *= style->line_width;
+
+ *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 < 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-supported-features.h b/gfx/cairo/cairo/src/cairo-supported-features.h
new file mode 100644
index 000000000..aacbab781
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-supported-features.h
@@ -0,0 +1,25 @@
+/* Generated by configure. Do not edit. */
+#ifndef CAIRO_SUPPORTED_FEATURES_H
+#define CAIRO_SUPPORTED_FEATURES_H
+
+/* This is a dummy header, to trick gtk-doc only */
+
+#define CAIRO_HAS_XLIB_SURFACE 1
+#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1
+#define CAIRO_HAS_QUARTZ_SURFACE 1
+#define CAIRO_HAS_QUARTZ_FONT 1
+#define CAIRO_HAS_WIN32_SURFACE 1
+#define CAIRO_HAS_WIN32_FONT 1
+#define CAIRO_HAS_PNG_FUNCTIONS 1
+#define CAIRO_HAS_EGL_FUNCTIONS 1
+#define CAIRO_HAS_GLX_FUNCTIONS 1
+#define CAIRO_HAS_FT_FONT 1
+#define CAIRO_HAS_FC_FONT 1
+#define CAIRO_HAS_PS_SURFACE 1
+#define CAIRO_HAS_PDF_SURFACE 1
+#define CAIRO_HAS_SVG_SURFACE 1
+#define CAIRO_HAS_IMAGE_SURFACE 1
+#define CAIRO_HAS_META_SURFACE 1
+#define CAIRO_HAS_USER_FONT 1
+
+#endif
diff --git a/gfx/cairo/cairo/src/cairo-surface-clipper-private.h b/gfx/cairo/cairo/src/cairo-surface-clipper-private.h
new file mode 100644
index 000000000..b9ca3cb1c
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-clipper-private.h
@@ -0,0 +1,72 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.u>
+ */
+
+#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_bool_t is_clipped;
+ cairo_surface_clipper_intersect_clip_path_func_t intersect_clip_path;
+};
+
+cairo_private cairo_status_t
+_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper,
+ cairo_clip_t *clip);
+
+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 000000000..948730047
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-clipper.c
@@ -0,0 +1,135 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-surface-clipper-private.h"
+
+/* A collection of routines to facilitate vector surface clipping */
+
+static cairo_status_t
+_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper,
+ cairo_clip_path_t *clip_path)
+{
+ cairo_status_t status;
+
+ if (clip_path->prev != NULL) {
+ status =
+ _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
+ clip_path->prev);
+ 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,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_bool_t clear;
+
+ /* XXX as we cache a reference to the path, and compare every time,
+ * we may in future need to install a notification if the clip->path
+ * is every modified (e.g. cairo_clip_translate).
+ */
+
+ if (clip == NULL && clipper->clip.path == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (clip != NULL && clipper->clip.path != NULL &&
+ _cairo_clip_equal (clip, &clipper->clip))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* all clipped out state should never propagate this far */
+ assert (clip == NULL || clip->path != NULL);
+
+ /* Check whether this clip is a continuation of the previous.
+ * If not, we have to remove the current clip and rebuild.
+ */
+ clear = clip == NULL || clip->path->prev != clipper->clip.path;
+
+ _cairo_clip_reset (&clipper->clip);
+ _cairo_clip_init_copy (&clipper->clip, clip);
+
+ if (clear) {
+ clipper->is_clipped = FALSE;
+ status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0);
+ if (unlikely (status))
+ return status;
+
+ if (clip != NULL && clip->path != NULL) {
+ status =
+ _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
+ clip->path);
+ clipper->is_clipped = TRUE;
+ }
+ } else {
+ cairo_clip_path_t *path = clip->path;
+
+ clipper->is_clipped = TRUE;
+ status = clipper->intersect_clip_path (clipper,
+ &path->path,
+ path->fill_rule,
+ path->tolerance,
+ path->antialias);
+ }
+
+ return status;
+}
+
+void
+_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper,
+ cairo_surface_clipper_intersect_clip_path_func_t func)
+{
+ _cairo_clip_init (&clipper->clip);
+ clipper->is_clipped = FALSE;
+ clipper->intersect_clip_path = func;
+}
+
+void
+_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper)
+{
+ _cairo_clip_reset (&clipper->clip);
+ clipper->is_clipped = FALSE;
+}
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 000000000..e993de62e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-fallback-private.h
@@ -0,0 +1,139 @@
+/* -*- 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 <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H
+#define CAIRO_SURFACE_FALLBACK_PRIVATE_H
+
+#include "cairoint.h"
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_paint (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_mask (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_stroke (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_fill (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip);
+
+cairo_private cairo_surface_t *
+_cairo_surface_fallback_snapshot (cairo_surface_t *surface);
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_composite (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ cairo_surface_t *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects);
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_antialias_t antialias,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_status_t
+_cairo_surface_fallback_clone_similar (cairo_surface_t *surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out);
+
+#endif
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 000000000..51893ee65
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-fallback.c
@@ -0,0 +1,1638 @@
+/* -*- 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 <cworth@cworth.org>
+ * Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-spans-private.h"
+#include "cairo-surface-fallback-private.h"
+
+typedef struct {
+ cairo_surface_t *dst;
+ cairo_rectangle_int_t extents;
+ cairo_image_surface_t *image;
+ cairo_rectangle_int_t image_rect;
+ void *image_extra;
+} fallback_state_t;
+
+/**
+ * _fallback_init:
+ *
+ * Acquire destination image surface needed for an image-based
+ * fallback.
+ *
+ * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not
+ * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all
+ * went well, or some error status otherwise.
+ **/
+static cairo_int_status_t
+_fallback_init (fallback_state_t *state,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ cairo_status_t status;
+
+ state->extents.x = x;
+ state->extents.y = y;
+ state->extents.width = width;
+ state->extents.height = height;
+
+ state->dst = dst;
+
+ status = _cairo_surface_acquire_dest_image (dst, &state->extents,
+ &state->image, &state->image_rect,
+ &state->image_extra);
+ if (unlikely (status))
+ return status;
+
+
+ /* XXX: This NULL value tucked away in state->image is a rather
+ * ugly interface. Cleaner would be to push the
+ * CAIRO_INT_STATUS_NOTHING_TO_DO value down into
+ * _cairo_surface_acquire_dest_image and its backend
+ * counterparts. */
+ assert (state->image != NULL);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_fallback_fini (fallback_state_t *state)
+{
+ _cairo_surface_release_dest_image (state->dst, &state->extents,
+ state->image, &state->image_rect,
+ state->image_extra);
+}
+
+typedef cairo_status_t
+(*cairo_draw_func_t) (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region);
+
+static cairo_status_t
+_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern,
+ cairo_clip_t *clip,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *mask;
+ cairo_region_t *clip_region = NULL, *fallback_region = NULL;
+ cairo_status_t status;
+ cairo_bool_t clip_surface = FALSE;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status) ||
+ status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ {
+ return status;
+ }
+
+ clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with
+ * a mask (as called via _cairo_surface_mask) triggers assertion failures.
+ */
+ mask = _cairo_surface_create_similar_solid (dst,
+ CAIRO_CONTENT_ALPHA,
+ extents->width,
+ extents->height,
+ CAIRO_COLOR_TRANSPARENT,
+ TRUE);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ if (clip_region && (extents->x || extents->y)) {
+ fallback_region = cairo_region_copy (clip_region);
+ status = fallback_region->status;
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ cairo_region_translate (fallback_region,
+ -extents->x,
+ -extents->y);
+ clip_region = fallback_region;
+ }
+
+ status = draw_func (draw_closure, CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base, mask,
+ extents->x, extents->y,
+ extents,
+ clip_region);
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ if (clip_surface)
+ status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y);
+
+ _cairo_pattern_init_for_surface (mask_pattern, mask);
+
+ CLEANUP_SURFACE:
+ if (fallback_region)
+ cairo_region_destroy (fallback_region);
+ cairo_surface_destroy (mask);
+
+ return status;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+_clip_and_composite_with_mask (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_pattern_t mask_pattern;
+ cairo_status_t status;
+
+ status = _create_composite_mask_pattern (&mask_pattern,
+ clip,
+ draw_func, draw_closure,
+ dst, extents);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _cairo_surface_composite (op,
+ src, &mask_pattern.base, dst,
+ extents->x, extents->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ NULL);
+
+ _cairo_pattern_fini (&mask_pattern.base);
+ }
+
+ return status;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+_clip_and_composite_combine (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *intermediate;
+ cairo_surface_pattern_t pattern;
+ cairo_surface_pattern_t clip_pattern;
+ cairo_surface_t *clip_surface;
+ int clip_x, clip_y;
+ cairo_status_t status;
+
+ /* We'd be better off here creating a surface identical in format
+ * to dst, but we have no way of getting that information. Instead
+ * we ask the backend to create a similar surface of identical content,
+ * in the belief that the backend will do something useful - like use
+ * an identical format. For example, the xlib backend will endeavor to
+ * use a compatible depth to enable core protocol routines.
+ */
+ intermediate =
+ _cairo_surface_create_similar_scratch (dst, dst->content,
+ extents->width,
+ extents->height);
+ if (intermediate == NULL) {
+ intermediate =
+ _cairo_image_surface_create_with_content (dst->content,
+ extents->width,
+ extents->width);
+ }
+ if (unlikely (intermediate->status))
+ return intermediate->status;
+
+ /* Initialize the intermediate surface from the destination surface */
+ _cairo_pattern_init_for_surface (&pattern, dst);
+ status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL, intermediate,
+ extents->x, extents->y,
+ 0, 0,
+ 0, 0,
+ extents->width, extents->height,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ status = (*draw_func) (draw_closure, op,
+ src, intermediate,
+ extents->x, extents->y,
+ extents,
+ NULL);
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ assert (clip->path != NULL);
+ clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ goto CLEANUP_SURFACE;
+
+ _cairo_pattern_init_for_surface (&clip_pattern, clip_surface);
+
+ /* Combine that with the clip */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN,
+ &clip_pattern.base, NULL, intermediate,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ 0, 0,
+ 0, 0,
+ extents->width, extents->height,
+ NULL);
+ if (unlikely (status))
+ goto CLEANUP_CLIP;
+
+ /* Punch the clip out of the destination */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
+ &clip_pattern.base, NULL, dst,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ NULL);
+ if (unlikely (status))
+ goto CLEANUP_CLIP;
+
+ /* Now add the two results together */
+ _cairo_pattern_init_for_surface (&pattern, intermediate);
+ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
+ &pattern.base, NULL, dst,
+ 0, 0,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ CLEANUP_CLIP:
+ _cairo_pattern_fini (&clip_pattern.base);
+ CLEANUP_SURFACE:
+ cairo_surface_destroy (intermediate);
+
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+_clip_and_composite_source (cairo_clip_t *clip,
+ const cairo_pattern_t *src,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_pattern_t mask_pattern;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status) ||
+ status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ {
+ return status;
+ }
+ }
+
+ /* Create a surface that is mask IN clip */
+ status = _create_composite_mask_pattern (&mask_pattern,
+ clip,
+ draw_func, draw_closure,
+ dst, extents);
+ if (unlikely (status))
+ return status;
+
+ /* Compute dest' = dest OUT (mask IN clip) */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
+ &mask_pattern.base, NULL, dst,
+ 0, 0,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ clip_region);
+
+ if (unlikely (status))
+ goto CLEANUP_MASK_PATTERN;
+
+ /* Now compute (src IN (mask IN clip)) ADD dest' */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
+ src, &mask_pattern.base, dst,
+ extents->x, extents->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ clip_region);
+
+ CLEANUP_MASK_PATTERN:
+ _cairo_pattern_fini (&mask_pattern.base);
+ return status;
+}
+
+static int
+_cairo_rectangle_empty (const cairo_rectangle_int_t *rect)
+{
+ return rect->width == 0 || rect->height == 0;
+}
+
+/**
+ * _clip_and_composite:
+ * @clip: a #cairo_clip_t
+ * @op: the operator to draw with
+ * @src: source pattern
+ * @draw_func: function that can be called to draw with the mask onto a surface.
+ * @draw_closure: data to pass to @draw_func.
+ * @dst: destination surface
+ * @extents: rectangle holding a bounding box for the operation; this
+ * rectangle will be used as the size for the temporary
+ * surface.
+ *
+ * When there is a surface clip, we typically need to create an intermediate
+ * surface. This function handles the logic of creating a temporary surface
+ * drawing to it, then compositing the result onto the target surface.
+ *
+ * @draw_func is to called to draw the mask; it will be called no more
+ * than once.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded.
+ **/
+static cairo_status_t
+_clip_and_composite (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_status_t status;
+
+ if (_cairo_rectangle_empty (extents))
+ /* Nothing to do */
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = _clip_and_composite_source (clip,
+ src,
+ draw_func, draw_closure,
+ dst, extents);
+ } else {
+ cairo_bool_t clip_surface = FALSE;
+ cairo_region_t *clip_region = NULL;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status) ||
+ status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ {
+ return status;
+ }
+
+ clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (clip_surface) {
+ if (_cairo_operator_bounded_by_mask (op)) {
+ status = _clip_and_composite_with_mask (clip, op,
+ src,
+ draw_func, draw_closure,
+ dst, extents);
+ } else {
+ status = _clip_and_composite_combine (clip, op,
+ src,
+ draw_func, draw_closure,
+ dst, extents);
+ }
+ } else {
+ status = draw_func (draw_closure, op,
+ src, dst,
+ 0, 0,
+ extents,
+ clip_region);
+ }
+ }
+
+ return status;
+}
+
+/* Composites a region representing a set of trapezoids.
+ */
+static cairo_status_t
+_composite_trap_region (cairo_clip_t *clip,
+ const cairo_pattern_t *src,
+ cairo_operator_t op,
+ cairo_surface_t *dst,
+ cairo_region_t *trap_region,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_status_t status;
+ cairo_surface_pattern_t mask_pattern;
+ cairo_pattern_t *mask = NULL;
+ int mask_x = 0, mask_y =0;
+
+ if (clip != NULL) {
+ cairo_surface_t *clip_surface = NULL;
+ int clip_x, clip_y;
+
+ clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ return clip_surface->status;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ _cairo_pattern_init_for_surface (&mask_pattern, clip_surface);
+ mask_x = extents->x - clip_x;
+ mask_y = extents->y - clip_y;
+ mask = &mask_pattern.base;
+ }
+
+ status = _cairo_surface_composite (op, src, mask, dst,
+ extents->x, extents->y,
+ mask_x, mask_y,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ trap_region);
+
+ if (mask != NULL)
+ _cairo_pattern_fini (mask);
+
+ return status;
+}
+
+typedef struct {
+ cairo_traps_t *traps;
+ cairo_antialias_t antialias;
+} cairo_composite_traps_info_t;
+
+static cairo_status_t
+_composite_traps_draw_func (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ cairo_composite_traps_info_t *info = closure;
+ cairo_status_t status;
+ cairo_region_t *extents_region = NULL;
+
+ if (dst_x != 0 || dst_y != 0)
+ _cairo_traps_translate (info->traps, - dst_x, - dst_y);
+
+ if (clip_region == NULL &&
+ !_cairo_operator_bounded_by_source (op)) {
+ extents_region = cairo_region_create_rectangle (extents);
+ if (unlikely (extents_region->status))
+ return extents_region->status;
+ cairo_region_translate (extents_region, -dst_x, -dst_y);
+ clip_region = extents_region;
+ }
+
+ status = _cairo_surface_composite_trapezoids (op,
+ src, dst, info->antialias,
+ extents->x, extents->y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height,
+ info->traps->traps,
+ info->traps->num_traps,
+ clip_region);
+
+ if (extents_region)
+ cairo_region_destroy (extents_region);
+
+ return status;
+}
+
+enum {
+ HAS_CLEAR_REGION = 0x1,
+};
+
+static cairo_status_t
+_clip_and_composite_region (const cairo_pattern_t *src,
+ cairo_operator_t op,
+ cairo_surface_t *dst,
+ cairo_region_t *trap_region,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_region_t clear_region;
+ unsigned int has_region = 0;
+ cairo_status_t status;
+
+ if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) {
+ /* If we optimize drawing with an unbounded operator to
+ * _cairo_surface_fill_rectangles() or to drawing with a
+ * clip region, then we have an additional region to clear.
+ */
+ _cairo_region_init_rectangle (&clear_region, extents);
+ status = cairo_region_subtract (&clear_region, trap_region);
+ if (unlikely (status))
+ return status;
+
+ if (! cairo_region_is_empty (&clear_region))
+ has_region |= HAS_CLEAR_REGION;
+ }
+
+ if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) &&
+ clip == NULL)
+ {
+ const cairo_color_t *color;
+
+ if (op == CAIRO_OPERATOR_CLEAR)
+ color = CAIRO_COLOR_TRANSPARENT;
+ else
+ color = &((cairo_solid_pattern_t *)src)->color;
+
+ /* Solid rectangles special case */
+ status = _cairo_surface_fill_region (dst, op, color, trap_region);
+ } else {
+ /* For a simple rectangle, we can just use composite(), for more
+ * rectangles, we have to set a clip region. The cost of rasterizing
+ * trapezoids is pretty high for most backends currently, so it's
+ * worthwhile even if a region is needed.
+ *
+ * If we have a clip surface, we set it as the mask; this only works
+ * for bounded operators other than SOURCE; for unbounded operators,
+ * clip and mask cannot be interchanged. For SOURCE, the operator
+ * as implemented by the backends is different in its handling
+ * of the mask then what we want.
+ *
+ * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has
+ * more than rectangle and the destination doesn't support clip
+ * regions. In that case, we fall through.
+ */
+ status = _composite_trap_region (clip, src, op, dst,
+ trap_region, extents);
+ }
+
+ if (has_region & HAS_CLEAR_REGION) {
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _cairo_surface_fill_region (dst,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear_region);
+ }
+ _cairo_region_fini (&clear_region);
+ }
+
+ return status;
+}
+
+/* avoid using region code to re-validate boxes */
+static cairo_status_t
+_fill_rectangles (cairo_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_traps_t *traps,
+ cairo_clip_t *clip)
+{
+ const cairo_color_t *color;
+ cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+ cairo_rectangle_int_t *rects = stack_rects;
+ cairo_status_t status;
+ int i;
+
+ if (! traps->is_rectilinear || ! traps->maybe_region)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* XXX: convert clip region to geometric boxes? */
+ if (clip != NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* XXX: fallback for the region_subtract() operation */
+ if (! _cairo_operator_bounded_by_mask (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (traps->has_intersections) {
+ if (traps->is_rectangular) {
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
+ } else {
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ for (i = 0; i < traps->num_traps; i++) {
+ if (! _cairo_fixed_is_integer (traps->traps[i].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ if (traps->num_traps > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (traps->num_traps,
+ sizeof (cairo_rectangle_int_t));
+ if (unlikely (rects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < traps->num_traps; i++) {
+ int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
+ int y1 = _cairo_fixed_integer_part (traps->traps[i].top);
+ int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
+ int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
+
+ rects[i].x = x1;
+ rects[i].y = y1;
+ rects[i].width = x2 - x1;
+ rects[i].height = y2 - y1;
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR)
+ color = CAIRO_COLOR_TRANSPARENT;
+ else
+ color = &((cairo_solid_pattern_t *)src)->color;
+
+ status = _cairo_surface_fill_rectangles (dst, op, color, rects, i);
+
+ if (rects != stack_rects)
+ free (rects);
+
+ return status;
+}
+
+/* fast-path for very common composite of a single rectangle */
+static cairo_status_t
+_composite_rectangle (cairo_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_traps_t *traps,
+ cairo_clip_t *clip)
+{
+ cairo_rectangle_int_t rect;
+
+ if (clip != NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_fixed_is_integer (traps->traps[0].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[0].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x);
+ rect.y = _cairo_fixed_integer_part (traps->traps[0].top);
+ rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x;
+ rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y;
+
+ return _cairo_surface_composite (op, src, NULL, dst,
+ rect.x, rect.y,
+ 0, 0,
+ rect.x, rect.y,
+ rect.width, rect.height,
+ NULL);
+}
+
+/* Warning: This call modifies the coordinates of traps */
+static cairo_status_t
+_clip_and_composite_trapezoids (const cairo_pattern_t *src,
+ cairo_operator_t op,
+ cairo_surface_t *dst,
+ cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_composite_traps_info_t traps_info;
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t clip_surface = FALSE;
+ cairo_status_t status;
+
+ if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+
+ clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* Use a fast path if the trapezoids consist of a simple region,
+ * but we can only do this if we do not have a clip surface, or can
+ * substitute the mask with the clip.
+ */
+ if (! clip_surface ||
+ (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE))
+ {
+ cairo_region_t *trap_region = NULL;
+
+ if (_cairo_operator_bounded_by_source (op)) {
+ status = _fill_rectangles (dst, op, src, traps, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _composite_rectangle (dst, op, src, traps, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ status = _cairo_traps_extract_region (traps, &trap_region);
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+
+ if (trap_region != NULL) {
+ status = cairo_region_intersect_rectangle (trap_region, extents);
+ if (unlikely (status)) {
+ cairo_region_destroy (trap_region);
+ return status;
+ }
+
+ if (clip_region != NULL) {
+ status = cairo_region_intersect (trap_region, clip_region);
+ if (unlikely (status)) {
+ cairo_region_destroy (trap_region);
+ return status;
+ }
+ }
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t trap_extents;
+
+ cairo_region_get_extents (trap_region, &trap_extents);
+ if (! _cairo_rectangle_intersect (extents, &trap_extents)) {
+ cairo_region_destroy (trap_region);
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ status = _clip_and_composite_region (src, op, dst,
+ trap_region,
+ clip_surface ? clip : NULL,
+ extents);
+ cairo_region_destroy (trap_region);
+
+ if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
+ return status;
+ }
+ }
+
+ /* No fast path, exclude self-intersections and clip trapezoids. */
+ if (traps->has_intersections) {
+ if (traps->is_rectangular)
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
+ else if (traps->is_rectilinear)
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
+ else
+ status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Otherwise render the trapezoids to a mask and composite in the usual
+ * fashion.
+ */
+ traps_info.traps = traps;
+ traps_info.antialias = antialias;
+
+ return _clip_and_composite (clip, op, src,
+ _composite_traps_draw_func,
+ &traps_info, dst, extents);
+}
+
+cairo_status_t
+_cairo_surface_fallback_paint (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_clip_path_t *clip_path = clip ? clip->path : NULL;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ cairo_boxes_t boxes;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_status_t status;
+ cairo_traps_t traps;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ &rect,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status))
+ return status;
+
+ /* If the clip cannot be reduced to a set of boxes, we will need to
+ * use a clipmask. Paint is special as it is the only operation that
+ * does not implicitly use a mask, so we may be able to reduce this
+ * operation to a fill...
+ */
+ if (clip != NULL && clip_path->prev == NULL &&
+ _cairo_operator_bounded_by_mask (op))
+ {
+ return _cairo_surface_fill (surface, op, source,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL);
+ }
+
+ /* meh, surface-fallback is dying anyway... */
+ _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
+ status = _cairo_traps_init_boxes (&traps, &boxes);
+ if (unlikely (status))
+ goto CLEANUP_BOXES;
+
+ status = _clip_and_composite_trapezoids (source, op, surface,
+ &traps, CAIRO_ANTIALIAS_DEFAULT,
+ clip,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+ _cairo_traps_fini (&traps);
+
+CLEANUP_BOXES:
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_mask_draw_func (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ cairo_pattern_t *mask = closure;
+ cairo_status_t status;
+ cairo_region_t *extents_region = NULL;
+
+ if (clip_region == NULL &&
+ !_cairo_operator_bounded_by_source (op)) {
+ extents_region = cairo_region_create_rectangle (extents);
+ if (unlikely (extents_region->status))
+ return extents_region->status;
+ cairo_region_translate (extents_region, -dst_x, -dst_y);
+ clip_region = extents_region;
+ }
+
+ if (src) {
+ status = _cairo_surface_composite (op,
+ src, mask, dst,
+ extents->x, extents->y,
+ extents->x, extents->y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height,
+ clip_region);
+ } else {
+ status = _cairo_surface_composite (op,
+ mask, NULL, dst,
+ extents->x, extents->y,
+ 0, 0, /* unused */
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height,
+ clip_region);
+ }
+
+ if (extents_region)
+ cairo_region_destroy (extents_region);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_fallback_mask (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_status_t status;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ &rect,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _clip_and_composite (clip, op, source,
+ _cairo_surface_mask_draw_func,
+ (void *) mask,
+ surface,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+}
+
+cairo_status_t
+_cairo_surface_fallback_stroke (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_polygon_t polygon;
+ cairo_traps_t traps;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_status_t status;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &rect,
+ op, source,
+ path, stroke_style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status))
+ return status;
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ _cairo_traps_init (&traps);
+ _cairo_traps_limit (&traps, clip_boxes, num_boxes);
+
+ if (path->is_rectilinear) {
+ status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
+ stroke_style,
+ ctm,
+ &traps);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ goto DO_TRAPS;
+
+ if (_cairo_status_is_error (status))
+ goto CLEANUP;
+ }
+
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ if (polygon.num_edges == 0)
+ goto DO_TRAPS;
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
+ if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ goto CLEANUP;
+ }
+
+ /* Fall back to trapezoid fills. */
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ &polygon,
+ CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ DO_TRAPS:
+ status = _clip_and_composite_trapezoids (source, op, surface,
+ &traps, antialias,
+ clip,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+ CLEANUP:
+ _cairo_traps_fini (&traps);
+ _cairo_polygon_fini (&polygon);
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_fallback_fill (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_polygon_t polygon;
+ cairo_traps_t traps;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_bool_t is_rectilinear;
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_status_t status;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ &rect,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status))
+ return status;
+
+ _cairo_traps_init (&traps);
+ _cairo_traps_limit (&traps, clip_boxes, num_boxes);
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ if (path->is_empty_fill)
+ goto DO_TRAPS;
+
+ is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path);
+ if (is_rectilinear) {
+ status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+ fill_rule,
+ &traps);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ goto DO_TRAPS;
+
+ if (_cairo_status_is_error (status))
+ goto CLEANUP;
+ }
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ if (polygon.num_edges == 0)
+ goto DO_TRAPS;
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
+ if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ goto CLEANUP;
+ }
+
+ if (is_rectilinear) {
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
+ &polygon,
+ fill_rule);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ goto DO_TRAPS;
+
+ if (unlikely (_cairo_status_is_error (status)))
+ goto CLEANUP;
+ }
+
+ /* Fall back to trapezoid fills. */
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ &polygon,
+ fill_rule);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ DO_TRAPS:
+ status = _clip_and_composite_trapezoids (source, op, surface,
+ &traps, antialias,
+ clip,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+ CLEANUP:
+ _cairo_traps_fini (&traps);
+ _cairo_polygon_fini (&polygon);
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ return status;
+}
+
+typedef struct {
+ cairo_scaled_font_t *font;
+ cairo_glyph_t *glyphs;
+ int num_glyphs;
+} cairo_show_glyphs_info_t;
+
+static cairo_status_t
+_cairo_surface_old_show_glyphs_draw_func (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ cairo_show_glyphs_info_t *glyph_info = closure;
+ cairo_status_t status;
+ cairo_region_t *extents_region = NULL;
+
+ if (clip_region == NULL &&
+ !_cairo_operator_bounded_by_source (op)) {
+ extents_region = cairo_region_create_rectangle (extents);
+ if (unlikely (extents_region->status))
+ return extents_region->status;
+ cairo_region_translate (extents_region, -dst_x, -dst_y);
+ clip_region = extents_region;
+ }
+
+ /* Modifying the glyph array is fine because we know that this function
+ * will be called only once, and we've already made a copy of the
+ * glyphs in the wrapper.
+ */
+ if (dst_x != 0 || dst_y != 0) {
+ int i;
+
+ for (i = 0; i < glyph_info->num_glyphs; ++i) {
+ ((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x;
+ ((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y;
+ }
+ }
+
+ status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src,
+ dst,
+ extents->x, extents->y,
+ extents->x - dst_x,
+ extents->y - dst_y,
+ extents->width,
+ extents->height,
+ glyph_info->glyphs,
+ glyph_info->num_glyphs,
+ clip_region);
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_scaled_font_show_glyphs (glyph_info->font,
+ op,
+ src, dst,
+ extents->x, extents->y,
+ extents->x - dst_x,
+ extents->y - dst_y,
+ extents->width, extents->height,
+ glyph_info->glyphs,
+ glyph_info->num_glyphs,
+ clip_region);
+ }
+
+ if (extents_region)
+ cairo_region_destroy (extents_region);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip)
+{
+ cairo_show_glyphs_info_t glyph_info;
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_status_t status;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_glyphs (&extents,
+ &rect,
+ op, source,
+ scaled_font,
+ glyphs, num_glyphs,
+ clip,
+ NULL);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_rectangle (clip, &extents.mask))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status))
+ return status;
+ }
+
+ glyph_info.font = scaled_font;
+ glyph_info.glyphs = glyphs;
+ glyph_info.num_glyphs = num_glyphs;
+
+ return _clip_and_composite (clip, op, source,
+ _cairo_surface_old_show_glyphs_draw_func,
+ &glyph_info,
+ surface,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+}
+
+cairo_surface_t *
+_cairo_surface_fallback_snapshot (cairo_surface_t *surface)
+{
+ cairo_surface_t *snapshot;
+ cairo_status_t status;
+ cairo_format_t format;
+ cairo_surface_pattern_t pattern;
+ cairo_image_surface_t *image;
+ void *image_extra;
+
+ status = _cairo_surface_acquire_source_image (surface,
+ &image, &image_extra);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ format = image->format;
+ if (format == CAIRO_FORMAT_INVALID) {
+ /* Non-standard images formats can be generated when retrieving
+ * images from unusual xservers, for example.
+ */
+ format = _cairo_format_from_content (image->base.content);
+ }
+ snapshot = cairo_image_surface_create (format,
+ image->width,
+ image->height);
+ if (cairo_surface_status (snapshot)) {
+ _cairo_surface_release_source_image (surface, image, image_extra);
+ return snapshot;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, &image->base);
+ status = _cairo_surface_paint (snapshot,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+ _cairo_surface_release_source_image (surface, image, image_extra);
+ if (unlikely (status)) {
+ cairo_surface_destroy (snapshot);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ return snapshot;
+}
+
+cairo_status_t
+_cairo_surface_fallback_composite (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ cairo_surface_t *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ fallback_state_t state;
+ cairo_region_t *fallback_region = NULL;
+ cairo_status_t status;
+
+ status = _fallback_init (&state, dst, dst_x, dst_y, width, height);
+ if (unlikely (status))
+ return status;
+
+ /* We know this will never fail with the image backend; but
+ * instead of calling into it directly, we call
+ * _cairo_surface_composite so that we get the correct device
+ * offset handling.
+ */
+
+ if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) {
+ fallback_region = cairo_region_copy (clip_region);
+ status = fallback_region->status;
+ if (unlikely (status))
+ goto FAIL;
+
+ cairo_region_translate (fallback_region,
+ -state.image_rect.x,
+ -state.image_rect.y);
+ clip_region = fallback_region;
+ }
+
+ status = _cairo_surface_composite (op, src, mask,
+ &state.image->base,
+ src_x, src_y, mask_x, mask_y,
+ dst_x - state.image_rect.x,
+ dst_y - state.image_rect.y,
+ width, height,
+ clip_region);
+ FAIL:
+ if (fallback_region != NULL)
+ cairo_region_destroy (fallback_region);
+ _fallback_fini (&state);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ fallback_state_t state;
+ cairo_rectangle_int_t *offset_rects = NULL;
+ cairo_status_t status;
+ int x1, y1, x2, y2;
+ int i;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (num_rects <= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* Compute the bounds of the rectangles, so that we know what area of the
+ * destination surface to fetch
+ */
+ x1 = rects[0].x;
+ y1 = rects[0].y;
+ x2 = rects[0].x + rects[0].width;
+ y2 = rects[0].y + rects[0].height;
+
+ for (i = 1; i < num_rects; i++) {
+ if (rects[i].x < x1)
+ x1 = rects[i].x;
+ if (rects[i].y < y1)
+ y1 = rects[i].y;
+
+ if ((int) (rects[i].x + rects[i].width) > x2)
+ x2 = rects[i].x + rects[i].width;
+ if ((int) (rects[i].y + rects[i].height) > y2)
+ y2 = rects[i].y + rects[i].height;
+ }
+
+ status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1);
+ if (unlikely (status))
+ return status;
+
+ /* If the fetched image isn't at 0,0, we need to offset the rectangles */
+
+ if (state.image_rect.x != 0 || state.image_rect.y != 0) {
+ offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t));
+ if (unlikely (offset_rects == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto DONE;
+ }
+
+ for (i = 0; i < num_rects; i++) {
+ offset_rects[i].x = rects[i].x - state.image_rect.x;
+ offset_rects[i].y = rects[i].y - state.image_rect.y;
+ offset_rects[i].width = rects[i].width;
+ offset_rects[i].height = rects[i].height;
+ }
+
+ rects = offset_rects;
+ }
+
+ status = _cairo_surface_fill_rectangles (&state.image->base,
+ op, color,
+ rects, num_rects);
+
+ free (offset_rects);
+
+ DONE:
+ _fallback_fini (&state);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_antialias_t antialias,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region)
+{
+ fallback_state_t state;
+ cairo_region_t *fallback_region = NULL;
+ cairo_trapezoid_t *offset_traps = NULL;
+ cairo_status_t status;
+
+ status = _fallback_init (&state, dst, dst_x, dst_y, width, height);
+ if (unlikely (status))
+ return status;
+
+ /* If the destination image isn't at 0,0, we need to offset the trapezoids */
+
+ if (state.image_rect.x != 0 || state.image_rect.y != 0) {
+ offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t));
+ if (offset_traps == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL;
+ }
+
+ _cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps,
+ - state.image_rect.x, - state.image_rect.y,
+ 1.0, 1.0);
+ traps = offset_traps;
+
+ /* similarly we need to adjust the region */
+ if (clip_region != NULL) {
+ fallback_region = cairo_region_copy (clip_region);
+ status = fallback_region->status;
+ if (unlikely (status))
+ goto FAIL;
+
+ cairo_region_translate (fallback_region,
+ -state.image_rect.x,
+ -state.image_rect.y);
+ clip_region = fallback_region;
+ }
+ }
+
+ status = _cairo_surface_composite_trapezoids (op, pattern,
+ &state.image->base,
+ antialias,
+ src_x, src_y,
+ dst_x - state.image_rect.x,
+ dst_y - state.image_rect.y,
+ width, height,
+ traps, num_traps,
+ clip_region);
+ FAIL:
+ if (offset_traps != NULL)
+ free (offset_traps);
+
+ if (fallback_region != NULL)
+ cairo_region_destroy (fallback_region);
+
+ _fallback_fini (&state);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_fallback_clone_similar (cairo_surface_t *surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_surface_t *new_surface;
+ cairo_surface_pattern_t pattern;
+ cairo_status_t status;
+
+ new_surface = _cairo_surface_create_similar_scratch (surface,
+ src->content,
+ width, height);
+ if (new_surface == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (unlikely (new_surface->status))
+ return new_surface->status;
+
+ /* We have to copy these here, so that the coordinate spaces are correct */
+ new_surface->device_transform = src->device_transform;
+ new_surface->device_transform_inverse = src->device_transform_inverse;
+
+ _cairo_pattern_init_for_surface (&pattern, src);
+ cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y);
+ pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+ status = _cairo_surface_paint (new_surface,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ if (unlikely (status)) {
+ cairo_surface_destroy (new_surface);
+ return status;
+ }
+
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+ *clone_out = new_surface;
+ return CAIRO_STATUS_SUCCESS;
+}
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 000000000..b7877b3de
--- /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 <chris@chris-wilson.co.u>
+ */
+
+#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,
+ 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,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_stroke (cairo_surface_t *surface,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_offset_fill (cairo_surface_t *surface,
+ int x, int y,
+ cairo_operator_t op,
+ const cairo_pattern_t*source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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,
+ 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 000000000..fdbe1a124
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-offset.c
@@ -0,0 +1,342 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-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,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_pattern_union_t source_copy;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ if (clip != NULL) {
+ cairo_matrix_init_translate (&m, -x, -y);
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ 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);
+
+ FINISH:
+ if (dev_clip != clip)
+ _cairo_clip_reset (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,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_pattern_union_t source_copy;
+ cairo_pattern_union_t mask_copy;
+
+ if (unlikely (target->status))
+ return target->status;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ if (clip != NULL) {
+ cairo_matrix_init_translate (&m, -x, -y);
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ 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);
+
+ FINISH:
+ if (dev_clip != clip)
+ _cairo_clip_reset (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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t*stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_path_fixed_t path_copy, *dev_path = path;
+ cairo_clip_t clip_copy, *dev_clip = 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 (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ 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);
+ if (clip != NULL) {
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ cairo_matrix_init_translate (&m, x, y);
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ 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_reset (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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t path_copy, *dev_path = path;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_pattern_union_t source_copy;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (x | y) {
+ cairo_matrix_t m;
+
+ 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;
+
+ if (clip != NULL) {
+ cairo_matrix_init_translate (&m, -x, -y);
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ cairo_matrix_init_translate (&m, x, y);
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ }
+
+ 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_reset (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,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_pattern_union_t source_copy;
+ cairo_glyph_t *dev_glyphs;
+ int i;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (clip && clip->all_clipped)
+ 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;
+
+ if (clip != NULL) {
+ cairo_matrix_init_translate (&m, -x, -y);
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ cairo_matrix_init_translate (&m, x, y);
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+
+ 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);
+
+ FINISH:
+ if (dev_clip != clip)
+ _cairo_clip_reset (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 000000000..61acf4b05
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-private.h
@@ -0,0 +1,103 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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"
+
+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 finished : 1;
+ unsigned is_clear : 1;
+ unsigned has_font_options : 1;
+ unsigned owns_device : 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;
+};
+
+#endif /* CAIRO_SURFACE_PRIVATE_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 000000000..bbb2bf2a0
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-snapshot-private.h
@@ -0,0 +1,48 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
+#define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
+
+#include "cairo-surface-private.h"
+
+struct _cairo_surface_snapshot {
+ cairo_surface_t base;
+
+ 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 000000000..2dbf2507a
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-snapshot.c
@@ -0,0 +1,255 @@
+/* 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-surface-snapshot-private.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;
+
+ if (surface->clone != NULL) {
+ cairo_surface_finish (surface->clone);
+ status = surface->clone->status;
+
+ cairo_surface_destroy (surface->clone);
+ }
+
+ return status;
+}
+
+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;
+
+ return _cairo_surface_acquire_source_image (surface->target, image_out, extra_out);
+}
+
+static void
+_cairo_surface_snapshot_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *extra)
+{
+ cairo_surface_snapshot_t *surface = abstract_surface;
+
+ _cairo_surface_release_source_image (surface->target, image, 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;
+
+ return _cairo_surface_get_extents (surface->target, extents);
+}
+
+static const cairo_surface_backend_t _cairo_surface_snapshot_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT,
+
+ NULL, /* create similar */
+ _cairo_surface_snapshot_finish,
+
+ _cairo_surface_snapshot_acquire_source_image,
+ _cairo_surface_snapshot_release_source_image,
+ NULL, NULL, /* acquire, release dest */
+ NULL, /* clone similar */
+ NULL, /* composite */
+ NULL, /* fill rectangles */
+ NULL, /* composite trapezoids */
+ NULL, /* create span renderer */
+ NULL, /* check span renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_surface_snapshot_get_extents,
+};
+
+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_image_surface_t *clone;
+ void *extra;
+ cairo_status_t status;
+
+ /* 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.
+ */
+
+ status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra);
+ if (unlikely (status)) {
+ snapshot->target = _cairo_surface_create_in_error (status);
+ status = _cairo_surface_set_error (surface, status);
+ return;
+ }
+
+ clone = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ image->pixman_format,
+ image->width,
+ image->height,
+ 0);
+ if (likely (clone->base.status == CAIRO_STATUS_SUCCESS)) {
+ if (clone->stride == image->stride) {
+ memcpy (clone->data, image->data, image->stride * image->height);
+ } else {
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image, NULL, clone->pixman_image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ image->width, image->height);
+ }
+ clone->base.is_clear = FALSE;
+
+ snapshot->clone = &clone->base;
+ } else {
+ snapshot->clone = &clone->base;
+ status = _cairo_surface_set_error (surface, clone->base.status);
+ }
+
+ _cairo_surface_release_source_image (snapshot->target, image, extra);
+ snapshot->target = snapshot->clone;
+ snapshot->base.type = snapshot->target->type;
+}
+
+/**
+ * _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;
+
+ if (unlikely (surface->status))
+ return _cairo_surface_create_in_error (surface->status);
+
+ if (surface->finished)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+ if (surface->snapshot_of != NULL)
+ return cairo_surface_reference (surface);
+
+ if (surface->backend->snapshot != NULL) {
+ cairo_surface_t *snap;
+
+ snap = _cairo_surface_has_snapshot (surface, surface->backend);
+ if (snap != NULL)
+ return cairo_surface_reference (snap);
+
+ snap = surface->backend->snapshot (surface);
+ if (snap != NULL) {
+ if (unlikely (snap->status))
+ return snap;
+
+ status = _cairo_surface_copy_mime_data (snap, surface);
+ if (unlikely (status)) {
+ cairo_surface_destroy (snap);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ snap->device_transform = surface->device_transform;
+ snap->device_transform_inverse = surface->device_transform_inverse;
+
+ cairo_surface_attach_snapshot (surface, snap, NULL);
+
+ return snap;
+ }
+ }
+
+ snapshot = (cairo_surface_snapshot_t *)
+ _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend);
+ if (snapshot != NULL)
+ return cairo_surface_reference (&snapshot->base);
+
+ snapshot = malloc (sizeof (cairo_surface_snapshot_t));
+ 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);
+ snapshot->base.type = surface->type;
+
+ 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-private.h b/gfx/cairo/cairo/src/cairo-surface-subsurface-private.h
new file mode 100644
index 000000000..435e1eb83
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-subsurface-private.h
@@ -0,0 +1,49 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
+#define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
+
+#include "cairo-surface-private.h"
+
+struct _cairo_surface_subsurface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t extents;
+
+ cairo_surface_t *target;
+};
+
+#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 000000000..178966263
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-subsurface.c
@@ -0,0 +1,552 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-offset-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);
+
+ 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;
+ return surface->target->backend->create_similar (surface->target, content, width, height);
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t target_clip;
+
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &rect);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_surface_offset_paint (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source, &target_clip);
+ CLEANUP:
+ _cairo_clip_fini (&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,
+ cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t target_clip;
+
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &rect);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_surface_offset_mask (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source, mask, &target_clip);
+ CLEANUP:
+ _cairo_clip_fini (&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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t target_clip;
+
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &rect);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_surface_offset_fill (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source, path, fill_rule, tolerance, antialias,
+ &target_clip);
+ CLEANUP:
+ _cairo_clip_fini (&target_clip);
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_surface_subsurface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t target_clip;
+
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &rect);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_surface_offset_stroke (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source, path, stroke_style, ctm, ctm_inverse,
+ tolerance, antialias,
+ &target_clip);
+ CLEANUP:
+ _cairo_clip_fini (&target_clip);
+ 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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
+ cairo_status_t status;
+ cairo_clip_t target_clip;
+
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &rect);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ status = _cairo_surface_offset_glyphs (surface->target,
+ -surface->extents.x, -surface->extents.y,
+ op, source,
+ scaled_font, glyphs, num_glyphs,
+ &target_clip);
+ *remaining_glyphs = 0;
+ CLEANUP:
+ _cairo_clip_fini (&target_clip);
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_subsurface_flush (void *abstract_surface)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = CAIRO_STATUS_SUCCESS;
+ if (surface->target->backend->flush != NULL)
+ status = surface->target->backend->flush (surface->target);
+
+ return status;
+}
+
+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);
+}
+
+struct extra {
+ cairo_image_surface_t *image;
+ void *image_extra;
+};
+
+static void
+cairo_surface_paint_to_target (cairo_surface_t *target,
+ cairo_surface_subsurface_t *subsurface)
+{
+ cairo_t *cr;
+
+ cr = cairo_create (target);
+
+ cairo_set_source_surface (cr,
+ subsurface->target,
+ - subsurface->extents.x,
+ - subsurface->extents.y);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+}
+
+static cairo_status_t
+_cairo_surface_subsurface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **extra_out)
+{
+ cairo_rectangle_int_t target_extents;
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+ struct extra *extra;
+ uint8_t *data;
+ cairo_bool_t ret;
+
+ if (surface->target->type == CAIRO_SURFACE_TYPE_RECORDING) {
+ cairo_recording_surface_t *meta = (cairo_recording_surface_t *) surface->target;
+ cairo_surface_t *snapshot;
+
+ snapshot = _cairo_surface_has_snapshot (&surface->base,
+ &_cairo_image_surface_backend);
+ if (snapshot != NULL) {
+ *image_out = (cairo_image_surface_t *) cairo_surface_reference (snapshot);
+ *extra_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (! _cairo_surface_has_snapshot (&meta->base,
+ &_cairo_image_surface_backend))
+ {
+ image = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_content (meta->content,
+ surface->extents.width,
+ surface->extents.height);
+ if (unlikely (image->base.status))
+ return image->base.status;
+
+ cairo_surface_paint_to_target (&image->base, surface);
+
+ cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
+
+ *image_out = image;
+ *extra_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ extra = malloc (sizeof (struct extra));
+ if (unlikely (extra == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = _cairo_surface_acquire_source_image (surface->target, &extra->image, &extra->image_extra);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ ret = _cairo_surface_get_extents (&extra->image->base, &target_extents);
+ assert (ret);
+
+ /* only copy if we need to perform sub-byte manipulation */
+ if (PIXMAN_FORMAT_BPP (extra->image->pixman_format) >= 8 &&
+ target_extents.x <= surface->extents.x &&
+ target_extents.y <= surface->extents.y &&
+ surface->extents.x + surface->extents.width <= target_extents.x + target_extents.width &&
+ surface->extents.y + surface->extents.height <= target_extents.y + target_extents.height) {
+
+ assert ((PIXMAN_FORMAT_BPP (extra->image->pixman_format) % 8) == 0);
+
+ data = extra->image->data + surface->extents.y * extra->image->stride;
+ data += PIXMAN_FORMAT_BPP (extra->image->pixman_format) / 8 * surface->extents.x;
+
+ image = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (data,
+ extra->image->pixman_format,
+ surface->extents.width,
+ surface->extents.height,
+ extra->image->stride);
+ if (unlikely ((status = image->base.status)))
+ goto CLEANUP_IMAGE;
+
+ image->base.is_clear = FALSE;
+ } else {
+ image = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ extra->image->pixman_format,
+ surface->extents.width,
+ surface->extents.height,
+ 0);
+ if (unlikely ((status = image->base.status)))
+ goto CLEANUP_IMAGE;
+
+ cairo_surface_paint_to_target (&image->base, surface);
+ }
+
+ *image_out = image;
+ *extra_out = extra;
+ return CAIRO_STATUS_SUCCESS;
+
+CLEANUP_IMAGE:
+ _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra);
+CLEANUP:
+ free (extra);
+ return status;
+}
+
+static void
+_cairo_surface_subsurface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *abstract_extra)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+
+ if (abstract_extra != NULL) {
+ struct extra *extra = abstract_extra;
+
+ _cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra);
+ free (extra);
+ }
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_surface_t *
+_cairo_surface_subsurface_snapshot (void *abstract_surface)
+{
+ cairo_surface_subsurface_t *surface = abstract_surface;
+ cairo_surface_subsurface_t *snapshot;
+
+ snapshot = malloc (sizeof (cairo_surface_subsurface_t));
+ if (unlikely (snapshot == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ _cairo_surface_init (&snapshot->base,
+ &_cairo_surface_subsurface_backend,
+ NULL, /* device */
+ surface->target->content);
+ snapshot->target = _cairo_surface_snapshot (surface->target);
+ if (unlikely (snapshot->target->status)) {
+ cairo_status_t status;
+
+ status = snapshot->target->status;
+ free (snapshot);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ snapshot->base.type = snapshot->target->type;
+ snapshot->extents = surface->extents;
+
+ return &snapshot->base;
+}
+
+static const cairo_surface_backend_t _cairo_surface_subsurface_backend = {
+ CAIRO_SURFACE_TYPE_SUBSURFACE,
+ _cairo_surface_subsurface_create_similar,
+ _cairo_surface_subsurface_finish,
+
+ _cairo_surface_subsurface_acquire_source_image,
+ _cairo_surface_subsurface_release_source_image,
+ NULL, NULL, /* acquire, release dest */
+ NULL, /* clone similar */
+ NULL, /* composite */
+ NULL, /* fill rectangles */
+ NULL, /* composite trapezoids */
+ NULL, /* create span renderer */
+ NULL, /* check span renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_surface_subsurface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_surface_subsurface_get_font_options,
+ _cairo_surface_subsurface_flush,
+ _cairo_surface_subsurface_mark_dirty,
+ NULL, /* font_fini */
+ NULL, /* glyph_fini */
+
+ _cairo_surface_subsurface_paint,
+ _cairo_surface_subsurface_mask,
+ _cairo_surface_subsurface_stroke,
+ _cairo_surface_subsurface_fill,
+ _cairo_surface_subsurface_glyphs,
+
+ _cairo_surface_subsurface_snapshot,
+};
+
+/**
+ * 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.
+ *
+ * <note><para>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.</para></note>
+ *
+ * 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 (target->status))
+ return _cairo_surface_create_in_error (target->status);
+ if (unlikely (target->finished))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+
+ surface = malloc (sizeof (cairo_surface_subsurface_t));
+ if (unlikely (surface == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ assert (_cairo_matrix_is_translation (&target->device_transform));
+ x += target->device_transform.x0;
+ y += target->device_transform.y0;
+
+ _cairo_surface_init (&surface->base,
+ &_cairo_surface_subsurface_backend,
+ NULL, /* device */
+ target->content);
+
+ /* 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 (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);
+
+ return &surface->base;
+}
+/* XXX observe mark-dirty */
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 000000000..a1f05d386
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-wrapper-private.h
@@ -0,0 +1,171 @@
+/* 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 <chris@chris-wilson.co.u>
+ */
+
+#ifndef CAIRO_SURFACE_WRAPPER_PRIVATE_H
+#define CAIRO_SURFACE_WRAPPER_PRIVATE_H
+
+#include "cairo-types-private.h"
+
+CAIRO_BEGIN_DECLS
+
+struct _cairo_surface_wrapper {
+ cairo_surface_t *target;
+
+ cairo_bool_t has_extents;
+ cairo_rectangle_int_t extents;
+};
+
+cairo_private void
+_cairo_surface_wrapper_init (cairo_surface_wrapper_t *wrapper,
+ cairo_surface_t *target);
+
+cairo_private void
+_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private void
+_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper);
+
+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,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+cairo_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,
+ cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip);
+
+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);
+
+cairo_private cairo_status_t
+_cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper);
+
+static inline cairo_bool_t
+_cairo_surface_wrapper_is_active (cairo_surface_wrapper_t *wrapper)
+{
+ return wrapper->target != (cairo_surface_t *) 0;
+}
+
+CAIRO_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 000000000..d3f126e18
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface-wrapper.c
@@ -0,0 +1,713 @@
+/* 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 <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-error-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);
+
+ /* Device transform should already have been applied before cairo_surface_wrapper_* functions
+ * are called in _cairo_gstate_copy_transformed_pattern which all the gstate drawing
+ * functions call through _cairo_gstate_copy_transformed_source and such. */
+
+ if (! _cairo_matrix_is_identity (ctm_inverse))
+ _cairo_pattern_transform (pattern, ctm_inverse);
+}
+
+static inline cairo_bool_t
+_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper)
+{
+ return ! _cairo_matrix_is_identity (&wrapper->target->device_transform);
+}
+
+static cairo_bool_t
+_cairo_surface_wrapper_needs_extents_transform (cairo_surface_wrapper_t *wrapper)
+{
+ return wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y);
+}
+
+cairo_status_t
+_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper,
+ cairo_image_surface_t **image_out,
+ 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);
+}
+
+cairo_status_t
+_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_pattern_union_t source_copy;
+ cairo_clip_t target_clip;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ if (wrapper->has_extents) {
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = clip = &target_clip;
+ }
+
+ if (clip && clip->all_clipped) {
+ status = CAIRO_STATUS_SUCCESS;
+ goto FINISH;
+ }
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
+ _cairo_surface_wrapper_needs_extents_transform (wrapper))
+ {
+ cairo_matrix_t m;
+
+ cairo_matrix_init_identity (&m);
+
+ if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
+ cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper))
+ cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
+
+ if (clip != NULL) {
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ 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);
+
+ FINISH:
+ if (wrapper->has_extents)
+ _cairo_clip_reset (&target_clip);
+ if (dev_clip != clip)
+ _cairo_clip_reset (dev_clip);
+ return status;
+}
+
+cairo_status_t
+_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_pattern_union_t source_copy;
+ cairo_pattern_union_t mask_copy;
+ cairo_clip_t target_clip;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ if (wrapper->has_extents) {
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = clip = &target_clip;
+ }
+
+ if (clip && clip->all_clipped) {
+ status = CAIRO_STATUS_SUCCESS;
+ goto FINISH;
+ }
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
+ _cairo_surface_wrapper_needs_extents_transform (wrapper))
+ {
+ cairo_matrix_t m;
+
+ cairo_matrix_init_identity (&m);
+
+ if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
+ cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper))
+ cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
+
+ if (clip != NULL) {
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ 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);
+
+ FINISH:
+ if (wrapper->has_extents)
+ _cairo_clip_reset (&target_clip);
+ if (dev_clip != clip)
+ _cairo_clip_reset (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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t path_copy, *dev_path = path;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_matrix_t dev_ctm = *ctm;
+ cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
+ cairo_pattern_union_t source_copy;
+ cairo_clip_t target_clip;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ if (wrapper->has_extents) {
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = clip = &target_clip;
+ }
+
+ if (clip && clip->all_clipped) {
+ status = CAIRO_STATUS_SUCCESS;
+ goto FINISH;
+ }
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
+ _cairo_surface_wrapper_needs_extents_transform (wrapper))
+ {
+ cairo_matrix_t m;
+
+ cairo_matrix_init_identity (&m);
+
+ if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
+ cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper))
+ cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
+
+ 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;
+
+ if (clip != NULL) {
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
+
+ status = cairo_matrix_invert (&m);
+ 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;
+ }
+ else
+ {
+ if (clip != NULL) {
+ dev_clip = &clip_copy;
+ _cairo_clip_init_copy (&clip_copy, clip);
+ }
+ }
+
+ status = _cairo_surface_stroke (wrapper->target, op, source,
+ dev_path, stroke_style,
+ &dev_ctm, &dev_ctm_inverse,
+ tolerance, antialias,
+ dev_clip);
+
+ FINISH:
+ if (dev_path != path)
+ _cairo_path_fixed_fini (dev_path);
+ if (wrapper->has_extents)
+ _cairo_clip_reset (&target_clip);
+ if (dev_clip != clip)
+ _cairo_clip_reset (dev_clip);
+ 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,
+ cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t path_copy, *dev_path = path;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_matrix_t dev_ctm = *stroke_ctm;
+ cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse;
+ cairo_pattern_union_t stroke_source_copy;
+ cairo_pattern_union_t fill_source_copy;
+ cairo_clip_t target_clip;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ if (wrapper->has_extents) {
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = clip = &target_clip;
+ }
+
+ if (clip && clip->all_clipped) {
+ status = CAIRO_STATUS_SUCCESS;
+ goto FINISH;
+ }
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
+ _cairo_surface_wrapper_needs_extents_transform (wrapper))
+ {
+ cairo_matrix_t m;
+
+ cairo_matrix_init_identity (&m);
+
+ if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
+ cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper))
+ cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
+
+ 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;
+
+ if (clip != NULL) {
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
+
+ status = cairo_matrix_invert (&m);
+ 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;
+ }
+ else
+ {
+ if (clip != NULL) {
+ dev_clip = &clip_copy;
+ _cairo_clip_init_copy (&clip_copy, clip);
+ }
+ }
+
+ status = _cairo_surface_fill_stroke (wrapper->target,
+ fill_op, fill_source, fill_rule,
+ 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);
+ if (wrapper->has_extents)
+ _cairo_clip_reset (&target_clip);
+ if (dev_clip != clip)
+ _cairo_clip_reset (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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_path_fixed_t path_copy, *dev_path = path;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_pattern_union_t source_copy;
+ cairo_clip_t target_clip;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ if (wrapper->has_extents) {
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = clip = &target_clip;
+ }
+
+ if (clip && clip->all_clipped) {
+ status = CAIRO_STATUS_SUCCESS;
+ goto FINISH;
+ }
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
+ _cairo_surface_wrapper_needs_extents_transform (wrapper))
+ {
+ cairo_matrix_t m;
+
+ cairo_matrix_init_identity (&m);
+
+ if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
+ cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper))
+ cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
+
+ 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;
+
+ if (clip != NULL) {
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ status = cairo_matrix_invert (&m);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _copy_transformed_pattern (&source_copy.base, source, &m);
+ source = &source_copy.base;
+ }
+ else
+ {
+ if (clip != NULL) {
+ dev_clip = &clip_copy;
+ _cairo_clip_init_copy (&clip_copy, clip);
+ }
+ }
+
+ status = _cairo_surface_fill (wrapper->target, op, source,
+ dev_path, fill_rule,
+ tolerance, antialias,
+ dev_clip);
+
+ FINISH:
+ if (dev_path != path)
+ _cairo_path_fixed_fini (dev_path);
+ if (wrapper->has_extents)
+ _cairo_clip_reset (&target_clip);
+ if (dev_clip != clip)
+ _cairo_clip_reset (dev_clip);
+ 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,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_clip_t clip_copy, *dev_clip = clip;
+ cairo_glyph_t *dev_glyphs = glyphs;
+ cairo_pattern_union_t source_copy;
+ cairo_clip_t target_clip;
+
+ if (unlikely (wrapper->target->status))
+ return wrapper->target->status;
+
+ if (glyphs == NULL || num_glyphs == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (wrapper->has_extents) {
+ _cairo_clip_init_copy (&target_clip, clip);
+ status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = clip = &target_clip;
+ }
+
+ if (clip && clip->all_clipped) {
+ status = CAIRO_STATUS_SUCCESS;
+ goto FINISH;
+ }
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
+ _cairo_surface_wrapper_needs_extents_transform (wrapper))
+ {
+ cairo_matrix_t m;
+ int i;
+
+ cairo_matrix_init_identity (&m);
+
+ if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
+ cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
+
+ if (_cairo_surface_wrapper_needs_device_transform (wrapper))
+ cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
+
+ if (clip != NULL) {
+ status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
+ if (unlikely (status))
+ goto FINISH;
+
+ dev_clip = &clip_copy;
+ }
+
+ dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (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 (clip != NULL) {
+ dev_clip = &clip_copy;
+ _cairo_clip_init_copy (&clip_copy, clip);
+ }
+ }
+
+ status = _cairo_surface_show_text_glyphs (wrapper->target, op, source,
+ utf8, utf8_len,
+ dev_glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ dev_clip);
+
+ FINISH:
+ if (dev_clip != clip)
+ _cairo_clip_reset (dev_clip);
+ if (wrapper->has_extents)
+ _cairo_clip_reset (&target_clip);
+ if (dev_glyphs != glyphs)
+ free (dev_glyphs);
+ return status;
+}
+
+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_similar_scratch (wrapper->target,
+ content, width, height);
+}
+
+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);
+ }
+}
+
+void
+_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper,
+ const cairo_rectangle_int_t *extents)
+{
+ if (extents != NULL) {
+ wrapper->extents = *extents;
+ wrapper->has_extents = TRUE;
+ } else {
+ wrapper->has_extents = FALSE;
+ }
+}
+
+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)
+{
+ return _cairo_surface_snapshot (wrapper->target);
+}
+
+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);
+ wrapper->has_extents = FALSE;
+}
+
+void
+_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper)
+{
+ cairo_surface_destroy (wrapper->target);
+}
+
+cairo_status_t
+_cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper)
+{
+ if (wrapper->target->backend->flush) {
+ return wrapper->target->backend->flush(wrapper->target);
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c
new file mode 100644
index 000000000..b57b944b8
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-surface.c
@@ -0,0 +1,3326 @@
+/* -*- 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-surface-fallback-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-region-private.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 <firstterm>context</firstterm>.
+ *
+ * A cairo surface is created by using <firstterm>backend</firstterm>-specific
+ * constructors, typically of the form
+ * cairo_<emphasis>backend</emphasis>_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.
+ * <example>
+ * <title>Directly modifying an image surface</title>
+ * <programlisting>
+ * 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);
+ * }
+ * </programlisting>
+ * </example>
+ * 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 */ \
+ FALSE, /* finished */ \
+ TRUE, /* is_clear */ \
+ FALSE, /* has_font_options */ \
+ FALSE, /* owns_device */ \
+ 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);
+
+/**
+ * _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_status_t
+_cairo_surface_set_error (cairo_surface_t *surface,
+ cairo_status_t status)
+{
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+ status = CAIRO_STATUS_SUCCESS;
+
+ if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ /* Don't overwrite an existing error. This preserves the first
+ * error, which is the most significant. */
+ _cairo_status_set_error (&surface->status, status);
+
+ 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;
+}
+slim_hidden_def (cairo_surface_get_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;
+}
+slim_hidden_def(cairo_surface_get_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.
+ **/
+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)
+ {
+ /* XXX is_similar? */
+ if (snapshot->backend == backend)
+ return snapshot;
+ }
+
+ return NULL;
+}
+
+static cairo_bool_t
+_cairo_surface_is_writable (cairo_surface_t *surface)
+{
+ return ! surface->finished &&
+ surface->snapshot_of == NULL &&
+ ! _cairo_surface_has_snapshots (surface) &&
+ ! _cairo_surface_has_mime_data (surface);
+}
+
+static void
+_cairo_surface_begin_modification (cairo_surface_t *surface)
+{
+ assert (surface->status == CAIRO_STATUS_SUCCESS);
+ assert (! surface->finished);
+ assert (surface->snapshot_of == NULL);
+
+ cairo_surface_detach_snapshots (surface);
+ _cairo_surface_detach_mime_data (surface);
+}
+
+void
+_cairo_surface_init (cairo_surface_t *surface,
+ const cairo_surface_backend_t *backend,
+ cairo_device_t *device,
+ cairo_content_t content)
+{
+ CAIRO_MUTEX_INITIALIZE ();
+
+ surface->backend = backend;
+ surface->device = cairo_device_reference (device);
+ surface->content = content;
+ surface->type = backend->type;
+
+ 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->is_clear = FALSE;
+ surface->owns_device = (device != NULL);
+ surface->has_font_options = FALSE;
+ surface->permit_subpixel_antialiasing = TRUE;
+
+ _cairo_user_data_array_init (&surface->user_data);
+ _cairo_user_data_array_init (&surface->mime_data);
+
+ 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;
+}
+
+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_t *
+_cairo_surface_create_similar_scratch (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ cairo_surface_t *surface;
+
+ if (unlikely (other->status))
+ return _cairo_surface_create_in_error (other->status);
+
+ if (other->backend->create_similar == NULL)
+ return NULL;
+
+ surface = other->backend->create_similar (other,
+ content, width, height);
+ if (surface == NULL || surface->status)
+ return surface;
+
+ _cairo_surface_copy_similar_properties (surface, other);
+
+ return surface;
+}
+
+/**
+ * cairo_surface_create_similar:
+ * @other: an existing surface used to select the backend of the new surface
+ * @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
+ * 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.)
+ *
+ * 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.
+ **/
+cairo_surface_t *
+cairo_surface_create_similar (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ 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 (! CAIRO_CONTENT_VALID (content)))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
+
+ return _cairo_surface_create_similar_solid (other,
+ content, width, height,
+ CAIRO_COLOR_TRANSPARENT,
+ TRUE);
+}
+
+cairo_surface_t *
+_cairo_surface_create_similar_solid (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height,
+ const cairo_color_t *color,
+ cairo_bool_t allow_fallback)
+{
+ cairo_status_t status;
+ cairo_surface_t *surface;
+ cairo_solid_pattern_t pattern;
+
+ surface = _cairo_surface_create_similar_scratch (other, content,
+ width, height);
+ if (surface == NULL && allow_fallback)
+ surface = _cairo_image_surface_create_with_content (content,
+ width, height);
+ if (surface == NULL || surface->status)
+ return surface;
+
+ _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_t *
+_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other,
+ const cairo_solid_pattern_t *solid_pattern)
+{
+ if (other->backend->create_solid_pattern_surface != NULL) {
+ cairo_surface_t *surface;
+
+ surface = other->backend->create_solid_pattern_surface (other,
+ solid_pattern);
+ if (surface)
+ return surface;
+ }
+
+ return _cairo_surface_create_similar_solid (other,
+ _cairo_color_get_content (&solid_pattern->color),
+ 1, 1,
+ &solid_pattern->color,
+ FALSE);
+}
+
+cairo_int_status_t
+_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other,
+ cairo_surface_t *solid_surface,
+ const cairo_solid_pattern_t *solid_pattern)
+{
+ /* Solid pattern surface for these backends are special and not trivial
+ * to repaint. Skip repainting.
+ *
+ * This does not work optimally with things like analysis surface that
+ * are proxies. But returning UNSUPPORTED is *safe* as it only
+ * disables some caching.
+ */
+ if (other->backend->create_solid_pattern_surface != NULL &&
+ ! other->backend->can_repaint_solid_pattern_surface (solid_surface,
+ solid_pattern))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ return _cairo_surface_paint (solid_surface,
+ CAIRO_OPERATOR_SOURCE,
+ &solid_pattern->base,
+ NULL);
+}
+
+/**
+ * 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.
+ *
+ * The number of references to a #cairo_surface_t can be get using
+ * cairo_surface_get_reference_count().
+ *
+ * Return value: the referenced #cairo_surface_t.
+ **/
+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().
+ **/
+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 (surface);
+
+ /* paranoid check that nobody took a reference whilst finishing */
+ assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
+
+ _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);
+
+ 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);
+}
+
+/**
+ * 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 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.
+ **/
+void
+cairo_surface_finish (cairo_surface_t *surface)
+{
+ cairo_status_t status;
+
+ if (surface == NULL)
+ return;
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ if (surface->finished)
+ return;
+
+ /* update the snapshots *before* we declare the surface as finished */
+ cairo_surface_detach_snapshots (surface);
+ if (surface->snapshot_of != NULL)
+ cairo_surface_detach_snapshot (surface);
+
+ cairo_surface_flush (surface);
+ surface->finished = TRUE;
+
+ /* call finish even if in error mode */
+ if (surface->backend->finish) {
+ status = surface->backend->finish (surface);
+ if (unlikely (status))
+ status = _cairo_surface_set_error (surface, status);
+ }
+}
+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.
+ **/
+void *
+cairo_surface_get_user_data (cairo_surface_t *surface,
+ const cairo_user_data_key_t *key)
+{
+ return _cairo_user_data_array_get_data (&surface->user_data,
+ key);
+}
+
+/**
+ * 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.
+ **/
+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;
+
+ 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;
+ if (unlikely (surface->status))
+ return;
+
+ /* The number of mime-types attached to a surface is usually small,
+ * typically zero. Therefore it is quicker to do a strcmp() against
+ * each key than it is to intern the string (i.e. compute a hash,
+ * search the hash table, and do a final strcmp).
+ */
+ num_slots = surface->mime_data.num_elements;
+ slots = _cairo_array_index (&surface->mime_data, 0);
+ for (i = 0; i < num_slots; i++) {
+ if (strcmp ((char *) slots[i].key, mime_type) == 0) {
+ cairo_mime_data_t *mime_data = slots[i].user_data;
+
+ *data = mime_data->data;
+ *length = mime_data->length;
+ return;
+ }
+ }
+}
+slim_hidden_def (cairo_surface_get_mime_data);
+
+static void
+_cairo_mime_data_destroy (void *ptr)
+{
+ cairo_mime_data_t *mime_data = ptr;
+
+ if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count))
+ return;
+
+ if (mime_data->destroy && mime_data->closure)
+ mime_data->destroy (mime_data->closure);
+
+ free (mime_data);
+}
+
+/**
+ * CAIRO_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_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.
+ *
+ * See corresponding backend surface docs for details about which MIME
+ * types it can handle. Caution: the associated MIME data will be
+ * discarded if you draw on the surface afterwards. Use this function
+ * with care.
+ *
+ * Since: 1.10
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
+ * slot could not be allocated for the user data.
+ **/
+cairo_status_t
+cairo_surface_set_mime_data (cairo_surface_t *surface,
+ const char *mime_type,
+ const unsigned char *data,
+ unsigned long length,
+ cairo_destroy_func_t destroy,
+ void *closure)
+{
+ cairo_status_t status;
+ cairo_mime_data_t *mime_data;
+
+ if (unlikely (surface->status))
+ return surface->status;
+ if (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 = malloc (sizeof (cairo_mime_data_t));
+ if (unlikely (mime_data == NULL))
+ return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1);
+
+ mime_data->data = (unsigned char *) data;
+ mime_data->length = length;
+ mime_data->destroy = destroy;
+ mime_data->closure = closure;
+ } else
+ mime_data = NULL;
+
+ status = _cairo_user_data_array_set_data (&surface->mime_data,
+ (cairo_user_data_key_t *) mime_type,
+ mime_data,
+ _cairo_mime_data_destroy);
+ if (unlikely (status)) {
+ if (mime_data != NULL)
+ free (mime_data);
+
+ return _cairo_surface_set_error (surface, status);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_surface_set_mime_data);
+
+static void
+_cairo_mime_data_reference (const void *key, void *elt, void *closure)
+{
+ cairo_mime_data_t *mime_data = elt;
+
+ _cairo_reference_count_inc (&mime_data->ref_count);
+}
+
+cairo_status_t
+_cairo_surface_copy_mime_data (cairo_surface_t *dst,
+ cairo_surface_t *src)
+{
+ cairo_status_t status;
+
+ if (dst->status)
+ return dst->status;
+
+ if (src->status)
+ return _cairo_surface_set_error (dst, src->status);
+
+ /* first copy the mime-data, discarding any already set on dst */
+ status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data);
+ if (unlikely (status))
+ return _cairo_surface_set_error (dst, status);
+
+ /* now increment the reference counters for the copies */
+ _cairo_user_data_array_foreach (&dst->mime_data,
+ _cairo_mime_data_reference,
+ NULL);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_surface_set_font_options:
+ * @surface: a #cairo_surface_t
+ * @options: a #cairo_font_options_t object that contains the
+ * 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)
+{
+ cairo_status_t status;
+
+ if (surface->status)
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (surface->finished) {
+ status = _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ 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().
+ **/
+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_flush:
+ * @surface: a #cairo_surface_t
+ *
+ * Do any pending drawing for the surface and also restore any
+ * temporary modifications cairo has made to the surface's
+ * state. This function must be called before switching from
+ * drawing on the surface with cairo to drawing on it directly
+ * with native APIs. If the surface doesn't support direct access,
+ * then this function does nothing.
+ **/
+void
+cairo_surface_flush (cairo_surface_t *surface)
+{
+ cairo_status_t status;
+
+ if (surface->status)
+ return;
+
+ if (surface->finished)
+ return;
+
+ /* update the current snapshots *before* the user updates the surface */
+ cairo_surface_detach_snapshots (surface);
+
+ if (surface->backend->flush) {
+ status = surface->backend->flush (surface);
+ if (unlikely (status))
+ status = _cairo_surface_set_error (surface, status);
+ }
+}
+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.
+ */
+void
+cairo_surface_mark_dirty (cairo_surface_t *surface)
+{
+ cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1);
+}
+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.
+ */
+void
+cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ cairo_status_t status;
+
+ if (surface->status)
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (surface->finished) {
+ status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ 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;
+
+ 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))
+ 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
+ * @sx: a scale factor in the X direction
+ * @sy: a scale factor in the Y direction
+ *
+ * Private function for setting an extra scale factor to affect all
+ * drawing to a surface. This is used, for example, when replaying a
+ * recording surface to an image fallback intended for an eventual
+ * vector-oriented backend. Since the recording surface will record
+ * coordinates in one backend space, but the image fallback uses a
+ * different backend space, (differing by the fallback resolution
+ * scale factors), we need a scale factor correction.
+ *
+ * Caution: Not all places we use device transform correctly handle
+ * both a translate and a scale. An audit would be nice.
+ **/
+void
+_cairo_surface_set_device_scale (cairo_surface_t *surface,
+ double sx,
+ double sy)
+{
+ cairo_status_t status;
+
+ if (surface->status)
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (surface->finished) {
+ status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ _cairo_surface_begin_modification (surface);
+
+ surface->device_transform.xx = sx;
+ surface->device_transform.yy = sy;
+ 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);
+}
+
+/**
+ * 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.
+ **/
+void
+cairo_surface_set_device_offset (cairo_surface_t *surface,
+ double x_offset,
+ double y_offset)
+{
+ cairo_status_t status;
+
+ if (surface->status)
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (surface->finished) {
+ status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+ _cairo_surface_begin_modification (surface);
+
+ 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 resoultion 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 (surface->status)
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (surface->finished) {
+ status = _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.
+ */
+ status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX);
+ return;
+ }
+
+ _cairo_surface_begin_modification (surface);
+
+ 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;
+}
+
+int
+_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface)
+{
+ return surface->backend->fill == NULL ? 10240 : 256;
+}
+
+cairo_bool_t
+_cairo_surface_has_device_transform (cairo_surface_t *surface)
+{
+ 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 (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);
+
+ if (PIXMAN_FORMAT_BPP((*image_out)->pixman_format) == 0) {
+ volatile char* acquire_source_image_ptr[10];
+ volatile char* crasher;
+ int i;
+ for (i = 0; i < 10; i++) {
+ acquire_source_image_ptr[i] = (char*)surface->backend->acquire_source_image;
+ }
+ crasher = NULL;
+ *crasher = acquire_source_image_ptr[5];
+ }
+ _cairo_debug_check_image_surface_is_defined (&(*image_out)->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_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);
+}
+
+/**
+ * _cairo_surface_acquire_dest_image:
+ * @surface: a #cairo_surface_t
+ * @interest_rect: area of @surface for which fallback drawing is being done.
+ * A value of %NULL indicates that the entire surface is desired.
+ * XXXX I'd like to get rid of being able to pass %NULL here (nothing seems to)
+ * @image_out: location to store a pointer to an image surface that includes at least
+ * the intersection of @interest_rect with the visible area of @surface.
+ * This surface could be @surface itself, a surface held internal to @surface,
+ * or it could be a new surface with a copy of the relevant portion of @surface.
+ * If a new surface is created, it should have the same channels and depth
+ * as @surface so that copying to and from it is exact.
+ * @image_rect: location to store area of the original surface occupied
+ * by the surface stored in @image.
+ * @image_extra: location to store image specific backend data
+ *
+ * Retrieves a local image for a surface for implementing a fallback drawing
+ * operation. After calling this function, the implementation of the fallback
+ * drawing operation draws the primitive to the surface stored in @image_out
+ * then calls _cairo_surface_release_dest_image(),
+ * which, if a temporary surface was created, copies the bits back to the
+ * main surface and frees the temporary surface.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY.
+ * %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that
+ * the backend can't draw with fallbacks. It's possible for the routine
+ * to store %NULL in @local_out and return %CAIRO_STATUS_SUCCESS;
+ * that indicates that no part of @interest_rect is visible, so no drawing
+ * is necessary. _cairo_surface_release_dest_image() should not be called in that
+ * case.
+ **/
+cairo_status_t
+_cairo_surface_acquire_dest_image (cairo_surface_t *surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+{
+ cairo_status_t status;
+
+ if (surface->status)
+ return surface->status;
+
+ assert (_cairo_surface_is_writable (surface));
+
+ if (surface->backend->acquire_dest_image == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = surface->backend->acquire_dest_image (surface,
+ interest_rect,
+ image_out,
+ image_rect,
+ image_extra);
+ if (unlikely (status))
+ return _cairo_surface_set_error (surface, status);
+
+ _cairo_debug_check_image_surface_is_defined (&(*image_out)->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_surface_release_dest_image:
+ * @surface: a #cairo_surface_t
+ * @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image()
+ * @image: same as returned from the matching _cairo_surface_acquire_dest_image()
+ * @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image()
+ * @image_extra: same as return from the matching _cairo_surface_acquire_dest_image()
+ *
+ * Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if
+ * necessary, copying the image from @image back to @surface and freeing any
+ * resources that were allocated.
+ **/
+void
+_cairo_surface_release_dest_image (cairo_surface_t *surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ assert (_cairo_surface_is_writable (surface));
+
+ if (surface->backend->release_dest_image)
+ surface->backend->release_dest_image (surface, interest_rect,
+ image, image_rect, image_extra);
+}
+
+static cairo_status_t
+_cairo_recording_surface_clone_similar (cairo_surface_t *surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_recording_surface_t *recorder = (cairo_recording_surface_t *) src;
+ cairo_surface_t *similar;
+ cairo_status_t status;
+
+ similar = _cairo_surface_has_snapshot (src, surface->backend);
+ if (similar != NULL) {
+ *clone_out = cairo_surface_reference (similar);
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (recorder->unbounded ||
+ width*height*8 < recorder->extents.width*recorder->extents.height)
+ {
+ similar = _cairo_surface_create_similar_solid (surface,
+ src->content,
+ width, height,
+ CAIRO_COLOR_TRANSPARENT,
+ FALSE);
+ if (similar == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (unlikely (similar->status))
+ return similar->status;
+
+ cairo_surface_set_device_offset (similar, -src_x, -src_y);
+
+ status = _cairo_recording_surface_replay (src, similar);
+ if (unlikely (status)) {
+ cairo_surface_destroy (similar);
+ return status;
+ }
+ } else {
+ similar = _cairo_surface_create_similar_scratch (surface,
+ src->content,
+ recorder->extents.width,
+ recorder->extents.height);
+ if (similar == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (unlikely (similar->status))
+ return similar->status;
+
+ status = _cairo_recording_surface_replay (src, similar);
+ if (unlikely (status)) {
+ cairo_surface_destroy (similar);
+ return status;
+ }
+
+ cairo_surface_attach_snapshot (src, similar, NULL);
+
+ src_x = src_y = 0;
+ }
+
+ *clone_out = similar;
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+struct acquire_source_image_data
+{
+ cairo_surface_t *src;
+ cairo_image_surface_t *image;
+ void *image_extra;
+};
+
+static void
+_wrap_release_source_image (void *data)
+{
+ struct acquire_source_image_data *acquire_data = data;
+ _cairo_surface_release_source_image (acquire_data->src,
+ acquire_data->image,
+ acquire_data->image_extra);
+ free(data);
+}
+
+static cairo_status_t
+_wrap_image (cairo_surface_t *src,
+ cairo_image_surface_t *image,
+ void *image_extra,
+ cairo_image_surface_t **out)
+{
+ static cairo_user_data_key_t wrap_image_key;
+ cairo_image_surface_t *surface;
+ cairo_status_t status;
+
+ struct acquire_source_image_data *data = malloc (sizeof (*data));
+ if (unlikely (data == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ data->src = src;
+ data->image = image;
+ data->image_extra = image_extra;
+
+ surface = (cairo_image_surface_t*)
+ _cairo_image_surface_create_with_pixman_format (image->data,
+ image->pixman_format,
+ image->width,
+ image->height,
+ image->stride);
+ status = surface->base.status;
+ if (status) {
+ free (data);
+ return status;
+ }
+
+ status = _cairo_user_data_array_set_data (&surface->base.user_data,
+ &wrap_image_key,
+ data,
+ _wrap_release_source_image);
+ if (status) {
+ cairo_surface_destroy (&surface->base);
+ free (data);
+ return status;
+ }
+
+ pixman_image_set_component_alpha (
+ surface->pixman_image,
+ pixman_image_get_component_alpha (image->pixman_image));
+
+ *out = surface;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * _cairo_surface_clone_similar:
+ * @surface: a #cairo_surface_t
+ * @src: the source image
+ * @src_x: extent for the rectangle in src we actually care about
+ * @src_y: extent for the rectangle in src we actually care about
+ * @width: extent for the rectangle in src we actually care about
+ * @height: extent for the rectangle in src we actually care about
+ * @clone_out: location to store a surface compatible with @surface
+ * and with contents identical to @src. The caller must call
+ * cairo_surface_destroy() on the result.
+ *
+ * Creates a surface with contents identical to @src but that
+ * can be used efficiently with @surface. If @surface and @src are
+ * already compatible then it may return a new reference to @src.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored
+ * in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another
+ * error like %CAIRO_STATUS_NO_MEMORY.
+ **/
+cairo_status_t
+_cairo_surface_clone_similar (cairo_surface_t *surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+ cairo_image_surface_t *image;
+ void *image_extra;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (unlikely (surface->finished))
+ return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+#if CAIRO_HAS_TEE_SURFACE
+
+ if (src->type == CAIRO_SURFACE_TYPE_TEE) {
+ cairo_surface_t *match;
+
+ match = _cairo_tee_surface_find_match (src,
+ surface->backend,
+ src->content);
+ if (match != NULL)
+ src = match;
+ }
+
+#endif
+
+ if (surface->backend->clone_similar != NULL) {
+ status = surface->backend->clone_similar (surface, src,
+ src_x, src_y,
+ width, height,
+ clone_offset_x,
+ clone_offset_y,
+ clone_out);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ if (_cairo_surface_is_image (src))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* First check to see if we can replay to a similar surface */
+ if (_cairo_surface_is_recording (src)) {
+ return _cairo_recording_surface_clone_similar (surface, src,
+ src_x, src_y,
+ width, height,
+ clone_offset_x,
+ clone_offset_y,
+ clone_out);
+ }
+
+ /* If we failed, try again with an image surface */
+ status = _cairo_surface_acquire_source_image (src, &image, &image_extra);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _wrap_image(src, image, image_extra, &image);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ _cairo_surface_release_source_image (src, image, image_extra);
+ } else {
+ status =
+ surface->backend->clone_similar (surface, &image->base,
+ src_x, src_y,
+ width, height,
+ clone_offset_x,
+ clone_offset_y,
+ clone_out);
+ cairo_surface_destroy(&image->base);
+ }
+ }
+ }
+ }
+
+ /* If we're still unsupported, hit our fallback path to get a clone */
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status =
+ _cairo_surface_fallback_clone_similar (surface, src,
+ src_x, src_y,
+ width, height,
+ clone_offset_x,
+ clone_offset_y,
+ clone_out);
+ }
+
+ if (unlikely (status))
+ return status;
+
+ /* Update the clone's device_transform (which the underlying surface
+ * backend knows nothing about) */
+ if (*clone_out != src) {
+ (*clone_out)->device_transform = src->device_transform;
+ (*clone_out)->device_transform_inverse = src->device_transform_inverse;
+ }
+
+ return status;
+}
+
+/**
+ * _cairo_surface_is_similar
+ * @surface_a: a #cairo_surface_t
+ * @surface_b: a #cairo_surface_t
+ * @content: a #cairo_content_t
+ *
+ * Find out whether the given surfaces share the same backend,
+ * and if so, whether they can be considered similar.
+ *
+ * The definition of "similar" depends on the backend. In
+ * general, it means that the surface is equivalent to one
+ * that would have been generated by a call to cairo_surface_create_similar().
+ *
+ * Return value: %TRUE if the surfaces are similar.
+ **/
+cairo_bool_t
+_cairo_surface_is_similar (cairo_surface_t *surface_a,
+ cairo_surface_t *surface_b)
+{
+ if (surface_a->backend != surface_b->backend)
+ return FALSE;
+
+ if (surface_a->backend->is_similar != NULL)
+ return surface_a->backend->is_similar (surface_a, surface_b);
+
+ return TRUE;
+}
+
+cairo_status_t
+_cairo_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ cairo_surface_t *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_int_status_t status;
+
+ if (unlikely (dst->status))
+ return dst->status;
+
+ assert (_cairo_surface_is_writable (dst));
+
+ if (mask) {
+ /* These operators aren't interpreted the same way by the backends;
+ * they are implemented in terms of other operators in cairo-gstate.c
+ */
+ assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
+ }
+
+ if (dst->backend->composite) {
+ status = dst->backend->composite (op,
+ src, mask, dst,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height,
+ clip_region);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return _cairo_surface_set_error (dst, status);
+ }
+
+ return _cairo_surface_set_error (dst,
+ _cairo_surface_fallback_composite (op,
+ src, mask, dst,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height,
+ clip_region));
+}
+
+/**
+ * _cairo_surface_fill_rectangle:
+ * @surface: a #cairo_surface_t
+ * @op: the operator to apply to the rectangle
+ * @color: the source color
+ * @x: X coordinate of rectangle, in backend coordinates
+ * @y: Y coordinate of rectangle, in backend coordinates
+ * @width: width of rectangle, in backend coordinates
+ * @height: height of rectangle, in backend coordinates
+ *
+ * Applies an operator to a rectangle using a solid color as the source.
+ * See _cairo_surface_fill_rectangles() for full details.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred
+ **/
+cairo_status_t
+_cairo_surface_fill_rectangle (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ cairo_rectangle_int_t rect;
+
+ if (surface->status)
+ return surface->status;
+
+ assert (_cairo_surface_is_writable (surface));
+
+ rect.x = x;
+ rect.y = y;
+ rect.width = width;
+ rect.height = height;
+
+ return _cairo_surface_fill_rectangles (surface, op, color, &rect, 1);
+}
+
+/**
+ * _cairo_surface_fill_region:
+ * @surface: a #cairo_surface_t
+ * @op: the operator to apply to the region
+ * @color: the source color
+ * @region: the region to modify, in backend coordinates
+ *
+ * Applies an operator to a set of rectangles specified as a
+ * #cairo_region_t using a solid color as the source.
+ * See _cairo_surface_fill_rectangles() for full details.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred
+ **/
+cairo_status_t
+_cairo_surface_fill_region (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_region_t *region)
+{
+ int num_rects;
+ cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+ cairo_rectangle_int_t *rects = stack_rects;
+ cairo_status_t status;
+ int i;
+
+ if (surface->status)
+ return surface->status;
+
+ assert (_cairo_surface_is_writable (surface));
+
+ num_rects = cairo_region_num_rectangles (region);
+ if (num_rects == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* catch a common reduction of _cairo_clip_combine_with_surface() */
+ if (op == CAIRO_OPERATOR_IN &&
+ _cairo_color_equal (color, CAIRO_COLOR_WHITE))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (num_rects > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (num_rects,
+ sizeof (cairo_rectangle_int_t));
+ if (rects == NULL) {
+ return _cairo_surface_set_error (surface,
+ _cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+ }
+
+ for (i = 0; i < num_rects; i++)
+ cairo_region_get_rectangle (region, i, &rects[i]);
+
+ status = _cairo_surface_fill_rectangles (surface,
+ op, color, rects, num_rects);
+
+ if (rects != stack_rects)
+ free (rects);
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+/**
+ * _cairo_surface_fill_rectangles:
+ * @surface: a #cairo_surface_t
+ * @op: the operator to apply to the region
+ * @color: the source color
+ * @rects: the rectangles to modify, in backend coordinates
+ * @num_rects: the number of rectangles in @rects
+ *
+ * Applies an operator to a set of rectangles using a solid color
+ * as the source. Note that even if the operator is an unbounded operator
+ * such as %CAIRO_OPERATOR_IN, only the given set of rectangles
+ * is affected. This differs from _cairo_surface_composite_trapezoids()
+ * where the entire destination rectangle is cleared.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred
+ **/
+cairo_status_t
+_cairo_surface_fill_rectangles (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_int_status_t status;
+
+ if (surface->status)
+ return surface->status;
+
+ assert (_cairo_surface_is_writable (surface));
+
+ if (num_rects == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->backend->fill_rectangles) {
+ status = surface->backend->fill_rectangles (surface,
+ op, color,
+ rects, num_rects);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return _cairo_surface_set_error (surface, status);
+ }
+
+ return _cairo_surface_set_error (surface,
+ _cairo_surface_fallback_fill_rectangles (surface,
+ op, color,
+ rects, num_rects));
+}
+
+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;
+}
+
+cairo_status_t
+_cairo_surface_paint (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_rectangle_int_t extents;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_OVER &&
+ _cairo_pattern_is_clear (source))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_surface_begin_modification (surface);
+
+ if (surface->backend->paint != NULL) {
+ status = surface->backend->paint (surface, op, source, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto FINISH;
+ }
+
+ status = _cairo_surface_fallback_paint (surface, op, source, clip);
+
+ FINISH:
+ surface->is_clear = op == CAIRO_OPERATOR_CLEAR &&
+ (clip == NULL ||
+ (_cairo_surface_get_extents (surface, &extents) &&
+ _cairo_clip_contains_rectangle (clip, &extents)));
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_mask (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
+ return CAIRO_STATUS_SUCCESS;
+
+ /* If 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;
+ }
+
+ if (op == CAIRO_OPERATOR_OVER &&
+ _cairo_pattern_is_clear (source))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ status = _pattern_has_error (mask);
+ if (unlikely (status))
+ return status;
+
+ _cairo_surface_begin_modification (surface);
+
+ if (surface->backend->mask != NULL) {
+ status = surface->backend->mask (surface, op, source, mask, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto FINISH;
+ }
+
+ status = _cairo_surface_fallback_mask (surface, op, source, mask, clip);
+
+ FINISH:
+ surface->is_clear = FALSE;
+
+ 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,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (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;
+
+ _cairo_surface_begin_modification (surface);
+
+ 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:
+ surface->is_clear = FALSE;
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_stroke (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_OVER &&
+ _cairo_pattern_is_clear (source))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_surface_begin_modification (surface);
+
+ if (surface->backend->stroke != NULL) {
+ status = surface->backend->stroke (surface, op, source,
+ path, stroke_style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto FINISH;
+ }
+
+ status = _cairo_surface_fallback_stroke (surface, op, source,
+ path, stroke_style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+
+ FINISH:
+ surface->is_clear = FALSE;
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_fill (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_OVER &&
+ _cairo_pattern_is_clear (source))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_surface_begin_modification (surface);
+
+ if (surface->backend->fill != NULL) {
+ status = surface->backend->fill (surface, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto FINISH;
+ }
+
+ status = _cairo_surface_fallback_fill (surface, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+
+ FINISH:
+ surface->is_clear = FALSE;
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+cairo_status_t
+_cairo_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_antialias_t antialias,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region)
+{
+ cairo_int_status_t status;
+
+ if (dst->status)
+ return dst->status;
+
+ assert (_cairo_surface_is_writable (dst));
+
+ /* These operators aren't interpreted the same way by the backends;
+ * they are implemented in terms of other operators in cairo-gstate.c
+ */
+ assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
+
+ if (dst->backend->composite_trapezoids) {
+ status = dst->backend->composite_trapezoids (op,
+ pattern, dst,
+ antialias,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height,
+ traps, num_traps,
+ clip_region);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return _cairo_surface_set_error (dst, status);
+ }
+
+ return _cairo_surface_set_error (dst,
+ _cairo_surface_fallback_composite_trapezoids (op, pattern, dst,
+ antialias,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height,
+ traps, num_traps,
+ clip_region));
+}
+
+cairo_span_renderer_t *
+_cairo_surface_create_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects,
+ cairo_region_t *clip_region)
+{
+ assert (dst->snapshot_of == NULL);
+
+ if (unlikely (dst->status))
+ return _cairo_span_renderer_create_in_error (dst->status);
+
+ if (unlikely (dst->finished))
+ return _cairo_span_renderer_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+ if (dst->backend->create_span_renderer) {
+ return dst->backend->create_span_renderer (op,
+ pattern, dst,
+ antialias,
+ rects,
+ clip_region);
+ }
+ ASSERT_NOT_REACHED;
+ return _cairo_span_renderer_create_in_error (CAIRO_INT_STATUS_UNSUPPORTED);
+}
+
+cairo_bool_t
+_cairo_surface_check_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_antialias_t antialias)
+{
+ assert (dst->snapshot_of == NULL);
+ assert (dst->status == CAIRO_STATUS_SUCCESS);
+ assert (! dst->finished);
+
+ /* XXX: Currently we have no mono span renderer */
+ if (antialias == CAIRO_ANTIALIAS_NONE)
+ return FALSE;
+
+ if (dst->backend->check_span_renderer != NULL)
+ return dst->backend->check_span_renderer (op, pattern, dst, antialias);
+
+ return FALSE;
+}
+
+/**
+ * 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)
+{
+ cairo_status_t status_ignored;
+
+ if (surface->status)
+ return;
+
+ assert (surface->snapshot_of == NULL);
+
+ if (surface->finished) {
+ status_ignored = _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;
+
+ status_ignored = _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_ignored;
+
+ if (surface->status)
+ return;
+
+ if (surface->finished) {
+ status_ignored = _cairo_surface_set_error (surface,
+ CAIRO_STATUS_SURFACE_FINISHED);
+ return;
+ }
+
+ _cairo_surface_begin_modification (surface);
+
+ /* It's fine if some backends don't implement show_page */
+ if (surface->backend->show_page == NULL)
+ return;
+
+ status_ignored = _cairo_surface_set_error (surface,
+ surface->backend->show_page (surface));
+}
+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;
+
+ bounded = FALSE;
+ if (! surface->status && surface->backend->get_extents != NULL)
+ bounded = surface->backend->get_extents (surface, extents);
+
+ if (! bounded)
+ _cairo_unbounded_rectangle_init (extents);
+
+ return bounded;
+}
+
+/**
+ * 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)
+{
+ cairo_status_t status_ignored;
+
+ if (surface->status)
+ return FALSE;
+
+ if (surface->finished) {
+ status_ignored = _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);
+
+/**
+ * cairo_surface_set_subpixel_antialiasing:
+ * @surface: a #cairo_surface_t
+ *
+ * Sets whether the surface permits subpixel antialiasing. By default,
+ * surfaces permit subpixel antialiasing.
+ *
+ * Enabling subpixel antialiasing for CONTENT_COLOR_ALPHA surfaces generally
+ * requires that the pixels in the areas under a subpixel antialiasing
+ * operation already be opaque.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface,
+ cairo_subpixel_antialiasing_t enabled)
+{
+ if (surface->status)
+ return;
+
+ if (surface->finished) {
+ _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
+ return;
+ }
+
+ surface->permit_subpixel_antialiasing =
+ enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
+}
+slim_hidden_def (cairo_surface_set_subpixel_antialiasing);
+
+/**
+ * cairo_surface_get_subpixel_antialiasing:
+ * @surface: a #cairo_surface_t
+ *
+ * Gets whether the surface supports subpixel antialiasing. By default,
+ * CAIRO_CONTENT_COLOR surfaces support subpixel antialiasing but other
+ * surfaces do not.
+ *
+ * Since: 1.12
+ **/
+cairo_subpixel_antialiasing_t
+cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface)
+{
+ if (surface->status)
+ return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED;
+
+ return surface->permit_subpixel_antialiasing ?
+ CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED;
+}
+slim_hidden_def (cairo_surface_get_subpixel_antialiasing);
+
+/* Note: the backends may modify the contents of the glyph array as long as
+ * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to
+ * 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,
+ cairo_clip_t *clip)
+{
+ cairo_status_t status;
+ cairo_scaled_font_t *dev_scaled_font = scaled_font;
+
+ if (unlikely (surface->status))
+ return surface->status;
+
+ if (num_glyphs == 0 && utf8_len == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (clip && clip->all_clipped)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _pattern_has_error (source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_surface_begin_modification (surface);
+
+ if (_cairo_surface_has_device_transform (surface) &&
+ ! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL))
+ {
+ cairo_font_options_t font_options;
+ cairo_matrix_t dev_ctm, font_matrix;
+
+ cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix);
+ cairo_scaled_font_get_ctm (scaled_font, &dev_ctm);
+ cairo_matrix_multiply (&dev_ctm, &dev_ctm, &surface->device_transform);
+ cairo_scaled_font_get_font_options (scaled_font, &font_options);
+ dev_scaled_font = cairo_scaled_font_create (cairo_scaled_font_get_font_face (scaled_font),
+ &font_matrix,
+ &dev_ctm,
+ &font_options);
+ }
+ status = cairo_scaled_font_status (dev_scaled_font);
+ if (unlikely (status))
+ return _cairo_surface_set_error (surface, status);
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* 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,
+ dev_scaled_font,
+ clip);
+ }
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
+ surface->backend->show_glyphs)
+ {
+ int remaining_glyphs = num_glyphs;
+ status = surface->backend->show_glyphs (surface, op,
+ source,
+ glyphs, num_glyphs,
+ dev_scaled_font,
+ clip,
+ &remaining_glyphs);
+ glyphs += num_glyphs - remaining_glyphs;
+ num_glyphs = remaining_glyphs;
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0)
+ status = CAIRO_STATUS_SUCCESS;
+ }
+ } else {
+ /* A mere show_glyphs call. Try show_glyphs backend method first */
+ if (surface->backend->show_glyphs != NULL) {
+ int remaining_glyphs = num_glyphs;
+ status = surface->backend->show_glyphs (surface, op,
+ source,
+ glyphs, num_glyphs,
+ dev_scaled_font,
+ clip,
+ &remaining_glyphs);
+ glyphs += num_glyphs - remaining_glyphs;
+ num_glyphs = remaining_glyphs;
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0)
+ status = CAIRO_STATUS_SUCCESS;
+ } 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,
+ dev_scaled_font,
+ clip);
+ }
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_surface_fallback_show_glyphs (surface, op,
+ source,
+ glyphs, num_glyphs,
+ dev_scaled_font,
+ clip);
+ }
+
+ if (dev_scaled_font != scaled_font)
+ cairo_scaled_font_destroy (dev_scaled_font);
+
+ surface->is_clear = FALSE;
+
+ return _cairo_surface_set_error (surface, status);
+}
+
+/* XXX: Previously, we had a function named _cairo_surface_show_glyphs
+ * with not-so-useful semantics. We've now got a
+ * _cairo_surface_show_text_glyphs with the proper semantics, and its
+ * fallback still uses this old function (which still needs to be
+ * cleaned up in terms of both semantics and naming). */
+cairo_status_t
+_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_region_t *clip_region)
+{
+ cairo_status_t status;
+
+ if (dst->status)
+ return dst->status;
+
+ assert (_cairo_surface_is_writable (dst));
+
+ if (dst->backend->old_show_glyphs) {
+ status = dst->backend->old_show_glyphs (scaled_font,
+ op, pattern, dst,
+ source_x, source_y,
+ dest_x, dest_y,
+ width, height,
+ glyphs, num_glyphs,
+ clip_region);
+ } else
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return _cairo_surface_set_error (dst, status);
+}
+
+static cairo_status_t
+_cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst,
+ cairo_rectangle_int_t *src_rectangle,
+ cairo_rectangle_int_t *mask_rectangle,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_rectangle_int_t dst_rectangle;
+ cairo_region_t clear_region;
+ cairo_status_t status;
+
+ /* The area that was drawn is the area in the destination rectangle but
+ * not within the source or the mask.
+ */
+ dst_rectangle.x = dst_x;
+ dst_rectangle.y = dst_y;
+ dst_rectangle.width = width;
+ dst_rectangle.height = height;
+
+ _cairo_region_init_rectangle (&clear_region, &dst_rectangle);
+
+ if (clip_region != NULL) {
+ status = cairo_region_intersect (&clear_region, clip_region);
+ if (unlikely (status))
+ goto CLEANUP_REGIONS;
+ }
+
+ if (src_rectangle != NULL) {
+ if (! _cairo_rectangle_intersect (&dst_rectangle, src_rectangle))
+ goto EMPTY;
+ }
+
+ if (mask_rectangle != NULL) {
+ if (! _cairo_rectangle_intersect (&dst_rectangle, mask_rectangle))
+ goto EMPTY;
+ }
+
+ /* Now compute the area that is in dst but not drawn */
+ status = cairo_region_subtract_rectangle (&clear_region, &dst_rectangle);
+ if (unlikely (status) || cairo_region_is_empty (&clear_region))
+ goto CLEANUP_REGIONS;
+
+ EMPTY:
+ status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear_region);
+
+ CLEANUP_REGIONS:
+ _cairo_region_fini (&clear_region);
+
+ return _cairo_surface_set_error (dst, status);
+}
+
+/**
+ * _cairo_surface_composite_fixup_unbounded:
+ * @dst: the destination surface
+ * @src_attr: source surface attributes (from _cairo_pattern_acquire_surface())
+ * @src_width: width of source surface
+ * @src_height: height of source surface
+ * @mask_attr: mask surface attributes or %NULL if no mask
+ * @mask_width: width of mask surface
+ * @mask_height: height of mask surface
+ * @src_x: @src_x from _cairo_surface_composite()
+ * @src_y: @src_y from _cairo_surface_composite()
+ * @mask_x: @mask_x from _cairo_surface_composite()
+ * @mask_y: @mask_y from _cairo_surface_composite()
+ * @dst_x: @dst_x from _cairo_surface_composite()
+ * @dst_y: @dst_y from _cairo_surface_composite()
+ * @width: @width from _cairo_surface_composite()
+ * @height: @height_x from _cairo_surface_composite()
+ *
+ * Eeek! Too many parameters! This is a helper function to take care of fixing
+ * up for bugs in libpixman and RENDER where, when asked to composite an
+ * untransformed surface with an unbounded operator (like CLEAR or SOURCE)
+ * only the region inside both the source and the mask is affected.
+ * This function clears the region that should have been drawn but was wasn't.
+ **/
+cairo_status_t
+_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst,
+ cairo_surface_attributes_t *src_attr,
+ int src_width,
+ int src_height,
+ cairo_surface_attributes_t *mask_attr,
+ int mask_width,
+ int mask_height,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_rectangle_int_t src_tmp, mask_tmp;
+ cairo_rectangle_int_t *src_rectangle = NULL;
+ cairo_rectangle_int_t *mask_rectangle = NULL;
+
+ if (unlikely (dst->status))
+ return dst->status;
+
+ assert (_cairo_surface_is_writable (dst));
+
+ /* The RENDER/libpixman operators are clipped to the bounds of the untransformed,
+ * non-repeating sources and masks. Other sources and masks can be ignored.
+ */
+ if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) &&
+ src_attr->extend == CAIRO_EXTEND_NONE)
+ {
+ src_tmp.x = (dst_x - (src_x + src_attr->x_offset));
+ src_tmp.y = (dst_y - (src_y + src_attr->y_offset));
+ src_tmp.width = src_width;
+ src_tmp.height = src_height;
+
+ src_rectangle = &src_tmp;
+ }
+
+ if (mask_attr &&
+ _cairo_matrix_is_integer_translation (&mask_attr->matrix, NULL, NULL) &&
+ mask_attr->extend == CAIRO_EXTEND_NONE)
+ {
+ mask_tmp.x = (dst_x - (mask_x + mask_attr->x_offset));
+ mask_tmp.y = (dst_y - (mask_y + mask_attr->y_offset));
+ mask_tmp.width = mask_width;
+ mask_tmp.height = mask_height;
+
+ mask_rectangle = &mask_tmp;
+ }
+
+ return _cairo_surface_composite_fixup_unbounded_internal (dst, src_rectangle, mask_rectangle,
+ dst_x, dst_y, width, height,
+ clip_region);
+}
+
+/**
+ * _cairo_surface_composite_shape_fixup_unbounded:
+ * @dst: the destination surface
+ * @src_attr: source surface attributes (from _cairo_pattern_acquire_surface())
+ * @src_width: width of source surface
+ * @src_height: height of source surface
+ * @mask_width: width of mask surface
+ * @mask_height: height of mask surface
+ * @src_x: @src_x from _cairo_surface_composite()
+ * @src_y: @src_y from _cairo_surface_composite()
+ * @mask_x: @mask_x from _cairo_surface_composite()
+ * @mask_y: @mask_y from _cairo_surface_composite()
+ * @dst_x: @dst_x from _cairo_surface_composite()
+ * @dst_y: @dst_y from _cairo_surface_composite()
+ * @width: @width from _cairo_surface_composite()
+ * @height: @height_x from _cairo_surface_composite()
+ *
+ * Like _cairo_surface_composite_fixup_unbounded(), but instead of
+ * handling the case where we have a source pattern and a mask
+ * pattern, handle the case where we are compositing a source pattern
+ * using a mask we create ourselves, as in
+ * _cairo_surface_composite_glyphs() or _cairo_surface_composite_trapezoids()
+ **/
+cairo_status_t
+_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst,
+ cairo_surface_attributes_t *src_attr,
+ int src_width,
+ int src_height,
+ int mask_width,
+ int mask_height,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_rectangle_int_t src_tmp, *src= NULL;
+ cairo_rectangle_int_t mask;
+
+ if (dst->status)
+ return dst->status;
+
+ assert (_cairo_surface_is_writable (dst));
+
+ /* The RENDER/libpixman operators are clipped to the bounds of the untransformed,
+ * non-repeating sources and masks. Other sources and masks can be ignored.
+ */
+ if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) &&
+ src_attr->extend == CAIRO_EXTEND_NONE)
+ {
+ src_tmp.x = (dst_x - (src_x + src_attr->x_offset));
+ src_tmp.y = (dst_y - (src_y + src_attr->y_offset));
+ src_tmp.width = src_width;
+ src_tmp.height = src_height;
+
+ src = &src_tmp;
+ }
+
+ mask.x = dst_x - mask_x;
+ mask.y = dst_y - mask_y;
+ mask.width = mask_width;
+ mask.height = mask_height;
+
+ return _cairo_surface_composite_fixup_unbounded_internal (dst, src, &mask,
+ dst_x, dst_y, width, height,
+ clip_region);
+}
+
+/**
+ * _cairo_surface_set_resolution
+ * @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;
+}
+
+/* Generic methods for determining operation extents. */
+
+static void
+_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip)
+{
+ const cairo_rectangle_int_t *clip_extents;
+ cairo_bool_t is_empty;
+
+ clip_extents = NULL;
+ if (clip != NULL)
+ clip_extents = _cairo_clip_get_extents (clip);
+
+ if (clip_extents != NULL)
+ is_empty = _cairo_rectangle_intersect (extents, clip_extents);
+}
+
+static void
+_cairo_surface_operation_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_bool_t is_empty;
+
+ is_empty = _cairo_surface_get_extents (surface, extents);
+
+ if (_cairo_operator_bounded_by_source (op)) {
+ cairo_rectangle_int_t source_extents;
+
+ _cairo_pattern_get_extents (source, &source_extents);
+ is_empty = _cairo_rectangle_intersect (extents, &source_extents);
+ }
+
+ _rectangle_intersect_clip (extents, clip);
+}
+
+cairo_status_t
+_cairo_surface_paint_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ _cairo_surface_operation_extents (surface, op, source, clip, extents);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_mask_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_bool_t is_empty;
+
+ _cairo_surface_operation_extents (surface, op, source, clip, extents);
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t mask_extents;
+
+ _cairo_pattern_get_extents (mask, &mask_extents);
+ is_empty = _cairo_rectangle_intersect (extents, &mask_extents);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_stroke_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_status_t status;
+ cairo_bool_t is_empty;
+
+ _cairo_surface_operation_extents (surface, op, source, clip, extents);
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t mask_extents;
+
+ status = _cairo_path_fixed_stroke_extents (path, style,
+ ctm, ctm_inverse,
+ tolerance,
+ &mask_extents);
+ if (unlikely (status))
+ return status;
+
+ is_empty = _cairo_rectangle_intersect (extents, &mask_extents);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_fill_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_bool_t is_empty;
+
+ _cairo_surface_operation_extents (surface, op, source, clip, extents);
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t mask_extents;
+
+ _cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
+ &mask_extents);
+ is_empty = _cairo_rectangle_intersect (extents, &mask_extents);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_surface_glyphs_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_status_t status;
+ cairo_bool_t is_empty;
+
+ _cairo_surface_operation_extents (surface, op, source, clip, extents);
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t glyph_extents;
+
+ status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+ glyphs,
+ num_glyphs,
+ &glyph_extents,
+ NULL);
+ if (unlikely (status))
+ return status;
+
+ is_empty = _cairo_rectangle_intersect (extents, &glyph_extents);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_surface_t *
+_cairo_surface_create_in_error (cairo_status_t 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:
+ 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 000000000..ddbf464b1
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-svg-surface-private.h
@@ -0,0 +1,74 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2004 Red Hat, Inc
+ * Copyright © 2005-2006 Emmanuel Pacaud <emmanuel.pacaud@free.fr>
+ * 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 <krh@redhat.com>
+ * Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#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_surface {
+ cairo_surface_t base;
+
+ cairo_content_t content;
+
+ double width;
+ double height;
+
+ cairo_svg_document_t *document;
+
+ cairo_output_stream_t *xml_node;
+ cairo_array_t page_set;
+
+ 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 000000000..fb612fadf
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-svg-surface.c
@@ -0,0 +1,2848 @@
+/* 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 <emmanuel.pacaud@free.fr>
+ * 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 <krh@redhat.com>
+ * Emmanuel Pacaud <emmanuel.pacaud@free.fr>
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#define _BSD_SOURCE /* for snprintf() */
+#include "cairoint.h"
+#include "cairo-svg.h"
+#include "cairo-analysis-surface-private.h"
+#include "cairo-error-private.h"
+#include "cairo-image-info-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-clipper-private.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.
+ */
+
+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 void
+_cairo_svg_surface_emit_path (cairo_output_stream_t *output,
+ 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"
+};
+
+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_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);
+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.
+ *
+ * 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];
+}
+
+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, "</g>\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,
+ "<clipPath id=\"clip%d\">\n"
+ " <path ",
+ document->clip_id);
+ _cairo_svg_surface_emit_path (document->xml_node_defs, path, NULL);
+
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "/>\n"
+ "</clipPath>\n");
+
+ _cairo_output_stream_printf (surface->xml_node,
+ "<g clip-path=\"url(#clip%d)\" "
+ "clip-rule=\"%s\">\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_svg_surface_t *surface;
+ cairo_surface_t *paginated;
+ cairo_status_t status, status_ignored;
+
+ surface = 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);
+
+ surface->width = width;
+ surface->height = height;
+
+ 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,
+ "<rect width=\"%f\" height=\"%f\" "
+ "style=\"opacity:1;stroke:none;"
+ "fill:rgb(0,0,0);\"/>\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;
+
+ 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);
+ 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)
+{
+ unsigned int i;
+ cairo_svg_page_t page;
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ 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, "</g>\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,
+ 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_DIRECTION_FORWARD,
+ _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,
+ "<path style=\"stroke:none;\" ");
+
+ _cairo_svg_surface_emit_path (document->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, "<g");
+ _cairo_svg_surface_emit_transform (document->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,
+ "<rect x=\"%d\" y=\"%d\" width=\"1\" height=\"1\"/>\n",
+ x, y);
+ }
+ }
+ }
+ }
+ _cairo_output_stream_printf (document->xml_node_glyphs, "</g>\n");
+
+ cairo_surface_destroy (&image->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
+ cairo_scaled_font_t *scaled_font,
+ unsigned long scaled_font_glyph_index,
+ unsigned int font_id,
+ unsigned int subset_glyph_index)
+{
+ cairo_status_t status;
+
+ _cairo_output_stream_printf (document->xml_node_glyphs,
+ "<symbol overflow=\"visible\" id=\"glyph%d-%d\">\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, "</symbol>\n");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
+ void *closure)
+{
+ cairo_svg_document_t *document = closure;
+ unsigned int i;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ _cairo_scaled_font_freeze_cache (font_subset->scaled_font);
+ for (i = 0; i < font_subset->num_glyphs; i++) {
+ 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;
+ }
+
+ /* 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);
+
+ 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,
+ "<filter id=\"alpha\" "
+ "filterUnits=\"objectBoundingBox\" "
+ "x=\"0%%\" y=\"0%%\" "
+ "width=\"100%%\" height=\"100%%\">\n"
+ " <feColorMatrix type=\"matrix\" "
+ "in=\"SourceGraphic\" "
+ "values=\"0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0\"/>\n"
+ "</filter>\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] = '=';
+ 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;
+
+ _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_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: &amp; and &quot;.
+ **/
+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, "&amp;");
+ else // p == '"'
+ _cairo_output_stream_printf (stream, "&quot;");
+ }
+ }
+
+ /* 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)
+{
+ cairo_rectangle_int_t extents;
+ cairo_bool_t is_bounded;
+ cairo_status_t status;
+ const unsigned char *uri;
+ unsigned long uri_len;
+
+ if (_cairo_user_data_array_get_data (&surface->user_data,
+ (cairo_user_data_key_t *) document))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ is_bounded = _cairo_surface_get_extents (surface, &extents);
+ assert (is_bounded);
+
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<image id=\"image%d\" width=\"%d\" height=\"%d\"",
+ surface->unique_id,
+ extents.width, extents.height);
+
+ _cairo_output_stream_printf (document->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");
+
+ /* and tag it */
+ return _cairo_user_data_array_set_data (&surface->user_data,
+ (cairo_user_data_key_t *) document,
+ document, NULL);
+}
+
+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;
+
+ p2u = pattern->base.matrix;
+ status = cairo_matrix_invert (&p2u);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ status = _cairo_svg_surface_emit_surface (svg_surface->document,
+ pattern->surface);
+ 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,
+ "<pattern id=\"pattern%d\" "
+ "patternUnits=\"userSpaceOnUse\" "
+ "width=\"%d\" height=\"%d\" ",
+ pattern_id,
+ extents.width, extents.height);
+ _cairo_svg_surface_emit_transform (output,
+ " patternTransform",
+ &p2u, parent_matrix);
+ _cairo_output_stream_printf (output, ">\n ");
+ }
+
+ _cairo_output_stream_printf (output,
+ "<use xlink:href=\"#image%d\"",
+ pattern->surface->unique_id);
+ if (extra_attributes)
+ _cairo_output_stream_printf (output, " %s", extra_attributes);
+
+ if (pattern_id == invalid_pattern_id) {
+ _cairo_svg_surface_emit_operator (output, svg_surface, op);
+ _cairo_svg_surface_emit_transform (output,
+ " transform",
+ &p2u, parent_matrix);
+ }
+ _cairo_output_stream_printf (output, "/>\n");
+
+
+ if (pattern_id != invalid_pattern_id)
+ _cairo_output_stream_printf (output, "</pattern>\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)
+{
+ cairo_status_t status;
+ cairo_surface_t *paginated_surface;
+ cairo_svg_surface_t *svg_surface;
+ cairo_array_t *page_set;
+
+ cairo_output_stream_t *contents;
+
+ if (_cairo_user_data_array_get_data (&source->base.user_data,
+ (cairo_user_data_key_t *) document))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ paginated_surface = _cairo_svg_surface_create_for_document (document,
+ source->content,
+ source->extents_pixels.width,
+ source->extents_pixels.height);
+ 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;
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<clipPath id=\"clip%d\">\n"
+ " <rect width=\"%f\" height=\"%f\"/>\n"
+ "</clipPath>\n",
+ svg_surface->base_clip,
+ svg_surface->width,
+ svg_surface->height);
+ }
+
+ if (source->content == CAIRO_CONTENT_ALPHA) {
+ _cairo_svg_surface_emit_alpha_filter (document);
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<g id=\"surface%d\" "
+ "clip-path=\"url(#clip%d)\" "
+ "filter=\"url(#alpha)\">\n",
+ source->base.unique_id,
+ svg_surface->base_clip);
+ } else {
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<g id=\"surface%d\" "
+ "clip-path=\"url(#clip%d)\">\n",
+ source->base.unique_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, "</g>\n");
+
+ status = cairo_surface_status (paginated_surface);
+ cairo_surface_destroy (paginated_surface);
+
+ if (unlikely (status))
+ return status;
+
+ /* and tag it */
+ return _cairo_user_data_array_set_data (&source->base.user_data,
+ (cairo_user_data_key_t *) document,
+ document, NULL);
+}
+
+static cairo_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;
+
+ p2u = pattern->base.matrix;
+ status = cairo_matrix_invert (&p2u);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ recording_surface = (cairo_recording_surface_t *) pattern->surface;
+ status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
+ if (unlikely (status))
+ return status;
+
+ if (pattern_id != invalid_pattern_id) {
+ _cairo_output_stream_printf (output,
+ "<pattern id=\"pattern%d\" "
+ "patternUnits=\"userSpaceOnUse\" "
+ "width=\"%d\" height=\"%d\"",
+ pattern_id,
+ recording_surface->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,
+ "<use xlink:href=\"#surface%d\"",
+ recording_surface->base.unique_id);
+
+ if (pattern_id == invalid_pattern_id) {
+ _cairo_svg_surface_emit_operator (output, surface, op);
+ _cairo_svg_surface_emit_transform (output, " transform", &p2u, parent_matrix);
+ }
+
+ if (extra_attributes)
+ _cairo_output_stream_printf (output, " %s", extra_attributes);
+
+ _cairo_output_stream_printf (output, "/>\n");
+
+ if (pattern_id != invalid_pattern_id)
+ _cairo_output_stream_printf (output, "</pattern>\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 (_cairo_surface_is_recording (pattern->surface)) {
+ 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,
+ "<stop offset=\"%f\" style=\""
+ "stop-color:rgb(%f%%,%f%%,%f%%);"
+ "stop-opacity:%f;\"/>\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,
+ "<stop offset=\"%f\" style=\""
+ "stop-color:rgb(%f%%,%f%%,%f%%);"
+ "stop-opacity:%f;\"/>\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,
+ "<stop offset=\"0\" style=\""
+ "stop-color:rgb(%f%%,%f%%,%f%%);"
+ "stop-opacity:%f;\"/>\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,
+ "<stop offset=\"%f\" style=\""
+ "stop-color:rgb(%f%%,%f%%,%f%%);"
+ "stop-opacity:%f;\"/>\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,
+ "<stop offset=\"%f\" style=\""
+ "stop-color:rgb(%f%%,%f%%,%f%%);"
+ "stop-opacity:%f;\"/>\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,
+ "<stop offset=\"1\" style=\""
+ "stop-color:rgb(%f%%,%f%%,%f%%);"
+ "stop-opacity:%f;\"/>\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;
+ double x0, y0, x1, y1;
+ 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);
+
+ x0 = _cairo_fixed_to_double (pattern->p1.x);
+ y0 = _cairo_fixed_to_double (pattern->p1.y);
+ x1 = _cairo_fixed_to_double (pattern->p2.x);
+ y1 = _cairo_fixed_to_double (pattern->p2.y);
+
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<linearGradient id=\"linear%d\" "
+ "gradientUnits=\"userSpaceOnUse\" "
+ "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
+ document->linear_pattern_id,
+ x0, y0, x1, y1);
+
+ _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,
+ "</linearGradient>\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_point_t *c0, *c1;
+ cairo_fixed_t radius0, radius1;
+
+ extend = pattern->base.base.extend;
+
+ if (pattern->r1 < pattern->r2) {
+ c0 = &pattern->c1;
+ c1 = &pattern->c2;
+ radius0 = pattern->r1;
+ radius1 = pattern->r2;
+ reverse_stops = FALSE;
+ } else {
+ c0 = &pattern->c2;
+ c1 = &pattern->c1;
+ radius0 = pattern->r2;
+ radius1 = pattern->r1;
+ reverse_stops = TRUE;
+ }
+
+ x0 = _cairo_fixed_to_double (c0->x);
+ y0 = _cairo_fixed_to_double (c0->y);
+ r0 = _cairo_fixed_to_double (radius0);
+ x1 = _cairo_fixed_to_double (c1->x);
+ y1 = _cairo_fixed_to_double (c1->y);
+ r1 = _cairo_fixed_to_double (radius1);
+
+ p2u = pattern->base.base.matrix;
+ status = cairo_matrix_invert (&p2u);
+ /* cairo_pattern_set_matrix ensures the matrix is invertible */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ if (pattern->r1 == pattern->r2) {
+ unsigned int n_stops = pattern->base.n_stops;
+
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<radialGradient id=\"radial%d\" "
+ "gradientUnits=\"userSpaceOnUse\" "
+ "cx=\"%f\" cy=\"%f\" "
+ "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
+ document->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,
+ "<stop offset=\"0\" style=\""
+ "stop-color:rgb(0%%,0%%,0%%);"
+ "stop-opacity:0;\"/>\n");
+ else {
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<stop offset=\"0\" style=\""
+ "stop-color:rgb(%f%%,%f%%,%f%%);"
+ "stop-opacity %f;\"/>\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,
+ "<stop offset=\"0\" style=\""
+ "stop-color:rgb(%f%%,%f%%,%f%%);"
+ "stop-opacity:%f;\"/>\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,
+ "<radialGradient id=\"radial%d\" "
+ "gradientUnits=\"userSpaceOnUse\" "
+ "cx=\"%f\" cy=\"%f\" "
+ "fx=\"%f\" fy=\"%f\" r=\"%f\" ",
+ document->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,
+ "<stop offset=\"0\" style=\""
+ "stop-color:rgb(0%%,0%%,0%%);"
+ "stop-opacity:0;\"/>\n");
+ if (r0 != 0.0)
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "<stop offset=\"%f\" style=\""
+ "stop-color:rgb(0%%,0%%,0%%);"
+ "stop-opacity:0;\"/>\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,
+ "<stop offset=\"1.0\" style=\""
+ "stop-color:rgb(0%%,0%%,0%%);"
+ "stop-opacity:0;\"/>\n");
+ }
+
+ _cairo_output_stream_printf (document->xml_node_defs,
+ "</radialGradient>\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);
+ }
+ 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,
+ cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ cairo_clip_t *clip)
+{
+ cairo_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, "<path style=\"");
+ status = _cairo_svg_surface_emit_fill_style (surface->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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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, "<path style=\" stroke:none;");
+ status = _cairo_svg_surface_emit_fill_style (surface->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 TRUE;
+}
+
+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,
+ "<rect x=\"0\" y=\"0\" "
+ "width=\"%f\" height=\"%f\" "
+ "style=\"",
+ surface->width, surface->height);
+ _cairo_svg_surface_emit_operator_for_style (output, surface, op);
+ status = _cairo_svg_surface_emit_pattern (surface, source, output, FALSE, NULL);
+ if (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,
+ 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,
+ "<rect "
+ "width=\"%f\" height=\"%f\" "
+ "style=\"opacity:1;"
+ "stroke:none;"
+ "fill:rgb(0,0,0);\"/>\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,
+ 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 = cairo_surface_get_content (surface_pattern->surface);
+ 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,
+ "<mask id=\"mask%d\">\n"
+ "%s",
+ mask_id,
+ discard_filter ? "" : " <g filter=\"url(#alpha)\">\n");
+ status = _cairo_svg_surface_emit_paint (mask_stream, surface, CAIRO_OPERATOR_OVER, mask, source, NULL);
+ if (unlikely (status)) {
+ cairo_status_t ignore = _cairo_output_stream_destroy (mask_stream);
+ return status;
+ (void) ignore;
+ }
+
+ _cairo_output_stream_printf (mask_stream,
+ "%s"
+ "</mask>\n",
+ discard_filter ? "" : " </g>\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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_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, "<path style=\"fill:none;");
+ status = _cairo_svg_surface_emit_stroke_style (surface->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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_svg_surface_t *surface = abstract_surface;
+ cairo_svg_document_t *document = surface->document;
+ cairo_path_fixed_t path;
+ cairo_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, "<g style=\"");
+ status = _cairo_svg_surface_emit_pattern (surface, pattern,
+ surface->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, "</g>\n");
+
+ glyphs += i;
+ num_glyphs -= i;
+ goto FALLBACK;
+ }
+
+ if (unlikely (status))
+ return status;
+
+ _cairo_output_stream_printf (surface->xml_node,
+ " <use xlink:href=\"#glyph%d-%d\" "
+ "x=\"%f\" y=\"%f\"/>\n",
+ subset_glyph.font_id,
+ subset_glyph.subset_glyph_index,
+ glyphs[i].x, glyphs[i].y);
+ }
+
+ _cairo_output_stream_printf (surface->xml_node, "</g>\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);
+}
+
+static const cairo_surface_backend_t cairo_svg_surface_backend = {
+ CAIRO_SURFACE_TYPE_SVG,
+ NULL, /* create_similar: handled by wrapper */
+ _cairo_svg_surface_finish,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* _cairo_svg_surface_composite, */
+ NULL, /* _cairo_svg_surface_fill_rectangles, */
+ NULL, /* _cairo_svg_surface_composite_trapezoids,*/
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ _cairo_svg_surface_copy_page,
+ _cairo_svg_surface_show_page,
+ _cairo_svg_surface_get_extents,
+ NULL, /* _cairo_svg_surface_old_show_glyphs, */
+ _cairo_svg_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark dirty rectangle */
+ NULL, /* scaled font fini */
+ NULL, /* scaled glyph fini */
+ _cairo_svg_surface_paint,
+ _cairo_svg_surface_mask,
+ _cairo_svg_surface_stroke,
+ _cairo_svg_surface_fill,
+ _cairo_svg_surface_show_glyphs,
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ _cairo_svg_surface_fill_stroke
+};
+
+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 = 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->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,
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" "
+ "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
+ "width=\"%fpt\" height=\"%fpt\" "
+ "viewBox=\"0 0 %f %f\" version=\"%s\">\n",
+ document->width, document->height,
+ 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, "<defs>\n");
+ if (_cairo_memory_stream_length (document->xml_node_glyphs) > 0) {
+ _cairo_output_stream_printf (output, "<g>\n");
+ _cairo_memory_stream_copy (document->xml_node_glyphs, output);
+ _cairo_output_stream_printf (output, "</g>\n");
+ }
+ _cairo_memory_stream_copy (document->xml_node_defs, output);
+ _cairo_output_stream_printf (output, "</defs>\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, "<pageSet>\n");
+ for (i = 0; i < surface->page_set.num_elements; i++) {
+ page = _cairo_array_index (&surface->page_set, i);
+ _cairo_output_stream_printf (output, "<page>\n");
+ _cairo_output_stream_printf (output,
+ "<g id=\"surface%d\">\n",
+ page->surface_id);
+ _cairo_memory_stream_copy (page->xml_node, output);
+ _cairo_output_stream_printf (output, "</g>\n</page>\n");
+ }
+ _cairo_output_stream_printf (output, "</pageSet>\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,
+ "<g id=\"surface%d\">\n",
+ page->surface_id);
+ _cairo_memory_stream_copy (page->xml_node, output);
+ _cairo_output_stream_printf (output, "</g>\n");
+ }
+ }
+
+ _cairo_output_stream_printf (output, "</svg>\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 void
+_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;
+}
+
+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_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 000000000..0f739fc04
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-svg.h
@@ -0,0 +1,82 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * cairo-svg.h
+ *
+ * Copyright © 2005 Emmanuel Pacaud <emmanuel.pacaud@univ-poitiers.fr>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" 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.
+ * @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification.
+ *
+ * #cairo_svg_version_t is used to describe the version number of the SVG
+ * specification that a generated SVG file will conform to.
+ */
+typedef enum _cairo_svg_version {
+ CAIRO_SVG_VERSION_1_1,
+ CAIRO_SVG_VERSION_1_2
+} cairo_svg_version_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_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-system.c b/gfx/cairo/cairo/src/cairo-system.c
new file mode 100644
index 000000000..1ff4d078f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-system.c
@@ -0,0 +1,97 @@
+/* -*- 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 <otaylor@redhat.com>
+ * Stuart Parmenter <stuart@mozilla.com>
+ * Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+/* 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 "cairo-clip-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-win32-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+
+#include <windows.h>
+
+/* 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/cairo/src/cairo-tee-surface-private.h b/gfx/cairo/cairo/src/cairo-tee-surface-private.h
new file mode 100644
index 000000000..a83cfc959
--- /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 <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_TEE_SURFACE_PRIVATE_H
+#define CAIRO_TEE_SURFACE_PRIVATE_H
+
+#include "cairoint.h"
+
+cairo_private cairo_surface_t *
+_cairo_tee_surface_find_match (void *abstract_surface,
+ const cairo_surface_backend_t *backend,
+ cairo_content_t content);
+
+#endif /* CAIRO_TEE_SURFACE_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-tee-surface.c b/gfx/cairo/cairo/src/cairo-tee-surface.c
new file mode 100644
index 000000000..bca07716f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-tee-surface.c
@@ -0,0 +1,718 @@
+/* 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This surface supports redirecting all its input to multiple surfaces.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-tee.h"
+
+#include "cairo-error-private.h"
+#include "cairo-tee-surface-private.h"
+#include "cairo-surface-wrapper-private.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_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 const cairo_pattern_t *
+_cairo_tee_surface_match_source (cairo_tee_surface_t *surface,
+ const cairo_pattern_t *source,
+ int index,
+ cairo_surface_wrapper_t *dest,
+ cairo_surface_pattern_t *temp)
+{
+ cairo_surface_t *s;
+ cairo_status_t status = cairo_pattern_get_surface ((cairo_pattern_t *)source, &s);
+ if (status == CAIRO_STATUS_SUCCESS &&
+ cairo_surface_get_type (s) == CAIRO_SURFACE_TYPE_TEE) {
+ cairo_surface_t *tee_surf = cairo_tee_surface_index (s, index);
+ if (tee_surf->status == CAIRO_STATUS_SUCCESS &&
+ tee_surf->backend == dest->target->backend) {
+ status = _cairo_pattern_init_copy (&temp->base, source);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy (temp->surface);
+ temp->surface = tee_surf;
+ cairo_surface_reference (temp->surface);
+ return &temp->base;
+ }
+ }
+ }
+
+ return source;
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
+ const cairo_pattern_t *matched_source;
+ cairo_surface_pattern_t temp;
+
+ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_paint (&surface->master, op, matched_source, clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ return status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_paint (&slaves[n], op, matched_source, clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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,
+ cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
+ const cairo_pattern_t *matched_source;
+ cairo_surface_pattern_t temp;
+
+ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_mask (&surface->master,
+ op, matched_source, mask, clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ return status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_mask (&slaves[n],
+ op, matched_source, mask, clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
+ const cairo_pattern_t *matched_source;
+ cairo_surface_pattern_t temp;
+
+ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_stroke (&surface->master,
+ op, matched_source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ return status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_stroke (&slaves[n],
+ op, matched_source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_tee_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
+ const cairo_pattern_t *matched_source;
+ cairo_surface_pattern_t temp;
+
+ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_fill (&surface->master,
+ op, matched_source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ return status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_fill (&slaves[n],
+ op, matched_source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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,
+ cairo_clip_t *clip)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
+ cairo_glyph_t *glyphs_copy;
+ const cairo_pattern_t *matched_source;
+ cairo_surface_pattern_t temp;
+
+ /* XXX: This copying is ugly. */
+ glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unlikely (glyphs_copy == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op,
+ matched_source,
+ utf8, utf8_len,
+ glyphs_copy, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ goto CLEANUP;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
+ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op,
+ matched_source,
+ utf8, utf8_len,
+ glyphs_copy, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
+ if (matched_source == &temp.base) {
+ _cairo_pattern_fini (&temp.base);
+ }
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+
+ CLEANUP:
+ free (glyphs_copy);
+ return status;
+}
+
+static cairo_status_t
+_cairo_tee_surface_flush (void *abstract_surface)
+{
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
+
+ status = _cairo_surface_wrapper_flush(&surface->master);
+ if (unlikely (status))
+ return status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ status = _cairo_surface_wrapper_flush(&slaves[n]);
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t cairo_tee_surface_backend = {
+ CAIRO_SURFACE_TYPE_TEE,
+ _cairo_tee_surface_create_similar,
+ _cairo_tee_surface_finish,
+ _cairo_tee_surface_acquire_source_image,
+ _cairo_tee_surface_release_source_image,
+ NULL, NULL, /* dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_tee_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_tee_surface_get_font_options,
+ _cairo_tee_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ _cairo_tee_surface_paint,
+ _cairo_tee_surface_mask,
+ _cairo_tee_surface_stroke,
+ _cairo_tee_surface_fill,
+ NULL, /* replaced by show_text_glyphs */
+
+ _cairo_tee_surface_snapshot,
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+
+ _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 = 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);
+
+ _cairo_surface_wrapper_init (&surface->master, master);
+ /* we trust that these are already set and remain constant */
+ surface->base.device_transform = master->device_transform;
+ surface->base.device_transform_inverse = master->device_transform_inverse;
+
+ _cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t));
+
+ 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;
+ 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;
+ }
+
+ surface = (cairo_tee_surface_t *) abstract_surface;
+ if (target == surface->master.target) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_INDEX));
+ 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) {
+ status = _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,
+ 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 000000000..9c048c6fe
--- /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 <chris@chris-wilson.co.uk>
+ */
+
+#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,
+ 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-tor-scan-converter.c b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c
new file mode 100644
index 000000000..dc8dad1c5
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c
@@ -0,0 +1,2220 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* glitter-paths - polygon scan converter
+ *
+ * Copyright (c) 2008 M Joonas Pihlaja
+ * Copyright (c) 2007 David Turner
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* This is the Glitter paths scan converter incorporated into cairo.
+ * The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
+ * of
+ *
+ * http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
+ */
+/* Glitter-paths is a stand alone polygon rasteriser derived from
+ * David Turner's reimplementation of Tor Anderssons's 15x17
+ * supersampling rasteriser from the Apparition graphics library. The
+ * main new feature here is cheaply choosing per-scan line between
+ * doing fully analytical coverage computation for an entire row at a
+ * time vs. using a supersampling approach.
+ *
+ * David Turner's code can be found at
+ *
+ * http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
+ *
+ * In particular this file incorporates large parts of ftgrays_tor10.h
+ * from raster-comparison-20070813.tar.bz2
+ */
+/* Overview
+ *
+ * A scan converter's basic purpose to take polygon edges and convert
+ * them into an RLE compressed A8 mask. This one works in two phases:
+ * gathering edges and generating spans.
+ *
+ * 1) As the user feeds the scan converter edges they are vertically
+ * clipped and bucketted into a _polygon_ data structure. The edges
+ * are also snapped from the user's coordinates to the subpixel grid
+ * coordinates used during scan conversion.
+ *
+ * user
+ * |
+ * | edges
+ * V
+ * polygon buckets
+ *
+ * 2) Generating spans works by performing a vertical sweep of pixel
+ * rows from top to bottom and maintaining an _active_list_ of edges
+ * that intersect the row. From the active list the fill rule
+ * determines which edges are the left and right edges of the start of
+ * each span, and their contribution is then accumulated into a pixel
+ * coverage list (_cell_list_) as coverage deltas. Once the coverage
+ * deltas of all edges are known we can form spans of constant pixel
+ * coverage by summing the deltas during a traversal of the cell list.
+ * At the end of a pixel row the cell list is sent to a coverage
+ * blitter for rendering to some target surface.
+ *
+ * The pixel coverages are computed by either supersampling the row
+ * and box filtering a mono rasterisation, or by computing the exact
+ * coverages of edges in the active list. The supersampling method is
+ * used whenever some edge starts or stops within the row or there are
+ * edge intersections in the row.
+ *
+ * polygon bucket for \
+ * current pixel row |
+ * | |
+ * | activate new edges | Repeat GRID_Y times if we
+ * V \ are supersampling this row,
+ * active list / or just once if we're computing
+ * | | analytical coverage.
+ * | coverage deltas |
+ * V |
+ * pixel coverage list /
+ * |
+ * V
+ * coverage blitter
+ */
+#include "cairoint.h"
+#include "cairo-spans-private.h"
+#include "cairo-error-private.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/*-------------------------------------------------------------------------
+ * cairo specific config
+ */
+#define I static
+
+/* Prefer cairo's status type. */
+#define GLITTER_HAVE_STATUS_T 1
+#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS
+#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY
+typedef cairo_status_t glitter_status_t;
+
+/* The input coordinate scale and the rasterisation grid scales. */
+#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
+#define GRID_Y 15
+
+/* Set glitter up to use a cairo span renderer to do the coverage
+ * blitting. */
+struct pool;
+struct cell_list;
+
+static glitter_status_t
+blit_with_span_renderer(
+ struct cell_list *coverages,
+ cairo_span_renderer_t *span_renderer,
+ struct pool *span_pool,
+ int y,
+ int height,
+ int xmin,
+ int xmax);
+
+static glitter_status_t
+blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height);
+
+#define GLITTER_BLIT_COVERAGES_ARGS \
+ cairo_span_renderer_t *span_renderer, \
+ struct pool *span_pool
+
+#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \
+ cairo_status_t status = blit_with_span_renderer (cells, \
+ span_renderer, \
+ span_pool, \
+ y, height, \
+ xmin, xmax); \
+ if (unlikely (status)) \
+ return status; \
+} while (0)
+
+#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \
+ cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \
+ if (unlikely (status)) \
+ return status; \
+} while (0)
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.h
+ */
+
+/* "Input scaled" numbers are fixed precision reals with multiplier
+ * 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as
+ * pixel scaled numbers. These get converted to the internal grid
+ * scaled numbers as soon as possible. Internal overflow is possible
+ * if GRID_X/Y inside glitter-paths.c is larger than
+ * 1<<GLITTER_INPUT_BITS. */
+#ifndef GLITTER_INPUT_BITS
+# define GLITTER_INPUT_BITS 8
+#endif
+#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
+typedef int glitter_input_scaled_t;
+
+#if !GLITTER_HAVE_STATUS_T
+typedef enum {
+ GLITTER_STATUS_SUCCESS = 0,
+ GLITTER_STATUS_NO_MEMORY
+} glitter_status_t;
+#endif
+
+#ifndef I
+# define I /*static*/
+#endif
+
+/* Opaque type for scan converting. */
+typedef struct glitter_scan_converter glitter_scan_converter_t;
+
+/* Reset a scan converter to accept polygon edges and set the clip box
+ * in pixels. Allocates O(ymax-ymin) bytes of memory. The clip box
+ * is set to integer pixel coordinates xmin <= x < xmax, ymin <= y <
+ * ymax. */
+I glitter_status_t
+glitter_scan_converter_reset(
+ glitter_scan_converter_t *converter,
+ int xmin, int ymin,
+ int xmax, int ymax);
+
+/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
+ * converter. The coordinates represent pixel positions scaled by
+ * 2**GLITTER_PIXEL_BITS. If this function fails then the scan
+ * converter should be reset or destroyed. Dir must be +1 or -1,
+ * with the latter reversing the orientation of the edge. */
+I glitter_status_t
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ const cairo_edge_t *edge);
+
+/* Render the polygon in the scan converter to the given A8 format
+ * image raster. Only the pixels accessible as pixels[y*stride+x] for
+ * x,y inside the clip box are written to, where xmin <= x < xmax,
+ * ymin <= y < ymax. The image is assumed to be clear on input.
+ *
+ * If nonzero_fill is true then the interior of the polygon is
+ * computed with the non-zero fill rule. Otherwise the even-odd fill
+ * rule is used.
+ *
+ * The scan converter must be reset or destroyed after this call. */
+#ifndef GLITTER_BLIT_COVERAGES_ARGS
+# define GLITTER_BLIT_COVERAGES_ARGS unsigned char *raster_pixels, long raster_stride
+#endif
+I glitter_status_t
+glitter_scan_converter_render(
+ glitter_scan_converter_t *converter,
+ int nonzero_fill,
+ GLITTER_BLIT_COVERAGES_ARGS);
+
+/*-------------------------------------------------------------------------
+ * glitter-paths.c: Implementation internal types
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* All polygon coordinates are snapped onto a subsample grid. "Grid
+ * scaled" numbers are fixed precision reals with multiplier GRID_X or
+ * GRID_Y. */
+typedef int grid_scaled_t;
+typedef int grid_scaled_x_t;
+typedef int grid_scaled_y_t;
+
+/* Default x/y scale factors.
+ * You can either define GRID_X/Y_BITS to get a power-of-two scale
+ * or define GRID_X/Y separately. */
+#if !defined(GRID_X) && !defined(GRID_X_BITS)
+# define GRID_X_BITS 8
+#endif
+#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
+# define GRID_Y 15
+#endif
+
+/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
+#ifdef GRID_X_BITS
+# define GRID_X (1 << GRID_X_BITS)
+#endif
+#ifdef GRID_Y_BITS
+# define GRID_Y (1 << GRID_Y_BITS)
+#endif
+
+/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
+ * integer and fractional parts. The integer part is floored. */
+#if defined(GRID_X_TO_INT_FRAC)
+ /* do nothing */
+#elif defined(GRID_X_BITS)
+# define GRID_X_TO_INT_FRAC(x, i, f) \
+ _GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
+#else
+# define GRID_X_TO_INT_FRAC(x, i, f) \
+ _GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
+#endif
+
+#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \
+ (i) = (t) / (m); \
+ (f) = (t) % (m); \
+ if ((f) < 0) { \
+ --(i); \
+ (f) += (m); \
+ } \
+} while (0)
+
+#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \
+ (f) = (t) & ((1 << (b)) - 1); \
+ (i) = (t) >> (b); \
+} while (0)
+
+/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want
+ * to be able to represent exactly areas of subpixel trapezoids whose
+ * vertices are given in grid scaled coordinates. The scale factor
+ * comes from needing to accurately represent the area 0.5*dx*dy of a
+ * triangle with base dx and height dy in grid scaled numbers. */
+typedef int grid_area_t;
+#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
+
+/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
+#if GRID_XY == 510
+# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1)
+#elif GRID_XY == 255
+# define GRID_AREA_TO_ALPHA(c) (c)
+#elif GRID_XY == 64
+# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6))
+#elif GRID_XY == 128
+# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255)
+#elif GRID_XY == 256
+# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255)
+#elif GRID_XY == 15
+# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c))
+#elif GRID_XY == 2*256*15
+# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 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;
+
+ /* 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;
+};
+
+/* Number of subsample rows per y-bucket. Must be GRID_Y. */
+#define EDGE_Y_BUCKET_HEIGHT GRID_Y
+
+#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
+
+struct bucket {
+ /* Unsorted list of edges starting within this bucket. */
+ struct edge *edges;
+
+ /* Set to non-zero if there are edges starting strictly within the
+ * bucket. */
+ unsigned have_inside_edges;
+};
+
+/* A collection of sorted and vertically clipped edges of the polygon.
+ * Edges are moved from the polygon to an active list while scan
+ * converting. */
+struct polygon {
+ /* The clip extents. */
+ grid_scaled_x_t xmin, xmax;
+ grid_scaled_y_t ymin, ymax;
+
+ /* Array of edges all starting in the same bucket. An edge is put
+ * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
+ * it is added to the polygon. */
+ struct bucket *y_buckets;
+ struct bucket y_buckets_embedded[64];
+
+ struct {
+ struct pool base[1];
+ struct edge embedded[32];
+ } edge_pool;
+};
+
+/* A cell records the effect on pixel coverage of polygon edges
+ * passing through a pixel. It contains two accumulators of pixel
+ * coverage.
+ *
+ * Consider the effects of a polygon edge on the coverage of a pixel
+ * it intersects and that of the following one. The coverage of the
+ * following pixel is the height of the edge multiplied by the width
+ * of the pixel, and the coverage of the pixel itself is the area of
+ * the trapezoid formed by the edge and the right side of the pixel.
+ *
+ * +-----------------------+-----------------------+
+ * | | |
+ * | | |
+ * |_______________________|_______________________|
+ * | \...................|.......................|\
+ * | \..................|.......................| |
+ * | \.................|.......................| |
+ * | \....covered.....|.......................| |
+ * | \....area.......|.......................| } covered height
+ * | \..............|.......................| |
+ * |uncovered\.............|.......................| |
+ * | area \............|.......................| |
+ * |___________\...........|.......................|/
+ * | | |
+ * | | |
+ * | | |
+ * +-----------------------+-----------------------+
+ *
+ * Since the coverage of the following pixel will always be a multiple
+ * of the width of the pixel, we can store the height of the covered
+ * area instead. The coverage of the pixel itself is the total
+ * coverage minus the area of the uncovered area to the left of the
+ * edge. As it's faster to compute the uncovered area we only store
+ * that and subtract it from the total coverage later when forming
+ * spans to blit.
+ *
+ * The heights and areas are signed, with left edges of the polygon
+ * having positive sign and right edges having negative sign. When
+ * two edges intersect they swap their left/rightness so their
+ * contribution above and below the intersection point must be
+ * computed separately. */
+struct cell {
+ struct cell *next;
+ int x;
+ grid_area_t uncovered_area;
+ grid_scaled_y_t covered_height;
+};
+
+/* A cell list represents the scan line sparsely as cells ordered by
+ * ascending x. It is geared towards scanning the cells in order
+ * using an internal cursor. */
+struct cell_list {
+ /* Points to the left-most cell in the scan line. */
+ struct cell *head;
+ /* Sentinel node */
+ struct cell tail;
+
+ /* Cursor state for iterating through the cell list. Points to
+ * a pointer to the current cell: either &cell_list->head or the next
+ * field of the previous cell. */
+ struct cell **cursor;
+
+ /* Cells in the cell list are owned by the cell list and are
+ * allocated from this pool. */
+ struct {
+ struct pool base[1];
+ struct cell embedded[32];
+ } cell_pool;
+};
+
+struct cell_pair {
+ struct cell *cell1;
+ struct cell *cell2;
+};
+
+/* The active list contains edges in the current scan line ordered by
+ * the x-coordinate of the intercept of the edge and the scan line. */
+struct active_list {
+ /* Leftmost edge on the current scan line. */
+ struct edge *head;
+
+ /* A lower bound on the height of the active edges is used to
+ * estimate how soon some active edge ends. We can't advance the
+ * scan conversion by a full pixel row if an edge ends somewhere
+ * within it. */
+ grid_scaled_y_t min_height;
+};
+
+struct glitter_scan_converter {
+ struct polygon polygon[1];
+ struct active_list active[1];
+ struct cell_list coverages[1];
+
+ /* Clip box. */
+ grid_scaled_x_t xmin, xmax;
+ grid_scaled_y_t ymin, ymax;
+};
+
+/* Compute the floored division a/b. Assumes / and % perform symmetric
+ * division. */
+inline static struct quorem
+floored_divrem(int a, int b)
+{
+ struct quorem qr;
+ qr.quo = a/b;
+ qr.rem = a%b;
+ if ((a^b)<0 && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
+ * division. */
+static struct quorem
+floored_muldivrem(int x, int a, int b)
+{
+ struct quorem qr;
+ long long xa = (long long)x*a;
+ qr.quo = xa/b;
+ qr.rem = xa%b;
+ if ((xa>=0) != (b>=0) && qr.rem) {
+ qr.quo -= 1;
+ qr.rem += b;
+ }
+ return qr;
+}
+
+static void
+_pool_chunk_init(
+ struct _pool_chunk *p,
+ struct _pool_chunk *prev_chunk,
+ size_t capacity)
+{
+ p->prev_chunk = prev_chunk;
+ p->size = 0;
+ p->capacity = capacity;
+}
+
+static struct _pool_chunk *
+_pool_chunk_create(
+ struct _pool_chunk *prev_chunk,
+ size_t size)
+{
+ struct _pool_chunk *p;
+ size_t size_with_head = size + sizeof(struct _pool_chunk);
+ if (size_with_head < size)
+ return NULL;
+ p = malloc(size_with_head);
+ if (p)
+ _pool_chunk_init(p, prev_chunk, size);
+ return p;
+}
+
+static void
+pool_init(
+ struct pool *pool,
+ size_t default_capacity,
+ size_t embedded_capacity)
+{
+ pool->current = pool->sentinel;
+ pool->first_free = NULL;
+ pool->default_capacity = default_capacity;
+ _pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
+}
+
+static void
+pool_fini(struct pool *pool)
+{
+ struct _pool_chunk *p = pool->current;
+ do {
+ while (NULL != p) {
+ struct _pool_chunk *prev = p->prev_chunk;
+ if (p != pool->sentinel)
+ free(p);
+ p = prev;
+ }
+ p = pool->first_free;
+ pool->first_free = NULL;
+ } while (NULL != p);
+ pool_init(pool, 0, 0);
+}
+
+/* Satisfy an allocation by first allocating a new large enough chunk
+ * and adding it to the head of the pool's chunk list. This function
+ * is called as a fallback if pool_alloc() couldn't do a quick
+ * allocation from the current chunk in the pool. */
+static void *
+_pool_alloc_from_new_chunk(
+ struct pool *pool,
+ size_t size)
+{
+ struct _pool_chunk *chunk;
+ void *obj;
+ size_t capacity;
+
+ /* If the allocation is smaller than the default chunk size then
+ * try getting a chunk off the free list. Force alloc of a new
+ * chunk for large requests. */
+ capacity = size;
+ chunk = NULL;
+ if (size < pool->default_capacity) {
+ capacity = pool->default_capacity;
+ chunk = pool->first_free;
+ if (chunk) {
+ pool->first_free = chunk->prev_chunk;
+ _pool_chunk_init(chunk, pool->current, chunk->capacity);
+ }
+ }
+
+ if (NULL == chunk) {
+ chunk = _pool_chunk_create (pool->current, capacity);
+ if (unlikely (NULL == chunk))
+ return NULL;
+ }
+ pool->current = chunk;
+
+ obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+}
+
+/* Allocate size bytes from the pool. The first allocated address
+ * returned from a pool is aligned to sizeof(void*). Subsequent
+ * addresses will maintain alignment as long as multiples of void* are
+ * allocated. Returns the address of a new memory area or %NULL on
+ * allocation failures. The pool retains ownership of the returned
+ * memory. */
+inline static void *
+pool_alloc (struct pool *pool, size_t size)
+{
+ struct _pool_chunk *chunk = pool->current;
+
+ if (size <= chunk->capacity - chunk->size) {
+ void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
+ chunk->size += size;
+ return obj;
+ } else {
+ return _pool_alloc_from_new_chunk(pool, size);
+ }
+}
+
+/* Relinquish all pool_alloced memory back to the pool. */
+static void
+pool_reset (struct pool *pool)
+{
+ /* Transfer all used chunks to the chunk free list. */
+ struct _pool_chunk *chunk = pool->current;
+ if (chunk != pool->sentinel) {
+ while (chunk->prev_chunk != pool->sentinel) {
+ chunk = chunk->prev_chunk;
+ }
+ chunk->prev_chunk = pool->first_free;
+ pool->first_free = pool->current;
+ }
+ /* Reset the sentinel as the current chunk. */
+ pool->current = pool->sentinel;
+ pool->sentinel->size = 0;
+}
+
+/* Rewinds the cell list's cursor to the beginning. After rewinding
+ * we're good to cell_list_find() the cell any x coordinate. */
+inline static void
+cell_list_rewind (struct cell_list *cells)
+{
+ cells->cursor = &cells->head;
+}
+
+/* Rewind the cell list if its cursor has been advanced past x. */
+inline static void
+cell_list_maybe_rewind (struct cell_list *cells, int x)
+{
+ struct cell *tail = *cells->cursor;
+ if (tail->x > x)
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_init(struct cell_list *cells)
+{
+ pool_init(cells->cell_pool.base,
+ 256*sizeof(struct cell),
+ sizeof(cells->cell_pool.embedded));
+ cells->tail.next = NULL;
+ cells->tail.x = INT_MAX;
+ cells->head = &cells->tail;
+ cell_list_rewind (cells);
+}
+
+static void
+cell_list_fini(struct cell_list *cells)
+{
+ pool_fini (cells->cell_pool.base);
+}
+
+/* Empty the cell list. This is called at the start of every pixel
+ * row. */
+inline static void
+cell_list_reset (struct cell_list *cells)
+{
+ cell_list_rewind (cells);
+ cells->head = &cells->tail;
+ pool_reset (cells->cell_pool.base);
+}
+
+static struct cell *
+cell_list_alloc (struct cell_list *cells,
+ struct cell **cursor,
+ struct cell *tail,
+ int x)
+{
+ struct cell *cell;
+
+ cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+ if (unlikely (NULL == cell))
+ return NULL;
+
+ *cursor = cell;
+ cell->next = tail;
+ cell->x = x;
+ cell->uncovered_area = 0;
+ cell->covered_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 **cursor = cells->cursor;
+ struct cell *tail;
+
+ while (1) {
+ UNROLL3({
+ tail = *cursor;
+ if (tail->x >= x) {
+ break;
+ }
+ cursor = &tail->next;
+ });
+ }
+ cells->cursor = cursor;
+
+ if (tail->x == x)
+ return tail;
+
+ return cell_list_alloc (cells, cursor, tail, x);
+}
+
+/* Find two cells at x1 and x2. This is exactly equivalent
+ * to
+ *
+ * pair.cell1 = cell_list_find(cells, x1);
+ * pair.cell2 = cell_list_find(cells, x2);
+ *
+ * except with less function call overhead. */
+inline static struct cell_pair
+cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+{
+ struct cell_pair pair;
+ struct cell **cursor = cells->cursor;
+ struct cell *cell1;
+ struct cell *cell2;
+ struct cell *newcell;
+
+ /* Find first cell at x1. */
+ while (1) {
+ UNROLL3({
+ cell1 = *cursor;
+ if (cell1->x > x1)
+ break;
+
+ if (cell1->x == x1)
+ goto found_first;
+
+ cursor = &cell1->next;
+ });
+ }
+
+ /* New first cell at x1. */
+ newcell = pool_alloc (cells->cell_pool.base,
+ sizeof (struct cell));
+ if (likely (NULL != newcell)) {
+ *cursor = newcell;
+ newcell->next = cell1;
+ newcell->x = x1;
+ newcell->uncovered_area = 0;
+ newcell->covered_height = 0;
+ }
+ cell1 = newcell;
+ found_first:
+
+ /* Find second cell at x2. */
+ while (1) {
+ UNROLL3({
+ cell2 = *cursor;
+ if (cell2->x > x2)
+ break;
+ if (cell2->x == x2)
+ goto found_second;
+ cursor = &cell2->next;
+ });
+ }
+
+ /* New second cell at x2. */
+ newcell = pool_alloc (cells->cell_pool.base,
+ sizeof (struct cell));
+ if (likely (NULL != newcell)) {
+ *cursor = newcell;
+ newcell->next = cell2;
+ newcell->x = x2;
+ newcell->uncovered_area = 0;
+ newcell->covered_height = 0;
+ }
+ cell2 = newcell;
+ found_second:
+
+ cells->cursor = cursor;
+ pair.cell1 = cell1;
+ pair.cell2 = cell2;
+ return pair;
+}
+
+/* Add an unbounded subpixel span covering subpixels >= x to the
+ * coverage cells. */
+static glitter_status_t
+cell_list_add_unbounded_subspan (struct cell_list *cells,
+ grid_scaled_x_t x)
+{
+ struct cell *cell;
+ int ix, fx;
+
+ GRID_X_TO_INT_FRAC(x, ix, fx);
+
+ cell = cell_list_find (cells, ix);
+ if (likely (cell != NULL)) {
+ cell->uncovered_area += 2*fx;
+ cell->covered_height++;
+ return GLITTER_STATUS_SUCCESS;
+ }
+
+ return GLITTER_STATUS_NO_MEMORY;
+}
+
+/* Add a subpixel span covering [x1, x2) to the coverage cells. */
+inline static glitter_status_t
+cell_list_add_subspan(
+ struct cell_list *cells,
+ grid_scaled_x_t x1,
+ grid_scaled_x_t x2)
+{
+ int ix1, fx1;
+ int ix2, fx2;
+
+ GRID_X_TO_INT_FRAC(x1, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2, ix2, fx2);
+
+ if (ix1 != ix2) {
+ struct cell_pair p;
+ p = cell_list_find_pair(cells, ix1, ix2);
+ if (likely (p.cell1 != NULL && p.cell2 != NULL)) {
+ p.cell1->uncovered_area += 2*fx1;
+ ++p.cell1->covered_height;
+ p.cell2->uncovered_area -= 2*fx2;
+ --p.cell2->covered_height;
+ return GLITTER_STATUS_SUCCESS;
+ }
+ } else {
+ struct cell *cell = cell_list_find(cells, ix1);
+ if (likely (cell != NULL)) {
+ cell->uncovered_area += 2*(fx1-fx2);
+ return GLITTER_STATUS_SUCCESS;
+ }
+ }
+ return GLITTER_STATUS_NO_MEMORY;
+}
+
+/* Adds the analytical coverage of an edge crossing the current pixel
+ * row to the coverage cells and advances the edge's x position to the
+ * following row.
+ *
+ * This function is only called when we know that during this pixel row:
+ *
+ * 1) The relative order of all edges on the active list doesn't
+ * change. In particular, no edges intersect within this row to pixel
+ * precision.
+ *
+ * 2) No new edges start in this row.
+ *
+ * 3) No existing edges end mid-row.
+ *
+ * This function depends on being called with all edges from the
+ * active list in the order they appear on the list (i.e. with
+ * non-decreasing x-coordinate.) */
+static glitter_status_t
+cell_list_render_edge(
+ struct cell_list *cells,
+ struct edge *edge,
+ int sign)
+{
+ 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);
+ if (unlikely (NULL == cell))
+ return GLITTER_STATUS_NO_MEMORY;
+
+ cell->covered_height += sign*GRID_Y;
+ cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
+ return GLITTER_STATUS_SUCCESS;
+ }
+
+ /* Orient the edge left-to-right. */
+ dx = x2.quo - x1.quo;
+ if (dx >= 0) {
+ y1 = 0;
+ y2 = GRID_Y;
+ } else {
+ int tmp;
+ tmp = ix1; ix1 = ix2; ix2 = tmp;
+ tmp = fx1; fx1 = fx2; fx2 = tmp;
+ dx = -dx;
+ sign = -sign;
+ y1 = GRID_Y;
+ y2 = 0;
+ }
+ dy = y2 - y1;
+
+ /* Add coverage for all pixels [ix1,ix2] on this row crossed
+ * by the edge. */
+ {
+ struct cell_pair pair;
+ struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
+
+ /* When rendering a previous edge on the active list we may
+ * advance the cell list cursor past the leftmost pixel of the
+ * current edge even though the two edges don't intersect.
+ * e.g. consider two edges going down and rightwards:
+ *
+ * --\_+---\_+-----+-----+----
+ * \_ \_ | |
+ * | \_ | \_ | |
+ * | \_| \_| |
+ * | \_ \_ |
+ * ----+-----+-\---+-\---+----
+ *
+ * The left edge touches cells past the starting cell of the
+ * right edge. Fortunately such cases are rare.
+ *
+ * The rewinding is never necessary if the current edge stays
+ * within a single column because we've checked before calling
+ * this function that the active list order won't change. */
+ cell_list_maybe_rewind(cells, ix1);
+
+ pair = cell_list_find_pair(cells, ix1, ix1+1);
+ if (unlikely (!pair.cell1 || !pair.cell2))
+ return GLITTER_STATUS_NO_MEMORY;
+
+ pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
+ pair.cell1->covered_height += sign*y.quo;
+ y.quo += y1;
+
+ if (ix1+1 < ix2) {
+ struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
+ struct cell *cell = pair.cell2;
+
+ ++ix1;
+ do {
+ grid_scaled_y_t y_skip = dydx_full.quo;
+ y.rem += dydx_full.rem;
+ if (y.rem >= dx) {
+ ++y_skip;
+ y.rem -= dx;
+ }
+
+ y.quo += y_skip;
+
+ y_skip *= sign;
+ cell->uncovered_area += y_skip*GRID_X;
+ cell->covered_height += y_skip;
+
+ ++ix1;
+ cell = cell_list_find(cells, ix1);
+ if (unlikely (NULL == cell))
+ return GLITTER_STATUS_NO_MEMORY;
+ } while (ix1 != ix2);
+
+ pair.cell2 = cell;
+ }
+ pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
+ pair.cell2->covered_height += sign*(y2 - y.quo);
+ }
+
+ return GLITTER_STATUS_SUCCESS;
+}
+
+static void
+polygon_init (struct polygon *polygon)
+{
+ polygon->ymin = polygon->ymax = 0;
+ polygon->xmin = polygon->xmax = 0;
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ pool_init (polygon->edge_pool.base,
+ 8192 - sizeof (struct _pool_chunk),
+ sizeof (polygon->edge_pool.embedded));
+}
+
+static void
+polygon_fini (struct polygon *polygon)
+{
+ if (polygon->y_buckets != polygon->y_buckets_embedded)
+ free (polygon->y_buckets);
+
+ pool_fini (polygon->edge_pool.base);
+}
+
+/* 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_x_t xmin,
+ grid_scaled_x_t xmax,
+ grid_scaled_y_t ymin,
+ grid_scaled_y_t ymax)
+{
+ unsigned h = ymax - ymin;
+ unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
+ ymin);
+
+ 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 bucket));
+ if (unlikely (NULL == polygon->y_buckets))
+ goto bail_no_mem;
+ }
+ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct bucket));
+
+ polygon->ymin = ymin;
+ polygon->ymax = ymax;
+ polygon->xmin = xmin;
+ polygon->xmax = xmax;
+ 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 j = e->ytop - polygon->ymin;
+ unsigned ix = j / EDGE_Y_BUCKET_HEIGHT;
+ unsigned offset = j % EDGE_Y_BUCKET_HEIGHT;
+ struct edge **ptail = &polygon->y_buckets[ix].edges;
+ e->next = *ptail;
+ *ptail = e;
+ polygon->y_buckets[ix].have_inside_edges |= offset;
+}
+
+inline static glitter_status_t
+polygon_add_edge (struct polygon *polygon,
+ const cairo_edge_t *edge)
+{
+ struct edge *e;
+ grid_scaled_x_t dx;
+ grid_scaled_y_t dy;
+ grid_scaled_y_t ytop, ybot;
+ grid_scaled_y_t ymin = polygon->ymin;
+ grid_scaled_y_t ymax = polygon->ymax;
+
+ assert (edge->bottom > edge->top);
+
+ if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
+ return GLITTER_STATUS_SUCCESS;
+
+ e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
+ if (unlikely (NULL == e))
+ return GLITTER_STATUS_NO_MEMORY;
+
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+ e->dy = dy;
+ e->dir = edge->dir;
+
+ ytop = edge->top >= ymin ? edge->top : ymin;
+ ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+ e->ytop = ytop;
+ e->height_left = ybot - ytop;
+
+ if (dx == 0) {
+ e->vertical = TRUE;
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ e->dxdy.quo = 0;
+ e->dxdy.rem = 0;
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+
+ /* Drop edges to the right of the clip extents. */
+ if (e->x.quo >= polygon->xmax)
+ return GLITTER_STATUS_SUCCESS;
+
+ /* Offset vertical edges at the left side of the clip extents
+ * to just shy of the left side. We depend on this when
+ * checking for possible intersections within the clip
+ * rectangle. */
+ if (e->x.quo <= polygon->xmin) {
+ e->x.quo = polygon->xmin - 1;
+ }
+ } else {
+ e->vertical = FALSE;
+ e->dxdy = floored_divrem (dx, dy);
+ if (ytop == edge->line.p1.y) {
+ e->x.quo = edge->line.p1.x;
+ e->x.rem = 0;
+ } else {
+ e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+ e->x.quo += edge->line.p1.x;
+ }
+
+ if (e->x.quo >= polygon->xmax && e->dxdy.quo >= 0)
+ return GLITTER_STATUS_SUCCESS;
+
+ if (e->height_left >= GRID_Y) {
+ e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+ } else {
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
+ }
+ }
+
+ _polygon_insert_edge_into_its_y_bucket (polygon, e);
+
+ e->x.rem -= dy; /* Bias the remainder for faster
+ * edge advancement. */
+ return GLITTER_STATUS_SUCCESS;
+}
+
+static void
+active_list_reset (struct active_list *active)
+{
+ active->head = NULL;
+ active->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;
+
+ head = head_a;
+ next = &head;
+
+ while (1) {
+ while (head_a != NULL && head_a->x.quo <= head_b->x.quo) {
+ next = &head_a->next;
+ head_a = head_a->next;
+ }
+
+ *next = head_b;
+ if (head_a == NULL)
+ return head;
+
+ while (head_b != NULL && head_b->x.quo <= head_a->x.quo) {
+ next = &head_b->next;
+ head_b = head_b->next;
+ }
+
+ *next = head_a;
+ if (head_b == NULL)
+ return head;
+ }
+}
+
+/*
+ * 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,
+ grid_scaled_x_t xmin)
+{
+ const struct edge *e;
+ grid_scaled_x_t 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;
+ }
+
+ /* There's may be an intersection if the edge sort order might
+ * change. */
+ if (x.quo <= prev_x) {
+ /* Ignore intersections to the left of the clip extents.
+ * This assumes that all vertical edges on or at the left
+ * side of the clip rectangle have been shifted slightly
+ * to the left in polygon_add_edge(). */
+ if (prev_x >= xmin || x.quo >= xmin || e->x.quo >= xmin)
+ return 0;
+ }
+ else {
+ prev_x = x.quo;
+ }
+ e = e->next;
+ }
+
+ 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,
+ grid_scaled_y_t y,
+ struct polygon *polygon)
+{
+ /* Split off the edges on the current subrow and merge them into
+ * the active list. */
+ unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin);
+ int min_height = active->min_height;
+ struct edge *subrow_edges = NULL;
+ struct edge **ptail = &polygon->y_buckets[ix].edges;
+
+ while (1) {
+ struct edge *tail = *ptail;
+ if (NULL == tail) break;
+
+ if (y == tail->ytop) {
+ *ptail = tail->next;
+ tail->next = subrow_edges;
+ subrow_edges = tail;
+ if (tail->height_left < min_height)
+ min_height = tail->height_left;
+ } else {
+ ptail = &tail->next;
+ }
+ }
+ if (subrow_edges) {
+ sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
+ active->head = merge_sorted_edges (active->head, subrow_edges);
+ active->min_height = min_height;
+ }
+}
+
+/* Advance the edges on the active list by one subsample row by
+ * updating their x positions. Drop edges from the list that end. */
+inline static void
+active_list_substep_edges(
+ struct active_list *active)
+{
+ struct edge **cursor = &active->head;
+ grid_scaled_x_t prev_x = INT_MIN;
+ struct edge *unsorted = NULL;
+
+ while (1) {
+ struct edge *edge;
+
+ UNROLL3({
+ edge = *cursor;
+ if (NULL == edge)
+ break;
+
+ if (0 != --edge->height_left) {
+ edge->x.quo += edge->dxdy.quo;
+ edge->x.rem += edge->dxdy.rem;
+ if (edge->x.rem >= 0) {
+ ++edge->x.quo;
+ edge->x.rem -= edge->dy;
+ }
+
+ if (edge->x.quo < prev_x) {
+ *cursor = edge->next;
+ edge->next = unsorted;
+ unsorted = edge;
+ } else {
+ prev_x = edge->x.quo;
+ cursor = &edge->next;
+ }
+
+ } else {
+ *cursor = edge->next;
+ }
+ });
+ }
+
+ if (unsorted) {
+ sort_edges (unsorted, UINT_MAX, &unsorted);
+ active->head = merge_sorted_edges (active->head, unsorted);
+ }
+}
+
+inline static glitter_status_t
+apply_nonzero_fill_rule_for_subrow (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge *edge = active->head;
+ int winding = 0;
+ int xstart;
+ int xend;
+ int status;
+
+ cell_list_rewind (coverages);
+
+ while (NULL != edge) {
+ xstart = edge->x.quo;
+ winding = edge->dir;
+ while (1) {
+ edge = edge->next;
+ if (NULL == edge)
+ return cell_list_add_unbounded_subspan (coverages, xstart);
+
+ winding += edge->dir;
+ if (0 == winding) {
+ if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+ break;
+ }
+ }
+
+ xend = edge->x.quo;
+ status = cell_list_add_subspan (coverages, xstart, xend);
+ if (unlikely (status))
+ return status;
+
+ edge = edge->next;
+ }
+
+ return GLITTER_STATUS_SUCCESS;
+}
+
+static glitter_status_t
+apply_evenodd_fill_rule_for_subrow (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge *edge = active->head;
+ int xstart;
+ int xend;
+ int status;
+
+ cell_list_rewind (coverages);
+
+ while (NULL != edge) {
+ xstart = edge->x.quo;
+
+ while (1) {
+ edge = edge->next;
+ if (NULL == edge)
+ return cell_list_add_unbounded_subspan (coverages, xstart);
+
+ if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
+ break;
+
+ edge = edge->next;
+ }
+
+ xend = edge->x.quo;
+ status = cell_list_add_subspan (coverages, xstart, xend);
+ if (unlikely (status))
+ return status;
+
+ edge = edge->next;
+ }
+
+ return GLITTER_STATUS_SUCCESS;
+}
+
+static glitter_status_t
+apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge **cursor = &active->head;
+ struct edge *left_edge;
+ int status;
+
+ left_edge = *cursor;
+ while (NULL != left_edge) {
+ struct edge *right_edge;
+ int winding = left_edge->dir;
+
+ left_edge->height_left -= GRID_Y;
+ if (left_edge->height_left)
+ cursor = &left_edge->next;
+ else
+ *cursor = left_edge->next;
+
+ while (1) {
+ right_edge = *cursor;
+ if (NULL == right_edge)
+ return cell_list_render_edge (coverages, left_edge, +1);
+
+ right_edge->height_left -= GRID_Y;
+ if (right_edge->height_left)
+ cursor = &right_edge->next;
+ else
+ *cursor = right_edge->next;
+
+ winding += right_edge->dir;
+ if (0 == winding) {
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
+ }
+
+ if (! right_edge->vertical) {
+ right_edge->x.quo += right_edge->dxdy_full.quo;
+ right_edge->x.rem += right_edge->dxdy_full.rem;
+ if (right_edge->x.rem >= 0) {
+ ++right_edge->x.quo;
+ right_edge->x.rem -= right_edge->dy;
+ }
+ }
+ }
+
+ status = cell_list_render_edge (coverages, left_edge, +1);
+ if (unlikely (status))
+ return status;
+
+ status = cell_list_render_edge (coverages, right_edge, -1);
+ if (unlikely (status))
+ return status;
+
+ left_edge = *cursor;
+ }
+
+ return GLITTER_STATUS_SUCCESS;
+}
+
+static glitter_status_t
+apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+ struct cell_list *coverages)
+{
+ struct edge **cursor = &active->head;
+ struct edge *left_edge;
+ int status;
+
+ left_edge = *cursor;
+ while (NULL != left_edge) {
+ struct edge *right_edge;
+ int winding = left_edge->dir;
+
+ left_edge->height_left -= GRID_Y;
+ if (left_edge->height_left)
+ cursor = &left_edge->next;
+ else
+ *cursor = left_edge->next;
+
+ while (1) {
+ right_edge = *cursor;
+ if (NULL == right_edge)
+ return cell_list_render_edge (coverages, left_edge, +1);
+
+ right_edge->height_left -= GRID_Y;
+ if (right_edge->height_left)
+ cursor = &right_edge->next;
+ else
+ *cursor = right_edge->next;
+
+ winding += right_edge->dir;
+ if ((winding & 1) == 0) {
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
+ }
+
+ if (! right_edge->vertical) {
+ right_edge->x.quo += right_edge->dxdy_full.quo;
+ right_edge->x.rem += right_edge->dxdy_full.rem;
+ if (right_edge->x.rem >= 0) {
+ ++right_edge->x.quo;
+ right_edge->x.rem -= right_edge->dy;
+ }
+ }
+ }
+
+ status = cell_list_render_edge (coverages, left_edge, +1);
+ if (unlikely (status))
+ return status;
+
+ status = cell_list_render_edge (coverages, right_edge, -1);
+ if (unlikely (status))
+ return status;
+
+ left_edge = *cursor;
+ }
+
+ return GLITTER_STATUS_SUCCESS;
+}
+
+/* If the user hasn't configured a coverage blitter, use a default one
+ * that blits spans directly to an A8 raster. */
+#ifndef GLITTER_BLIT_COVERAGES
+
+inline static void
+blit_span(
+ unsigned char *row_pixels,
+ int x, unsigned len,
+ grid_area_t coverage)
+{
+ int alpha = GRID_AREA_TO_ALPHA(coverage);
+ if (1 == len) {
+ row_pixels[x] = alpha;
+ }
+ else {
+ memset(row_pixels + x, alpha, len);
+ }
+}
+
+#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \
+ do { \
+ int __y = y; \
+ int __h = height; \
+ do { \
+ blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \
+ } while (--__h); \
+ } while (0)
+
+static void
+blit_cells(
+ struct cell_list *cells,
+ unsigned char *row_pixels,
+ int xmin, int xmax)
+{
+ struct cell *cell = cells->head;
+ int prev_x = xmin;
+ int coverage = 0;
+ if (NULL == cell)
+ return;
+
+ while (NULL != cell && cell->x < xmin) {
+ coverage += cell->covered_height;
+ cell = cell->next;
+ }
+ coverage *= GRID_X*2;
+
+ for (; NULL != cell; cell = cell->next) {
+ int x = cell->x;
+ int area;
+ if (x >= xmax)
+ break;
+ if (x > prev_x && 0 != coverage) {
+ blit_span(row_pixels, prev_x, x - prev_x, coverage);
+ }
+
+ coverage += cell->covered_height * GRID_X*2;
+ area = coverage - cell->uncovered_area;
+ if (area) {
+ blit_span(row_pixels, x, 1, area);
+ }
+ prev_x = x+1;
+ }
+
+ if (0 != coverage && prev_x < xmax) {
+ blit_span(row_pixels, prev_x, xmax - prev_x, coverage);
+ }
+}
+#endif /* GLITTER_BLIT_COVERAGES */
+
+static void
+_glitter_scan_converter_init(glitter_scan_converter_t *converter)
+{
+ polygon_init(converter->polygon);
+ active_list_init(converter->active);
+ cell_list_init(converter->coverages);
+ converter->xmin=0;
+ converter->ymin=0;
+ converter->xmax=0;
+ converter->ymax=0;
+}
+
+static void
+_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
+{
+ polygon_fini(converter->polygon);
+ cell_list_fini(converter->coverages);
+ converter->xmin=0;
+ converter->ymin=0;
+ converter->xmax=0;
+ converter->ymax=0;
+}
+
+static grid_scaled_t
+int_to_grid_scaled(int i, int scale)
+{
+ /* Clamp to max/min representable scaled number. */
+ if (i >= 0) {
+ if (i >= INT_MAX/scale)
+ i = INT_MAX/scale;
+ }
+ else {
+ if (i <= INT_MIN/scale)
+ i = INT_MIN/scale;
+ }
+ return i*scale;
+}
+
+#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
+#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
+
+I glitter_status_t
+glitter_scan_converter_reset(
+ glitter_scan_converter_t *converter,
+ int xmin, int ymin,
+ int xmax, int ymax)
+{
+ glitter_status_t status;
+
+ converter->xmin = 0; converter->xmax = 0;
+ converter->ymin = 0; converter->ymax = 0;
+
+ xmin = int_to_grid_scaled_x(xmin);
+ ymin = int_to_grid_scaled_y(ymin);
+ xmax = int_to_grid_scaled_x(xmax);
+ ymax = int_to_grid_scaled_y(ymax);
+
+ active_list_reset(converter->active);
+ cell_list_reset(converter->coverages);
+ status = polygon_reset(converter->polygon, xmin, xmax, ymin, ymax);
+ if (status)
+ return status;
+
+ converter->xmin = xmin;
+ converter->xmax = xmax;
+ converter->ymin = ymin;
+ converter->ymax = ymax;
+ return GLITTER_STATUS_SUCCESS;
+}
+
+/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
+ * These macros convert an input coordinate in the client's
+ * device space to the rasterisation grid.
+ */
+/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
+ * shifts if possible, and something saneish if not.
+ */
+#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
+#else
+# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
+#endif
+
+#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
+# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
+#else
+# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
+#endif
+
+#define INPUT_TO_GRID_general(in, out, grid_scale) do { \
+ long long tmp__ = (long long)(grid_scale) * (in); \
+ tmp__ >>= GLITTER_INPUT_BITS; \
+ (out) = tmp__; \
+} while (0)
+
+I glitter_status_t
+glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ const cairo_edge_t *edge)
+{
+ cairo_edge_t e;
+
+ INPUT_TO_GRID_Y (edge->top, e.top);
+ INPUT_TO_GRID_Y (edge->bottom, e.bottom);
+ if (e.top >= e.bottom)
+ return GLITTER_STATUS_SUCCESS;
+
+ /* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
+ INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
+ INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
+ if (e.line.p1.y == e.line.p2.y)
+ return GLITTER_STATUS_SUCCESS;
+
+ INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
+ INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
+
+ e.dir = edge->dir;
+
+ return polygon_add_edge (converter->polygon, &e);
+}
+
+#ifndef GLITTER_BLIT_COVERAGES_BEGIN
+# define GLITTER_BLIT_COVERAGES_BEGIN
+#endif
+
+#ifndef GLITTER_BLIT_COVERAGES_END
+# define GLITTER_BLIT_COVERAGES_END
+#endif
+
+#ifndef GLITTER_BLIT_COVERAGES_EMPTY
+# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax)
+#endif
+
+static cairo_bool_t
+active_list_is_vertical (struct active_list *active)
+{
+ struct edge *e;
+
+ for (e = active->head; e != NULL; e = e->next) {
+ if (! e->vertical)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+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;
+ }
+}
+
+I glitter_status_t
+glitter_scan_converter_render(
+ glitter_scan_converter_t *converter,
+ int nonzero_fill,
+ GLITTER_BLIT_COVERAGES_ARGS)
+{
+ int i, j;
+ int ymax_i = converter->ymax / GRID_Y;
+ int ymin_i = converter->ymin / GRID_Y;
+ int xmin_i, xmax_i;
+ grid_scaled_x_t xmin = converter->xmin;
+ int h = ymax_i - ymin_i;
+ struct polygon *polygon = converter->polygon;
+ struct cell_list *coverages = converter->coverages;
+ struct active_list *active = converter->active;
+
+ xmin_i = converter->xmin / GRID_X;
+ xmax_i = converter->xmax / GRID_X;
+ if (xmin_i >= xmax_i)
+ return GLITTER_STATUS_SUCCESS;
+
+ /* Let the coverage blitter initialise itself. */
+ GLITTER_BLIT_COVERAGES_BEGIN;
+
+ /* Render each pixel row. */
+ for (i = 0; i < h; i = j) {
+ int do_full_step = 0;
+ glitter_status_t status = 0;
+
+ j = i + 1;
+
+ /* Determine if we can ignore this row or use the full pixel
+ * stepper. */
+ if (polygon->y_buckets[i].edges == NULL) {
+ if (! active->head) {
+ for (; j < h && ! polygon->y_buckets[j].edges; j++)
+ ;
+ GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i);
+ continue;
+ }
+ do_full_step = active_list_can_step_full_row (active, xmin);
+ }
+ else if (! polygon->y_buckets[i].have_inside_edges) {
+ grid_scaled_y_t y = (i+ymin_i)*GRID_Y;
+ active_list_merge_edges_from_polygon (active, y, polygon);
+ do_full_step = active_list_can_step_full_row (active, xmin);
+ }
+
+ if (do_full_step) {
+ /* Step by a full pixel row's worth. */
+ if (nonzero_fill) {
+ status = apply_nonzero_fill_rule_and_step_edges (active,
+ coverages);
+ } else {
+ status = apply_evenodd_fill_rule_and_step_edges (active,
+ coverages);
+ }
+
+ if (active_list_is_vertical (active)) {
+ while (j < h &&
+ polygon->y_buckets[j].edges == NULL &&
+ active->min_height >= 2*GRID_Y)
+ {
+ active->min_height -= GRID_Y;
+ j++;
+ }
+ if (j != i + 1)
+ step_edges (active, j - (i + 1));
+ }
+ } else {
+ /* Supersample this row. */
+ grid_scaled_y_t suby;
+ for (suby = 0; suby < GRID_Y; suby++) {
+ grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
+
+ active_list_merge_edges_from_polygon (active, y, polygon);
+
+ if (nonzero_fill) {
+ status |= apply_nonzero_fill_rule_for_subrow (active,
+ coverages);
+ } else {
+ status |= apply_evenodd_fill_rule_for_subrow (active,
+ coverages);
+ }
+
+ active_list_substep_edges(active);
+ }
+ }
+
+ if (unlikely (status))
+ return status;
+
+ GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i);
+ cell_list_reset (coverages);
+
+ if (! active->head)
+ active->min_height = INT_MAX;
+ else
+ active->min_height -= GRID_Y;
+ }
+
+ /* Clean up the coverage blitter. */
+ GLITTER_BLIT_COVERAGES_END;
+
+ return GLITTER_STATUS_SUCCESS;
+}
+
+/*-------------------------------------------------------------------------
+ * cairo specific implementation: the coverage blitter and
+ * scan converter subclass. */
+
+static glitter_status_t
+blit_with_span_renderer (struct cell_list *cells,
+ cairo_span_renderer_t *renderer,
+ struct pool *span_pool,
+ int y, int height,
+ int xmin, int xmax)
+{
+ struct cell *cell = cells->head;
+ int prev_x = xmin;
+ int cover = 0;
+ cairo_half_open_span_t *spans;
+ unsigned num_spans;
+
+ if (cell == NULL)
+ return blit_empty_with_span_renderer (renderer, y, height);
+
+ /* Skip cells to the left of the clip region. */
+ while (cell != NULL && cell->x < xmin) {
+ cover += cell->covered_height;
+ cell = cell->next;
+ }
+ cover *= GRID_X*2;
+
+ /* Count number of cells remaining. */
+ {
+ struct cell *next = cell;
+ num_spans = 1;
+ while (next != NULL) {
+ next = next->next;
+ ++num_spans;
+ }
+ num_spans = 2*num_spans;
+ }
+
+ /* Allocate enough spans for the row. */
+ pool_reset (span_pool);
+ spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
+ if (unlikely (spans == NULL))
+ return GLITTER_STATUS_NO_MEMORY;
+
+ num_spans = 0;
+
+ /* Form the spans from the coverages and areas. */
+ for (; cell != NULL; cell = cell->next) {
+ int x = cell->x;
+ int area;
+
+ if (x >= xmax)
+ break;
+
+ if (x > prev_x) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ ++num_spans;
+ }
+
+ cover += cell->covered_height*GRID_X*2;
+ area = cover - cell->uncovered_area;
+
+ spans[num_spans].x = x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
+ ++num_spans;
+
+ prev_x = x+1;
+ }
+
+ if (prev_x <= xmax) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ ++num_spans;
+ }
+
+ if (prev_x < xmax && 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);
+}
+
+static glitter_status_t
+blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height)
+{
+ return renderer->render_rows (renderer, y, height, NULL, 0);
+}
+
+struct _cairo_tor_scan_converter {
+ cairo_scan_converter_t base;
+
+ glitter_scan_converter_t converter[1];
+ cairo_fill_rule_t fill_rule;
+
+ struct {
+ struct pool base[1];
+ cairo_half_open_span_t embedded[32];
+ } span_pool;
+};
+
+typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t;
+
+static void
+_cairo_tor_scan_converter_destroy (void *converter)
+{
+ cairo_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_tor_scan_converter_add_edge (void *converter,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ int top, int bottom,
+ int dir)
+{
+ cairo_tor_scan_converter_t *self = converter;
+ cairo_status_t status;
+ cairo_edge_t edge;
+
+ edge.line.p1 = *p1;
+ edge.line.p2 = *p2;
+ edge.top = top;
+ edge.bottom = bottom;
+ edge.dir = dir;
+
+ status = glitter_scan_converter_add_edge (self->converter, &edge);
+ if (unlikely (status))
+ return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_tor_scan_converter_add_polygon (void *converter,
+ const cairo_polygon_t *polygon)
+{
+ cairo_tor_scan_converter_t *self = converter;
+ cairo_status_t status;
+ int i;
+
+ for (i = 0; i < polygon->num_edges; i++) {
+ status = glitter_scan_converter_add_edge (self->converter,
+ &polygon->edges[i]);
+ if (unlikely (status)) {
+ return _cairo_scan_converter_set_error (self,
+ _cairo_error (status));
+ }
+ }
+
+ 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;
+
+ status = glitter_scan_converter_render (self->converter,
+ self->fill_rule == CAIRO_FILL_RULE_WINDING,
+ renderer,
+ self->span_pool.base);
+ if (unlikely (status))
+ return _cairo_scan_converter_set_error (self, _cairo_error (status));
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_scan_converter_t *
+_cairo_tor_scan_converter_create (int xmin,
+ int ymin,
+ int xmax,
+ int ymax,
+ cairo_fill_rule_t fill_rule)
+{
+ cairo_tor_scan_converter_t *self;
+ cairo_status_t status;
+
+ self = calloc (1, sizeof(struct _cairo_tor_scan_converter));
+ if (unlikely (self == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto bail_nomem;
+ }
+
+ self->base.destroy = _cairo_tor_scan_converter_destroy;
+ self->base.add_edge = _cairo_tor_scan_converter_add_edge;
+ self->base.add_polygon = _cairo_tor_scan_converter_add_polygon;
+ self->base.generate = _cairo_tor_scan_converter_generate;
+
+ pool_init (self->span_pool.base,
+ 250 * sizeof(self->span_pool.embedded[0]),
+ sizeof(self->span_pool.embedded));
+
+ _glitter_scan_converter_init (self->converter);
+ status = glitter_scan_converter_reset (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-toy-font-face.c b/gfx/cairo/cairo/src/cairo-toy-font-face.c
new file mode 100644
index 000000000..4c690da53
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-toy-font-face.c
@@ -0,0 +1,526 @@
+/* -*- 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 <cworth@cworth.org>
+ * Graydon Hoare <graydon@redhat.com>
+ * Owen Taylor <otaylor@redhat.com>
+ * Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#define _BSD_SOURCE /* for strdup() */
+#include "cairoint.h"
+#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;
+
+ assert (hash != 0);
+ key->base.hash_entry.hash = hash;
+}
+
+static cairo_status_t
+_cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face,
+ cairo_font_face_t **impl_font_face)
+{
+ const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT;
+ cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (unlikely (font_face->base.status))
+ return font_face->base.status;
+
+ if (backend->create_for_toy != NULL &&
+ 0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT,
+ strlen (CAIRO_USER_FONT_FAMILY_DEFAULT)))
+ {
+ status = backend->create_for_toy (font_face, impl_font_face);
+ }
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ backend = &_cairo_user_font_face_backend;
+ status = backend->create_for_toy (font_face, impl_font_face);
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_toy_font_face_init (cairo_toy_font_face_t *font_face,
+ const char *family,
+ cairo_font_slant_t slant,
+ cairo_font_weight_t weight)
+{
+ char *family_copy;
+ cairo_status_t status;
+
+ family_copy = strdup (family);
+ if (unlikely (family_copy == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight);
+ font_face->owns_family = TRUE;
+
+ _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend);
+
+ status = _cairo_toy_font_face_create_impl_face (font_face,
+ &font_face->impl_face);
+ if (unlikely (status)) {
+ free (family_copy);
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_toy_font_face_fini (cairo_toy_font_face_t *font_face)
+{
+ /* We assert here that we own font_face->family before casting
+ * away the const qualifer. */
+ assert (font_face->owns_family);
+ free ((char*) font_face->family);
+
+ if (font_face->impl_face)
+ cairo_font_face_destroy (font_face->impl_face);
+}
+
+static int
+_cairo_toy_font_face_keys_equal (const void *key_a,
+ const void *key_b)
+{
+ const cairo_toy_font_face_t *face_a = key_a;
+ const cairo_toy_font_face_t *face_b = key_b;
+
+ return (strcmp (face_a->family, face_b->family) == 0 &&
+ face_a->slant == face_b->slant &&
+ face_a->weight == face_b->weight);
+}
+
+/**
+ * cairo_toy_font_face_create:
+ * @family: a font family name, encoded in UTF-8
+ * @slant: the slant for the font
+ * @weight: the weight for the font
+ *
+ * Creates a font face from a triplet of family, slant, and weight.
+ * These font faces are used in implementation of the the #cairo_t "toy"
+ * font API.
+ *
+ * If @family is the zero-length string "", the platform-specific default
+ * family is assumed. The default family then can be queried using
+ * cairo_toy_font_face_get_family().
+ *
+ * The cairo_select_font_face() function uses this to create font faces.
+ * See that function for limitations and other details of toy font faces.
+ *
+ * Return value: a newly created #cairo_font_face_t. Free with
+ * cairo_font_face_destroy() when you are done using it.
+ *
+ * Since: 1.8
+ **/
+cairo_font_face_t *
+cairo_toy_font_face_create (const char *family,
+ cairo_font_slant_t slant,
+ cairo_font_weight_t weight)
+{
+ cairo_status_t status;
+ cairo_toy_font_face_t key, *font_face;
+ cairo_hash_table_t *hash_table;
+
+ if (family == NULL)
+ return (cairo_font_face_t*) &_cairo_font_face_null_pointer;
+
+ /* Make sure we've got valid UTF-8 for the family */
+ status = _cairo_utf8_to_ucs4 (family, -1, NULL, NULL);
+ if (unlikely (status)) {
+ if (status == CAIRO_STATUS_INVALID_STRING)
+ return (cairo_font_face_t*) &_cairo_font_face_invalid_string;
+
+ return (cairo_font_face_t*) &_cairo_font_face_nil;
+ }
+
+ switch (slant) {
+ case CAIRO_FONT_SLANT_NORMAL:
+ case CAIRO_FONT_SLANT_ITALIC:
+ case CAIRO_FONT_SLANT_OBLIQUE:
+ break;
+ default:
+ return (cairo_font_face_t*) &_cairo_font_face_invalid_slant;
+ }
+
+ switch (weight) {
+ case CAIRO_FONT_WEIGHT_NORMAL:
+ case CAIRO_FONT_WEIGHT_BOLD:
+ break;
+ default:
+ return (cairo_font_face_t*) &_cairo_font_face_invalid_weight;
+ }
+
+ if (*family == '\0')
+ family = CAIRO_FONT_FAMILY_DEFAULT;
+
+ hash_table = _cairo_toy_font_face_hash_table_lock ();
+ if (unlikely (hash_table == NULL))
+ goto UNWIND;
+
+ _cairo_toy_font_face_init_key (&key, family, slant, weight);
+
+ /* Return existing font_face if it exists in the hash table. */
+ font_face = _cairo_hash_table_lookup (hash_table,
+ &key.base.hash_entry);
+ if (font_face != NULL) {
+ if (font_face->base.status == CAIRO_STATUS_SUCCESS) {
+ /* We increment the reference count here manually to avoid
+ double-locking. */
+ _cairo_reference_count_inc (&font_face->base.ref_count);
+ _cairo_toy_font_face_hash_table_unlock ();
+ return &font_face->base;
+ }
+
+ /* remove the bad font from the hash table */
+ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+ font_face->base.hash_entry.hash = 0;
+ }
+
+ /* Otherwise create it and insert into hash table. */
+ font_face = malloc (sizeof (cairo_toy_font_face_t));
+ if (unlikely (font_face == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto UNWIND_HASH_TABLE_LOCK;
+ }
+
+ status = _cairo_toy_font_face_init (font_face, family, slant, weight);
+ if (unlikely (status))
+ goto UNWIND_FONT_FACE_MALLOC;
+
+ assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash);
+ status = _cairo_hash_table_insert (hash_table, &font_face->base.hash_entry);
+ if (unlikely (status))
+ goto UNWIND_FONT_FACE_INIT;
+
+ _cairo_toy_font_face_hash_table_unlock ();
+
+ return &font_face->base;
+
+ UNWIND_FONT_FACE_INIT:
+ _cairo_toy_font_face_fini (font_face);
+ UNWIND_FONT_FACE_MALLOC:
+ free (font_face);
+ UNWIND_HASH_TABLE_LOCK:
+ _cairo_toy_font_face_hash_table_unlock ();
+ UNWIND:
+ return (cairo_font_face_t*) &_cairo_font_face_nil;
+}
+slim_hidden_def (cairo_toy_font_face_create);
+
+static void
+_cairo_toy_font_face_destroy (void *abstract_face)
+{
+ cairo_toy_font_face_t *font_face = abstract_face;
+ cairo_hash_table_t *hash_table;
+
+ if (font_face == NULL ||
+ CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->base.ref_count))
+ return;
+
+ hash_table = _cairo_toy_font_face_hash_table_lock ();
+ /* All created objects must have been mapped in the hash table. */
+ assert (hash_table != NULL);
+
+ if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) {
+ /* somebody recreated the font whilst we waited for the lock */
+ _cairo_toy_font_face_hash_table_unlock ();
+ return;
+ }
+
+ if (font_face->base.hash_entry.hash != 0)
+ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+
+ _cairo_toy_font_face_hash_table_unlock ();
+
+ _cairo_toy_font_face_fini (font_face);
+}
+
+static cairo_status_t
+_cairo_toy_font_face_scaled_font_create (void *abstract_font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ cairo_scaled_font_t **scaled_font)
+{
+ cairo_toy_font_face_t *font_face = (cairo_toy_font_face_t *) abstract_font_face;
+
+ ASSERT_NOT_REACHED;
+
+ return _cairo_font_face_set_error (&font_face->base, CAIRO_STATUS_FONT_TYPE_MISMATCH);
+}
+
+static cairo_font_face_t *
+_cairo_toy_font_face_get_implementation (void *abstract_font_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options)
+{
+ cairo_toy_font_face_t *font_face = abstract_font_face;
+
+ if (font_face->impl_face) {
+ cairo_font_face_t *impl = font_face->impl_face;
+
+ if (impl->backend->get_implementation != NULL) {
+ return impl->backend->get_implementation (impl,
+ font_matrix,
+ ctm,
+ options);
+ }
+
+ return cairo_font_face_reference (impl);
+ }
+
+ return abstract_font_face;
+}
+
+static cairo_bool_t
+_cairo_font_face_is_toy (cairo_font_face_t *font_face)
+{
+ return font_face->backend == &_cairo_toy_font_face_backend;
+}
+
+/**
+ * cairo_toy_font_face_get_family:
+ * @font_face: A toy font face
+ *
+ * Gets the familly name of a toy font.
+ *
+ * Return value: The family name. This string is owned by the font face
+ * and remains valid as long as the font face is alive (referenced).
+ *
+ * Since: 1.8
+ **/
+const char *
+cairo_toy_font_face_get_family (cairo_font_face_t *font_face)
+{
+ cairo_toy_font_face_t *toy_font_face;
+
+ if (font_face->status)
+ return CAIRO_FONT_FAMILY_DEFAULT;
+
+ toy_font_face = (cairo_toy_font_face_t *) font_face;
+ if (! _cairo_font_face_is_toy (font_face)) {
+ if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+ return CAIRO_FONT_FAMILY_DEFAULT;
+ }
+ assert (toy_font_face->owns_family);
+ return toy_font_face->family;
+}
+
+/**
+ * cairo_toy_font_face_get_slant:
+ * @font_face: A toy font face
+ *
+ * Gets the slant a toy font.
+ *
+ * Return value: The slant value
+ *
+ * Since: 1.8
+ **/
+cairo_font_slant_t
+cairo_toy_font_face_get_slant (cairo_font_face_t *font_face)
+{
+ cairo_toy_font_face_t *toy_font_face;
+
+ if (font_face->status)
+ return CAIRO_FONT_SLANT_DEFAULT;
+
+ toy_font_face = (cairo_toy_font_face_t *) font_face;
+ if (! _cairo_font_face_is_toy (font_face)) {
+ if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+ return CAIRO_FONT_SLANT_DEFAULT;
+ }
+ return toy_font_face->slant;
+}
+slim_hidden_def (cairo_toy_font_face_get_slant);
+
+/**
+ * cairo_toy_font_face_get_weight:
+ * @font_face: A toy font face
+ *
+ * Gets the weight a toy font.
+ *
+ * Return value: The weight value
+ *
+ * Since: 1.8
+ **/
+cairo_font_weight_t
+cairo_toy_font_face_get_weight (cairo_font_face_t *font_face)
+{
+ cairo_toy_font_face_t *toy_font_face;
+
+ if (font_face->status)
+ return CAIRO_FONT_WEIGHT_DEFAULT;
+
+ toy_font_face = (cairo_toy_font_face_t *) font_face;
+ if (! _cairo_font_face_is_toy (font_face)) {
+ if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
+ return CAIRO_FONT_WEIGHT_DEFAULT;
+ }
+ return toy_font_face->weight;
+}
+slim_hidden_def (cairo_toy_font_face_get_weight);
+
+static const cairo_font_face_backend_t _cairo_toy_font_face_backend = {
+ CAIRO_FONT_TYPE_TOY,
+ NULL, /* create_for_toy */
+ _cairo_toy_font_face_destroy,
+ _cairo_toy_font_face_scaled_font_create,
+ _cairo_toy_font_face_get_implementation
+};
+
+void
+_cairo_toy_font_face_reset_static_data (void)
+{
+ cairo_hash_table_t *hash_table;
+
+ /* We manually acquire the lock rather than calling
+ * cairo_toy_font_face_hash_table_lock simply to avoid
+ * creating the table only to destroy it again. */
+ CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex);
+ hash_table = cairo_toy_font_face_hash_table;
+ cairo_toy_font_face_hash_table = NULL;
+ CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
+
+ if (hash_table != NULL)
+ _cairo_hash_table_destroy (hash_table);
+}
diff --git a/gfx/cairo/cairo/src/cairo-traps.c b/gfx/cairo/cairo/src/cairo-traps.c
new file mode 100644
index 000000000..2fe6684db
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-traps.c
@@ -0,0 +1,605 @@
+/* -*- 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 <keithp@keithp.com>
+ * Carl D. Worth <cworth@cworth.org>
+ *
+ * 2002-07-15: Converted from XRenderCompositeDoublePoly to #cairo_trap_t. Carl D. Worth
+ */
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-slope-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)
+{
+ traps->limits = limits;
+ traps->num_limits = num_limits;
+}
+
+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_NOACCESS (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,
+ cairo_line_t *left, cairo_line_t *right)
+{
+ cairo_trapezoid_t *trap;
+
+ 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;
+}
+
+/**
+ * _cairo_traps_init_box:
+ * @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;
+
+ /* 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;
+ }
+
+ 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;
+ }
+ }
+}
+
+
+/**
+ * _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_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 (! traps->maybe_region)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ for (i = 0; i < traps->num_traps; i++) {
+ if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
+ traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
+ ! _cairo_fixed_is_integer (traps->traps[i].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ if (traps->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 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
+ int y1 = _cairo_fixed_integer_part (traps->traps[i].top);
+ int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
+ int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
+
+ rects[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;
+}
+
+/* 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;
+}
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 000000000..f0822611d
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-truetype-subset-private.h
@@ -0,0 +1,203 @@
+/* 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 <krh@redhat.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+#ifndef CAIRO_TRUETYPE_SUBSET_PRIVATE_H
+#define CAIRO_TRUETYPE_SUBSET_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+#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) (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_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;
+
+
+
+/* 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 */
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_TRUETYPE_SUBSET_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-truetype-subset.c b/gfx/cairo/cairo/src/cairo-truetype-subset.c
new file mode 100644
index 000000000..9bf2f8ffc
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-truetype-subset.c
@@ -0,0 +1,1433 @@
+/* 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 <krh@redhat.com>
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+/*
+ * Useful links:
+ * http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6.html
+ * http://www.microsoft.com/typography/specs/default.htm
+ */
+
+#define _BSD_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-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;
+ unsigned int num_glyphs;
+ int *widths;
+ long x_min, y_min, x_max, y_max;
+ long ascent, descent;
+ int units_per_em;
+ } base;
+
+ subset_glyph_t *glyphs;
+ const cairo_scaled_font_backend_t *backend;
+ int num_glyphs_in_face;
+ int checksum_index;
+ cairo_array_t output;
+ cairo_array_t string_offsets;
+ unsigned long last_offset;
+ unsigned long last_boundary;
+ int *parent_to_subset;
+ cairo_status_t status;
+
+};
+
+/*
+ * Test that the structs we define for TrueType tables have the
+ * correct size, ie. they are not padded.
+ */
+#define check(T, S) COMPILE_TIME_ASSERT (sizeof (T) == (S))
+check (tt_head_t, 54);
+check (tt_hhea_t, 36);
+check (tt_maxp_t, 32);
+check (tt_name_record_t, 12);
+check (tt_name_t, 18);
+check (tt_name_t, 18);
+check (tt_composite_glyph_t, 16);
+check (tt_glyph_data_t, 26);
+#undef check
+
+static cairo_status_t
+cairo_truetype_font_use_glyph (cairo_truetype_font_t *font,
+ unsigned short glyph,
+ 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 == 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_truetype_font_t **font_return)
+{
+ cairo_status_t status;
+ 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;
+ */
+
+ 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 = malloc (sizeof (cairo_truetype_font_t));
+ if (unlikely (font == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->backend = backend;
+ font->num_glyphs_in_face = be16_to_cpu (maxp.num_glyphs);
+ font->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;
+
+ font->glyphs = calloc (font->num_glyphs_in_face + 1, sizeof (subset_glyph_t));
+ if (unlikely (font->glyphs == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail1;
+ }
+
+ font->parent_to_subset = calloc (font->num_glyphs_in_face, sizeof (int));
+ if (unlikely (font->parent_to_subset == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+
+ font->base.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 = 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);
+ }
+
+ font->base.widths = calloc (font->num_glyphs_in_face, sizeof (int));
+ if (unlikely (font->base.widths == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail4;
+ }
+
+ _cairo_array_init (&font->string_offsets, sizeof (unsigned long));
+ status = _cairo_array_grow_by (&font->string_offsets, 10);
+ if (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->base.widths);
+ fail4:
+ free (font->base.ps_name);
+ fail3:
+ free (font->parent_to_subset);
+ if (font->base.font_name)
+ free (font->base.font_name);
+ fail2:
+ free (font->glyphs);
+ fail1:
+ _cairo_array_fini (&font->output);
+ free (font);
+
+ return status;
+}
+
+static void
+cairo_truetype_font_destroy (cairo_truetype_font_t *font)
+{
+ _cairo_array_fini (&font->string_offsets);
+ free (font->base.widths);
+ free (font->base.ps_name);
+ if (font->base.font_name)
+ free (font->base.font_name);
+ free (font->parent_to_subset);
+ free (font->glyphs);
+ _cairo_array_fini (&font->output);
+ 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;
+}
+
+static cairo_status_t
+cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font,
+ unsigned long tag)
+{
+ unsigned int i;
+
+ cairo_truetype_font_write_be16 (font, 0); /* Table version */
+ cairo_truetype_font_write_be16 (font, 2); /* Num tables */
+
+ cairo_truetype_font_write_be16 (font, 3); /* Platform */
+ cairo_truetype_font_write_be16 (font, 0); /* Encoding */
+ cairo_truetype_font_write_be32 (font, 20); /* Offset to start of table */
+
+ cairo_truetype_font_write_be16 (font, 1); /* Platform */
+ cairo_truetype_font_write_be16 (font, 0); /* Encoding */
+ cairo_truetype_font_write_be32 (font, 52); /* Offset to start of table */
+
+ /* Output a format 4 encoding table. */
+
+ cairo_truetype_font_write_be16 (font, 4); /* Format */
+ cairo_truetype_font_write_be16 (font, 32); /* Length */
+ cairo_truetype_font_write_be16 (font, 0); /* Version */
+ cairo_truetype_font_write_be16 (font, 4); /* 2*segcount */
+ cairo_truetype_font_write_be16 (font, 4); /* searchrange */
+ cairo_truetype_font_write_be16 (font, 1); /* entry selector */
+ cairo_truetype_font_write_be16 (font, 0); /* rangeshift */
+ cairo_truetype_font_write_be16 (font, 0xf000 + font->base.num_glyphs - 1); /* end count[0] */
+ cairo_truetype_font_write_be16 (font, 0xffff); /* end count[1] */
+ cairo_truetype_font_write_be16 (font, 0); /* reserved */
+ cairo_truetype_font_write_be16 (font, 0xf000); /* startCode[0] */
+ cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[1] */
+ cairo_truetype_font_write_be16 (font, 0x1000); /* delta[0] */
+ cairo_truetype_font_write_be16 (font, 1); /* delta[1] */
+ cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[0] */
+ cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[1] */
+
+ /* Output a format 6 encoding table. */
+
+ cairo_truetype_font_write_be16 (font, 6);
+ cairo_truetype_font_write_be16 (font, 10 + 2 * font->base.num_glyphs);
+ cairo_truetype_font_write_be16 (font, 0);
+ cairo_truetype_font_write_be16 (font, 0); /* First character */
+ cairo_truetype_font_write_be16 (font, font->base.num_glyphs);
+ for (i = 0; i < font->base.num_glyphs; i++)
+ cairo_truetype_font_write_be16 (font, i);
+
+ 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->num_glyphs_in_face + 1);
+ else
+ size = sizeof (int32_t) * (font->num_glyphs_in_face + 1);
+
+ u.bytes = 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))
+ return _cairo_truetype_font_set_error (font, status);
+
+ start_offset = _cairo_array_num_elements (&font->output);
+ for (i = 0; i < font->base.num_glyphs; i++) {
+ 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 != 0) {
+ status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_glyf, begin, buffer, &size);
+ if (unlikely (status))
+ goto FAIL;
+
+ status = cairo_truetype_font_remap_composite_glyph (font, buffer, size);
+ if (unlikely (status))
+ goto FAIL;
+ }
+ }
+
+ 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->base.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->base.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->base.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->base.num_glyphs + 1; i++)
+ cairo_truetype_font_write_be16 (font, font->glyphs[i].location / 2);
+ } else {
+ for (i = 0; i < font->base.num_glyphs + 1; i++)
+ 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->base.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->num_glyphs_in_face)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (font->parent_to_subset[glyph] == 0) {
+ font->parent_to_subset[glyph] = font->base.num_glyphs;
+ font->glyphs[font->base.num_glyphs].parent_index = glyph;
+ font->base.num_glyphs++;
+ }
+
+ *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. 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_STATUS_SUCCESS)
+ has_cvt = TRUE;
+
+ size = 0;
+ if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_fpgm, 0, NULL,
+ &size) == CAIRO_STATUS_SUCCESS)
+ has_fpgm = TRUE;
+
+ size = 0;
+ if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
+ TT_TAG_prep, 0, NULL,
+ &size) == CAIRO_STATUS_SUCCESS)
+ has_prep = TRUE;
+
+ font->num_tables = 0;
+ pos = 1;
+ 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;
+ 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);
+}
+
+cairo_status_t
+_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset,
+ cairo_scaled_font_subset_t *font_subset)
+{
+ 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, &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->font_name = strdup (font->base.font_name);
+ if (unlikely (truetype_subset->font_name == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ } else {
+ truetype_subset->font_name = NULL;
+ }
+
+ /* The widths array returned must contain only widths for the
+ * glyphs in font_subset. Any subglyphs appended after
+ * font_subset->num_glyphs are omitted. */
+ truetype_subset->widths = calloc (sizeof (double),
+ font->scaled_font_subset->num_glyphs);
+ if (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->base.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 = 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 = 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:
+ if (truetype_subset->font_name)
+ free (truetype_subset->font_name);
+ fail2:
+ free (truetype_subset->ps_name);
+ fail1:
+ cairo_truetype_font_destroy (font);
+
+ return status;
+}
+
+void
+_cairo_truetype_subset_fini (cairo_truetype_subset_t *subset)
+{
+ free (subset->ps_name);
+ if (subset->font_name)
+ free (subset->font_name);
+ 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;
+ char buf[4];
+ unsigned int num_segments, i;
+ unsigned long size;
+ uint16_t *start_code;
+ uint16_t *end_code;
+ uint16_t *delta;
+ uint16_t *range_offset;
+ uint16_t *glyph_array;
+ uint16_t c;
+
+ backend = scaled_font->backend;
+ size = 4;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_cmap, table_offset,
+ (unsigned char *) &buf,
+ &size);
+ if (unlikely (status))
+ return status;
+
+ /* All table formats have the same first two words */
+ map = (tt_segment_map_t *) buf;
+ if (be16_to_cpu (map->format) != 4)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = be16_to_cpu (map->length);
+ map = 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]);
+ glyph_array = &(range_offset[num_segments]);
+
+ /* search for glyph in segments with rangeOffset=0 */
+ for (i = 0; i < num_segments; i++) {
+ c = index - be16_to_cpu (delta[i]);
+ if (range_offset[i] == 0 &&
+ c >= be16_to_cpu (start_code[i]) &&
+ c <= be16_to_cpu (end_code[i]))
+ {
+ *ucs4 = c;
+ goto found;
+ }
+ }
+
+ /* search for glyph in segments with rangeOffset=1 */
+ for (i = 0; i < num_segments; i++) {
+ if (range_offset[i] != 0) {
+ uint16_t *glyph_ids = &range_offset[i] + be16_to_cpu (range_offset[i])/2;
+ int range_size = be16_to_cpu (end_code[i]) - be16_to_cpu (start_code[i]) + 1;
+ 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 = be16_to_cpu (start_code[i]) + 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_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+ const cairo_scaled_font_backend_t *backend;
+ tt_cmap_t *cmap;
+ char buf[4];
+ int num_tables, i;
+ unsigned long size;
+
+ backend = scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 4;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_cmap, 0,
+ (unsigned char *) &buf,
+ &size);
+ if (unlikely (status))
+ return status;
+
+ cmap = (tt_cmap_t *) buf;
+ num_tables = be16_to_cpu (cmap->num_tables);
+ size = 4 + num_tables*sizeof(tt_cmap_index_t);
+ cmap = _cairo_malloc_ab_plus_c (num_tables, sizeof (tt_cmap_index_t), 4);
+ if (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;
+}
+
+cairo_int_status_t
+_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font,
+ char **ps_name_out,
+ char **font_name_out)
+{
+ cairo_status_t status;
+ const cairo_scaled_font_backend_t *backend;
+ tt_name_t *name;
+ tt_name_record_t *record;
+ unsigned long size;
+ int i, j;
+ char *ps_name = NULL;
+ char *font_name = NULL;
+
+ backend = scaled_font->backend;
+ if (!backend->load_truetype_table)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_name, 0,
+ NULL,
+ &size);
+ if (status)
+ return status;
+
+ name = malloc (size);
+ if (name == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = backend->load_truetype_table (scaled_font,
+ TT_TAG_name, 0,
+ (unsigned char *) name,
+ &size);
+ if (status)
+ goto fail;
+
+ /* Extract the font name and PS name from the name table. At
+ * present this just looks for the Mac platform/Roman encoded font
+ * name. It should be extended to use any suitable font name in
+ * the name table.
+ */
+ for (i = 0; i < be16_to_cpu(name->num_records); i++) {
+ record = &(name->records[i]);
+ if ((be16_to_cpu (record->platform) == 1) &&
+ (be16_to_cpu (record->encoding) == 0)) {
+
+ if (be16_to_cpu (record->name) == 4) {
+ font_name = malloc (be16_to_cpu(record->length) + 1);
+ if (font_name == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+ strncpy(font_name,
+ ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
+ be16_to_cpu (record->length));
+ font_name[be16_to_cpu (record->length)] = 0;
+ }
+
+ if (be16_to_cpu (record->name) == 6) {
+ ps_name = malloc (be16_to_cpu(record->length) + 1);
+ if (ps_name == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+ strncpy(ps_name,
+ ((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
+ be16_to_cpu (record->length));
+ ps_name[be16_to_cpu (record->length)] = 0;
+ }
+
+ if (font_name && ps_name)
+ break;
+ }
+ }
+
+ free (name);
+
+ /* Ensure PS name does not contain any spaces */
+ if (ps_name) {
+ for (i = 0, j = 0; ps_name[j]; j++) {
+ if (ps_name[j] == ' ')
+ continue;
+ ps_name[i++] = ps_name[j];
+ }
+ ps_name[i] = '\0';
+ }
+
+ *ps_name_out = ps_name;
+ *font_name_out = font_name;
+
+ return CAIRO_STATUS_SUCCESS;
+
+fail:
+ free (name);
+
+ if (ps_name != NULL)
+ free (ps_name);
+
+ if (font_name != NULL)
+ free (font_name);
+
+ *ps_name_out = NULL;
+ *font_name_out = NULL;
+
+ return status;
+}
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/gfx/cairo/cairo/src/cairo-type1-fallback.c b/gfx/cairo/cairo/src/cairo-type1-fallback.c
new file mode 100644
index 000000000..b93c42348
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-type1-fallback.c
@@ -0,0 +1,887 @@
+/* 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 <ajohnson@redneon.com>
+ */
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.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;
+ 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;
+ 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,
+ CAIRO_DIRECTION_FORWARD,
+ _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");
+ 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_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 = 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)type1_subset->x_min,
+ (int)type1_subset->y_min,
+ (int)type1_subset->x_max,
+ (int)type1_subset->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-private.h b/gfx/cairo/cairo/src/cairo-type1-private.h
new file mode 100644
index 000000000..1630397bc
--- /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 <ajohnson@redneon.com>
+ */
+
+#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 000000000..ffa9bfb8f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-type1-subset.c
@@ -0,0 +1,1434 @@
+/* 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 <krh@redhat.com>
+ */
+
+/*
+ * Useful links:
+ * http://partners.adobe.com/public/developer/en/font/T1_SPEC.PDF
+ */
+
+
+#define _BSD_SOURCE /* for snprintf(), strdup() */
+#include "cairoint.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"
+
+/* XXX: Eventually, we need to handle other font backends */
+#if CAIRO_HAS_FT_FONT
+
+#include "cairo-ft-private.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_TYPE1_TABLES_H
+
+#include <ctype.h>
+
+typedef struct _cairo_type1_font_subset {
+ cairo_scaled_font_subset_t *scaled_font_subset;
+
+ struct {
+ cairo_unscaled_font_t *unscaled_font;
+ unsigned int font_id;
+ char *base_font;
+ unsigned int num_glyphs;
+ double x_min, y_min, x_max, y_max;
+ double ascent, descent;
+
+ const char *data;
+ unsigned long header_size;
+ unsigned long data_size;
+ unsigned long trailer_size;
+ } base;
+
+ FT_Face face;
+ int num_glyphs;
+
+ struct {
+ int subset_index;
+ double width;
+ char *name;
+ } *glyphs;
+
+ cairo_output_stream_t *output;
+ cairo_array_t contents;
+
+ const char *rd, *nd;
+
+ char *type1_data;
+ unsigned int type1_length;
+ char *type1_end;
+
+ char *header_segment;
+ int header_segment_size;
+ char *eexec_segment;
+ int eexec_segment_size;
+ cairo_bool_t eexec_segment_is_ascii;
+
+ char *cleartext;
+ char *cleartext_end;
+
+ int header_size;
+
+ unsigned short eexec_key;
+ cairo_bool_t hex_encode;
+ int hex_column;
+} cairo_type1_font_subset_t;
+
+
+static cairo_status_t
+_cairo_type1_font_subset_init (cairo_type1_font_subset_t *font,
+ cairo_unscaled_font_t *unscaled_font,
+ cairo_bool_t hex_encode)
+{
+ cairo_ft_unscaled_font_t *ft_unscaled_font;
+ cairo_status_t status;
+ FT_Face face;
+ PS_FontInfoRec font_info;
+ int i, j;
+
+ ft_unscaled_font = (cairo_ft_unscaled_font_t *) unscaled_font;
+
+ face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font);
+ if (unlikely (face == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (FT_Get_PS_Font_Info(face, &font_info) != 0) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto fail1;
+ }
+
+ /* OpenType/CFF fonts also have a PS_FontInfoRec */
+#if HAVE_FT_LOAD_SFNT_TABLE
+ if (FT_IS_SFNT (face)) {
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto fail1;
+ }
+#endif
+
+ memset (font, 0, sizeof (*font));
+ font->base.unscaled_font = _cairo_unscaled_font_reference (unscaled_font);
+ font->base.num_glyphs = face->num_glyphs;
+ font->base.x_min = face->bbox.xMin / (double)face->units_per_EM;
+ font->base.y_min = face->bbox.yMin / (double)face->units_per_EM;
+ font->base.x_max = face->bbox.xMax / (double)face->units_per_EM;
+ font->base.y_max = face->bbox.yMax / (double)face->units_per_EM;
+ font->base.ascent = face->ascender / (double)face->units_per_EM;
+ font->base.descent = face->descender / (double)face->units_per_EM;
+
+ if (face->family_name) {
+ font->base.base_font = strdup (face->family_name);
+ if (unlikely (font->base.base_font == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail2;
+ }
+ for (i = 0, j = 0; font->base.base_font[j]; j++) {
+ if (font->base.base_font[j] == ' ')
+ continue;
+ font->base.base_font[i++] = font->base.base_font[j];
+ }
+ font->base.base_font[i] = '\0';
+ }
+
+ font->glyphs = calloc (face->num_glyphs, sizeof font->glyphs[0]);
+ if (unlikely (font->glyphs == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail3;
+ }
+
+ font->hex_encode = hex_encode;
+ font->num_glyphs = 0;
+ for (i = 0; i < face->num_glyphs; i++)
+ font->glyphs[i].subset_index = -1;
+
+ _cairo_array_init (&font->contents, sizeof (char));
+
+ _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ fail3:
+ if (font->base.base_font)
+ free (font->base.base_font);
+ fail2:
+ _cairo_unscaled_font_destroy (unscaled_font);
+ fail1:
+ _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
+
+ return status;
+}
+
+static void
+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++;
+}
+
+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;
+ int size, i;
+
+ p = (unsigned char *) font->type1_data;
+ font->type1_end = font->type1_data + font->type1_length;
+ if (p[0] == 0x80 && p[1] == 0x01) {
+ font->header_segment_size =
+ p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
+ font->header_segment = (char *) p + 6;
+
+ p += 6 + font->header_segment_size;
+ font->eexec_segment_size =
+ p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
+ font->eexec_segment = (char *) p + 6;
+ font->eexec_segment_is_ascii = (p[1] == 1);
+
+ p += 6 + font->eexec_segment_size;
+ while (p < (unsigned char *) (font->type1_end) && p[1] != 0x03) {
+ size = p[2] | (p[3] << 8) | (p[4] << 16) | (p[5] << 24);
+ p += 6 + size;
+ }
+ 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 (!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_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");
+ for (i = 1; i < font->base.num_glyphs; i++) {
+ if (font->glyphs[i].subset_index < 0)
+ continue;
+ _cairo_output_stream_printf (font->output,
+ "dup %d /%s put\n",
+ font->glyphs[i].subset_index,
+ font->glyphs[i].name);
+ }
+ _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;
+
+ _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 = malloc (font->eexec_segment_size);
+ 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] = ' ';
+
+ 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 int
+cairo_type1_font_subset_lookup_glyph (cairo_type1_font_subset_t *font,
+ const char *glyph_name, int length)
+{
+ unsigned int i;
+
+ for (i = 0; i < font->base.num_glyphs; i++) {
+ if (font->glyphs[i].name &&
+ strncmp (font->glyphs[i].name, glyph_name, length) == 0 &&
+ font->glyphs[i].name[length] == '\0')
+ return i;
+ }
+
+ return -1;
+}
+
+static cairo_status_t
+cairo_type1_font_subset_get_glyph_names_and_widths (cairo_type1_font_subset_t *font)
+{
+ unsigned int i;
+ char buffer[256];
+ FT_Error error;
+
+ /* Get glyph names and width using the freetype API */
+ for (i = 0; i < font->base.num_glyphs; i++) {
+ if (font->glyphs[i].name != NULL)
+ continue;
+
+ error = FT_Load_Glyph (font->face, i,
+ FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING |
+ FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
+ if (error != FT_Err_Ok) {
+ /* propagate fatal errors from FreeType */
+ if (error == FT_Err_Out_Of_Memory)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ font->glyphs[i].width = font->face->glyph->metrics.horiAdvance / (double)font->face->units_per_EM;
+
+ error = FT_Get_Glyph_Name(font->face, i, buffer, sizeof buffer);
+ if (error != FT_Err_Ok) {
+ /* propagate fatal errors from FreeType */
+ if (error == FT_Err_Out_Of_Memory)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ font->glyphs[i].name = strdup (buffer);
+ if (unlikely (font->glyphs[i].name == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out)
+{
+ 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 = (p[1] << 24) | (p[2] << 16) | (p[3] << 8) | p[4];
+ p += 5;
+ }
+
+ return p;
+}
+
+#if 0
+/*
+ * The two tables that follow are generated using this perl code:
+ */
+
+@encoding = (
+ /* 0 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ /* 16 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ /* 32 */
+ "space", "exclam", "quotedbl", "numbersign",
+ "dollar", "percent", "ampersand", "quoteright",
+ "parenleft", "parenright", "asterisk", "plus",
+ "comma", "hyphen", "period", "slash",
+ /* 48 */
+ "zero", "one", "two", "three",
+ "four", "five", "six", "seven",
+ "eight", "nine", "colon", "semicolon",
+ "less", "equal", "greater", "question",
+ /* 64 */
+ "at", "A", "B", "C",
+ "D", "E", "F", "G",
+ "H", "I", "J", "K",
+ "L", "M", "N", "O",
+ /* 80 */
+ "P", "Q", "R", "S",
+ "T", "U", "V", "W",
+ "X", "Y", "Z", "bracketleft",
+ "backslash", "bracketright", "asciicircum", "underscore",
+ /* 96 */
+ "quoteleft", "a", "b", "c",
+ "d", "e", "f", "g",
+ "h", "i", "j", "k",
+ "l", "m", "n", "o",
+ /* 112 */
+ "p", "q", "r", "s",
+ "t", "u", "v", "w",
+ "x", "y", "z", "braceleft",
+ "bar", "braceright", "asciitilde", NULL,
+ /* 128 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ /* 144 */
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ /* 160 */
+ NULL, "exclamdown", "cent", "sterling",
+ "fraction", "yen", "florin", "section",
+ "currency", "quotesingle", "quotedblleft", "guillemotleft",
+ "guilsinglleft","guilsinglright","fi", "fl",
+ /* 176 */
+ NULL, "endash", "dagger", "daggerdbl",
+ "periodcentered",NULL, "paragraph", "bullet",
+ "quotesinglbase","quotedblbase","quotedblright","guillemotright",
+ "ellipsis", "perthousand", NULL, "questiondown",
+ /* 192 */
+ NULL, "grave", "acute", "circumflex",
+ "tilde", "macron", "breve", "dotaccent",
+ "dieresis", NULL, "ring", "cedilla",
+ NULL, "hungarumlaut", "ogonek", "caron",
+ /* 208 */
+ "emdash", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ /* 224 */
+ NULL, "AE", NULL, "ordfeminine",
+ NULL, NULL, NULL, NULL,
+ "Lslash", "Oslash", "OE", "ordmasculine",
+ NULL, NULL, NULL, NULL,
+ /* 240 */
+ NULL, "ae", NULL, NULL,
+ NULL, "dotlessi", NULL, NULL,
+ "lslash", "oslash", "oe", "germandbls",
+ NULL, NULL, NULL, NULL
+ );
+
+print "static const char ps_standard_encoding_symbol[] = {\n";
+$s = qq( "\\0");
+for $sym (@encoding) {
+ if (! ($sym eq NULL)) {
+ $ss = qq( "$sym\\0");
+ if (length($s) + length($ss) > 78) {
+ print qq( $s\n);
+ $s = "";
+ }
+ $s .= $ss;
+ }
+}
+print qq( $s\n);
+print "};\n\n";
+print "static const int16_t ps_standard_encoding_offset[256] = {\n";
+$offset = 1;
+$s = qq();
+for $sym (@encoding) {
+ if (! ($sym eq NULL)) {
+ $ss = qq( $offset/*$sym*/,);
+ $offset += length($sym) + 1;
+ } else {
+ $ss = qq( 0,);
+ }
+ if (length($s) + length($ss) > 78) {
+ print qq( $s\n);
+ $s = "";
+ }
+ $s .= $ss;
+}
+print qq( $s\n);
+print "};\n";
+exit;
+#endif
+
+static const char ps_standard_encoding_symbol[] = {
+ "\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0"
+ "ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0"
+ "plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0"
+ "three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0"
+ "semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0"
+ "C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0"
+ "P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0"
+ "bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0"
+ "quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0"
+ "k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0"
+ "x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0"
+ "exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0"
+ "section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0"
+ "guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0"
+ "daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0"
+ "quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0"
+ "perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0"
+ "macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0"
+ "hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0"
+ "Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0"
+ "oslash\0" "oe\0" "germandbls\0"
+};
+
+static const int16_t ps_standard_encoding_offset[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
+ 34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/,
+ 70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
+ 111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
+ 140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
+ 170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
+ 202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
+ 232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
+ 246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
+ 260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
+ 274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
+ 302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/,
+ 348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
+ 362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
+ 376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
+ 390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
+ 410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/,
+ 474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/,
+ 510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/,
+ 551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/,
+ 586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/,
+ 628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/,
+ 670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0,
+ 706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/,
+ 742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/,
+ 0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/,
+ 813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/,
+ 855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0,
+ 874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/,
+ 900/*germandbls*/, 0, 0, 0, 0,
+};
+
+#define ps_standard_encoding(index) ((index) ? ps_standard_encoding_symbol+ps_standard_encoding_offset[(index)] : NULL)
+
+static cairo_status_t
+use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index)
+{
+ const char *glyph_name;
+
+ if (index < 0 || index > 255)
+ return CAIRO_STATUS_SUCCESS;
+
+ glyph_name = ps_standard_encoding(index);
+ if (glyph_name == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ index = cairo_type1_font_subset_lookup_glyph (font,
+ glyph_name,
+ strlen(glyph_name));
+ if (index < 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cairo_type1_font_subset_use_glyph (font, index);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#define TYPE1_CHARSTRING_COMMAND_ESCAPE (12)
+#define TYPE1_CHARSTRING_COMMAND_SEAC (32 + 6)
+
+static cairo_status_t
+cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font,
+ const char *name, int name_length,
+ const char *encrypted_charstring, int encrypted_charstring_length)
+{
+ cairo_status_t status;
+ unsigned char *charstring;
+ const unsigned char *end;
+ const unsigned char *p;
+ int stack[5], sp, value;
+ int command;
+
+ charstring = malloc (encrypted_charstring_length);
+ 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 + 4;
+ sp = 0;
+
+ while (p < end) {
+ if (*p < 32) {
+ command = *p++;
+
+ if (command == TYPE1_CHARSTRING_COMMAND_ESCAPE)
+ command = 32 + *p++;
+
+ switch (command) {
+ case TYPE1_CHARSTRING_COMMAND_SEAC:
+ /* The seac command takes five integer arguments. The
+ * last two are glyph indices into the PS standard
+ * encoding give the names of the glyphs that this
+ * glyph is composed from. All we need to do is to
+ * make sure those glyphs are present in the subset
+ * under their standard names. */
+ if (unlikely (sp < 5))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = use_standard_encoding_glyph (font, stack[3]);
+ if (unlikely (status))
+ return status;
+
+ status = use_standard_encoding_glyph (font, stack[4]);
+ if (unlikely (status))
+ return status;
+
+ sp = 0;
+ break;
+
+ default:
+ sp = 0;
+ break;
+ }
+ } else {
+ /* integer argument */
+ p = cairo_type1_font_subset_decode_integer (p, &value);
+ if (sp < 5)
+ stack[sp++] = value;
+ }
+ }
+
+ free (charstring);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+write_used_glyphs (cairo_type1_font_subset_t *font,
+ const char *name, int name_length,
+ const char *charstring, int charstring_length)
+{
+ cairo_status_t status;
+ char buffer[256];
+ int length;
+
+ 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,
+ 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, glyph_index;
+ const char *p, *charstring, *name;
+ char *end;
+
+ /* 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;
+
+ 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;
+
+ glyph_index = cairo_type1_font_subset_lookup_glyph (font,
+ name, name_length);
+ if (font->glyphs[glyph_index].subset_index >= 0) {
+ cairo_status_t status = func (font,
+ name, name_length,
+ charstring, charstring_length);
+ if (unlikely (status))
+ return status;
+ }
+ }
+
+ *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, *charstrings, *dict_start;
+ const char *closefile_token;
+ char buffer[32], *glyph_count_end;
+ int num_charstrings, length;
+
+ /* The private dict holds hint information, common subroutines and
+ * the actual glyph definitions (charstrings).
+ *
+ * FIXME: update this comment.
+ *
+ * What we do here is scan directly the /CharString token, which
+ * marks the beginning of the glyph definitions. Then we parse
+ * through the glyph definitions and weed out the glyphs not in
+ * our subset. Everything else before and after the glyph
+ * definitions is copied verbatim to the output. It might be
+ * worthwile to figure out which of the common subroutines are
+ * used by the glyphs in the subset and get rid of the rest. */
+
+ /* FIXME: The /Subrs array contains binary data and could
+ * conceivably have "/CharStrings" in it, so we might need to skip
+ * this more cleverly. */
+ charstrings = find_token (font->cleartext, font->cleartext_end, "/CharStrings");
+ if (charstrings == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Scan past /CharStrings and the integer following it. */
+ p = charstrings + strlen ("/CharStrings");
+ num_charstrings = strtol (p, &glyph_count_end, 10);
+ 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;
+
+ status = cairo_type1_font_subset_get_glyph_names_and_widths (font);
+ if (unlikely (status))
+ return status;
+
+ /* Now that we have the private dictionary broken down in
+ * sections, do the first pass through the glyph definitions to
+ * figure out which subrs and othersubrs are use and which extra
+ * glyphs may be required by the seac operator. */
+ status = cairo_type1_font_subset_for_each_glyph (font,
+ dict_start,
+ font->cleartext_end,
+ cairo_type1_font_subset_look_for_seac,
+ &p);
+ if (unlikely (status))
+ return status;
+
+ closefile_token = find_token (p, font->cleartext_end, "closefile");
+ if (closefile_token == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = cairo_type1_font_subset_get_glyph_names_and_widths (font);
+ if (unlikely (status))
+ return status;
+
+ /* We're ready to start outputting. First write the header,
+ * i.e. the public part of the font dict.*/
+ status = cairo_type1_font_subset_write_header (font, name);
+ if (unlikely (status))
+ return status;
+
+ font->base.header_size = _cairo_output_stream_get_position (font->output);
+
+
+ /* Start outputting the private dict. First output everything up
+ * to the /CharStrings token. */
+ status = cairo_type1_font_subset_write_encrypted (font, font->cleartext,
+ charstrings - font->cleartext);
+ 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);
+ } 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");
+ } 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 = "|-";
+ } else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) {
+ font->rd = "RD";
+ font->nd = "ND";
+ } 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_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_status_t
+cairo_type1_font_subset_generate (void *abstract_font,
+ const char *name)
+
+{
+ cairo_type1_font_subset_t *font = abstract_font;
+ cairo_ft_unscaled_font_t *ft_unscaled_font;
+ unsigned long ret;
+ cairo_status_t status;
+
+ ft_unscaled_font = (cairo_ft_unscaled_font_t *) font->base.unscaled_font;
+ font->face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font);
+ if (unlikely (font->face == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font->type1_length = font->face->stream->size;
+ font->type1_data = malloc (font->type1_length);
+ if (unlikely (font->type1_data == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto fail;
+ }
+
+ if (font->face->stream->read != NULL) {
+ /* Note that read() may be implemented as a macro, thanks POSIX!, so we
+ * need to wrap the following usage in parentheses in order to
+ * disambiguate it for the pre-processor - using the verbose function
+ * pointer dereference for clarity.
+ */
+ ret = (* font->face->stream->read) (font->face->stream, 0,
+ (unsigned char *) font->type1_data,
+ font->type1_length);
+ if (ret != font->type1_length) {
+ status = _cairo_error (CAIRO_STATUS_READ_ERROR);
+ goto fail;
+ }
+ } else {
+ memcpy (font->type1_data,
+ font->face->stream->base, font->type1_length);
+ }
+
+ status = _cairo_array_grow_by (&font->contents, 4096);
+ if (unlikely (status))
+ goto fail;
+
+ font->output = _cairo_output_stream_create (type1_font_write, NULL, font);
+ if (unlikely ((status = font->output->status)))
+ goto fail;
+
+ status = cairo_type1_font_subset_write (font, name);
+ if (unlikely (status))
+ goto fail;
+
+ font->base.data = _cairo_array_index (&font->contents, 0);
+
+ fail:
+ _cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
+
+ 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);
+ if (font->glyphs != NULL) {
+ for (i = 0; i < font->base.num_glyphs; i++)
+ free (font->glyphs[i].name);
+ }
+
+ _cairo_unscaled_font_destroy (font->base.unscaled_font);
+
+ if (font->output != NULL)
+ status = _cairo_output_stream_destroy (font->output);
+
+ if (font->base.base_font)
+ free (font->base.base_font);
+ free (font->glyphs);
+
+ 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, status_ignored;
+ unsigned long parent_glyph, length;
+ unsigned int i;
+ cairo_unscaled_font_t *unscaled_font;
+ char buf[30];
+
+ /* XXX: Need to fix this to work with a general cairo_unscaled_font_t. */
+ if (!_cairo_scaled_font_is_ft (scaled_font_subset->scaled_font))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (_cairo_ft_scaled_font_is_vertical (scaled_font_subset->scaled_font))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font_subset->scaled_font);
+
+ status = _cairo_type1_font_subset_init (&font, unscaled_font, hex_encode);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < scaled_font_subset->num_glyphs; i++) {
+ parent_glyph = scaled_font_subset->glyphs[i];
+ cairo_type1_font_subset_use_glyph (&font, parent_glyph);
+ }
+
+ status = cairo_type1_font_subset_generate (&font, name);
+ if (unlikely (status))
+ goto fail1;
+
+ 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/1000.0;
+ type1_subset->y_min = font.base.y_min/1000.0;
+ type1_subset->x_max = font.base.x_max/1000.0;
+ type1_subset->y_max = font.base.y_max/1000.0;
+ type1_subset->ascent = font.base.ascent/1000.0;
+ type1_subset->descent = font.base.descent/1000.0;
+
+ length = font.base.header_size +
+ font.base.data_size +
+ font.base.trailer_size;
+ type1_subset->data = 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:
+ status_ignored = _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_ft_unscaled_font_t *unscaled;
+ FT_Face face;
+ PS_FontInfoRec font_info;
+ cairo_bool_t is_type1 = FALSE;
+
+ if (!_cairo_scaled_font_is_ft (scaled_font))
+ return FALSE;
+ unscaled = (cairo_ft_unscaled_font_t *) _cairo_ft_scaled_font_get_unscaled_font (scaled_font);
+ face = _cairo_ft_unscaled_font_lock_face (unscaled);
+ if (!face)
+ return FALSE;
+
+ if (FT_Get_PS_Font_Info(face, &font_info) == 0)
+ is_type1 = TRUE;
+
+ /* OpenType/CFF fonts also have a PS_FontInfoRec */
+#if HAVE_FT_LOAD_SFNT_TABLE
+ if (FT_IS_SFNT (face))
+ is_type1 = FALSE;
+#endif
+
+ _cairo_ft_unscaled_font_unlock_face (unscaled);
+
+ return is_type1;
+}
+
+#endif /* CAIRO_HAS_FT_FONT */
+
+#endif /* CAIRO_HAS_FONT_SUBSET */
diff --git a/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h b/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h
new file mode 100644
index 000000000..b4abcf604
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface-private.h
@@ -0,0 +1,87 @@
+/* -*- 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 <ajohnson@redneon.com>
+ */
+
+#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_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_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 000000000..74257d4bf
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c
@@ -0,0 +1,563 @@
+/* -*- 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 <ajohnson@redneon.com>
+ */
+
+#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-error-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_type3_glyph_surface_t *surface;
+ cairo_matrix_t invert_y_axis;
+
+ if (unlikely (stream != NULL && stream->status))
+ return _cairo_surface_create_in_error (stream->status);
+
+ surface = malloc (sizeof (cairo_type3_glyph_surface_t));
+ if (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);
+
+ 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_matrix_init_scale (&invert_y_axis, 1, -1);
+ cairo_matrix_multiply (&surface->cairo_to_pdf, &surface->cairo_to_pdf, &invert_y_axis);
+
+ _cairo_pdf_operators_init (&surface->pdf_operators,
+ surface->stream,
+ &surface->cairo_to_pdf,
+ font_subsets);
+
+ _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,
+ 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,
+ 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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_type3_glyph_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ cairo_scaled_font_t *font;
+ cairo_matrix_t new_ctm, invert_y_axis;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
+
+ cairo_matrix_init_scale (&invert_y_axis, 1, -1);
+ cairo_matrix_multiply (&new_ctm, &invert_y_axis, &scaled_font->ctm);
+ cairo_matrix_multiply (&new_ctm, &surface->cairo_to_pdf, &new_ctm);
+ 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,
+ NULL, /* _cairo_type3_glyph_surface_create_similar */
+ _cairo_type3_glyph_surface_finish,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* cairo_type3_glyph_surface_copy_page */
+ NULL, /* _cairo_type3_glyph_surface_show_page */
+ NULL, /* _cairo_type3_glyph_surface_get_extents */
+ NULL, /* old_show_glyphs */
+ NULL, /* _cairo_type3_glyph_surface_get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+ _cairo_type3_glyph_surface_paint,
+ _cairo_type3_glyph_surface_mask,
+ _cairo_type3_glyph_surface_stroke,
+ _cairo_type3_glyph_surface_fill,
+ _cairo_type3_glyph_surface_show_glyphs,
+ NULL, /* snapshot */
+};
+
+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);
+ mat.xx = image->width;
+ mat.xy = 0;
+ mat.yx = 0;
+ mat.yy = image->height;
+ mat.x0 = x;
+ mat.y0 = y;
+ cairo_matrix_multiply (&mat, &mat, &surface->scaled_font->scale_inverse);
+ mat.y0 *= -1;
+
+ 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_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_status_is_error (status))
+ goto cleanup;
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = CAIRO_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_STATUS_SUCCESS;
+
+cleanup:
+ _cairo_scaled_font_thaw_cache (surface->scaled_font);
+
+ status2 = _cairo_output_stream_destroy (null_stream);
+ if (status == CAIRO_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_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_STATUS_SUCCESS)
+ status = CAIRO_INT_STATUS_IMAGE_FALLBACK;
+ }
+ if (_cairo_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_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->p2.y),
+ _cairo_fixed_to_double (bbox->p2.x),
+ - _cairo_fixed_to_double (bbox->p1.y));
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ cairo_output_stream_t *mem_stream;
+
+ mem_stream = _cairo_memory_stream_create ();
+ status = mem_stream->status;
+ if (unlikely (status))
+ goto FAIL;
+
+ _cairo_type3_glyph_surface_set_stream (surface, mem_stream);
+
+ _cairo_output_stream_printf (surface->stream, "q\n");
+ status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
+ &surface->base);
+
+ status2 = _cairo_pdf_operators_flush (&surface->pdf_operators);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ _cairo_output_stream_printf (surface->stream, "Q\n");
+
+ _cairo_type3_glyph_surface_set_stream (surface, stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ _cairo_memory_stream_copy (mem_stream, stream);
+
+ status2 = _cairo_output_stream_destroy (mem_stream);
+ if (status == CAIRO_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 000000000..93b035d7c
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-types-private.h
@@ -0,0 +1,484 @@
+/* -*- 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 <cworth@cworth.org>
+ */
+
+#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"
+
+/**
+ * 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_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_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_backend cairo_scaled_font_backend_t;
+typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t;
+typedef struct _cairo_solid_pattern cairo_solid_pattern_t;
+typedef struct _cairo_surface_backend cairo_surface_backend_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_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;
+
+struct _cairo_observer {
+ cairo_list_t link;
+ void (*callback) (cairo_observer_t *self, void *arg);
+};
+
+/**
+ * cairo_hash_entry_t:
+ *
+ * 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 reponsible 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;
+
+ cairo_bool_t is_snapshot;
+};
+
+/**
+ * cairo_lcd_filter_t:
+ * @CAIRO_LCD_FILTER_DEFAULT: Use the default LCD filter for
+ * font backend and target device
+ * @CAIRO_LCD_FILTER_NONE: Do not perform LCD filtering
+ * @CAIRO_LCD_FILTER_INTRA_PIXEL: Intra-pixel filter
+ * @CAIRO_LCD_FILTER_FIR3: FIR filter with a 3x3 kernel
+ * @CAIRO_LCD_FILTER_FIR5: FIR filter with a 5x5 kernel
+ *
+ * The LCD filter specifies the low-pass filter applied to LCD-optimized
+ * bitmaps generated with an antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL.
+ *
+ * Note: This API was temporarily made available in the public
+ * interface during the 1.7.x development series, but was made private
+ * before 1.8.
+ **/
+typedef enum _cairo_lcd_filter {
+ CAIRO_LCD_FILTER_DEFAULT,
+ CAIRO_LCD_FILTER_NONE,
+ CAIRO_LCD_FILTER_INTRA_PIXEL,
+ CAIRO_LCD_FILTER_FIR3,
+ CAIRO_LCD_FILTER_FIR5
+} cairo_lcd_filter_t;
+
+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;
+};
+
+/* 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;
+
+/* Sure wish C had a real enum type so that this would be distinct
+ * from #cairo_status_t. Oh well, without that, I'll use this bogus 100
+ * offset. We want to keep it fit in int8_t as the compiler may choose
+ * that for #cairo_status_t */
+typedef enum _cairo_int_status {
+ CAIRO_INT_STATUS_UNSUPPORTED = 100,
+ CAIRO_INT_STATUS_DEGENERATE,
+ CAIRO_INT_STATUS_NOTHING_TO_DO,
+ CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY,
+ CAIRO_INT_STATUS_IMAGE_FALLBACK,
+ CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN,
+
+ CAIRO_INT_STATUS_LAST_STATUS
+} cairo_int_status_t;
+
+typedef enum _cairo_internal_surface_type {
+ CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000,
+ CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
+ CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
+ CAIRO_INTERNAL_SURFACE_TYPE_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;
+
+#define CAIRO_HAS_TEST_PAGINATED_SURFACE 1
+#define CAIRO_HAS_TEST_NULL_SURFACE 1
+#define CAIRO_HAS_TEST_WRAPPING_SURFACE 1
+
+typedef struct _cairo_slope {
+ cairo_fixed_t dx;
+ 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_distance_double {
+ double dx;
+ double dy;
+} cairo_distance_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_point_t first_point;
+ cairo_point_t last_point;
+ cairo_point_t current_point;
+ cairo_slope_t current_edge;
+ cairo_bool_t has_current_point;
+ cairo_bool_t has_current_edge;
+
+ cairo_box_t extents;
+ cairo_box_t limit;
+ const cairo_box_t *limits;
+ 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);
+
+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;
+
+struct _cairo_mime_data {
+ cairo_reference_count_t ref_count;
+ unsigned char *data;
+ unsigned long length;
+ cairo_destroy_func_t destroy;
+ void *closure;
+};
+
+struct _cairo_pattern {
+ cairo_pattern_type_t type;
+ cairo_reference_count_t ref_count;
+ cairo_status_t status;
+ cairo_user_data_array_t user_data;
+
+ cairo_matrix_t matrix;
+ cairo_filter_t filter;
+ cairo_extend_t extend;
+
+ cairo_bool_t has_component_alpha;
+};
+
+struct _cairo_solid_pattern {
+ cairo_pattern_t base;
+ cairo_color_t color;
+};
+
+typedef struct _cairo_surface_pattern {
+ cairo_pattern_t base;
+
+ cairo_surface_t *surface;
+} cairo_surface_pattern_t;
+
+typedef struct _cairo_gradient_stop {
+ double offset;
+ cairo_color_stop_t color;
+} cairo_gradient_stop_t;
+
+typedef struct _cairo_gradient_pattern {
+ cairo_pattern_t base;
+
+ unsigned int n_stops;
+ unsigned int stops_size;
+ cairo_gradient_stop_t *stops;
+ cairo_gradient_stop_t stops_embedded[2];
+} cairo_gradient_pattern_t;
+
+typedef struct _cairo_linear_pattern {
+ cairo_gradient_pattern_t base;
+
+ cairo_point_t p1;
+ cairo_point_t p2;
+} cairo_linear_pattern_t;
+
+typedef struct _cairo_radial_pattern {
+ cairo_gradient_pattern_t base;
+
+ cairo_point_t c1;
+ cairo_fixed_t r1;
+ cairo_point_t c2;
+ cairo_fixed_t r2;
+} cairo_radial_pattern_t;
+
+typedef union {
+ cairo_gradient_pattern_t base;
+
+ cairo_linear_pattern_t linear;
+ cairo_radial_pattern_t radial;
+} cairo_gradient_pattern_union_t;
+
+typedef union {
+ cairo_pattern_type_t type;
+ cairo_pattern_t base;
+
+ cairo_solid_pattern_t solid;
+ cairo_surface_pattern_t surface;
+ cairo_gradient_pattern_union_t gradient;
+} cairo_pattern_union_t;
+
+/*
+ * A #cairo_unscaled_font_t is just an opaque handle we use in the
+ * glyph cache.
+ */
+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;
+
+typedef struct _cairo_scaled_glyph {
+ cairo_hash_entry_t hash_entry;
+
+ cairo_text_extents_t metrics; /* user-space metrics */
+ cairo_text_extents_t fs_metrics; /* font-space metrics */
+ cairo_box_t bbox; /* device-space bounds */
+ int16_t x_advance; /* device-space rounded X advance */
+ int16_t y_advance; /* device-space rounded Y advance */
+
+ unsigned int has_info;
+ cairo_image_surface_t *surface; /* device-space image */
+ cairo_path_fixed_t *path; /* device-space outline */
+ cairo_surface_t *recording_surface; /* device-space recording-surface */
+
+ void *surface_private; /* for the surface backend */
+} cairo_scaled_glyph_t;
+#endif /* CAIRO_TYPES_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-unicode.c b/gfx/cairo/cairo/src/cairo-unicode.c
new file mode 100644
index 000000000..88de39516
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-unicode.c
@@ -0,0 +1,422 @@
+/* -*- 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 <otaylor@redhat.com>
+ */
+
+#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;
+}
+
+#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);
+
+ if (wc < 0x10000) {
+ str16[i++] = wc;
+ } else {
+ str16[i++] = (wc - 0x10000) / 0x400 + 0xd800;
+ str16[i++] = (wc - 0x10000) % 0x400 + 0xdc00;
+ }
+
+ 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-user-font-private.h b/gfx/cairo/cairo/src/cairo-user-font-private.h
new file mode 100644
index 000000000..d54ef78b4
--- /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 <krh@redhat.com>
+ * Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#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 000000000..a524d588f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-user-font.c
@@ -0,0 +1,827 @@
+/* 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 <krh@redhat.com>
+ * Behdad Esfahbod <behdad@behdad.org>
+ */
+
+#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_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_GRAY: format = CAIRO_FORMAT_A8; break;
+ case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break;
+ 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_STATUS_SUCCESS &&
+ status != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
+ return status;
+
+ if (status == CAIRO_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 = 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,
+ NULL, /* 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 = 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 000000000..b07b48b37
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-version.c
@@ -0,0 +1,244 @@
+/* -*- 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 <cworth@cworth.org>
+ */
+
+#define CAIRO_VERSION_H 1
+
+#include "cairoint.h"
+
+/* get the "real" version info instead of dummy cairo-version.h */
+#undef CAIRO_VERSION_H
+#include "cairo-features.h"
+
+/**
+ * SECTION:cairo-version
+ * @Title: Version Information
+ * @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).
+ *
+ * <informalexample><programlisting>
+ * _____ 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
+ * </programlisting></informalexample>
+ *
+ * Here are a few examples of versions that one might see.
+ * <informalexample><programlisting>
+ * 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)
+ * </programlisting></informalexample>
+ * </para>
+ * <refsect2>
+ * <title>Compatibility</title>
+ * <para>
+ * 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.
+ * </para>
+ * </refsect2>
+ * <refsect2>
+ * <title>Examining the version</title>
+ * <para>
+ * 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.
+ *
+ * <informalexample><programlisting>
+ * 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
+ * </programlisting></informalexample>
+ *
+ * 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:
+ *
+ * <informalexample><programlisting>
+ * ##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 ());
+ * </programlisting></informalexample>
+ * </para>
+ * </refsect2>
+ */
+
+/**
+ * CAIRO_VERSION:
+ *
+ * The version of cairo available at compile-time, encoded using
+ * CAIRO_VERSION_ENCODE().
+ */
+
+/**
+ * CAIRO_VERSION_MAJOR:
+ *
+ * The major component of the version of cairo available at compile-time.
+ */
+
+/**
+ * CAIRO_VERSION_MINOR:
+ *
+ * The minor component of the version of cairo available at compile-time.
+ */
+
+/**
+ * CAIRO_VERSION_MICRO:
+ *
+ * The micro component of the version of cairo available at compile-time.
+ */
+
+/**
+ * CAIRO_VERSION_STRING:
+ *
+ * A human-readable string literal containing the version of cairo available
+ * at compile-time, in the form of "X.Y.Z".
+ */
+
+/**
+ * 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.
+ */
+
+/**
+ * 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:
+ *
+ * <informalexample><programlisting>
+ * if (cairo_version() >= CAIRO_VERSION_ENCODE(X,Y,Z)) {...}
+ * </programlisting></informalexample>
+ *
+ * See also cairo_version_string() as well as the compile-time
+ * equivalents %CAIRO_VERSION and %CAIRO_VERSION_STRING.
+ *
+ * Return value: the encoded version.
+ **/
+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.
+ **/
+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 000000000..ace09244c
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-version.h
@@ -0,0 +1,16 @@
+/* This is a dummy file.
+ * The actual version info is in toplevel cairo-version.h.
+ * The purpose of this file is to make most of the source files NOT depend
+ * on the real cairo-version.h, and as a result, changing library version
+ * would not cause a complete rebuild of all object files (just a relink).
+ * This is useful when bisecting. */
+#ifndef CAIRO_VERSION_H
+#define CAIRO_VERSION_H
+
+#if 0
+#define CAIRO_VERSION_MAJOR USE_cairo_version_OR_cairo_version_string_INSTEAD
+#define CAIRO_VERSION_MINOR USE_cairo_version_OR_cairo_version_string_INSTEAD
+#define CAIRO_VERSION_MICRO USE_cairo_version_OR_cairo_version_string_INSTEAD
+#endif
+
+#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 000000000..d3ae8aa52
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-vg-surface.c
@@ -0,0 +1,1926 @@
+/* -*- 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 <tardyp@gmail.com>
+ * Øyvind Kolås <pippin@gimp.org>
+ * Vladimi Vukicevic <vladimir@mozilla.com> (stubbed out base backend)
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-vg.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-surface-clipper-private.h"
+
+#include <pixman.h>
+#include <VG/openvg.h>
+
+//#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 0; //PIXMAN_r8g8b8x8;
+ case VG_sRGBA_8888: return 0;
+ case VG_sRGBA_8888_PRE: return 0; //PIXMAN_r8b8g8a8;
+ 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;
+ 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,
+ CAIRO_DIRECTION_FORWARD,
+ _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:
+ return VG_RENDERING_QUALITY_BETTER;
+
+ case CAIRO_ANTIALIAS_GRAY:
+ 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] = _cairo_fixed_to_double (lpat->p1.x);
+ linear[1] = _cairo_fixed_to_double (lpat->p1.y);
+ linear[2] = _cairo_fixed_to_double (lpat->p2.x);
+ linear[3] = _cairo_fixed_to_double (lpat->p2.y);
+
+ 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] = _cairo_fixed_to_double (rpat->c1.x);
+ radial[1] = _cairo_fixed_to_double (rpat->c1.y);
+ radial[2] = _cairo_fixed_to_double (rpat->c2.x);
+ radial[3] = _cairo_fixed_to_double (rpat->c2.y);
+ radial[4] = _cairo_fixed_to_double (rpat->r2);
+
+ 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,
+ cairo_path_fixed_t *path,
+ cairo_stroke_style_t *style,
+ cairo_matrix_t *ctm,
+ cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ 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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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,
+ 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,
+ 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);
+}
+
+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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ 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_SUBPIXEL,
+ 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] = (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_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+
+ *image_rect_out = *interest_rect;
+ *image_extra = NULL;
+ return _vg_get_image (surface,
+ interest_rect->x, interest_rect->y,
+ interest_rect->width, interest_rect->height,
+ image_out);
+}
+
+static void
+unpremultiply_argb (uint8_t *data,
+ int width,
+ int height,
+ int stride)
+{
+ int i;
+
+ while (height--) {
+ uint32_t *row = (uint32_t *) data;
+
+ for (i = 0; i < width; i ++) {
+ uint32_t p = row[i];
+ uint8_t alpha;
+
+ alpha = p >> 24;
+ if (alpha == 0) {
+ row[i] = 0;
+ } else if (alpha != 0xff) {
+ uint8_t r = (((p >> 16) & 0xff) * 255 + alpha / 2) / alpha;
+ uint8_t g = (((p >> 8) & 0xff) * 255 + alpha / 2) / alpha;
+ uint8_t b = (((p >> 0) & 0xff) * 255 + alpha / 2) / alpha;
+ row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
+ }
+ }
+
+ data += stride;
+ }
+}
+
+static void
+_vg_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_vg_surface_t *surface = abstract_surface;
+ cairo_bool_t needs_unpremultiply;
+
+ _vg_format_to_pixman (surface->format, &needs_unpremultiply);
+ if (needs_unpremultiply) {
+ unpremultiply_argb (image->data,
+ image->width, image->height,
+ image->stride);
+ }
+
+ vgImageSubData (surface->image,
+ image->data, image->stride,
+ surface->format,
+ image_rect->x, image_rect->y,
+ image_rect->width, image_rect->height);
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_vg_surface_finish (void *abstract_surface)
+{
+ 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_create_similar,
+ _vg_surface_finish,
+
+ _vg_surface_acquire_source_image,
+ _vg_surface_release_source_image,
+ _vg_surface_acquire_dest_image,
+ _vg_surface_release_dest_image,
+
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _vg_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _vg_surface_get_font_options, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ _vg_surface_paint,
+ _vg_surface_mask,
+ _vg_surface_stroke,
+ _vg_surface_fill,
+ _vg_surface_show_glyphs,
+
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+};
+
+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 = 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));
+
+ 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 <GL/glx.h>
+
+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 = 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 = 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 000000000..f9a62e51c
--- /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 <vladimir@mozilla.com>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_VG_H
+#define CAIRO_VG_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_VG_SURFACE
+
+#include <VG/openvg.h>
+
+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 <EGL/egl.h>
+
+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-wideint-private.h b/gfx/cairo/cairo/src/cairo-wideint-private.h
new file mode 100644
index 000000000..b9f8dae64
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-wideint-private.h
@@ -0,0 +1,320 @@
+/* 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 <keithp@keithp.com>
+ *
+ */
+
+#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_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;
+}
+
+#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 comparisions 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 000000000..9e49ad947
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-wideint-type-private.h
@@ -0,0 +1,162 @@
+/* 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 <keithp@keithp.com>
+ *
+ */
+
+#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 <stdint.h>
+#elif HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+#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 _UINTPTR_T_DEFINED
+#ifdef _WIN64
+ typedef unsigned __int64 uintptr_t;
+#else
+ typedef unsigned int uintptr_t;
+#endif
+#endif
+# ifndef HAVE_UINT64_T
+# define HAVE_UINT64_T 1
+# endif
+#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
+
+#if HAVE_BYTESWAP_H
+# include <byteswap.h>
+#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 000000000..78dedcdf0
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-wideint.c
@@ -0,0 +1,819 @@
+/* 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 <keithp@keithp.com>
+ */
+
+#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_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_int128_t
+_cairo_int128_negate (cairo_int128_t a)
+{
+ a.lo = _cairo_uint64_not (a.lo);
+ a.hi = _cairo_uint64_not (a.hi);
+ return _cairo_uint128_add (a, _cairo_uint32_to_uint128 (1));
+}
+
+cairo_int128_t
+_cairo_int128_not (cairo_int128_t a)
+{
+ 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-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c
new file mode 100644
index 000000000..f0bdc8457
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-win32-font.c
@@ -0,0 +1,2358 @@
+/* -*- 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-error-private.h"
+
+#include <wchar.h>
+
+#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.
+ */
+
+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_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 HDC hdc;
+
+ 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;
+ }
+ }
+
+ 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 ("_cairo_win32_get_system_text_quality");
+ return DEFAULT_QUALITY;
+ }
+
+ if (font_smoothing) {
+ if (_have_cleartype_quality ()) {
+ if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE,
+ 0, &smoothing_type, 0)) {
+ _cairo_win32_print_gdi_error ("_cairo_win32_get_system_text_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 = 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:
+ f->quality = ANTIALIASED_QUALITY;
+ break;
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ 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;
+
+ 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 = 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) {
+ if (glyph_indices) {
+ free (glyph_indices);
+ glyph_indices = NULL;
+ }
+ if (dx) {
+ 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:
+ if (glyph_indices)
+ free (glyph_indices);
+ if (dx)
+ 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;
+ 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;
+ GetTextMetrics (hdc, &metrics);
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ 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 == 1 && metrics.gmBlackBoxY == 1 &&
+ GetGlyphOutlineW (hdc,
+ _cairo_scaled_glyph_index (scaled_glyph),
+ GGO_NATIVE | GGO_GLYPH_INDEX,
+ &metrics, 0, NULL, &matrix) == 0) {
+ /* Workaround for GetGlyphOutline returning 1x1 bounding box
+ * for <space> glyph that is in fact empty.
+ */
+ metrics.gmBlackBoxX = metrics.gmBlackBoxY = 0;
+ }
+ 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;
+}
+
+/* Duplicate the green channel of a 4-channel mask in the alpha channel, then
+ * invert the whole mask.
+ */
+static void
+_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface)
+{
+ cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image;
+ int i, j;
+
+ for (i = 0; i < image->height; i++) {
+ uint32_t *p = (uint32_t *) (image->data + i * image->stride);
+ for (j = 0; j < image->width; j++) {
+ *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16));
+ p++;
+ }
+ }
+}
+
+/* Invert a mask
+ */
+static void
+_invert_argb32_mask (cairo_win32_surface_t *mask_surface)
+{
+ cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image;
+ int i, j;
+
+ for (i = 0; i < image->height; i++) {
+ uint32_t *p = (uint32_t *) (image->data + i * image->stride);
+ for (j = 0; j < image->width; j++) {
+ *p = 0xffffffff ^ *p;
+ p++;
+ }
+ }
+}
+
+/* Compute an alpha-mask from a monochrome RGB24 image
+ */
+static cairo_surface_t *
+_compute_a8_mask (cairo_win32_surface_t *mask_surface)
+{
+ cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image;
+ cairo_image_surface_t *image8;
+ int i, j;
+
+ if (image24->base.status)
+ return cairo_surface_reference (&image24->base);
+
+ image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8,
+ image24->width, image24->height);
+ if (image8->base.status)
+ return &image8->base;
+
+ for (i = 0; i < image24->height; i++) {
+ uint32_t *p = (uint32_t *) (image24->data + i * image24->stride);
+ unsigned char *q = (unsigned char *) (image8->data + i * image8->stride);
+
+ for (j = 0; j < image24->width; j++) {
+ *q = 255 - ((*p & 0x0000ff00) >> 8);
+ p++;
+ q++;
+ }
+ }
+
+ return &image8->base;
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_glyph_init (void *abstract_font,
+ cairo_scaled_glyph_t *scaled_glyph,
+ 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_show_glyphs (void *abstract_font,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *generic_surface,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_region_t *clip_region,
+ int *remaining_glyphs)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface;
+ cairo_status_t status;
+
+ if (width == 0 || height == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_surface_is_win32 (generic_surface) &&
+ surface->format == CAIRO_FORMAT_RGB24 &&
+ (generic_surface->permit_subpixel_antialiasing || scaled_font->quality != CLEARTYPE_QUALITY) &&
+ op == CAIRO_OPERATOR_OVER &&
+ _cairo_pattern_is_opaque_solid (pattern)) {
+
+ cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern;
+
+ /* When compositing OVER on a GDI-understood surface, with a
+ * solid opaque color, we can just call ExtTextOut directly.
+ */
+ COLORREF new_color;
+
+ status = _cairo_win32_surface_set_clip_region (surface, clip_region);
+ if (unlikely (status))
+ return status;
+
+ new_color = RGB (((int)solid_pattern->color.red_short) >> 8,
+ ((int)solid_pattern->color.green_short) >> 8,
+ ((int)solid_pattern->color.blue_short) >> 8);
+
+ return _draw_glyphs_on_surface (surface, scaled_font, new_color,
+ 0, 0,
+ glyphs, num_glyphs);
+ } else {
+ /* Otherwise, we need to draw using software fallbacks. We create a mask
+ * surface by drawing the the glyphs onto a DIB, black-on-white then
+ * inverting. GDI outputs gamma-corrected images so inverted black-on-white
+ * is very different from white-on-black. We favor the more common
+ * case where the final output is dark-on-light.
+ */
+ cairo_win32_surface_t *tmp_surface;
+ cairo_surface_t *mask_surface;
+ cairo_surface_pattern_t mask;
+ cairo_bool_t use_subpixel_antialiasing =
+ scaled_font->quality == CLEARTYPE_QUALITY && generic_surface->permit_subpixel_antialiasing;
+ RECT r;
+
+ tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height);
+ if (tmp_surface->base.status)
+ return tmp_surface->base.status;
+
+ r.left = 0;
+ r.top = 0;
+ r.right = width;
+ r.bottom = height;
+ FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH));
+
+ status = _draw_glyphs_on_surface (tmp_surface,
+ scaled_font, RGB (0, 0, 0),
+ dest_x, dest_y,
+ glyphs, num_glyphs);
+ if (status) {
+ cairo_surface_destroy (&tmp_surface->base);
+ return status;
+ }
+
+ if (use_subpixel_antialiasing) {
+ /* For ClearType, we need a 4-channel mask. If we are compositing on
+ * a surface with alpha, we need to compute the alpha channel of
+ * the mask (we just copy the green channel). But for a destination
+ * surface without alpha the alpha channel of the mask is ignored
+ */
+
+ if (surface->format != CAIRO_FORMAT_RGB24)
+ _compute_argb32_mask_alpha (tmp_surface);
+ else
+ _invert_argb32_mask (tmp_surface);
+
+ mask_surface = &tmp_surface->base;
+ } else {
+ mask_surface = _compute_a8_mask (tmp_surface);
+ cairo_surface_destroy (&tmp_surface->base);
+ status = mask_surface->status;
+ if (status)
+ return status;
+ }
+
+ /* For op == OVER, no-cleartype, a possible optimization here is to
+ * draw onto an intermediate ARGB32 surface and alpha-blend that with the
+ * destination
+ */
+ _cairo_pattern_init_for_surface (&mask, mask_surface);
+ cairo_surface_destroy (mask_surface);
+
+ if (use_subpixel_antialiasing)
+ mask.base.has_component_alpha = TRUE;
+
+ status = _cairo_surface_composite (op, pattern,
+ &mask.base,
+ &surface->base,
+ source_x, source_y,
+ 0, 0,
+ dest_x, dest_y,
+ width, height,
+ clip_region);
+
+ _cairo_pattern_fini (&mask.base);
+
+ return status;
+ }
+}
+
+static cairo_int_status_t
+_cairo_win32_scaled_font_load_truetype_table (void *abstract_font,
+ unsigned long tag,
+ long offset,
+ unsigned char *buffer,
+ unsigned long *length)
+{
+ cairo_win32_scaled_font_t *scaled_font = abstract_font;
+ HDC hdc;
+ cairo_status_t status;
+
+ hdc = _get_global_font_dc ();
+ assert (hdc != NULL);
+
+ tag = (tag&0x000000ff)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24;
+ status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
+ if (status)
+ return status;
+
+ *length = GetFontData (hdc, tag, offset, buffer, *length);
+ if (*length == GDI_ERROR)
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ 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 = 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:
+ if (glyph_indices)
+ free (glyph_indices);
+ if (utf16)
+ free (utf16);
+ free (glyph_set);
+exit1:
+ cairo_win32_scaled_font_done_font (&scaled_font->base);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t *scaled_glyph)
+{
+ cairo_status_t status;
+ cairo_glyph_t glyph;
+ cairo_win32_surface_t *surface;
+ cairo_t *cr;
+ cairo_surface_t *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_t *)
+ cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height);
+
+ cr = cairo_create (&surface->base);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_paint (cr);
+ status = cairo_status (cr);
+ cairo_destroy(cr);
+ if (status)
+ goto FAIL;
+
+ glyph.index = _cairo_scaled_glyph_index (scaled_glyph);
+ glyph.x = -x1;
+ glyph.y = -y1;
+ status = _draw_glyphs_on_surface (surface, scaled_font, RGB(0,0,0),
+ 0, 0, &glyph, 1);
+ if (status)
+ goto FAIL;
+
+ GdiFlush();
+
+ image = _compute_a8_mask (surface);
+ 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->base);
+
+ 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 = malloc (bytesGlyph);
+ if (!buffer) {
+ 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_show_glyphs,
+ _cairo_win32_scaled_font_load_truetype_table,
+ _cairo_win32_scaled_font_index_to_ucs4,
+};
+
+/* #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;
+};
+
+/* implement the platform-specific interface */
+
+static void
+_cairo_win32_font_face_destroy (void *abstract_face);
+
+static cairo_bool_t
+_is_scale (const cairo_matrix_t *matrix, double scale)
+{
+ return matrix->xx == scale && matrix->yy == scale &&
+ matrix->xy == 0. && matrix->yx == 0. &&
+ matrix->x0 == 0. && matrix->y0 == 0.;
+}
+
+static cairo_status_t
+_cairo_win32_font_face_scaled_font_create (void *abstract_face,
+ const cairo_matrix_t *font_matrix,
+ const cairo_matrix_t *ctm,
+ const cairo_font_options_t *options,
+ cairo_scaled_font_t **font)
+{
+ HFONT hfont = NULL;
+
+ cairo_win32_font_face_t *font_face = abstract_face;
+
+ if (font_face->hfont) {
+ /* Check whether it's OK to go ahead and use the font-face's HFONT. */
+ if (_is_scale (ctm, 1.) &&
+ _is_scale (font_matrix, -font_face->logfont.lfHeight)) {
+ hfont = font_face->hfont;
+ }
+ }
+
+ return _win32_scaled_font_create (&font_face->logfont,
+ hfont,
+ &font_face->base,
+ font_matrix, ctm, options,
+ font);
+}
+
+const cairo_font_face_backend_t _cairo_win32_font_face_backend = {
+ CAIRO_FONT_TYPE_WIN32,
+ _cairo_win32_font_face_create_for_toy,
+ _cairo_win32_font_face_destroy,
+ _cairo_win32_font_face_scaled_font_create
+};
+
+/* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t.
+ * The primary purpose of this mapping is to provide unique
+ * #cairo_font_face_t values so that our cache and mapping from
+ * #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.
+ *
+ * Only #cairo_font_face_t values with null 'hfont' (no
+ * HFONT preallocated by caller) are stored in this table. We rely
+ * on callers to manage the lifetime of the HFONT, and they can't
+ * do that if we share #cairo_font_face_t values with other callers.
+ */
+
+static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL;
+
+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 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;
+}
+
+static void
+_cairo_win32_font_face_destroy (void *abstract_face)
+{
+ cairo_hash_table_t *hash_table;
+ cairo_win32_font_face_t *font_face = abstract_face;
+
+ if (!font_face->hfont) {
+ hash_table = _cairo_win32_font_face_hash_table_lock ();
+ if (unlikely (hash_table == NULL)) {
+ return;
+ }
+ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+ _cairo_win32_font_face_hash_table_unlock ();
+ }
+}
+
+/**
+ * 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.
+ **/
+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;
+
+ if (!font) {
+ hash_table = _cairo_win32_font_face_hash_table_lock ();
+ if (unlikely (hash_table == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_font_face_t *)&_cairo_font_face_nil;
+ }
+
+ _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) {
+ cairo_font_face_reference (&font_face->base);
+ goto DONE;
+ }
+ }
+
+ /* Otherwise create it and insert into hash table. */
+ font_face = 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);
+
+ if (!font) {
+ assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash);
+ status = _cairo_hash_table_insert (hash_table,
+ &font_face->base.hash_entry);
+ if (unlikely (status))
+ goto FAIL;
+ }
+
+DONE:
+ if (!font) {
+ _cairo_win32_font_face_hash_table_unlock ();
+ }
+
+ return &font_face->base;
+
+FAIL:
+ if (!font) {
+ _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.
+ **/
+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.
+ **/
+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_scaled_font_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.
+ **/
+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()
+ **/
+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.
+ **/
+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/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
new file mode 100644
index 000000000..a6df34ae1
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
@@ -0,0 +1,1987 @@
+/* -*- 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 <ajohnson@redneon.com>
+ * Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as ETO_PDY */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include "cairoint.h"
+
+#include "cairo-error-private.h"
+#include "cairo-paginated-private.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-win32-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-image-info-private.h"
+#include "cairo-surface-clipper-private.h"
+
+#include <windows.h>
+
+#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 cairo_surface_backend_t cairo_win32_printing_surface_backend;
+static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend;
+
+static void
+_cairo_win32_printing_surface_init_ps_mode (cairo_win32_surface_t *surface)
+{
+ DWORD word;
+ INT ps_feature, ps_level;
+
+ word = PSIDENT_GDICENTRIC;
+ if (ExtEscape (surface->dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0)
+ return;
+
+ ps_feature = FEATURESETTING_PSLEVEL;
+ if (ExtEscape (surface->dc, GET_PS_FEATURESETTING, sizeof(INT),
+ (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0)
+ return;
+
+ if (ps_level >= 3)
+ surface->flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT;
+}
+
+static void
+_cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface)
+{
+ DWORD word;
+
+ word = CHECKJPEGFORMAT;
+ if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0)
+ surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG;
+
+ word = CHECKPNGFORMAT;
+ if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0)
+ surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG;
+}
+
+/* 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_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);
+ }
+}
+
+static cairo_int_status_t
+analyze_surface_pattern_transparency (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 (status)
+ return status;
+
+ 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_surface_release_source_image (pattern->surface, image, 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 (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_WIN32 &&
+ pattern->surface->backend->acquire_source_image == NULL)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static cairo_bool_t
+pattern_supported (cairo_win32_surface_t *surface, const cairo_pattern_t *pattern)
+{
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+ return TRUE;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
+ return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern);
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR)
+ return surface->flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT;
+
+ return FALSE;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ 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 ( _cairo_surface_is_recording (surface_pattern->surface))
+ 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) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
+
+ return analyze_surface_pattern_transparency (surface_pattern);
+ }
+
+ 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_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern)
+{
+ if (_cairo_win32_printing_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void
+_cairo_win32_printing_surface_init_clear_color (cairo_win32_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_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_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->dc, surface->brush);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_printing_surface_done_solid_brush (cairo_win32_surface_t *surface)
+{
+ if (surface->old_brush) {
+ SelectObject (surface->dc, surface->old_brush);
+ DeleteObject (surface->brush);
+ surface->old_brush = NULL;
+ }
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface,
+ RECT *clip)
+{
+ XFORM xform;
+
+ _cairo_matrix_to_win32_xform (&surface->ctm, &xform);
+ if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform");
+ GetClipBox (surface->dc, clip);
+
+ _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform);
+ if (!SetWorldTransform (surface->dc, &xform))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ RECT clip;
+ cairo_status_t status;
+
+ GetClipBox (surface->dc, &clip);
+ status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern);
+ if (status)
+ return status;
+
+ FillRect (surface->dc, &clip, surface->brush);
+ _cairo_win32_printing_surface_done_solid_brush (surface);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t *surface,
+ cairo_surface_pattern_t *pattern)
+{
+ cairo_content_t old_content;
+ cairo_matrix_t old_ctm;
+ cairo_bool_t old_has_ctm;
+ cairo_rectangle_int_t recording_extents;
+ cairo_status_t status;
+ cairo_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 *) pattern->surface;
+ cairo_box_t bbox;
+
+ extend = cairo_pattern_get_extend (&pattern->base);
+
+ p2d = pattern->base.matrix;
+ status = cairo_matrix_invert (&p2d);
+ /* _cairo_pattern_set_matrix guarantees invertibility */
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ old_ctm = surface->ctm;
+ old_has_ctm = surface->has_ctm;
+ cairo_matrix_multiply (&p2d, &p2d, &surface->ctm);
+ surface->ctm = p2d;
+ SaveDC (surface->dc);
+ _cairo_matrix_to_win32_xform (&p2d, &xform);
+
+ status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
+ if (status)
+ return status;
+
+ _cairo_box_round_to_rectangle (&bbox, &recording_extents);
+
+ status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip);
+ if (status)
+ return status;
+
+ 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)
+ return status;
+ }
+
+ 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->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->dc);
+
+ x = 0;
+ y = 0;
+ cairo_matrix_transform_point (&surface->ctm, &x, &y);
+ MoveToEx (surface->dc, (int) x, (int) y, NULL);
+
+ x = recording_extents.width;
+ y = 0;
+ cairo_matrix_transform_point (&surface->ctm, &x, &y);
+ LineTo (surface->dc, (int) x, (int) y);
+
+ x = recording_extents.width;
+ y = recording_extents.height;
+ cairo_matrix_transform_point (&surface->ctm, &x, &y);
+ LineTo (surface->dc, (int) x, (int) y);
+
+ x = 0;
+ y = recording_extents.height;
+ cairo_matrix_transform_point (&surface->ctm, &x, &y);
+ LineTo (surface->dc, (int) x, (int) y);
+
+ CloseFigure (surface->dc);
+ EndPath (surface->dc);
+ SelectClipPath (surface->dc, RGN_AND);
+
+ SaveDC (surface->dc); /* Allow clip path to be reset during replay */
+ status = _cairo_recording_surface_replay_region (&recording_surface->base, NULL,
+ &surface->base,
+ CAIRO_RECORDING_REGION_NATIVE);
+ assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
+ /* Restore both the clip save and our earlier path SaveDC */
+ RestoreDC (surface->dc, -2);
+
+ if (status)
+ return status;
+ }
+ }
+
+ surface->content = old_content;
+ surface->ctm = old_ctm;
+ surface->has_ctm = old_has_ctm;
+ RestoreDC (surface->dc, -1);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface,
+ cairo_surface_t *source,
+ const unsigned char **data,
+ unsigned 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->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
+ &mime_data, &mime_data_length);
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length);
+ if (status)
+ return status;
+
+ result = 0;
+ if (ExtEscape(surface->dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data,
+ sizeof(result), (char *) &result) <= 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (result != 1)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ *data = mime_data;
+ *length = mime_data_length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface,
+ cairo_surface_t *source,
+ const unsigned char **data,
+ unsigned 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->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG,
+ &mime_data, &mime_data_length);
+ if (mime_data == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length);
+ if (status)
+ return status;
+
+ result = 0;
+ if (ExtEscape(surface->dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data,
+ sizeof(result), (char *) &result) <= 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (result != 1)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ *data = mime_data;
+ *length = mime_data_length;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surface,
+ cairo_surface_pattern_t *pattern)
+{
+ cairo_status_t status;
+ cairo_extend_t extend;
+ cairo_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;
+ 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;
+ cairo_bool_t axis_swap;
+
+ /* If we can't use StretchDIBits with this surface, we can't do anything
+ * here.
+ */
+ if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+ background_color = CAIRO_COLOR_WHITE;
+ else
+ background_color = CAIRO_COLOR_BLACK;
+
+ extend = cairo_pattern_get_extend (&pattern->base);
+
+ status = _cairo_surface_acquire_source_image (pattern->surface,
+ &image, &image_extra);
+ if (status)
+ return status;
+
+ 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,
+ pattern->surface,
+ &mime_data,
+ &mime_size,
+ &mime_info);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ mime_type = BI_PNG;
+ status = _cairo_win32_printing_surface_check_png (surface,
+ pattern->surface,
+ &mime_data,
+ &mime_size,
+ &mime_info);
+ }
+ if (_cairo_status_is_error (status))
+ return status;
+
+ use_mime = (status == CAIRO_STATUS_SUCCESS);
+
+ m = pattern->base.matrix;
+ status = cairo_matrix_invert (&m);
+ /* _cairo_pattern_set_matrix guarantees invertibility */
+ assert (status == CAIRO_STATUS_SUCCESS);
+ cairo_matrix_multiply (&m, &m, &surface->ctm);
+ cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
+ /* Check if the matrix swaps the X and Y axes by checking if the diagonal
+ * is effectively zero. This can happen, for example, if it was composed
+ * with a rotation such as a landscape transform. Some printing devices
+ * don't support such transforms in StretchDIBits.
+ */
+ axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1;
+
+ if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) {
+ cairo_surface_t *opaque_surface;
+ cairo_surface_pattern_t image_pattern;
+ cairo_solid_pattern_t background_pattern;
+ int width = image->width, height = image->height;
+
+ if (axis_swap) {
+ width = image->height;
+ height = image->width;
+ }
+ opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ width,
+ height);
+ if (opaque_surface->status) {
+ status = opaque_surface->status;
+ goto CLEANUP_OPAQUE_IMAGE;
+ }
+
+ if (image->format != CAIRO_FORMAT_RGB24) {
+ _cairo_pattern_init_solid (&background_pattern,
+ background_color);
+ status = _cairo_surface_paint (opaque_surface,
+ CAIRO_OPERATOR_SOURCE,
+ &background_pattern.base,
+ NULL);
+ if (status)
+ goto CLEANUP_OPAQUE_IMAGE;
+ }
+
+ _cairo_pattern_init_for_surface (&image_pattern, &image->base);
+ if (axis_swap) {
+ /* swap the X and Y axes to undo the axis swap in the matrix */
+ cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
+ cairo_pattern_set_matrix (&image_pattern.base, &swap_xy);
+ cairo_matrix_multiply (&m, &swap_xy, &m);
+ }
+ status = _cairo_surface_paint (opaque_surface,
+ CAIRO_OPERATOR_OVER,
+ &image_pattern.base,
+ 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;
+
+ SaveDC (surface->dc);
+ _cairo_matrix_to_win32_xform (&m, &xform);
+
+ if (! SetWorldTransform (surface->dc, &xform)) {
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern");
+ goto CLEANUP_OPAQUE_IMAGE;
+ }
+
+ oldmode = SetStretchBltMode(surface->dc, HALFTONE);
+
+ GetClipBox (surface->dc, &clip);
+ if (extend == CAIRO_EXTEND_REPEAT || 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->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->dc, oldmode);
+ RestoreDC (surface->dc, -1);
+
+CLEANUP_OPAQUE_IMAGE:
+ if (opaque_image != image)
+ cairo_surface_destroy (&opaque_image->base);
+CLEANUP_IMAGE:
+ _cairo_surface_release_source_image (pattern->surface, image, image_extra);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_surface_t *surface,
+ cairo_surface_pattern_t *pattern)
+{
+ if (_cairo_surface_is_recording (pattern->surface)) {
+ return _cairo_win32_printing_surface_paint_recording_pattern (surface,
+ pattern);
+ } else {
+ return _cairo_win32_printing_surface_paint_image_pattern (surface,
+ pattern);
+ }
+}
+
+static void
+vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color)
+{
+ /* 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_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->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 = _cairo_fixed_to_double (pattern->p1.x);
+ p1y = _cairo_fixed_to_double (pattern->p1.y);
+ p2x = _cairo_fixed_to_double (pattern->p2.x);
+ p2y = _cairo_fixed_to_double (pattern->p2.y);
+ 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->dc, &xform))
+ return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2");
+
+ GetClipBox (surface->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 = malloc (sizeof (TRIVERTEX) * (num_rects*2*num_ranges + 4));
+ rect = 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->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->dc, -1);
+
+ return 0;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface,
+ const cairo_pattern_t *pattern)
+{
+ cairo_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:
+ status = _cairo_win32_printing_surface_paint_surface_pattern (surface,
+ (cairo_surface_pattern_t *) pattern);
+ 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;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+typedef struct _win32_print_path_info {
+ cairo_win32_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->dc, (int) x, (int) y, NULL);
+ } else {
+ MoveToEx (path_info->surface->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->dc, (int) x, (int) y);
+ } else {
+ LineTo (path_info->surface->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->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->dc);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_printing_surface_emit_path (cairo_win32_surface_t *surface,
+ cairo_path_fixed_t *path)
+{
+ win32_path_info_t path_info;
+
+ path_info.surface = surface;
+ return _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_win32_printing_surface_path_move_to,
+ _cairo_win32_printing_surface_path_line_to,
+ _cairo_win32_printing_surface_path_curve_to,
+ _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_surface_t *surface = abstract_surface;
+
+ /* Undo both SaveDC's that we did in start_page */
+ RestoreDC (surface->dc, -2);
+
+ 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_surface_t *surface = cairo_container_of (clipper,
+ cairo_win32_surface_t,
+ clipper);
+ cairo_status_t status;
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (path == NULL) {
+ RestoreDC (surface->dc, -1);
+ SaveDC (surface->dc);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ BeginPath (surface->dc);
+ status = _cairo_win32_printing_surface_emit_path (surface, path);
+ EndPath (surface->dc);
+
+ switch (fill_rule) {
+ case CAIRO_FILL_RULE_WINDING:
+ SetPolyFillMode (surface->dc, WINDING);
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ SetPolyFillMode (surface->dc, ALTERNATE);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ SelectClipPath (surface->dc, RGN_AND);
+
+ return status;
+}
+
+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,
+ cairo_clip_t *clip)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_solid_pattern_t clear;
+ cairo_status_t status;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (status)
+ return status;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ _cairo_win32_printing_surface_init_clear_color (surface, &clear);
+ source = (cairo_pattern_t*) &clear;
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+
+ assert (_cairo_win32_printing_surface_operation_supported (surface, op, source));
+
+ return _cairo_win32_printing_surface_paint_pattern (surface, source);
+}
+
+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,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ HPEN pen;
+ LOGBRUSH brush;
+ COLORREF color;
+ XFORM xform;
+ DWORD pen_style;
+ DWORD pen_width;
+ DWORD *dash_array;
+ HGDIOBJ obj;
+ unsigned int i;
+ cairo_solid_pattern_t clear;
+ cairo_matrix_t mat;
+ double scale;
+ double scaled_width;
+ double major, minor;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (status)
+ return status;
+
+ 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)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+ }
+
+ assert (_cairo_win32_printing_surface_operation_supported (surface, op, source));
+ 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++) {
+ DWORD dashes = (DWORD) (scale * style->dash[i]);
+ /* zero dash-lengths cause ExtCreatePen to fail. Make the dashes
+ * longer if necessary.
+ */
+ dash_array[i] = MAX(1, dashes);
+ }
+ } else {
+ pen_style |= PS_SOLID;
+ }
+
+ SetMiterLimit (surface->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);
+ scaled_width = scale * style->line_width;
+ if (scaled_width == 0.0)
+ return status;
+ pen_width = (DWORD)scaled_width;
+ if (pen_width == 0) {
+ /* ExtCreatePen will fail if passed zero width. We have to choose
+ * between drawing something too wide, or drawing nothing at all.
+ * Let's draw something.
+ */
+ pen_width = 1;
+ }
+ pen = ExtCreatePen(pen_style,
+ pen_width,
+ &brush,
+ style->num_dashes,
+ dash_array);
+ if (pen == NULL)
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen");
+ obj = SelectObject (surface->dc, pen);
+ if (obj == NULL)
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject");
+
+ BeginPath (surface->dc);
+ status = _cairo_win32_printing_surface_emit_path (surface, path);
+ EndPath (surface->dc);
+ if (status)
+ return status;
+
+ /*
+ * Switch to user space to set line parameters
+ */
+ SaveDC (surface->dc);
+
+ /* Some printers don't handle transformed strokes. Avoid the transform
+ * if not required for the pen shape. Use the SVD here to find the major
+ * and minor scales then check if they differ by more than 1 device unit.
+ * If the difference is smaller, then just treat the scaling as uniform.
+ * This check ignores rotations as the pen shape is symmetric before
+ * transformation.
+ */
+ _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor);
+ if (fabs (major - minor) > 1) {
+ /* Check if the matrix swaps the X and Y axes such that the diagonal
+ * is nearly zero. This was observed to cause problems with XPS export.
+ */
+ if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) {
+ /* swap the X and Y axes to undo the axis swap in the matrix */
+ cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
+ cairo_matrix_multiply (&mat, &swap_xy, &mat);
+ }
+ _cairo_matrix_to_win32_xform (&mat, &xform);
+ xform.eDx = 0.0f;
+ xform.eDy = 0.0f;
+
+ if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ StrokePath (surface->dc);
+ } else {
+ if (!WidenPath (surface->dc))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath");
+ if (!SelectClipPath (surface->dc, RGN_AND))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath");
+
+ /* Return to device space to paint the pattern */
+ _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform);
+ if (!SetWorldTransform (surface->dc, &xform))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform");
+ status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+ }
+ RestoreDC (surface->dc, -1);
+ DeleteObject (pen);
+ if (dash_array)
+ free (dash_array);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ cairo_solid_pattern_t clear;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (status)
+ return status;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ _cairo_win32_printing_surface_init_clear_color (surface, &clear);
+ source = (cairo_pattern_t*) &clear;
+ op = CAIRO_OPERATOR_SOURCE;
+ }
+
+ if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
+ return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+
+ assert (_cairo_win32_printing_surface_operation_supported (surface, op, source));
+
+ surface->path_empty = TRUE;
+ BeginPath (surface->dc);
+ status = _cairo_win32_printing_surface_emit_path (surface, path);
+ EndPath (surface->dc);
+
+ switch (fill_rule) {
+ case CAIRO_FILL_RULE_WINDING:
+ SetPolyFillMode (surface->dc, WINDING);
+ break;
+ case CAIRO_FILL_RULE_EVEN_ODD:
+ SetPolyFillMode (surface->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 (status)
+ return status;
+
+ FillPath (surface->dc);
+ _cairo_win32_printing_surface_done_solid_brush (surface);
+ } else if (surface->path_empty == FALSE) {
+ SaveDC (surface->dc);
+ SelectClipPath (surface->dc, RGN_AND);
+ status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+ RestoreDC (surface->dc, -1);
+ }
+
+ fflush(stderr);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ 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_show_glyphs_internal (
+ surface,
+ op,
+ source,
+ sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first],
+ i - first + 1,
+ scaled_font,
+ clip,
+ remaining_glyphs,
+ ! sequence_is_unicode);
+ 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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_win32_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_scaled_font_t *local_scaled_font = NULL;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (status)
+ return status;
+
+ 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))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ else
+ return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+ }
+#endif
+
+#if CAIRO_HAS_DWRITE_FONT
+ if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) {
+ return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+ }
+#endif
+
+ /* For non win32 fonts we need to check that each glyph has a
+ * path available. If a path is not available,
+ * _cairo_scaled_glyph_lookup() will return
+ * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be
+ * used.
+ */
+ for (i = 0; i < num_glyphs; i++) {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_PATH,
+ &scaled_glyph);
+ if (status)
+ return status;
+ }
+
+ return _cairo_win32_printing_surface_analyze_operation (surface, op, source);
+ }
+
+ 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 (opaque->status)
+ return opaque->status;
+ 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,
+ remaining_glyphs);
+ goto FINISH;
+ }
+#endif
+
+ SaveDC (surface->dc);
+ old_ctm = surface->ctm;
+ old_has_ctm = surface->has_ctm;
+ surface->has_ctm = TRUE;
+ surface->path_empty = TRUE;
+ BeginPath (surface->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->dc);
+ surface->ctm = old_ctm;
+ surface->has_ctm = old_has_ctm;
+ if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) {
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ status = _cairo_win32_printing_surface_select_solid_brush (surface, source);
+ if (status)
+ goto FINISH;
+
+ SetPolyFillMode (surface->dc, WINDING);
+ FillPath (surface->dc);
+ _cairo_win32_printing_surface_done_solid_brush (surface);
+ } else {
+ SelectClipPath (surface->dc, RGN_AND);
+ status = _cairo_win32_printing_surface_paint_pattern (surface, source);
+ }
+ }
+ RestoreDC (surface->dc, -1);
+
+ if (opaque)
+ cairo_pattern_destroy (opaque);
+
+FINISH:
+ if (local_scaled_font)
+ cairo_scaled_font_destroy (local_scaled_font);
+
+ return status;
+}
+
+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_surface_t *surface = abstract_surface;
+ XFORM xform;
+ double x_res, y_res;
+ cairo_matrix_t inverse_ctm;
+ cairo_status_t status;
+
+ SaveDC (surface->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 insufficent 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->dc, GM_ADVANCED);
+ GetWorldTransform(surface->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->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->dc, LOGPIXELSX);
+ y_res = GetDeviceCaps (surface->dc, LOGPIXELSY);
+ cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res);
+ _cairo_surface_set_resolution (&surface->base, x_res, y_res);
+
+ SaveDC (surface->dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface,
+ cairo_paginated_mode_t paginated_mode)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+
+ surface->paginated_mode = paginated_mode;
+}
+
+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; 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_surface_t *surface;
+ cairo_surface_t *paginated;
+ RECT rect;
+
+ surface = malloc (sizeof (cairo_win32_surface_t));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) {
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_win32_printing_surface_clipper_intersect_clip_path);
+
+ surface->image = NULL;
+ surface->format = CAIRO_FORMAT_RGB24;
+ surface->content = CAIRO_CONTENT_COLOR_ALPHA;
+ surface->d3d9surface = NULL;
+
+ surface->dc = hdc;
+ surface->bitmap = NULL;
+ surface->is_dib = FALSE;
+ surface->saved_dc_bitmap = NULL;
+ surface->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));
+ }
+
+ GetClipBox(hdc, &rect);
+ surface->extents.x = rect.left;
+ surface->extents.y = rect.top;
+ surface->extents.width = rect.right - rect.left;
+ surface->extents.height = rect.bottom - rect.top;
+
+ surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+ surface->flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING;
+
+ _cairo_win32_printing_surface_init_ps_mode (surface);
+ _cairo_win32_printing_surface_init_image_support (surface);
+ _cairo_win32_printing_surface_init_language_pack (surface);
+ _cairo_surface_init (&surface->base,
+ &cairo_win32_printing_surface_backend,
+ NULL, /* device */
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ paginated = _cairo_paginated_surface_create (&surface->base,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ &cairo_win32_surface_paginated_backend);
+
+ /* paginated keeps the only reference to surface now, drop ours */
+ cairo_surface_destroy (&surface->base);
+
+ return paginated;
+}
+
+cairo_bool_t
+_cairo_surface_is_win32_printing (cairo_surface_t *surface)
+{
+ return surface->backend == &cairo_win32_printing_surface_backend;
+}
+
+static const cairo_surface_backend_t cairo_win32_printing_surface_backend = {
+ CAIRO_SURFACE_TYPE_WIN32_PRINTING,
+ _cairo_win32_printing_surface_create_similar,
+ _cairo_win32_surface_finish,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ _cairo_win32_printing_surface_show_page,
+ _cairo_win32_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_win32_printing_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ _cairo_win32_printing_surface_paint,
+ NULL, /* mask */
+ _cairo_win32_printing_surface_stroke,
+ _cairo_win32_printing_surface_fill,
+ _cairo_win32_printing_surface_show_glyphs,
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+};
+
+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/cairo-win32-private.h b/gfx/cairo/cairo/src/cairo-win32-private.h
new file mode 100644
index 000000000..44c38535f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-win32-private.h
@@ -0,0 +1,249 @@
+/* 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 <otaylor@redhat.com>
+ */
+
+#ifndef CAIRO_WIN32_PRIVATE_H
+#define CAIRO_WIN32_PRIVATE_H
+
+#include "cairo-win32.h"
+#include "cairoint.h"
+#include "cairo-surface-clipper-private.h"
+
+#ifndef SHADEBLENDCAPS
+#define SHADEBLENDCAPS 120
+#endif
+#ifndef SB_NONE
+#define SB_NONE 0
+#endif
+
+#define WIN32_FONT_LOGICAL_SCALE 1
+
+
+CAIRO_BEGIN_DECLS
+
+typedef struct _cairo_win32_surface {
+ cairo_surface_t base;
+
+ cairo_format_t format;
+
+ HDC dc;
+
+ struct IDirect3DSurface9 *d3d9surface;
+
+ /* We create off-screen surfaces as DIBs or DDBs, based on what we created
+ * originally*/
+ HBITMAP bitmap;
+ cairo_bool_t is_dib;
+
+ /* Used to save the initial 1x1 monochrome bitmap for the DC to
+ * select back into the DC before deleting the DC and our
+ * bitmap. For Windows XP, this doesn't seem to be necessary
+ * ... we can just delete the DC and that automatically unselects
+ * out bitmap. But it's standard practice so apparently is needed
+ * on some versions of Windows.
+ */
+ HBITMAP saved_dc_bitmap;
+
+ cairo_surface_t *image;
+
+ cairo_rectangle_int_t extents;
+
+ /* Initial clip bits
+ * We need these kept around so that we maintain
+ * whatever clip was set on the original DC at creation
+ * time when cairo is asked to reset the surface clip.
+ */
+ cairo_rectangle_int_t clip_rect;
+ HRGN initial_clip_rgn;
+ cairo_bool_t had_simple_clip;
+ cairo_region_t *clip_region;
+
+ /* For path clipping to the printing-surface */
+ cairo_surface_clipper_t clipper;
+
+ /* Surface DC flags */
+ uint32_t flags;
+
+ /* printing surface bits */
+ cairo_paginated_mode_t paginated_mode;
+ cairo_content_t content;
+ cairo_bool_t path_empty;
+ cairo_bool_t has_ctm;
+ cairo_matrix_t ctm;
+ cairo_bool_t has_gdi_ctm;
+ cairo_matrix_t gdi_ctm;
+ HBRUSH brush, old_brush;
+ cairo_scaled_font_subsets_t *font_subsets;
+} cairo_win32_surface_t;
+
+/* 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 CHECKJPEGFORMAT escape function */
+ CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8),
+
+ /* if this DDB surface can be converted to a DIB if necessary */
+ CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<9),
+};
+
+cairo_status_t
+_cairo_win32_print_gdi_error (const char *context);
+
+cairo_bool_t
+_cairo_surface_is_win32 (cairo_surface_t *surface);
+
+cairo_bool_t
+_cairo_surface_is_win32_printing (cairo_surface_t *surface);
+
+cairo_status_t
+_cairo_win32_surface_finish (void *abstract_surface);
+
+cairo_bool_t
+_cairo_win32_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle);
+
+uint32_t
+_cairo_win32_flags_for_dc (HDC dc);
+
+cairo_status_t
+_cairo_win32_surface_set_clip_region (void *abstract_surface,
+ cairo_region_t *region);
+
+cairo_int_status_t
+_cairo_win32_surface_show_glyphs_internal (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs,
+ cairo_bool_t glyph_indices);
+
+cairo_int_status_t
+_cairo_win32_surface_show_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs);
+
+cairo_surface_t *
+_cairo_win32_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height);
+
+cairo_status_t
+_cairo_win32_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ cairo_content_t content,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out);
+
+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_int_status_t
+_cairo_win32_save_initial_clip (HDC dc, cairo_win32_surface_t *surface);
+
+cairo_int_status_t
+_cairo_win32_restore_initial_clip (cairo_win32_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/cairo-win32-refptr.h b/gfx/cairo/cairo/src/cairo-win32-refptr.h
new file mode 100644
index 000000000..0baf8ee7b
--- /dev/null
+++ b/gfx/cairo/cairo/src/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 <bschouten@mozilla.com>
+ */
+#ifndef CAIRO_WIN32_REFPTR_H
+#define CAIRO_WIN32_REFPTR_H
+
+template<typename T> class TemporaryRef;
+
+/**
+ * RefPtr points to a refcounted thing that has AddRef and Release
+ * methods to increase/decrease the refcount, respectively. After a
+ * RefPtr<T> 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<typename T>
+class RefPtr
+{
+ // To allow them to use unref()
+ friend class TemporaryRef<T>;
+
+ struct dontRef {};
+
+public:
+ RefPtr() : ptr(0) { }
+ RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {}
+ RefPtr(const TemporaryRef<T>& o) : ptr(o.drop()) {}
+ RefPtr(T* t) : ptr(ref(t)) {}
+
+ template<typename U>
+ RefPtr(const RefPtr<U>& o) : ptr(ref(o.get())) {}
+
+ ~RefPtr() { unref(ptr); }
+
+ RefPtr& operator=(const RefPtr& o) {
+ assign(ref(o.ptr));
+ return *this;
+ }
+ RefPtr& operator=(const TemporaryRef<T>& o) {
+ assign(o.drop());
+ return *this;
+ }
+ RefPtr& operator=(T* t) {
+ assign(ref(t));
+ return *this;
+ }
+
+ template<typename U>
+ RefPtr& operator=(const RefPtr<U>& o) {
+ assign(ref(o.get()));
+ return *this;
+ }
+
+ TemporaryRef<T> forget() {
+ T* tmp = ptr;
+ ptr = 0;
+ return TemporaryRef<T>(tmp, dontRef());
+ }
+
+ T* get() const { return ptr; }
+ operator T*() const { return ptr; }
+ T* operator->() const { return ptr; }
+ T& operator*() const { return *ptr; }
+ template<typename U>
+ operator TemporaryRef<U>() { return TemporaryRef<U>(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<T> 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<typename T>
+class TemporaryRef
+{
+ // To allow it to construct TemporaryRef from a bare T*
+ friend class RefPtr<T>;
+
+ typedef typename RefPtr<T>::dontRef dontRef;
+
+public:
+ TemporaryRef(T* t) : ptr(RefPtr<T>::ref(t)) {}
+ TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {}
+
+ template<typename U>
+ TemporaryRef(const TemporaryRef<U>& o) : ptr(o.drop()) {}
+
+ ~TemporaryRef() { RefPtr<T>::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/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
new file mode 100644
index 000000000..3a3d82989
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
@@ -0,0 +1,4102 @@
+/* -*- 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 <otaylor@redhat.com>
+ * Stuart Parmenter <stuart@mozilla.com>
+ * Vladimir Vukicevic <vladimir@pobox.com>
+ */
+
+#define WIN32_LEAN_AND_MEAN
+/* We require Windows 2000 features such as ETO_PDY */
+#if !defined(WINVER) || (WINVER < 0x0500)
+# define WINVER 0x0500
+#endif
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500)
+# define _WIN32_WINNT 0x0500
+#endif
+
+#include "cairoint.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-paginated-private.h"
+#include "cairo-win32-private.h"
+#include "cairo-scaled-font-subsets-private.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-private.h"
+#include <wchar.h>
+#include <windows.h>
+#include <d3d9.h>
+
+#if defined(__MINGW32__) && !defined(ETO_PDY)
+# define ETO_PDY 0x2000
+#endif
+
+#undef DEBUG_COMPOSITE
+
+/* for older SDKs */
+#ifndef SHADEBLENDCAPS
+#define SHADEBLENDCAPS 120
+#endif
+#ifndef SB_NONE
+#define SB_NONE 0x00000000
+#endif
+
+#define PELS_72DPI ((LONG)(72. / 0.0254))
+
+/**
+ * SECTION:cairo-win32
+ * @Title: Win32 Surfaces
+ * @Short_Description: Microsoft Windows surface support
+ * @See_Also: #cairo_surface_t
+ *
+ * The Microsoft Windows surface is used to render cairo graphics to
+ * Microsoft Windows windows, bitmaps, and printing device contexts.
+ *
+ * The surface returned by cairo_win32_printing_surface_create() is of surface
+ * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface
+ * type.
+ *
+ * The surface returned by the other win32 constructors is of surface type
+ * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type.
+ */
+
+/**
+ * CAIRO_HAS_WIN32_SURFACE:
+ *
+ * Defined if the Microsoft Windows surface backend is available.
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
+static const cairo_surface_backend_t cairo_win32_surface_backend;
+
+/**
+ * _cairo_win32_print_gdi_error:
+ * @context: context string to display along with the error
+ *
+ * Helper function to dump out a human readable form of the
+ * current error code.
+ *
+ * Return value: A cairo status code for the error code
+ **/
+cairo_status_t
+_cairo_win32_print_gdi_error (const char *context)
+{
+ void *lpMsgBuf;
+ DWORD last_error = GetLastError ();
+
+ if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ last_error,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR) &lpMsgBuf,
+ 0, NULL)) {
+ fprintf (stderr, "%s: Unknown GDI error", context);
+ } else {
+ fwprintf (stderr, L"%s: %S", context, (wchar_t *)lpMsgBuf);
+
+ LocalFree (lpMsgBuf);
+ }
+ fflush(stderr);
+
+ /* We should switch off of last_status, but we'd either return
+ * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there
+ * is no CAIRO_STATUS_UNKNOWN_ERROR.
+ */
+
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+}
+
+uint32_t
+_cairo_win32_flags_for_dc (HDC dc)
+{
+ uint32_t flags = 0;
+
+ if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
+ flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY;
+
+ /* These will always be possible, but the actual GetDeviceCaps
+ * calls will return whether they're accelerated or not.
+ * We may want to use our own (pixman) routines sometimes
+ * if they're eventually faster, but for now have GDI do
+ * everything.
+ */
+ flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
+ flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
+ flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
+ flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB;
+ } else {
+ int cap;
+
+ cap = GetDeviceCaps(dc, SHADEBLENDCAPS);
+ if (cap != SB_NONE)
+ flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
+
+ cap = GetDeviceCaps(dc, RASTERCAPS);
+ if (cap & RC_BITBLT)
+ flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
+ if (cap & RC_STRETCHBLT)
+ flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
+ if (cap & RC_STRETCHDIB)
+ flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB;
+ }
+
+ return flags;
+}
+
+static cairo_status_t
+_create_dc_and_bitmap (cairo_win32_surface_t *surface,
+ HDC original_dc,
+ cairo_format_t format,
+ int width,
+ int height,
+ unsigned char **bits_out,
+ int *rowstride_out)
+{
+ cairo_status_t status;
+
+ BITMAPINFO *bitmap_info = NULL;
+ struct {
+ BITMAPINFOHEADER bmiHeader;
+ RGBQUAD bmiColors[2];
+ } bmi_stack;
+ void *bits;
+
+ int num_palette = 0; /* Quiet GCC */
+ int i;
+
+ surface->dc = NULL;
+ surface->bitmap = NULL;
+ surface->is_dib = FALSE;
+
+ switch (format) {
+ default:
+ case CAIRO_FORMAT_INVALID:
+ return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ num_palette = 0;
+ break;
+
+ case CAIRO_FORMAT_A8:
+ num_palette = 256;
+ break;
+
+ case CAIRO_FORMAT_A1:
+ num_palette = 2;
+ break;
+ }
+
+ if (num_palette > 2) {
+ bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER));
+ if (!bitmap_info)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else {
+ bitmap_info = (BITMAPINFO *)&bmi_stack;
+ }
+
+ bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
+ bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width;
+ bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */
+ bitmap_info->bmiHeader.biSizeImage = 0;
+ bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */
+ bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */
+ bitmap_info->bmiHeader.biPlanes = 1;
+
+ switch (format) {
+ /* We can't create real RGB24 bitmaps because something seems to
+ * break if we do, especially if we don't set up an image
+ * fallback. It could be a bug with using a 24bpp pixman image
+ * (and creating one with masks). So treat them like 32bpp.
+ * Note: This causes problems when using BitBlt/AlphaBlend/etc!
+ * see end of file.
+ */
+ case CAIRO_FORMAT_RGB24:
+ case CAIRO_FORMAT_ARGB32:
+ bitmap_info->bmiHeader.biBitCount = 32;
+ bitmap_info->bmiHeader.biCompression = BI_RGB;
+ bitmap_info->bmiHeader.biClrUsed = 0; /* unused */
+ bitmap_info->bmiHeader.biClrImportant = 0;
+ break;
+
+ case CAIRO_FORMAT_A8:
+ bitmap_info->bmiHeader.biBitCount = 8;
+ bitmap_info->bmiHeader.biCompression = BI_RGB;
+ bitmap_info->bmiHeader.biClrUsed = 256;
+ bitmap_info->bmiHeader.biClrImportant = 0;
+
+ for (i = 0; i < 256; i++) {
+ bitmap_info->bmiColors[i].rgbBlue = i;
+ bitmap_info->bmiColors[i].rgbGreen = i;
+ bitmap_info->bmiColors[i].rgbRed = i;
+ bitmap_info->bmiColors[i].rgbReserved = 0;
+ }
+
+ break;
+
+ case CAIRO_FORMAT_A1:
+ bitmap_info->bmiHeader.biBitCount = 1;
+ bitmap_info->bmiHeader.biCompression = BI_RGB;
+ bitmap_info->bmiHeader.biClrUsed = 2;
+ bitmap_info->bmiHeader.biClrImportant = 0;
+
+ for (i = 0; i < 2; i++) {
+ bitmap_info->bmiColors[i].rgbBlue = i * 255;
+ bitmap_info->bmiColors[i].rgbGreen = i * 255;
+ bitmap_info->bmiColors[i].rgbRed = i * 255;
+ bitmap_info->bmiColors[i].rgbReserved = 0;
+ }
+
+ break;
+ }
+
+ surface->dc = CreateCompatibleDC (original_dc);
+ if (!surface->dc)
+ goto FAIL;
+
+ surface->bitmap = CreateDIBSection (surface->dc,
+ bitmap_info,
+ DIB_RGB_COLORS,
+ &bits,
+ NULL, 0);
+ if (!surface->bitmap)
+ goto FAIL;
+
+ surface->is_dib = TRUE;
+
+ GdiFlush();
+
+ surface->saved_dc_bitmap = SelectObject (surface->dc,
+ surface->bitmap);
+ if (!surface->saved_dc_bitmap)
+ goto FAIL;
+
+ if (bitmap_info && num_palette > 2)
+ free (bitmap_info);
+
+ if (bits_out)
+ *bits_out = bits;
+
+ if (rowstride_out) {
+ /* Windows bitmaps are padded to 32-bit (dword) boundaries */
+ switch (format) {
+ case CAIRO_FORMAT_ARGB32:
+ case CAIRO_FORMAT_RGB24:
+ *rowstride_out = 4 * width;
+ break;
+
+ case CAIRO_FORMAT_A8:
+ *rowstride_out = (width + 3) & ~3;
+ break;
+
+ case CAIRO_FORMAT_A1:
+ *rowstride_out = ((width + 31) & ~31) / 8;
+ break;
+ }
+ }
+
+ surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ FAIL:
+ status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap");
+
+ if (bitmap_info && num_palette > 2)
+ free (bitmap_info);
+
+ if (surface->saved_dc_bitmap) {
+ SelectObject (surface->dc, surface->saved_dc_bitmap);
+ surface->saved_dc_bitmap = NULL;
+ }
+
+ if (surface->bitmap) {
+ DeleteObject (surface->bitmap);
+ surface->bitmap = NULL;
+ }
+
+ if (surface->dc) {
+ DeleteDC (surface->dc);
+ surface->dc = NULL;
+ }
+
+ return status;
+}
+
+static cairo_surface_t *
+_cairo_win32_surface_create_for_dc (HDC original_dc,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_status_t status;
+ cairo_win32_surface_t *surface;
+ unsigned char *bits;
+ int rowstride;
+
+ if (! CAIRO_FORMAT_VALID (format))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+
+ surface = malloc (sizeof (cairo_win32_surface_t));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ surface->clip_region = NULL;
+
+ status = _create_dc_and_bitmap (surface, original_dc, format,
+ width, height,
+ &bits, &rowstride);
+ if (status)
+ goto FAIL;
+
+ surface->image = cairo_image_surface_create_for_data (bits, format,
+ width, height, rowstride);
+ status = surface->image->status;
+ if (status)
+ goto FAIL;
+
+ surface->format = format;
+ surface->d3d9surface = NULL;
+
+ surface->clip_rect.x = 0;
+ surface->clip_rect.y = 0;
+ surface->clip_rect.width = width;
+ surface->clip_rect.height = height;
+
+ surface->initial_clip_rgn = NULL;
+ surface->had_simple_clip = FALSE;
+
+ surface->extents = surface->clip_rect;
+ surface->font_subsets = NULL;
+
+ _cairo_surface_init (&surface->base,
+ &cairo_win32_surface_backend,
+ NULL, /* device */
+ _cairo_content_from_format (format));
+
+ return &surface->base;
+
+ FAIL:
+ if (surface->bitmap) {
+ SelectObject (surface->dc, surface->saved_dc_bitmap);
+ DeleteObject (surface->bitmap);
+ DeleteDC (surface->dc);
+ }
+ free (surface);
+
+ return _cairo_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_win32_surface_create_similar_internal (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height,
+ cairo_bool_t force_dib)
+{
+ cairo_win32_surface_t *src = abstract_src;
+ cairo_format_t format = _cairo_format_from_content (content);
+ cairo_surface_t *new_surf = NULL;
+
+ /* We force a DIB always if:
+ * - we need alpha; or
+ * - the parent is a DIB; or
+ * - the parent is for printing (because we don't care about the bit depth at that point)
+ *
+ * We also might end up with a DIB even if a DDB is requested if DDB creation failed
+ * due to out of memory.
+ */
+ if (src->is_dib ||
+ (content & CAIRO_CONTENT_ALPHA) ||
+ src->base.backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING)
+ {
+ force_dib = TRUE;
+ }
+
+ if (!force_dib) {
+ /* try to create a ddb */
+ new_surf = cairo_win32_surface_create_with_ddb (src->dc, CAIRO_FORMAT_RGB24, width, height);
+
+ if (new_surf->status != CAIRO_STATUS_SUCCESS)
+ new_surf = NULL;
+ }
+
+ if (new_surf == NULL) {
+ new_surf = _cairo_win32_surface_create_for_dc (src->dc, format, width, height);
+ }
+
+ return new_surf;
+}
+
+cairo_surface_t *
+_cairo_win32_surface_create_similar (void *abstract_src,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ return _cairo_win32_surface_create_similar_internal (abstract_src, content, width, height, FALSE);
+}
+
+cairo_status_t
+_cairo_win32_surface_finish (void *abstract_surface)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+
+ if (surface->image)
+ cairo_surface_destroy (surface->image);
+
+ /* If we created the Bitmap and DC, destroy them */
+ if (surface->bitmap) {
+ SelectObject (surface->dc, surface->saved_dc_bitmap);
+ DeleteObject (surface->bitmap);
+ DeleteDC (surface->dc);
+ } else {
+ _cairo_win32_restore_initial_clip (surface);
+ }
+
+ if (surface->d3d9surface) {
+ IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc);
+ IDirect3DSurface9_Release (surface->d3d9surface);
+ }
+
+ if (surface->initial_clip_rgn)
+ DeleteObject (surface->initial_clip_rgn);
+
+ if (surface->font_subsets != NULL)
+ _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+get_d3d9_dc_and_clear_clip (cairo_win32_surface_t *surface)
+{
+ IDirect3DSurface9_GetDC (surface->d3d9surface, &surface->dc);
+ // The DC that we get back from the surface will not have
+ // a clip so clear surface->clip_region so that we don't think we have
+ // one when we don't.
+ _cairo_win32_surface_set_clip_region (surface, NULL);
+}
+
+static cairo_status_t
+_cairo_win32_surface_d3d9_lock_rect (cairo_win32_surface_t *surface,
+ int x,
+ int y,
+ int width,
+ int height,
+ cairo_image_surface_t **local_out)
+{
+ cairo_image_surface_t *local;
+ cairo_int_status_t status;
+
+ RECT rectin = { x, y, x+width, y+height };
+ D3DLOCKED_RECT rectout;
+ HRESULT hr;
+ hr = IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc);
+ hr = IDirect3DSurface9_LockRect (surface->d3d9surface,
+ &rectout, &rectin, 0);
+ surface->dc = 0; // Don't use the DC when this is locked!
+ if (hr) {
+ get_d3d9_dc_and_clear_clip (surface);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ local = cairo_image_surface_create_for_data (rectout.pBits,
+ surface->format,
+ width, height,
+ rectout.Pitch);
+ if (local == NULL) {
+ IDirect3DSurface9_UnlockRect (surface->d3d9surface);
+ get_d3d9_dc_and_clear_clip (surface);
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ if (local->base.status) {
+ IDirect3DSurface9_UnlockRect (surface->d3d9surface);
+ get_d3d9_dc_and_clear_clip (surface);
+ return local->base.status;
+ }
+
+ *local_out = local;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface,
+ int x,
+ int y,
+ int width,
+ int height,
+ cairo_win32_surface_t **local_out)
+{
+ cairo_win32_surface_t *local;
+ cairo_int_status_t status;
+ cairo_content_t content = _cairo_content_from_format (surface->format);
+
+ local =
+ (cairo_win32_surface_t *) _cairo_win32_surface_create_similar_internal
+ (surface, content, width, height, TRUE);
+ if (local == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (local->base.status)
+ return local->base.status;
+
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Only BitBlt if the source surface supports it. */
+ if ((surface->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) &&
+ BitBlt (local->dc,
+ 0, 0,
+ width, height,
+ surface->dc,
+ x, y,
+ SRCCOPY))
+ {
+ status = CAIRO_STATUS_SUCCESS;
+ }
+
+ if (status) {
+ /* If we failed here, most likely the source or dest doesn't
+ * support BitBlt/AlphaBlend (e.g. a printer).
+ * You can't reliably get bits from a printer DC, so just fill in
+ * the surface as white (common case for printing).
+ */
+
+ RECT r;
+ r.left = r.top = 0;
+ r.right = width;
+ r.bottom = height;
+ FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
+ }
+
+ *local_out = local;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface)
+{
+ cairo_win32_surface_t *new_surface;
+ int width = surface->extents.width + surface->extents.x;
+ int height = surface->extents.height + surface->extents.y;
+
+ BOOL ok;
+ HBITMAP oldbitmap;
+
+ new_surface = (cairo_win32_surface_t*)
+ _cairo_win32_surface_create_for_dc (surface->dc,
+ surface->format,
+ width,
+ height);
+
+ if (new_surface->base.status)
+ return;
+
+ /* DDB can't be 32bpp, so BitBlt is safe */
+ ok = BitBlt (new_surface->dc,
+ 0, 0, width, height,
+ surface->dc,
+ 0, 0, SRCCOPY);
+
+ if (!ok)
+ goto out;
+
+ /* Now swap around new_surface and surface's internal bitmap
+ * pointers. */
+ DeleteDC (new_surface->dc);
+ new_surface->dc = NULL;
+
+ oldbitmap = SelectObject (surface->dc, new_surface->bitmap);
+ DeleteObject (oldbitmap);
+
+ surface->image = new_surface->image;
+ surface->is_dib = new_surface->is_dib;
+ surface->bitmap = new_surface->bitmap;
+
+ new_surface->bitmap = NULL;
+ new_surface->image = NULL;
+
+ /* Finally update flags */
+ surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
+ out:
+ cairo_surface_destroy ((cairo_surface_t*)new_surface);
+}
+
+static cairo_status_t
+_cairo_win32_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (!surface->image && !surface->is_dib && surface->bitmap &&
+ (surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0)
+ {
+ /* This is a DDB, and we're being asked to use it as a source for
+ * something that we couldn't support natively. So turn it into
+ * a DIB, so that we have an equivalent image surface, as long
+ * as we're allowed to via flags.
+ */
+ _cairo_win32_convert_ddb_to_dib (surface);
+ }
+
+ if (surface->image) {
+ *image_out = (cairo_image_surface_t *)surface->image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (surface->d3d9surface) {
+ cairo_image_surface_t *local;
+ status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface, 0, 0,
+ surface->extents.width,
+ surface->extents.height, &local);
+ if (status)
+ return status;
+
+ *image_out = local;
+ *image_extra = surface;
+ } else {
+ cairo_win32_surface_t *local;
+ status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0,
+ surface->extents.width,
+ surface->extents.height, &local);
+ if (status)
+ return status;
+
+ *image_out = (cairo_image_surface_t *)local->image;
+ *image_extra = local;
+ }
+ // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points
+ // to the original surface to get back the d3d9surface and properly unlock.
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_win32_surface_t *local = image_extra;
+
+ if (local && local->d3d9surface) {
+ IDirect3DSurface9_UnlockRect (local->d3d9surface);
+ get_d3d9_dc_and_clear_clip (surface);
+ cairo_surface_destroy ((cairo_surface_t *)image);
+ } else {
+ cairo_surface_destroy ((cairo_surface_t *)local);
+ }
+}
+
+static cairo_status_t
+_cairo_win32_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->image) {
+ GdiFlush();
+
+ *image_out = (cairo_image_surface_t *) surface->image;
+ *image_extra = NULL;
+ *image_rect = surface->extents;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (surface->d3d9surface) {
+ cairo_image_surface_t *local = NULL;
+ status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface,
+ interest_rect->x,
+ interest_rect->y,
+ interest_rect->width,
+ interest_rect->height, &local);
+
+ if (status)
+ return status;
+
+ *image_out = local;
+ *image_extra = surface;
+ } else {
+ cairo_win32_surface_t *local = NULL;
+ status = _cairo_win32_surface_get_subimage (abstract_surface,
+ interest_rect->x,
+ interest_rect->y,
+ interest_rect->width,
+ interest_rect->height, &local);
+
+ if (status)
+ return status;
+
+ *image_out = (cairo_image_surface_t *) local->image;
+ *image_extra = local;
+ }
+ // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points
+ // to the original surface to get back the d3d9surface and properly unlock.
+
+ *image_rect = *interest_rect;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_win32_surface_t *local = image_extra;
+
+ if (!local)
+ return;
+
+ if (local->d3d9surface) {
+ IDirect3DSurface9_UnlockRect (local->d3d9surface);
+ get_d3d9_dc_and_clear_clip (surface);
+ cairo_surface_destroy ((cairo_surface_t *)image);
+ } else {
+
+ /* clear any clip that's currently set on the surface
+ so that we can blit uninhibited. */
+ _cairo_win32_surface_set_clip_region (surface, NULL);
+
+ if (!BitBlt (surface->dc,
+ image_rect->x, image_rect->y,
+ image_rect->width, image_rect->height,
+ local->dc,
+ 0, 0,
+ SRCCOPY))
+ _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image");
+
+ cairo_surface_destroy ((cairo_surface_t *)local);
+ }
+
+}
+
+cairo_status_t
+_cairo_win32_surface_set_clip_region (void *abstract_surface,
+ cairo_region_t *region)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (surface->clip_region == region)
+ return CAIRO_STATUS_SUCCESS;
+
+ cairo_region_destroy (surface->clip_region);
+ surface->clip_region = cairo_region_reference (region);
+
+ /* The semantics we want is that any clip set by cairo combines
+ * is intersected with the clip on device context that the
+ * surface was created for. To implement this, we need to
+ * save the original clip when first setting a clip on surface.
+ */
+
+ /* Clear any clip set by cairo, return to the original first */
+ status = _cairo_win32_restore_initial_clip (surface);
+
+ /* Then combine any new region with it */
+ if (region) {
+ cairo_rectangle_int_t extents;
+ int num_rects;
+ RGNDATA *data;
+ size_t data_size;
+ RECT *rects;
+ int i;
+ HRGN gdi_region;
+
+ /* Create a GDI region for the cairo region */
+
+ cairo_region_get_extents (region, &extents);
+ num_rects = cairo_region_num_rectangles (region);
+ /* XXX see notes in _cairo_win32_save_initial_clip --
+ * this code will interact badly with a HDC which had an initial
+ * world transform -- we should probably manually transform the
+ * region rects, because SelectClipRgn takes device units, not
+ * logical units (unlike IntersectClipRect).
+ */
+
+ data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT);
+ data = malloc (data_size);
+ if (!data)
+ return _cairo_error(CAIRO_STATUS_NO_MEMORY);
+ rects = (RECT *)data->Buffer;
+
+ data->rdh.dwSize = sizeof (RGNDATAHEADER);
+ data->rdh.iType = RDH_RECTANGLES;
+ data->rdh.nCount = num_rects;
+ data->rdh.nRgnSize = num_rects * sizeof (RECT);
+ data->rdh.rcBound.left = extents.x;
+ data->rdh.rcBound.top = extents.y;
+ data->rdh.rcBound.right = extents.x + extents.width;
+ data->rdh.rcBound.bottom = extents.y + extents.height;
+
+ for (i = 0; i < num_rects; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (region, i, &rect);
+
+ rects[i].left = rect.x;
+ rects[i].top = rect.y;
+ rects[i].right = rect.x + rect.width;
+ rects[i].bottom = rect.y + rect.height;
+ }
+
+ gdi_region = ExtCreateRegion (NULL, data_size, data);
+ free (data);
+
+ if (!gdi_region)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* AND the new region into our DC */
+ if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR)
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region");
+
+ DeleteObject (gdi_region);
+ }
+
+ return status;
+}
+
+#if !defined(AC_SRC_OVER)
+#define AC_SRC_OVER 0x00
+#pragma pack(1)
+typedef struct {
+ BYTE BlendOp;
+ BYTE BlendFlags;
+ BYTE SourceConstantAlpha;
+ BYTE AlphaFormat;
+}BLENDFUNCTION;
+#pragma pack()
+#endif
+
+/* for compatibility with VC++ 6 */
+#ifndef AC_SRC_ALPHA
+#define AC_SRC_ALPHA 0x01
+#endif
+
+typedef BOOL (WINAPI *cairo_alpha_blend_func_t) (HDC hdcDest,
+ int nXOriginDest,
+ int nYOriginDest,
+ int nWidthDest,
+ int hHeightDest,
+ HDC hdcSrc,
+ int nXOriginSrc,
+ int nYOriginSrc,
+ int nWidthSrc,
+ int nHeightSrc,
+ BLENDFUNCTION blendFunction);
+
+static cairo_int_status_t
+_composite_alpha_blend (cairo_win32_surface_t *dst,
+ cairo_win32_surface_t *src,
+ int alpha,
+ int src_x,
+ int src_y,
+ int src_w,
+ int src_h,
+ int dst_x,
+ int dst_y,
+ int dst_w,
+ int dst_h)
+{
+ static unsigned alpha_blend_checked = FALSE;
+ static cairo_alpha_blend_func_t alpha_blend = NULL;
+
+ BLENDFUNCTION blend_function;
+
+ /* Check for AlphaBlend dynamically to allow compiling on
+ * MSVC 6 and use on older windows versions
+ */
+ if (!alpha_blend_checked) {
+ OSVERSIONINFO os;
+
+ os.dwOSVersionInfoSize = sizeof (os);
+ GetVersionEx (&os);
+
+ /* If running on Win98, disable using AlphaBlend()
+ * to avoid Win98 AlphaBlend() bug */
+ if (VER_PLATFORM_WIN32_WINDOWS != os.dwPlatformId ||
+ os.dwMajorVersion != 4 || os.dwMinorVersion != 10)
+ {
+ HMODULE msimg32_dll = LoadLibraryW (L"msimg32");
+
+ if (msimg32_dll != NULL)
+ alpha_blend = (cairo_alpha_blend_func_t)GetProcAddress (msimg32_dll,
+ "AlphaBlend");
+ }
+
+ alpha_blend_checked = TRUE;
+ }
+
+ if (alpha_blend == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (!(dst->flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ blend_function.BlendOp = AC_SRC_OVER;
+ blend_function.BlendFlags = 0;
+ blend_function.SourceConstantAlpha = alpha;
+ blend_function.AlphaFormat = (src->format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0;
+
+ if (!alpha_blend (dst->dc,
+ dst_x, dst_y,
+ dst_w, dst_h,
+ src->dc,
+ src_x, src_y,
+ src_w, src_h,
+ blend_function))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(AlphaBlend)");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* makes the alpha channel in a RGB24 surface 0xff */
+static void
+make_opaque (cairo_image_surface_t *image, cairo_rectangle_int_t src_r)
+{
+ int x; int y;
+ for (y = 0; y < src_r.height; y++) {
+ for (x = 0; x < src_r.width; x++) {
+ image->data[(src_r.y + y) * image->stride + (src_r.x + x)*4 + 3] = 0xff;
+ }
+ }
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_composite_inner (cairo_win32_surface_t *src,
+ cairo_image_surface_t *src_image,
+ cairo_win32_surface_t *dst,
+ cairo_rectangle_int_t src_extents,
+ cairo_rectangle_int_t src_r,
+ cairo_rectangle_int_t dst_r,
+ int alpha,
+ cairo_bool_t needs_alpha,
+ cairo_bool_t needs_scale)
+{
+ /* Then do BitBlt, StretchDIBits, StretchBlt, AlphaBlend, or MaskBlt */
+ if (src_image) {
+ if (needs_alpha || needs_scale)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) {
+ BITMAPINFO bi;
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biWidth = src_image->width;
+ bi.bmiHeader.biHeight = - src_image->height;
+ bi.bmiHeader.biSizeImage = 0;
+ bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
+ bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
+ bi.bmiHeader.biPlanes = 1;
+ bi.bmiHeader.biBitCount = 32;
+ bi.bmiHeader.biCompression = BI_RGB;
+ bi.bmiHeader.biClrUsed = 0;
+ bi.bmiHeader.biClrImportant = 0;
+
+ /* StretchDIBits is broken with top-down dibs; you need to do some
+ * special munging to make the coordinate space work (basically,
+ * need to address everything based on the bottom left, instead of top left,
+ * and need to tell it to flip the resulting image.
+ *
+ * See http://blog.vlad1.com/archives/2006/10/26/134/ and comments.
+ */
+ if (!StretchDIBits (dst->dc,
+ /* dst x,y,w,h */
+ dst_r.x, dst_r.y + dst_r.height - 1,
+ dst_r.width, - (int) dst_r.height,
+ /* src x,y,w,h */
+ src_r.x, src_extents.height - src_r.y + 1,
+ src_r.width, - (int) src_r.height,
+ src_image->data,
+ &bi,
+ DIB_RGB_COLORS,
+ SRCCOPY))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(StretchDIBits)");
+ }
+ } else if (!needs_alpha) {
+ if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) {
+ /* Because we store RGB24 & ARGB32 in the same way GDI has no way
+ * to ignore the alpha channel from a RGB24 source. Therefore, we set
+ * the alpha channel in our RGB24 source to opaque so that we can treat
+ * it like ARGB32. */
+ GdiFlush();
+ make_opaque(src->image, src_r);
+ }
+ /* BitBlt or StretchBlt? */
+ if (!needs_scale && (dst->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)) {
+ if (!BitBlt (dst->dc,
+ dst_r.x, dst_r.y,
+ dst_r.width, dst_r.height,
+ src->dc,
+ src_r.x, src_r.y,
+ SRCCOPY))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(BitBlt)");
+ } else if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) {
+ /* StretchBlt? */
+ /* XXX check if we want HALFTONE, based on the src filter */
+ BOOL success;
+ int oldmode = SetStretchBltMode(dst->dc, HALFTONE);
+ success = StretchBlt(dst->dc,
+ dst_r.x, dst_r.y,
+ dst_r.width, dst_r.height,
+ src->dc,
+ src_r.x, src_r.y,
+ src_r.width, src_r.height,
+ SRCCOPY);
+ SetStretchBltMode(dst->dc, oldmode);
+
+ if (!success)
+ return _cairo_win32_print_gdi_error ("StretchBlt");
+ }
+ } else if (needs_alpha && !needs_scale) {
+ RECT r = {0, 0, 5000, 5000};
+ //FillRect(dst->dc, &r, GetStockObject(DKGRAY_BRUSH));
+ return _composite_alpha_blend (dst, src, alpha,
+ src_r.x, src_r.y, src_r.width, src_r.height,
+ dst_r.x, dst_r.y, dst_r.width, dst_r.height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* from pixman-private.h */
+#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b))
+
+static cairo_int_status_t
+_cairo_win32_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ const cairo_pattern_t *mask_pattern,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_win32_surface_t *dst = abstract_dst;
+ cairo_win32_surface_t *src;
+ cairo_surface_pattern_t *src_surface_pattern;
+ int alpha;
+ double scalex, scaley;
+ cairo_fixed_t x0_fixed, y0_fixed;
+ cairo_int_status_t status;
+
+ cairo_bool_t needs_alpha, needs_scale, needs_repeat, needs_pad;
+ cairo_image_surface_t *src_image = NULL;
+
+ cairo_format_t src_format;
+ cairo_rectangle_int_t src_extents;
+
+ cairo_rectangle_int_t src_r = { src_x, src_y, width, height };
+ cairo_rectangle_int_t dst_r = { dst_x, dst_y, width, height };
+
+#ifdef DEBUG_COMPOSITE
+ fprintf (stderr, "+++ composite: %d %p %p %p [%d %d] [%d %d] [%d %d] %dx%d\n",
+ op, pattern, mask_pattern, abstract_dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height);
+#endif
+
+ /* If the destination can't do any of these, then
+ * we may as well give up, since this is what we'll
+ * look to for optimization.
+ */
+ if ((dst->flags & (CAIRO_WIN32_SURFACE_CAN_BITBLT |
+ CAIRO_WIN32_SURFACE_CAN_ALPHABLEND |
+ CAIRO_WIN32_SURFACE_CAN_STRETCHBLT |
+ CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+ == 0)
+ {
+ goto UNSUPPORTED;
+ }
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ goto UNSUPPORTED;
+
+ if (pattern->extend != CAIRO_EXTEND_NONE &&
+ pattern->extend != CAIRO_EXTEND_REPEAT &&
+ pattern->extend != CAIRO_EXTEND_PAD)
+ goto UNSUPPORTED;
+
+ if (mask_pattern) {
+ /* FIXME: When we fully support RENDER style 4-channel
+ * masks we need to check r/g/b != 1.0.
+ */
+ if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8;
+ } else {
+ alpha = 255;
+ }
+
+ src_surface_pattern = (cairo_surface_pattern_t *)pattern;
+ src = (cairo_win32_surface_t *)src_surface_pattern->surface;
+
+ if (src->base.type == CAIRO_SURFACE_TYPE_IMAGE &&
+ dst->flags & (CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+ {
+ /* In some very limited cases, we can use StretchDIBits to draw
+ * an image surface directly:
+ * - source is CAIRO_FORMAT_ARGB32
+ * - dest is CAIRO_FORMAT_ARGB32
+ * - alpha is 255
+ * - operator is SOURCE or OVER
+ * - image stride is 4*width
+ */
+ src_image = (cairo_image_surface_t*) src;
+
+ if (src_image->format != CAIRO_FORMAT_RGB24 ||
+ dst->format != CAIRO_FORMAT_RGB24 ||
+ alpha != 255 ||
+ (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) ||
+ src_image->stride != (src_image->width * 4))
+ {
+ goto UNSUPPORTED;
+ }
+
+ src_format = src_image->format;
+ src_extents.x = 0;
+ src_extents.y = 0;
+ src_extents.width = src_image->width;
+ src_extents.height = src_image->height;
+ } else if (src->base.backend != dst->base.backend) {
+ goto UNSUPPORTED;
+ } else {
+ src_format = src->format;
+ src_extents = src->extents;
+ }
+
+
+#ifdef DEBUG_COMPOSITE
+ fprintf (stderr, "Before check: src size: (%d %d) xy [%d %d] -> dst [%d %d %d %d] {srcmat %f %f %f %f}\n",
+ src_extents.width, src_extents.height,
+ src_x, src_y,
+ dst_x, dst_y, width, height,
+ pattern->matrix.x0, pattern->matrix.y0, pattern->matrix.xx, pattern->matrix.yy);
+#endif
+
+ /* We can only use GDI functions if the source and destination rectangles
+ * are on integer pixel boundaries. Figure that out here.
+ */
+ x0_fixed = _cairo_fixed_from_double(pattern->matrix.x0 / pattern->matrix.xx);
+ y0_fixed = _cairo_fixed_from_double(pattern->matrix.y0 / pattern->matrix.yy);
+
+ if (pattern->matrix.yx != 0.0 ||
+ pattern->matrix.xy != 0.0 ||
+ !_cairo_fixed_is_integer(x0_fixed) ||
+ !_cairo_fixed_is_integer(y0_fixed))
+ {
+ goto UNSUPPORTED;
+ }
+
+ scalex = pattern->matrix.xx;
+ scaley = pattern->matrix.yy;
+
+ src_r.x += _cairo_fixed_integer_part(x0_fixed);
+ src_r.y += _cairo_fixed_integer_part(y0_fixed);
+
+ /* Success, right? */
+ if (scalex == 0.0 || scaley == 0.0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (scalex != 1.0 || scaley != 1.0)
+ goto UNSUPPORTED;
+
+ /* If the src coordinates are outside of the source surface bounds,
+ * we have to fix them up, because this is an error for the GDI
+ * functions.
+ */
+
+#ifdef DEBUG_COMPOSITE
+ fprintf (stderr, "before: [%d %d %d %d] -> [%d %d %d %d]\n",
+ src_r.x, src_r.y, src_r.width, src_r.height,
+ dst_r.x, dst_r.y, dst_r.width, dst_r.height);
+ fflush (stderr);
+#endif
+
+ /* If the src rectangle doesn't wholly lie within the src extents,
+ * fudge things. We really need to do fixup on the unpainted
+ * region -- e.g. the SOURCE operator is broken for areas outside
+ * of the extents, because it won't clear that area to transparent
+ * black.
+ */
+
+ needs_pad = FALSE;
+ if (pattern->extend != CAIRO_EXTEND_REPEAT) {
+ needs_repeat = FALSE;
+
+ /* If the src rect and the extents of the source image don't overlap at all,
+ * we can't do anything useful here.
+ */
+ if (src_r.x > src_extents.width || src_r.y > src_extents.height ||
+ (src_r.x + src_r.width) < 0 || (src_r.y + src_r.height) < 0)
+ {
+ if (op == CAIRO_OPERATOR_OVER)
+ return CAIRO_STATUS_SUCCESS;
+ goto UNSUPPORTED;
+ }
+
+ if (src_r.x < 0) {
+ src_r.width += src_r.x;
+
+ dst_r.width += src_r.x;
+ dst_r.x -= src_r.x;
+
+ src_r.x = 0;
+ needs_pad = TRUE;
+ }
+
+ if (src_r.y < 0) {
+ src_r.height += src_r.y;
+
+ dst_r.height += src_r.y;
+ dst_r.y -= src_r.y;
+
+ src_r.y = 0;
+ needs_pad = TRUE;
+ }
+
+ if (src_r.x + src_r.width > src_extents.width) {
+ src_r.width = src_extents.width - src_r.x;
+ dst_r.width = src_r.width;
+ needs_pad = TRUE;
+ }
+
+ if (src_r.y + src_r.height > src_extents.height) {
+ src_r.height = src_extents.height - src_r.y;
+ dst_r.height = src_r.height;
+ needs_pad = TRUE;
+ }
+ } else {
+ needs_repeat = TRUE;
+ }
+
+ if (pattern->extend == CAIRO_EXTEND_PAD && needs_pad) {
+ goto UNSUPPORTED;
+ }
+
+ /*
+ * Operations that we can do:
+ *
+ * AlphaBlend uses the following formula for alpha when not use the per-pixel alpha (AlphaFormat = 0)
+ * Dst.Alpha = Src.Alpha * (SCA/255.0) + Dst.Alpha * (1.0 - (SCA/255.0))
+ * This turns into Dst.Alpha = Src.Alpha when SCA = 255.
+ * (http://msdn.microsoft.com/en-us/library/aa921335.aspx)
+ *
+ * RGB OVER RGB -> BitBlt (same as SOURCE)
+ * RGB OVER ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB
+ * by setting the alpha values of the source to 255.
+ * ARGB OVER ARGB -> AlphaBlend, with AC_SRC_ALPHA
+ * ARGB OVER RGB -> AlphaBlend, with AC_SRC_ALPHA; we'll have junk in the dst A byte
+ *
+ * RGB OVER RGB + mask -> AlphaBlend, no AC_SRC_ALPHA
+ * RGB OVER ARGB + mask -> Partially supported, We convert this operation into a ARGB OVER ARGB + mask
+ * by setting the alpha values of the source to 255.
+ * ARGB OVER ARGB + mask -> AlphaBlend, with AC_SRC_ALPHA
+ * ARGB OVER RGB + mask -> AlphaBlend, with AC_SRC_ALPHA; junk in the dst A byte
+ *
+ * RGB SOURCE RGB -> BitBlt
+ * RGB SOURCE ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB
+ * by setting the alpha values of the source to 255.
+ * ARGB SOURCE ARGB -> BitBlt
+ * ARGB SOURCE RGB -> BitBlt
+ *
+ * RGB SOURCE RGB + mask -> unsupported
+ * RGB SOURCE ARGB + mask -> unsupported
+ * ARGB SOURCE ARGB + mask -> unsupported
+ * ARGB SOURCE RGB + mask -> unsupported
+ */
+
+ /*
+ * Figure out what action to take.
+ */
+ if (op == CAIRO_OPERATOR_OVER) {
+ if (alpha == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (src_format == dst->format) {
+ if (alpha == 255 && src_format == CAIRO_FORMAT_RGB24) {
+ needs_alpha = FALSE;
+ } else {
+ needs_alpha = TRUE;
+ }
+ } else if (src_format == CAIRO_FORMAT_ARGB32 &&
+ dst->format == CAIRO_FORMAT_RGB24)
+ {
+ needs_alpha = TRUE;
+ } else if (src_format == CAIRO_FORMAT_RGB24 &&
+ dst->format == CAIRO_FORMAT_ARGB32 &&
+ src->image)
+ {
+ if (alpha == 255) {
+ needs_alpha = FALSE;
+ } else {
+ needs_alpha = TRUE;
+ }
+ } else {
+ goto UNSUPPORTED;
+ }
+ } else if (alpha == 255 && op == CAIRO_OPERATOR_SOURCE) {
+ if ((src_format == dst->format) ||
+ (src_format == CAIRO_FORMAT_ARGB32 && dst->format == CAIRO_FORMAT_RGB24) ||
+ (src_format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32 && src->image))
+ {
+ needs_alpha = FALSE;
+ } else {
+ goto UNSUPPORTED;
+ }
+ } else {
+ goto UNSUPPORTED;
+ }
+
+ if (scalex == 1.0 && scaley == 1.0) {
+ needs_scale = FALSE;
+ } else {
+ /* Should never be reached until we turn StretchBlt back on */
+ needs_scale = TRUE;
+ }
+
+#ifdef DEBUG_COMPOSITE
+ fprintf (stderr, "action: [%d %d %d %d] -> [%d %d %d %d]\n",
+ src_r.x, src_r.y, src_r.width, src_r.height,
+ dst_r.x, dst_r.y, dst_r.width, dst_r.height);
+ fflush (stderr);
+#endif
+
+ status = _cairo_win32_surface_set_clip_region (dst, clip_region);
+ if (status)
+ return status;
+
+ /* If we need to repeat, we turn the repeated blit into
+ * a bunch of piece-by-piece blits.
+ */
+ if (needs_repeat) {
+ cairo_rectangle_int_t piece_src_r, piece_dst_r;
+ uint32_t rendered_width = 0, rendered_height = 0;
+ uint32_t to_render_height, to_render_width;
+ int32_t piece_x, piece_y;
+ int32_t src_start_x = MOD(src_r.x, src_extents.width);
+ int32_t src_start_y = MOD(src_r.y, src_extents.height);
+
+ if (needs_scale)
+ goto UNSUPPORTED;
+
+ /* If both the src and dest have an image, we may as well fall
+ * back, because it will be faster than our separate blits.
+ * Our blit code will be fastest when the src is a DDB and the
+ * destination is a DDB.
+ */
+ if ((src_image || src->image) && dst->image)
+ goto UNSUPPORTED;
+
+ /* If the src is not a bitmap but an on-screen (or unknown)
+ * DC, chances are that fallback will be faster.
+ */
+ if (src->bitmap == NULL)
+ goto UNSUPPORTED;
+
+ /* If we can use PatBlt, just do so */
+ if (!src_image && !needs_alpha)
+ {
+ HBRUSH brush;
+ HGDIOBJ old_brush;
+ POINT old_brush_origin;
+
+ /* Set up the brush with our bitmap */
+ brush = CreatePatternBrush (src->bitmap);
+
+ /* SetBrushOrgEx sets the coordinates in the destination DC of where the
+ * pattern should start.
+ */
+ SetBrushOrgEx (dst->dc, dst_r.x - src_start_x,
+ dst_r.y - src_start_y, &old_brush_origin);
+
+ old_brush = SelectObject (dst->dc, brush);
+
+ PatBlt (dst->dc, dst_r.x, dst_r.y, dst_r.width, dst_r.height, PATCOPY);
+
+ /* Restore the old brush and pen */
+ SetBrushOrgEx (dst->dc, old_brush_origin.x, old_brush_origin.y, NULL);
+ SelectObject (dst->dc, old_brush);
+ DeleteObject (brush);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /* If we were not able to use PatBlt, then manually expand out the blit */
+
+ /* Arbitrary factor; we think that going through
+ * fallback will be faster if we have to do more
+ * than this amount of blits in either direction.
+ */
+ if (dst_r.width / src_extents.width > 5 ||
+ dst_r.height / src_extents.height > 5)
+ goto UNSUPPORTED;
+
+ for (rendered_height = 0;
+ rendered_height < dst_r.height;
+ rendered_height += to_render_height)
+ {
+ piece_y = (src_start_y + rendered_height) % src_extents.height;
+ to_render_height = src_extents.height - piece_y;
+
+ if (rendered_height + to_render_height > dst_r.height)
+ to_render_height = dst_r.height - rendered_height;
+
+ for (rendered_width = 0;
+ rendered_width < dst_r.width;
+ rendered_width += to_render_width)
+ {
+ piece_x = (src_start_x + rendered_width) % src_extents.width;
+ to_render_width = src_extents.width - piece_x;
+
+ if (rendered_width + to_render_width > dst_r.width)
+ to_render_width = dst_r.width - rendered_width;
+
+ piece_src_r.x = piece_x;
+ piece_src_r.y = piece_y;
+ piece_src_r.width = to_render_width;
+ piece_src_r.height = to_render_height;
+
+ piece_dst_r.x = dst_r.x + rendered_width;
+ piece_dst_r.y = dst_r.y + rendered_height;
+ piece_dst_r.width = to_render_width;
+ piece_dst_r.height = to_render_height;
+
+ status = _cairo_win32_surface_composite_inner (src, src_image, dst,
+ src_extents, piece_src_r, piece_dst_r,
+ alpha, needs_alpha, needs_scale);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ /* Uh oh. If something failed, and it's the first
+ * piece, then we can jump to UNSUPPORTED.
+ * Otherwise, this is bad times, because part of the
+ * rendering was already done. */
+ if (rendered_width == 0 &&
+ rendered_height == 0)
+ {
+ goto UNSUPPORTED;
+ }
+
+ return status;
+ }
+ }
+ }
+ } else {
+ status = _cairo_win32_surface_composite_inner (src, src_image, dst,
+ src_extents, src_r, dst_r,
+ alpha, needs_alpha, needs_scale);
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS)
+ return status;
+
+UNSUPPORTED:
+ /* Fall back to image surface directly, if this is a DIB surface */
+ if (dst->image) {
+ GdiFlush();
+
+ return dst->image->backend->composite (op, pattern, mask_pattern,
+ dst->image,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y,
+ width, height,
+ clip_region);
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+/* This big function tells us how to optimize operators for the
+ * case of solid destination and constant-alpha source
+ *
+ * Note: This function needs revisiting if we add support for
+ * super-luminescent colors (a == 0, r,g,b > 0)
+ */
+static enum { DO_CLEAR, DO_SOURCE, DO_NOTHING, DO_UNSUPPORTED }
+categorize_solid_dest_operator (cairo_operator_t op,
+ unsigned short alpha)
+{
+ enum { SOURCE_TRANSPARENT, SOURCE_LIGHT, SOURCE_SOLID, SOURCE_OTHER } source;
+
+ if (alpha >= 0xff00)
+ source = SOURCE_SOLID;
+ else if (alpha < 0x100)
+ source = SOURCE_TRANSPARENT;
+ else
+ source = SOURCE_OTHER;
+
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR: /* 0 0 */
+ case CAIRO_OPERATOR_OUT: /* 1 - Ab 0 */
+ return DO_CLEAR;
+ break;
+
+ case CAIRO_OPERATOR_SOURCE: /* 1 0 */
+ case CAIRO_OPERATOR_IN: /* Ab 0 */
+ return DO_SOURCE;
+ break;
+
+ case CAIRO_OPERATOR_OVER: /* 1 1 - Aa */
+ case CAIRO_OPERATOR_ATOP: /* Ab 1 - Aa */
+ if (source == SOURCE_SOLID)
+ return DO_SOURCE;
+ else if (source == SOURCE_TRANSPARENT)
+ return DO_NOTHING;
+ else
+ return DO_UNSUPPORTED;
+ break;
+
+ case CAIRO_OPERATOR_DEST_OUT: /* 0 1 - Aa */
+ case CAIRO_OPERATOR_XOR: /* 1 - Ab 1 - Aa */
+ if (source == SOURCE_SOLID)
+ return DO_CLEAR;
+ else if (source == SOURCE_TRANSPARENT)
+ return DO_NOTHING;
+ else
+ return DO_UNSUPPORTED;
+ break;
+
+ case CAIRO_OPERATOR_DEST: /* 0 1 */
+ case CAIRO_OPERATOR_DEST_OVER:/* 1 - Ab 1 */
+ case CAIRO_OPERATOR_SATURATE: /* min(1,(1-Ab)/Aa) 1 */
+ return DO_NOTHING;
+ break;
+
+ case CAIRO_OPERATOR_DEST_IN: /* 0 Aa */
+ case CAIRO_OPERATOR_DEST_ATOP:/* 1 - Ab Aa */
+ if (source == SOURCE_SOLID)
+ return DO_NOTHING;
+ else if (source == SOURCE_TRANSPARENT)
+ return DO_CLEAR;
+ else
+ return DO_UNSUPPORTED;
+ break;
+
+ case CAIRO_OPERATOR_ADD: /* 1 1 */
+ if (source == SOURCE_TRANSPARENT)
+ return DO_NOTHING;
+ else
+ return DO_UNSUPPORTED;
+ break;
+ }
+
+ ASSERT_NOT_REACHED;
+ return DO_UNSUPPORTED;
+}
+
+static cairo_status_t
+_cairo_win32_surface_fill_rectangles_stretchdib (HDC dc,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rect,
+ int num_rects)
+{
+ BITMAPINFO bi;
+ int pixel = ((color->alpha_short >> 8) << 24) |
+ ((color->red_short >> 8) << 16) |
+ ((color->green_short >> 8) << 8) |
+ (color->blue_short >> 8);
+ int i;
+
+ /* Experiments suggest that it's impossible to use FillRect to set the alpha value
+ of a Win32 HDC for a transparent window. So instead we use StretchDIBits of a single
+ pixel, which does work. */
+ bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bi.bmiHeader.biWidth = 1;
+ bi.bmiHeader.biHeight = 1;
+ bi.bmiHeader.biSizeImage = 0;
+ bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
+ bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
+ bi.bmiHeader.biPlanes = 1;
+ bi.bmiHeader.biBitCount = 32;
+ bi.bmiHeader.biCompression = BI_RGB;
+ bi.bmiHeader.biClrUsed = 0;
+ bi.bmiHeader.biClrImportant = 0;
+
+ for (i = 0; i < num_rects; i++) {
+ if (!StretchDIBits (dc,
+ /* dst x,y,w,h */
+ rect[i].x, rect[i].y, rect[i].width, rect[i].height,
+ /* src x,y,w,h */
+ 0, 0, 1, 1,
+ &pixel,
+ &bi,
+ DIB_RGB_COLORS,
+ SRCCOPY))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles_stretchdib(StretchDIBits)");
+ }
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+ COLORREF new_color;
+ HBRUSH new_brush;
+ int i;
+
+ status = _cairo_win32_surface_set_clip_region (surface, NULL);
+ if (status)
+ return status;
+
+ if (surface->format == CAIRO_FORMAT_ARGB32 &&
+ (surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB) &&
+ (op == CAIRO_OPERATOR_SOURCE ||
+ (op == CAIRO_OPERATOR_OVER && color->alpha_short >= 0xff00))) {
+ return _cairo_win32_surface_fill_rectangles_stretchdib (surface->dc,
+ color, rects, num_rects);
+ }
+ if (surface->format != CAIRO_FORMAT_RGB24)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all
+ * surfaces with alpha.)
+ */
+ switch (categorize_solid_dest_operator (op, color->alpha_short)) {
+ case DO_CLEAR:
+ new_color = RGB (0, 0, 0);
+ break;
+ case DO_SOURCE:
+ new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8);
+ break;
+ case DO_NOTHING:
+ return CAIRO_STATUS_SUCCESS;
+ case DO_UNSUPPORTED:
+ default:
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ new_brush = CreateSolidBrush (new_color);
+ if (!new_brush)
+ return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles");
+
+ for (i = 0; i < num_rects; i++) {
+ RECT rect;
+
+ rect.left = rects[i].x;
+ rect.top = rects[i].y;
+ rect.right = rects[i].x + rects[i].width;
+ rect.bottom = rects[i].y + rects[i].height;
+
+ if (!FillRect (surface->dc, &rect, new_brush))
+ goto FAIL;
+ }
+
+ DeleteObject (new_brush);
+
+ return CAIRO_STATUS_SUCCESS;
+
+ FAIL:
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles");
+
+ DeleteObject (new_brush);
+
+ return status;
+}
+
+cairo_bool_t
+_cairo_win32_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+
+ *rectangle = surface->extents;
+ return TRUE;
+}
+
+static cairo_status_t
+_cairo_win32_surface_flush (void *abstract_surface)
+{
+ return _cairo_win32_surface_set_clip_region (abstract_surface, NULL);
+}
+
+#define STACK_GLYPH_SIZE 256
+
+cairo_int_status_t
+_cairo_win32_surface_show_glyphs_internal (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs,
+ cairo_bool_t glyph_indexing)
+{
+#ifdef CAIRO_HAS_WIN32_FONT
+ if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) {
+#ifdef CAIRO_HAS_DWRITE_FONT
+ return _cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip);
+#endif
+ } else {
+ cairo_win32_surface_t *dst = surface;
+
+ WORD glyph_buf_stack[STACK_GLYPH_SIZE];
+ WORD *glyph_buf = glyph_buf_stack;
+ int dxy_buf_stack[2 * STACK_GLYPH_SIZE];
+ int *dxy_buf = dxy_buf_stack;
+
+ BOOL win_result = 0;
+ int i, j;
+
+ cairo_solid_pattern_t *solid_pattern;
+ COLORREF color;
+
+ cairo_matrix_t device_to_logical;
+
+ int start_x, start_y;
+ double user_x, user_y;
+ int logical_x, logical_y;
+ unsigned int glyph_index_option;
+
+ /* We can only handle win32 fonts */
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* We can only handle opaque solid color sources */
+ if (!_cairo_pattern_is_opaque_solid(source))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* We can only handle operator SOURCE or OVER with the destination
+ * having no alpha */
+ if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) ||
+ (dst->format != CAIRO_FORMAT_RGB24))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* If we have a fallback mask clip set on the dst, we have
+ * to go through the fallback path, but only if we're not
+ * doing this for printing */
+ if (clip != NULL) {
+ if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) {
+ cairo_region_t *clip_region;
+ cairo_status_t status;
+
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+ if (status)
+ return status;
+
+ _cairo_win32_surface_set_clip_region (surface, clip_region);
+ }
+ } else {
+ _cairo_win32_surface_set_clip_region (surface, NULL);
+ }
+
+ solid_pattern = (cairo_solid_pattern_t *)source;
+ color = RGB(((int)solid_pattern->color.red_short) >> 8,
+ ((int)solid_pattern->color.green_short) >> 8,
+ ((int)solid_pattern->color.blue_short) >> 8);
+
+ cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical);
+
+ SaveDC(dst->dc);
+
+ cairo_win32_scaled_font_select_font(scaled_font, dst->dc);
+ SetTextColor(dst->dc, color);
+ SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT);
+ SetBkMode(dst->dc, TRANSPARENT);
+
+ if (num_glyphs > STACK_GLYPH_SIZE) {
+ glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD));
+ dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2);
+ }
+
+ /* It is vital that dx values for dxy_buf are calculated from the delta of
+ * _logical_ x coordinates (not user x coordinates) or else the sum of all
+ * previous dx values may start to diverge from the current glyph's x
+ * coordinate due to accumulated rounding error. As a result strings could
+ * be painted shorter or longer than expected. */
+
+ user_x = glyphs[0].x;
+ user_y = glyphs[0].y;
+
+ cairo_matrix_transform_point(&device_to_logical,
+ &user_x, &user_y);
+
+ logical_x = _cairo_lround (user_x);
+ logical_y = _cairo_lround (user_y);
+
+ start_x = logical_x;
+ start_y = logical_y;
+
+ for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) {
+ glyph_buf[i] = (WORD) glyphs[i].index;
+ if (i == num_glyphs - 1) {
+ dxy_buf[j] = 0;
+ dxy_buf[j+1] = 0;
+ } else {
+ double next_user_x = glyphs[i+1].x;
+ double next_user_y = glyphs[i+1].y;
+ int next_logical_x, next_logical_y;
+
+ cairo_matrix_transform_point(&device_to_logical,
+ &next_user_x, &next_user_y);
+
+ next_logical_x = _cairo_lround (next_user_x);
+ next_logical_y = _cairo_lround (next_user_y);
+
+ dxy_buf[j] = _cairo_lround (next_logical_x - logical_x);
+ dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y);
+ /* note that GDI coordinate system is inverted */
+
+ logical_x = next_logical_x;
+ logical_y = next_logical_y;
+ }
+ }
+
+ if (glyph_indexing)
+ glyph_index_option = ETO_GLYPH_INDEX;
+ else
+ glyph_index_option = 0;
+
+ win_result = ExtTextOutW(dst->dc,
+ start_x,
+ start_y,
+ glyph_index_option | ETO_PDY,
+ NULL,
+ glyph_buf,
+ num_glyphs,
+ dxy_buf);
+ if (!win_result) {
+ _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)");
+ }
+
+ RestoreDC(dst->dc, -1);
+
+ if (glyph_buf != glyph_buf_stack) {
+ free(glyph_buf);
+ free(dxy_buf);
+ }
+ return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+#else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+#undef STACK_GLYPH_SIZE
+
+cairo_int_status_t
+_cairo_win32_surface_show_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ return _cairo_win32_surface_show_glyphs_internal (surface,
+ op,
+ source,
+ glyphs,
+ num_glyphs,
+ scaled_font,
+ clip,
+ remaining_glyphs,
+ TRUE);
+}
+
+static cairo_surface_t *
+cairo_win32_surface_create_internal (HDC hdc, cairo_format_t format)
+{
+ cairo_win32_surface_t *surface;
+
+ RECT rect;
+
+ surface = malloc (sizeof (cairo_win32_surface_t));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) {
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ surface->clip_region = NULL;
+ surface->image = NULL;
+ surface->format = format;
+
+ surface->d3d9surface = NULL;
+ surface->dc = hdc;
+ surface->bitmap = NULL;
+ surface->is_dib = FALSE;
+ surface->saved_dc_bitmap = NULL;
+ surface->brush = NULL;
+ surface->old_brush = NULL;
+ surface->font_subsets = NULL;
+
+ GetClipBox(hdc, &rect);
+ surface->extents.x = rect.left;
+ surface->extents.y = rect.top;
+ surface->extents.width = rect.right - rect.left;
+ surface->extents.height = rect.bottom - rect.top;
+
+ surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
+ _cairo_surface_init (&surface->base,
+ &cairo_win32_surface_backend,
+ NULL, /* device */
+ _cairo_content_from_format (format));
+
+ return &surface->base;
+}
+
+/**
+ * cairo_win32_surface_create:
+ * @hdc: the DC to create a surface for
+ *
+ * Creates a cairo surface that targets the given DC. The DC will be
+ * queried for its initial clip extents, and this will be used as the
+ * size of the cairo surface. The resulting surface will always be of
+ * format %CAIRO_FORMAT_RGB24; should you need another surface format,
+ * you will need to create one through
+ * cairo_win32_surface_create_with_dib() or call
+ * cairo_win32_surface_create_with_alpha.
+ *
+ * Return value: the newly created surface
+ **/
+cairo_surface_t *
+cairo_win32_surface_create (HDC hdc)
+{
+ /* Assume everything comes in as RGB24 */
+ return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_RGB24);
+}
+
+/**
+ * cairo_win32_surface_create_with_alpha:
+ * @hdc: the DC to create a surface for
+ *
+ * Creates a cairo surface that targets the given DC. The DC will be
+ * queried for its initial clip extents, and this will be used as the
+ * size of the cairo surface. The resulting surface will always be of
+ * format %CAIRO_FORMAT_ARGB32; this format is used when drawing into
+ * transparent windows.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.10
+ **/
+cairo_surface_t *
+cairo_win32_surface_create_with_alpha (HDC hdc)
+{
+ return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_ARGB32);
+}
+
+/**
+ * cairo_win32_surface_create_with_dib:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a device-independent-bitmap surface not associated with
+ * any particular existing surface or device context. The created
+ * bitmap will be uninitialized.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.2
+ **/
+cairo_surface_t *
+cairo_win32_surface_create_with_dib (cairo_format_t format,
+ int width,
+ int height)
+{
+ return _cairo_win32_surface_create_for_dc (NULL, format, width, height);
+}
+
+/**
+ * cairo_win32_surface_create_with_ddb:
+ * @hdc: the DC to create a surface for
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a device-independent-bitmap surface not associated with
+ * any particular existing surface or device context. The created
+ * bitmap will be uninitialized.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.4
+ **/
+cairo_surface_t *
+cairo_win32_surface_create_with_ddb (HDC hdc,
+ cairo_format_t format,
+ int width,
+ int height)
+{
+ cairo_win32_surface_t *new_surf;
+ HBITMAP ddb;
+ HDC screen_dc, ddb_dc;
+ HBITMAP saved_dc_bitmap;
+
+ if (format != CAIRO_FORMAT_RGB24)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+/* XXX handle these eventually
+ format != CAIRO_FORMAT_A8 ||
+ format != CAIRO_FORMAT_A1)
+*/
+
+ if (!hdc) {
+ screen_dc = GetDC (NULL);
+ hdc = screen_dc;
+ } else {
+ screen_dc = NULL;
+ }
+
+ ddb_dc = CreateCompatibleDC (hdc);
+ if (ddb_dc == NULL) {
+ new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ goto FINISH;
+ }
+
+ ddb = CreateCompatibleBitmap (hdc, width, height);
+ if (ddb == NULL) {
+ DeleteDC (ddb_dc);
+
+ /* Note that if an app actually does hit this out of memory
+ * condition, it's going to have lots of other issues, as
+ * video memory is probably exhausted. However, it can often
+ * continue using DIBs instead of DDBs.
+ */
+ new_surf = (cairo_win32_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ goto FINISH;
+ }
+
+ saved_dc_bitmap = SelectObject (ddb_dc, ddb);
+
+ new_surf = (cairo_win32_surface_t*) cairo_win32_surface_create (ddb_dc);
+ new_surf->bitmap = ddb;
+ new_surf->saved_dc_bitmap = saved_dc_bitmap;
+ new_surf->is_dib = FALSE;
+
+FINISH:
+ if (screen_dc)
+ ReleaseDC (NULL, screen_dc);
+
+ return (cairo_surface_t*) new_surf;
+}
+
+cairo_public cairo_surface_t *
+cairo_win32_surface_create_with_d3dsurface9 (IDirect3DSurface9 *surface)
+{
+ HDC dc;
+ cairo_surface_t *win_surface;
+
+ IDirect3DSurface9_AddRef (surface);
+ IDirect3DSurface9_GetDC (surface, &dc);
+ win_surface = cairo_win32_surface_create_internal(dc, CAIRO_FORMAT_RGB24);
+ if (likely(win_surface->status == CAIRO_STATUS_SUCCESS)) {
+ ((cairo_win32_surface_t*)win_surface)->d3d9surface = surface;
+ }
+ return win_surface;
+
+}
+/**
+ * _cairo_surface_is_win32:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a win32 surface. This will
+ * return False if this is a win32 printing surface; use
+ * _cairo_surface_is_win32_printing() to check for that.
+ *
+ * Return value: True if the surface is an win32 surface
+ **/
+int
+_cairo_surface_is_win32 (cairo_surface_t *surface)
+{
+ return surface->backend == &cairo_win32_surface_backend;
+}
+
+/**
+ * cairo_win32_surface_get_dc
+ * @surface: a #cairo_surface_t
+ *
+ * Returns the HDC associated with this surface, or %NULL if none.
+ * Also returns %NULL if the surface is not a win32 surface.
+ *
+ * Return value: HDC or %NULL if no HDC available.
+ *
+ * Since: 1.2
+ **/
+HDC
+cairo_win32_surface_get_dc (cairo_surface_t *surface)
+{
+ cairo_win32_surface_t *winsurf;
+
+ if (_cairo_surface_is_win32 (surface)){
+ winsurf = (cairo_win32_surface_t *) surface;
+
+ return winsurf->dc;
+ }
+
+ if (_cairo_surface_is_paginated (surface)) {
+ cairo_surface_t *target;
+
+ target = _cairo_paginated_surface_get_target (surface);
+
+#ifndef CAIRO_OMIT_WIN32_PRINTING
+ if (_cairo_surface_is_win32_printing (target)) {
+ winsurf = (cairo_win32_surface_t *) target;
+ return winsurf->dc;
+ }
+#endif
+ }
+
+ return NULL;
+}
+
+
+HDC
+cairo_win32_get_dc_with_clip (cairo_t *cr)
+{
+ cairo_surface_t *surface = cr->gstate->target;
+ cairo_clip_t clip;
+ _cairo_clip_init_copy(&clip, &cr->gstate->clip);
+
+ if (_cairo_surface_is_win32 (surface)){
+ cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) surface;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+
+ if (clip.path) {
+ status = _cairo_clip_get_region (&clip, &clip_region);
+ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+ if (status) {
+ _cairo_clip_fini(&clip);
+ return NULL;
+ }
+ }
+ _cairo_win32_surface_set_clip_region (winsurf, clip_region);
+
+ _cairo_clip_fini(&clip);
+ return winsurf->dc;
+ }
+
+ if (_cairo_surface_is_paginated (surface)) {
+ cairo_surface_t *target;
+
+ target = _cairo_paginated_surface_get_target (surface);
+
+#ifndef CAIRO_OMIT_WIN32_PRINTING
+ if (_cairo_surface_is_win32_printing (target)) {
+ cairo_status_t status;
+ cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) target;
+
+ status = _cairo_surface_clipper_set_clip (&winsurf->clipper, &clip);
+
+ _cairo_clip_fini(&clip);
+
+ if (status)
+ return NULL;
+
+ return winsurf->dc;
+ }
+#endif
+ }
+
+ _cairo_clip_fini(&clip);
+ return NULL;
+}
+
+
+
+/**
+ * cairo_win32_surface_get_image
+ * @surface: a #cairo_surface_t
+ *
+ * Returns a #cairo_surface_t image surface that refers to the same bits
+ * as the DIB of the Win32 surface. If the passed-in win32 surface
+ * is not a DIB surface, %NULL is returned.
+ *
+ * Return value: a #cairo_surface_t (owned by the win32 #cairo_surface_t),
+ * or %NULL if the win32 surface is not a DIB.
+ *
+ * Since: 1.4
+ */
+cairo_surface_t *
+cairo_win32_surface_get_image (cairo_surface_t *surface)
+{
+ if (!_cairo_surface_is_win32(surface))
+ return NULL;
+
+ return ((cairo_win32_surface_t*)surface)->image;
+}
+
+static cairo_bool_t
+_cairo_win32_surface_is_similar (void *surface_a,
+ void *surface_b)
+{
+ cairo_win32_surface_t *a = surface_a;
+ cairo_win32_surface_t *b = surface_b;
+
+ return a->dc == b->dc;
+}
+
+typedef struct _cairo_win32_surface_span_renderer {
+ cairo_span_renderer_t base;
+
+ cairo_operator_t op;
+ const cairo_pattern_t *pattern;
+ cairo_antialias_t antialias;
+
+ uint8_t *mask_data;
+ uint32_t mask_stride;
+
+ cairo_image_surface_t *mask;
+ cairo_win32_surface_t *dst;
+ cairo_region_t *clip_region;
+
+ cairo_composite_rectangles_t composite_rectangles;
+} cairo_win32_surface_span_renderer_t;
+
+static cairo_status_t
+_cairo_win32_surface_span_renderer_render_rows (
+ void *abstract_renderer,
+ int y,
+ int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+{
+ cairo_win32_surface_span_renderer_t *renderer = abstract_renderer;
+ while (height--)
+ _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_win32_surface_span_renderer_destroy (void *abstract_renderer)
+{
+ cairo_win32_surface_span_renderer_t *renderer = abstract_renderer;
+ if (!renderer) return;
+
+ if (renderer->mask != NULL)
+ cairo_surface_destroy (&renderer->mask->base);
+
+ free (renderer);
+}
+
+static cairo_status_t
+_cairo_win32_surface_span_renderer_finish (void *abstract_renderer)
+{
+ cairo_win32_surface_span_renderer_t *renderer = abstract_renderer;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (renderer->pattern == NULL || renderer->mask == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = cairo_surface_status (&renderer->mask->base);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ cairo_composite_rectangles_t *rects = &renderer->composite_rectangles;
+ cairo_win32_surface_t *dst = renderer->dst;
+ cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&renderer->mask->base);
+ /* composite onto the image surface directly if we can */
+ if (dst->image) {
+ GdiFlush(); /* XXX: I'm not sure if this needed or not */
+
+ status = dst->image->backend->composite (renderer->op,
+ renderer->pattern, mask_pattern, dst->image,
+ rects->bounded.x, rects->bounded.y,
+ 0, 0, /* mask.x, mask.y */
+ rects->bounded.x, rects->bounded.y,
+ rects->bounded.width, rects->bounded.height,
+ renderer->clip_region);
+ } else {
+ /* otherwise go through the fallback_composite path which
+ * will do the appropriate surface acquisition */
+ status = _cairo_surface_fallback_composite (
+ renderer->op,
+ renderer->pattern, mask_pattern, &dst->base,
+ rects->bounded.x, rects->bounded.y,
+ 0, 0, /* mask.x, mask.y */
+ rects->bounded.x, rects->bounded.y,
+ rects->bounded.width, rects->bounded.height,
+ renderer->clip_region);
+ }
+ cairo_pattern_destroy (mask_pattern);
+
+ }
+ if (status != CAIRO_STATUS_SUCCESS)
+ return _cairo_span_renderer_set_error (abstract_renderer,
+ status);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+
+ if (surface->image) {
+ return _cairo_surface_paint (surface->image, op, source, clip);
+ }
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+
+ if (surface->image) {
+ return _cairo_surface_mask (surface->image, op, source, mask, clip);
+ }
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+
+ if (surface->image) {
+ return _cairo_surface_stroke (surface->image, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+
+ }
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_int_status_t
+_cairo_win32_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_win32_surface_t *surface = abstract_surface;
+
+ if (surface->image) {
+ return _cairo_surface_fill (surface->image, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ }
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+
+#include "cairoint.h"
+
+#include "cairo-boxes-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-error-private.h"
+#include "cairo-region-private.h"
+#include "cairo-spans-private.h"
+#include "cairo-surface-fallback-private.h"
+
+typedef struct {
+ cairo_surface_t *dst;
+ cairo_rectangle_int_t extents;
+ cairo_image_surface_t *image;
+ cairo_rectangle_int_t image_rect;
+ void *image_extra;
+} fallback_state_t;
+
+/**
+ * _fallback_init:
+ *
+ * Acquire destination image surface needed for an image-based
+ * fallback.
+ *
+ * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not
+ * visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all
+ * went well, or some error status otherwise.
+ **/
+static cairo_int_status_t
+_fallback_init (fallback_state_t *state,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ cairo_status_t status;
+
+ state->extents.x = x;
+ state->extents.y = y;
+ state->extents.width = width;
+ state->extents.height = height;
+
+ state->dst = dst;
+
+ status = _cairo_surface_acquire_dest_image (dst, &state->extents,
+ &state->image, &state->image_rect,
+ &state->image_extra);
+ if (unlikely (status))
+ return status;
+
+
+ /* XXX: This NULL value tucked away in state->image is a rather
+ * ugly interface. Cleaner would be to push the
+ * CAIRO_INT_STATUS_NOTHING_TO_DO value down into
+ * _cairo_surface_acquire_dest_image and its backend
+ * counterparts. */
+ assert (state->image != NULL);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_fallback_fini (fallback_state_t *state)
+{
+ _cairo_surface_release_dest_image (state->dst, &state->extents,
+ state->image, &state->image_rect,
+ state->image_extra);
+}
+
+typedef cairo_status_t
+(*cairo_draw_func_t) (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region);
+
+static cairo_status_t
+_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern,
+ cairo_clip_t *clip,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *mask;
+ cairo_region_t *clip_region = NULL, *fallback_region = NULL;
+ cairo_status_t status;
+ cairo_bool_t clip_surface = FALSE;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status) ||
+ status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ {
+ return status;
+ }
+
+ clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with
+ * a mask (as called via _cairo_surface_mask) triggers assertion failures.
+ */
+ mask = _cairo_surface_create_similar_solid (dst,
+ CAIRO_CONTENT_ALPHA,
+ extents->width,
+ extents->height,
+ CAIRO_COLOR_TRANSPARENT,
+ TRUE);
+ if (unlikely (mask->status))
+ return mask->status;
+
+ if (clip_region && (extents->x || extents->y)) {
+ fallback_region = cairo_region_copy (clip_region);
+ status = fallback_region->status;
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ cairo_region_translate (fallback_region,
+ -extents->x,
+ -extents->y);
+ clip_region = fallback_region;
+ }
+
+ status = draw_func (draw_closure, CAIRO_OPERATOR_ADD,
+ &_cairo_pattern_white.base, mask,
+ extents->x, extents->y,
+ extents,
+ clip_region);
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ if (clip_surface)
+ status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y);
+
+ _cairo_pattern_init_for_surface (mask_pattern, mask);
+
+ CLEANUP_SURFACE:
+ if (fallback_region)
+ cairo_region_destroy (fallback_region);
+ cairo_surface_destroy (mask);
+
+ return status;
+}
+
+/* Handles compositing with a clip surface when the operator allows
+ * us to combine the clip with the mask
+ */
+static cairo_status_t
+_clip_and_composite_with_mask (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_pattern_t mask_pattern;
+ cairo_status_t status;
+
+ status = _create_composite_mask_pattern (&mask_pattern,
+ clip,
+ draw_func, draw_closure,
+ dst, extents);
+ if (likely (status == CAIRO_STATUS_SUCCESS)) {
+ status = _cairo_surface_composite (op,
+ src, &mask_pattern.base, dst,
+ extents->x, extents->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ NULL);
+
+ _cairo_pattern_fini (&mask_pattern.base);
+ }
+
+ return status;
+}
+
+/* Handles compositing with a clip surface when we have to do the operation
+ * in two pieces and combine them together.
+ */
+static cairo_status_t
+_clip_and_composite_combine (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_t *intermediate;
+ cairo_surface_pattern_t pattern;
+ cairo_surface_pattern_t clip_pattern;
+ cairo_surface_t *clip_surface;
+ int clip_x, clip_y;
+ cairo_status_t status;
+
+ /* We'd be better off here creating a surface identical in format
+ * to dst, but we have no way of getting that information. Instead
+ * we ask the backend to create a similar surface of identical content,
+ * in the belief that the backend will do something useful - like use
+ * an identical format. For example, the xlib backend will endeavor to
+ * use a compatible depth to enable core protocol routines.
+ */
+ intermediate =
+ _cairo_surface_create_similar_scratch (dst, dst->content,
+ extents->width,
+ extents->height);
+ if (intermediate == NULL) {
+ intermediate =
+ _cairo_image_surface_create_with_content (dst->content,
+ extents->width,
+ extents->width);
+ }
+ if (unlikely (intermediate->status))
+ return intermediate->status;
+
+ /* Initialize the intermediate surface from the destination surface */
+ _cairo_pattern_init_for_surface (&pattern, dst);
+ status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL, intermediate,
+ extents->x, extents->y,
+ 0, 0,
+ 0, 0,
+ extents->width, extents->height,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ status = (*draw_func) (draw_closure, op,
+ src, intermediate,
+ extents->x, extents->y,
+ extents,
+ NULL);
+ if (unlikely (status))
+ goto CLEANUP_SURFACE;
+
+ assert (clip->path != NULL);
+ clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ goto CLEANUP_SURFACE;
+
+ _cairo_pattern_init_for_surface (&clip_pattern, clip_surface);
+
+ /* Combine that with the clip */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN,
+ &clip_pattern.base, NULL, intermediate,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ 0, 0,
+ 0, 0,
+ extents->width, extents->height,
+ NULL);
+ if (unlikely (status))
+ goto CLEANUP_CLIP;
+
+ /* Punch the clip out of the destination */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
+ &clip_pattern.base, NULL, dst,
+ extents->x - clip_x,
+ extents->y - clip_y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ NULL);
+ if (unlikely (status))
+ goto CLEANUP_CLIP;
+
+ /* Now add the two results together */
+ _cairo_pattern_init_for_surface (&pattern, intermediate);
+ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
+ &pattern.base, NULL, dst,
+ 0, 0,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ CLEANUP_CLIP:
+ _cairo_pattern_fini (&clip_pattern.base);
+ CLEANUP_SURFACE:
+ cairo_surface_destroy (intermediate);
+
+ return status;
+}
+
+/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
+ * defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
+ */
+static cairo_status_t
+_clip_and_composite_source (cairo_clip_t *clip,
+ const cairo_pattern_t *src,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_surface_pattern_t mask_pattern;
+ cairo_region_t *clip_region = NULL;
+ cairo_status_t status;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status) ||
+ status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ {
+ return status;
+ }
+ }
+
+ /* Create a surface that is mask IN clip */
+ status = _create_composite_mask_pattern (&mask_pattern,
+ clip,
+ draw_func, draw_closure,
+ dst, extents);
+ if (unlikely (status))
+ return status;
+
+ /* Compute dest' = dest OUT (mask IN clip) */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
+ &mask_pattern.base, NULL, dst,
+ 0, 0,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ clip_region);
+
+ if (unlikely (status))
+ goto CLEANUP_MASK_PATTERN;
+
+ /* Now compute (src IN (mask IN clip)) ADD dest' */
+ status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
+ src, &mask_pattern.base, dst,
+ extents->x, extents->y,
+ 0, 0,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ clip_region);
+
+ CLEANUP_MASK_PATTERN:
+ _cairo_pattern_fini (&mask_pattern.base);
+ return status;
+}
+
+static int
+_cairo_rectangle_empty (const cairo_rectangle_int_t *rect)
+{
+ return rect->width == 0 || rect->height == 0;
+}
+
+/**
+ * _clip_and_composite:
+ * @clip: a #cairo_clip_t
+ * @op: the operator to draw with
+ * @src: source pattern
+ * @draw_func: function that can be called to draw with the mask onto a surface.
+ * @draw_closure: data to pass to @draw_func.
+ * @dst: destination surface
+ * @extents: rectangle holding a bounding box for the operation; this
+ * rectangle will be used as the size for the temporary
+ * surface.
+ *
+ * When there is a surface clip, we typically need to create an intermediate
+ * surface. This function handles the logic of creating a temporary surface
+ * drawing to it, then compositing the result onto the target surface.
+ *
+ * @draw_func is to called to draw the mask; it will be called no more
+ * than once.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded.
+ **/
+static cairo_status_t
+_clip_and_composite (cairo_clip_t *clip,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_draw_func_t draw_func,
+ void *draw_closure,
+ cairo_surface_t *dst,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_status_t status;
+
+ if (_cairo_rectangle_empty (extents))
+ /* Nothing to do */
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ if (op == CAIRO_OPERATOR_SOURCE) {
+ status = _clip_and_composite_source (clip,
+ src,
+ draw_func, draw_closure,
+ dst, extents);
+ } else {
+ cairo_bool_t clip_surface = FALSE;
+ cairo_region_t *clip_region = NULL;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status) ||
+ status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ {
+ return status;
+ }
+
+ clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (clip_surface) {
+ if (_cairo_operator_bounded_by_mask (op)) {
+ status = _clip_and_composite_with_mask (clip, op,
+ src,
+ draw_func, draw_closure,
+ dst, extents);
+ } else {
+ status = _clip_and_composite_combine (clip, op,
+ src,
+ draw_func, draw_closure,
+ dst, extents);
+ }
+ } else {
+ status = draw_func (draw_closure, op,
+ src, dst,
+ 0, 0,
+ extents,
+ clip_region);
+ }
+ }
+
+ return status;
+}
+
+/* Composites a region representing a set of trapezoids.
+ */
+static cairo_status_t
+_composite_trap_region (cairo_clip_t *clip,
+ const cairo_pattern_t *src,
+ cairo_operator_t op,
+ cairo_surface_t *dst,
+ cairo_region_t *trap_region,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_status_t status;
+ cairo_surface_pattern_t mask_pattern;
+ cairo_pattern_t *mask = NULL;
+ int mask_x = 0, mask_y =0;
+
+ if (clip != NULL) {
+ cairo_surface_t *clip_surface = NULL;
+ int clip_x, clip_y;
+
+ clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y);
+ if (unlikely (clip_surface->status))
+ return clip_surface->status;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ _cairo_pattern_init_for_surface (&mask_pattern, clip_surface);
+ mask_x = extents->x - clip_x;
+ mask_y = extents->y - clip_y;
+ mask = &mask_pattern.base;
+ }
+
+ status = _cairo_surface_composite (op, src, mask, dst,
+ extents->x, extents->y,
+ mask_x, mask_y,
+ extents->x, extents->y,
+ extents->width, extents->height,
+ trap_region);
+
+ if (mask != NULL)
+ _cairo_pattern_fini (mask);
+
+ return status;
+}
+
+typedef struct {
+ cairo_traps_t *traps;
+ cairo_antialias_t antialias;
+} cairo_composite_traps_info_t;
+
+static cairo_status_t
+_composite_traps_draw_func (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ cairo_composite_traps_info_t *info = closure;
+ cairo_status_t status;
+ cairo_region_t *extents_region = NULL;
+
+ if (dst_x != 0 || dst_y != 0)
+ _cairo_traps_translate (info->traps, - dst_x, - dst_y);
+
+ if (clip_region == NULL &&
+ !_cairo_operator_bounded_by_source (op)) {
+ extents_region = cairo_region_create_rectangle (extents);
+ if (unlikely (extents_region->status))
+ return extents_region->status;
+ cairo_region_translate (extents_region, -dst_x, -dst_y);
+ clip_region = extents_region;
+ }
+
+ status = _cairo_surface_composite_trapezoids (op,
+ src, dst, info->antialias,
+ extents->x, extents->y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height,
+ info->traps->traps,
+ info->traps->num_traps,
+ clip_region);
+
+ if (extents_region)
+ cairo_region_destroy (extents_region);
+
+ return status;
+}
+
+enum {
+ HAS_CLEAR_REGION = 0x1,
+};
+
+static cairo_status_t
+_clip_and_composite_region (const cairo_pattern_t *src,
+ cairo_operator_t op,
+ cairo_surface_t *dst,
+ cairo_region_t *trap_region,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_region_t clear_region;
+ unsigned int has_region = 0;
+ cairo_status_t status;
+
+ if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) {
+ /* If we optimize drawing with an unbounded operator to
+ * _cairo_surface_fill_rectangles() or to drawing with a
+ * clip region, then we have an additional region to clear.
+ */
+ _cairo_region_init_rectangle (&clear_region, extents);
+ status = cairo_region_subtract (&clear_region, trap_region);
+ if (unlikely (status))
+ return status;
+
+ if (! cairo_region_is_empty (&clear_region))
+ has_region |= HAS_CLEAR_REGION;
+ }
+
+ if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) &&
+ clip == NULL)
+ {
+ const cairo_color_t *color;
+
+ if (op == CAIRO_OPERATOR_CLEAR)
+ color = CAIRO_COLOR_TRANSPARENT;
+ else
+ color = &((cairo_solid_pattern_t *)src)->color;
+
+ /* Solid rectangles special case */
+ status = _cairo_surface_fill_region (dst, op, color, trap_region);
+ } else {
+ /* For a simple rectangle, we can just use composite(), for more
+ * rectangles, we have to set a clip region. The cost of rasterizing
+ * trapezoids is pretty high for most backends currently, so it's
+ * worthwhile even if a region is needed.
+ *
+ * If we have a clip surface, we set it as the mask; this only works
+ * for bounded operators other than SOURCE; for unbounded operators,
+ * clip and mask cannot be interchanged. For SOURCE, the operator
+ * as implemented by the backends is different in its handling
+ * of the mask then what we want.
+ *
+ * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has
+ * more than rectangle and the destination doesn't support clip
+ * regions. In that case, we fall through.
+ */
+ status = _composite_trap_region (clip, src, op, dst,
+ trap_region, extents);
+ }
+
+ if (has_region & HAS_CLEAR_REGION) {
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _cairo_surface_fill_region (dst,
+ CAIRO_OPERATOR_CLEAR,
+ CAIRO_COLOR_TRANSPARENT,
+ &clear_region);
+ }
+ _cairo_region_fini (&clear_region);
+ }
+
+ return status;
+}
+
+/* avoid using region code to re-validate boxes */
+static cairo_status_t
+_fill_rectangles (cairo_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_traps_t *traps,
+ cairo_clip_t *clip)
+{
+ const cairo_color_t *color;
+ cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
+ cairo_rectangle_int_t *rects = stack_rects;
+ cairo_status_t status;
+ int i;
+
+ if (! traps->is_rectilinear || ! traps->maybe_region)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* XXX: convert clip region to geometric boxes? */
+ if (clip != NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* XXX: fallback for the region_subtract() operation */
+ if (! _cairo_operator_bounded_by_mask (op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (traps->has_intersections) {
+ if (traps->is_rectangular) {
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
+ } else {
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
+ }
+ if (unlikely (status))
+ return status;
+ }
+
+ for (i = 0; i < traps->num_traps; i++) {
+ if (! _cairo_fixed_is_integer (traps->traps[i].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ }
+
+ if (traps->num_traps > ARRAY_LENGTH (stack_rects)) {
+ rects = _cairo_malloc_ab (traps->num_traps,
+ sizeof (cairo_rectangle_int_t));
+ if (unlikely (rects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ for (i = 0; i < traps->num_traps; i++) {
+ int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
+ int y1 = _cairo_fixed_integer_part (traps->traps[i].top);
+ int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
+ int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
+
+ rects[i].x = x1;
+ rects[i].y = y1;
+ rects[i].width = x2 - x1;
+ rects[i].height = y2 - y1;
+ }
+
+ if (op == CAIRO_OPERATOR_CLEAR)
+ color = CAIRO_COLOR_TRANSPARENT;
+ else
+ color = &((cairo_solid_pattern_t *)src)->color;
+
+ status = _cairo_surface_fill_rectangles (dst, op, color, rects, i);
+
+ if (rects != stack_rects)
+ free (rects);
+
+ return status;
+}
+
+/* fast-path for very common composite of a single rectangle */
+static cairo_status_t
+_composite_rectangle (cairo_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_traps_t *traps,
+ cairo_clip_t *clip)
+{
+ cairo_rectangle_int_t rect;
+
+ if (clip != NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_fixed_is_integer (traps->traps[0].top) ||
+ ! _cairo_fixed_is_integer (traps->traps[0].bottom) ||
+ ! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) ||
+ ! _cairo_fixed_is_integer (traps->traps[0].right.p1.x))
+ {
+ traps->maybe_region = FALSE;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x);
+ rect.y = _cairo_fixed_integer_part (traps->traps[0].top);
+ rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x;
+ rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y;
+
+ return _cairo_surface_composite (op, src, NULL, dst,
+ rect.x, rect.y,
+ 0, 0,
+ rect.x, rect.y,
+ rect.width, rect.height,
+ NULL);
+}
+
+/* Warning: This call modifies the coordinates of traps */
+static cairo_status_t
+_clip_and_composite_trapezoids (const cairo_pattern_t *src,
+ cairo_operator_t op,
+ cairo_surface_t *dst,
+ cairo_traps_t *traps,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents)
+{
+ cairo_composite_traps_info_t traps_info;
+ cairo_region_t *clip_region = NULL;
+ cairo_bool_t clip_surface = FALSE;
+ cairo_status_t status;
+
+ if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
+ return CAIRO_STATUS_SUCCESS;
+
+ clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ /* Use a fast path if the trapezoids consist of a simple region,
+ * but we can only do this if we do not have a clip surface, or can
+ * substitute the mask with the clip.
+ */
+ if (! clip_surface ||
+ (_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE))
+ {
+ cairo_region_t *trap_region = NULL;
+
+ if (_cairo_operator_bounded_by_source (op)) {
+ status = _fill_rectangles (dst, op, src, traps, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _composite_rectangle (dst, op, src, traps, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ status = _cairo_traps_extract_region (traps, &trap_region);
+ if (unlikely (_cairo_status_is_error (status)))
+ return status;
+
+ if (trap_region != NULL) {
+ status = cairo_region_intersect_rectangle (trap_region, extents);
+ if (unlikely (status)) {
+ cairo_region_destroy (trap_region);
+ return status;
+ }
+
+ if (clip_region != NULL) {
+ status = cairo_region_intersect (trap_region, clip_region);
+ if (unlikely (status)) {
+ cairo_region_destroy (trap_region);
+ return status;
+ }
+ }
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ cairo_rectangle_int_t trap_extents;
+
+ cairo_region_get_extents (trap_region, &trap_extents);
+ if (! _cairo_rectangle_intersect (extents, &trap_extents)) {
+ cairo_region_destroy (trap_region);
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ status = _clip_and_composite_region (src, op, dst,
+ trap_region,
+ clip_surface ? clip : NULL,
+ extents);
+ cairo_region_destroy (trap_region);
+
+ if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
+ return status;
+ }
+ }
+
+ /* No fast path, exclude self-intersections and clip trapezoids. */
+ if (traps->has_intersections) {
+ if (traps->is_rectangular)
+ status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
+ else if (traps->is_rectilinear)
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
+ else
+ status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Otherwise render the trapezoids to a mask and composite in the usual
+ * fashion.
+ */
+ traps_info.traps = traps;
+ traps_info.antialias = antialias;
+
+ return _clip_and_composite (clip, op, src,
+ _composite_traps_draw_func,
+ &traps_info, dst, extents);
+}
+
+typedef struct {
+ cairo_polygon_t *polygon;
+ cairo_fill_rule_t fill_rule;
+ cairo_antialias_t antialias;
+} cairo_composite_spans_info_t;
+
+static cairo_status_t
+_composite_spans_draw_func (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ cairo_composite_rectangles_t rects;
+ cairo_composite_spans_info_t *info = closure;
+
+ rects.source = *extents;
+ rects.mask = *extents;
+ rects.bounded = *extents;
+ /* The incoming dst_x/y are where we're pretending the origin of
+ * the dst surface is -- *not* the offset of a rectangle where
+ * we'd like to place the result. */
+ rects.bounded.x -= dst_x;
+ rects.bounded.y -= dst_y;
+ rects.unbounded = rects.bounded;
+ rects.is_bounded = _cairo_operator_bounded_by_either (op);
+
+ return _cairo_surface_composite_polygon (dst, op, src,
+ info->fill_rule,
+ info->antialias,
+ &rects,
+ info->polygon,
+ clip_region);
+}
+
+static cairo_status_t
+_cairo_win32_surface_span_renderer_composite
+ (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_image_surface_t *mask,
+ cairo_win32_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+
+ if (src == NULL || mask == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = cairo_surface_status (&mask->base);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&mask->base);
+ /* composite onto the image surface directly if we can */
+ if (dst->image) {
+ GdiFlush(); /* XXX: I'm not sure if this needed or not */
+
+ status = dst->image->backend->composite (op,
+ src, mask_pattern, dst->image,
+ extents->x, extents->y,
+ 0, 0, /* mask.x, mask.y */
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height,
+ clip_region);
+ } else {
+ /* otherwise go through the fallback_composite path which
+ * will do the appropriate surface acquisition */
+ status = _cairo_surface_fallback_composite (
+ op,
+ src, mask_pattern, &dst->base,
+ extents->x, extents->y,
+ 0, 0, /* mask.x, mask.y */
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height,
+ clip_region);
+ }
+ cairo_pattern_destroy (mask_pattern);
+
+ }
+ return status;
+}
+
+typedef struct _cairo_image_surface_span_renderer {
+ cairo_span_renderer_t base;
+
+ uint8_t *mask_data;
+ uint32_t mask_stride;
+} cairo_image_surface_span_renderer_t;
+
+cairo_status_t
+_cairo_image_surface_span (void *abstract_renderer,
+ int y, int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans);
+
+static cairo_status_t
+_composite_spans (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ cairo_composite_spans_info_t *info = closure;
+ cairo_image_surface_span_renderer_t renderer;
+ cairo_scan_converter_t *converter;
+ cairo_image_surface_t *mask;
+ cairo_status_t status;
+
+ converter = _cairo_tor_scan_converter_create (extents->x, extents->y,
+ extents->x + extents->width,
+ extents->y + extents->height,
+ info->fill_rule);
+ status = converter->add_polygon (converter, info->polygon);
+ if (unlikely (status))
+ goto CLEANUP_CONVERTER;
+
+ /* TODO: support rendering to A1 surfaces (or: go add span
+ * compositing to pixman.) */
+
+ {
+ mask = cairo_image_surface_create (CAIRO_FORMAT_A8,
+ extents->width,
+ extents->height);
+
+ if (cairo_surface_status(&mask->base)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_CONVERTER;
+ }
+ }
+
+ renderer.base.render_rows = _cairo_image_surface_span;
+ renderer.mask_stride = cairo_image_surface_get_stride (mask);
+ renderer.mask_data = cairo_image_surface_get_data (mask);
+ renderer.mask_data -= extents->y * renderer.mask_stride + extents->x;
+
+ status = converter->generate (converter, &renderer.base);
+ if (unlikely (status))
+ goto CLEANUP_RENDERER;
+
+ _cairo_win32_surface_span_renderer_composite
+ (closure,
+ op,
+ src,
+ mask,
+ (cairo_win32_surface_t*)dst, // ewwww
+ dst_x,
+ dst_y,
+ extents,
+ clip_region);
+#if 0
+ {
+ pixman_image_t *src;
+ int src_x, src_y;
+
+ src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
+ if (unlikely (src == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto CLEANUP_RENDERER;
+ }
+
+ pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
+ extents->x + src_x, extents->y + src_y,
+ 0, 0, /* mask.x, mask.y */
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height);
+ pixman_image_unref (src);
+ }
+#endif
+ CLEANUP_RENDERER:
+ cairo_surface_destroy (mask);
+ CLEANUP_CONVERTER:
+ converter->destroy (converter);
+ return status;
+}
+
+
+
+cairo_status_t
+_cairo_win32_surface_fallback_paint (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_clip_path_t *clip_path = clip ? clip->path : NULL;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ cairo_boxes_t boxes;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_status_t status;
+ cairo_traps_t traps;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_paint (&extents,
+ &rect,
+ op, source,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status))
+ return status;
+
+ /* If the clip cannot be reduced to a set of boxes, we will need to
+ * use a clipmask. Paint is special as it is the only operation that
+ * does not implicitly use a mask, so we may be able to reduce this
+ * operation to a fill...
+ */
+ if (clip != NULL && clip_path->prev == NULL &&
+ _cairo_operator_bounded_by_mask (op))
+ {
+ return _cairo_surface_fill (surface, op, source,
+ &clip_path->path,
+ clip_path->fill_rule,
+ clip_path->tolerance,
+ clip_path->antialias,
+ NULL);
+ }
+
+ /* meh, surface-fallback is dying anyway... */
+ _cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
+ status = _cairo_traps_init_boxes (&traps, &boxes);
+ if (unlikely (status))
+ goto CLEANUP_BOXES;
+
+ status = _clip_and_composite_trapezoids (source, op, surface,
+ &traps, CAIRO_ANTIALIAS_DEFAULT,
+ clip,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+ _cairo_traps_fini (&traps);
+
+CLEANUP_BOXES:
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_surface_mask_draw_func (void *closure,
+ cairo_operator_t op,
+ const cairo_pattern_t *src,
+ cairo_surface_t *dst,
+ int dst_x,
+ int dst_y,
+ const cairo_rectangle_int_t *extents,
+ cairo_region_t *clip_region)
+{
+ cairo_pattern_t *mask = closure;
+ cairo_status_t status;
+ cairo_region_t *extents_region = NULL;
+
+ if (clip_region == NULL &&
+ !_cairo_operator_bounded_by_source (op)) {
+ extents_region = cairo_region_create_rectangle (extents);
+ if (unlikely (extents_region->status))
+ return extents_region->status;
+ cairo_region_translate (extents_region, -dst_x, -dst_y);
+ clip_region = extents_region;
+ }
+
+ if (src) {
+ status = _cairo_surface_composite (op,
+ src, mask, dst,
+ extents->x, extents->y,
+ extents->x, extents->y,
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height,
+ clip_region);
+ } else {
+ status = _cairo_surface_composite (op,
+ mask, NULL, dst,
+ extents->x, extents->y,
+ 0, 0, /* unused */
+ extents->x - dst_x, extents->y - dst_y,
+ extents->width, extents->height,
+ clip_region);
+ }
+
+ if (extents_region)
+ cairo_region_destroy (extents_region);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_win32_surface_fallback_mask (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_status_t status;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_mask (&extents,
+ &rect,
+ op, source, mask, clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ if (clip != NULL && extents.is_bounded) {
+ status = _cairo_clip_rectangle (clip, &extents.bounded);
+ if (unlikely (status))
+ return status;
+ }
+
+ return _clip_and_composite (clip, op, source,
+ _cairo_surface_mask_draw_func,
+ (void *) mask,
+ surface,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+}
+
+cairo_status_t
+_cairo_win32_surface_fallback_stroke (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_polygon_t polygon;
+ cairo_traps_t traps;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_status_t status;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_stroke (&extents,
+ &rect,
+ op, source,
+ path, stroke_style, ctm,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status))
+ return status;
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ _cairo_traps_init (&traps);
+ _cairo_traps_limit (&traps, clip_boxes, num_boxes);
+
+ if (path->is_rectilinear) {
+ status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
+ stroke_style,
+ ctm,
+ &traps);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ goto DO_TRAPS;
+
+ if (_cairo_status_is_error (status))
+ goto CLEANUP;
+ }
+
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ if (polygon.num_edges == 0)
+ goto DO_TRAPS;
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
+ if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ goto CLEANUP;
+ }
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ cairo_composite_spans_info_t info;
+
+ info.polygon = &polygon;
+ info.fill_rule = CAIRO_FILL_RULE_WINDING;
+ info.antialias = antialias;
+
+ status = _clip_and_composite (clip, op, source,
+ _composite_spans,
+ &info, surface,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+ goto CLEANUP;
+ }
+
+ /* Fall back to trapezoid fills. */
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ &polygon,
+ CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ DO_TRAPS:
+ status = _clip_and_composite_trapezoids (source, op, surface,
+ &traps, antialias,
+ clip,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+ CLEANUP:
+ _cairo_traps_fini (&traps);
+ _cairo_polygon_fini (&polygon);
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ return status;
+}
+
+cairo_status_t
+_cairo_win32_surface_fallback_fill (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_polygon_t polygon;
+ cairo_traps_t traps;
+ cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
+ int num_boxes = ARRAY_LENGTH (boxes_stack);
+ cairo_bool_t is_rectilinear;
+ cairo_composite_rectangles_t extents;
+ cairo_rectangle_int_t rect;
+ cairo_status_t status;
+
+ if (!_cairo_surface_get_extents (surface, &rect))
+ ASSERT_NOT_REACHED;
+
+ status = _cairo_composite_rectangles_init_for_fill (&extents,
+ &rect,
+ op, source, path,
+ clip);
+ if (unlikely (status))
+ return status;
+
+ if (_cairo_clip_contains_extents (clip, &extents))
+ clip = NULL;
+
+ status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
+ if (unlikely (status))
+ return status;
+
+ _cairo_traps_init (&traps);
+ _cairo_traps_limit (&traps, clip_boxes, num_boxes);
+
+ _cairo_polygon_init (&polygon);
+ _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
+
+ if (_cairo_path_fixed_fill_is_empty (path))
+ goto DO_TRAPS;
+
+ is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path);
+ if (is_rectilinear) {
+ status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+ fill_rule,
+ &traps);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ goto DO_TRAPS;
+
+ if (_cairo_status_is_error (status))
+ goto CLEANUP;
+ }
+
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ if (polygon.num_edges == 0)
+ goto DO_TRAPS;
+
+ if (_cairo_operator_bounded_by_mask (op)) {
+ _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
+ if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ goto CLEANUP;
+ }
+
+ if (is_rectilinear) {
+ status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
+ &polygon,
+ fill_rule);
+ if (likely (status == CAIRO_STATUS_SUCCESS))
+ goto DO_TRAPS;
+
+ if (unlikely (_cairo_status_is_error (status)))
+ goto CLEANUP;
+ }
+
+
+ if (antialias != CAIRO_ANTIALIAS_NONE) {
+ cairo_composite_spans_info_t info;
+
+ info.polygon = &polygon;
+ info.fill_rule = fill_rule;
+ info.antialias = antialias;
+
+ status = _clip_and_composite (clip, op, source,
+ _composite_spans,
+ &info, surface,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+ goto CLEANUP;
+ }
+
+ /* Fall back to trapezoid fills. */
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ &polygon,
+ fill_rule);
+ if (unlikely (status))
+ goto CLEANUP;
+
+ DO_TRAPS:
+ status = _clip_and_composite_trapezoids (source, op, surface,
+ &traps, antialias,
+ clip,
+ extents.is_bounded ? &extents.bounded : &extents.unbounded);
+ CLEANUP:
+ _cairo_traps_fini (&traps);
+ _cairo_polygon_fini (&polygon);
+ if (clip_boxes != boxes_stack)
+ free (clip_boxes);
+
+ return status;
+}
+
+static const cairo_surface_backend_t cairo_win32_surface_backend = {
+ CAIRO_SURFACE_TYPE_WIN32,
+ _cairo_win32_surface_create_similar,
+ _cairo_win32_surface_finish,
+ _cairo_win32_surface_acquire_source_image,
+ _cairo_win32_surface_release_source_image,
+ _cairo_win32_surface_acquire_dest_image,
+ _cairo_win32_surface_release_dest_image,
+ NULL, /* clone similar */
+ _cairo_win32_surface_composite,
+ _cairo_win32_surface_fill_rectangles,
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_win32_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ _cairo_win32_surface_flush,
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ _cairo_win32_surface_fallback_paint,
+ _cairo_win32_surface_fallback_mask,
+ _cairo_win32_surface_fallback_stroke,
+ _cairo_win32_surface_fallback_fill,
+ _cairo_win32_surface_show_glyphs,
+
+ NULL, /* snapshot */
+ _cairo_win32_surface_is_similar,
+};
+
+/* Notes:
+ *
+ * Win32 alpha-understanding functions
+ *
+ * BitBlt - will copy full 32 bits from a 32bpp DIB to result
+ * (so it's safe to use for ARGB32->ARGB32 SOURCE blits)
+ * (but not safe going RGB24->ARGB32, if RGB24 is also represented
+ * as a 32bpp DIB, since the alpha isn't discarded!)
+ *
+ * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set,
+ * it will still copy over the src alpha, because the SCA value (255) will be
+ * multiplied by all the src components.
+ */
+
+
+cairo_int_status_t
+_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_surface_t *surface)
+{
+ RECT rect;
+ int clipBoxType;
+ int gm;
+ XFORM saved_xform;
+
+ /* GetClipBox/GetClipRgn and friends interact badly with a world transform
+ * set. GetClipBox returns values in logical (transformed) coordinates;
+ * it's unclear what GetClipRgn returns, because the region is empty in the
+ * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates.
+ * Similarily, IntersectClipRect works in logical units, whereas SelectClipRgn
+ * works in device units.
+ *
+ * So, avoid the whole mess and get rid of the world transform
+ * while we store our initial data and when we restore initial coordinates.
+ *
+ * XXX we may need to modify x/y by the ViewportOrg or WindowOrg
+ * here in GM_COMPATIBLE; unclear.
+ */
+ gm = GetGraphicsMode (hdc);
+ if (gm == GM_ADVANCED) {
+ GetWorldTransform (hdc, &saved_xform);
+ ModifyWorldTransform (hdc, NULL, MWT_IDENTITY);
+ }
+
+ clipBoxType = GetClipBox (hdc, &rect);
+ if (clipBoxType == ERROR) {
+ _cairo_win32_print_gdi_error ("cairo_win32_surface_create");
+ SetGraphicsMode (hdc, gm);
+ /* XXX: Can we make a more reasonable guess at the error cause here? */
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ surface->clip_rect.x = rect.left;
+ surface->clip_rect.y = rect.top;
+ surface->clip_rect.width = rect.right - rect.left;
+ surface->clip_rect.height = rect.bottom - rect.top;
+
+ surface->initial_clip_rgn = NULL;
+ surface->had_simple_clip = FALSE;
+
+ if (clipBoxType == COMPLEXREGION) {
+ surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0);
+ if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) {
+ DeleteObject(surface->initial_clip_rgn);
+ surface->initial_clip_rgn = NULL;
+ }
+ } else if (clipBoxType == SIMPLEREGION) {
+ surface->had_simple_clip = TRUE;
+ }
+
+ if (gm == GM_ADVANCED)
+ SetWorldTransform (hdc, &saved_xform);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface)
+{
+ cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+
+ XFORM saved_xform;
+ int gm = GetGraphicsMode (surface->dc);
+ if (gm == GM_ADVANCED) {
+ GetWorldTransform (surface->dc, &saved_xform);
+ ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY);
+ }
+
+ /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */
+ SelectClipRgn (surface->dc, surface->initial_clip_rgn);
+
+ if (surface->had_simple_clip) {
+ /* then if we had a simple clip, intersect */
+ IntersectClipRect (surface->dc,
+ surface->clip_rect.x,
+ surface->clip_rect.y,
+ surface->clip_rect.x + surface->clip_rect.width,
+ surface->clip_rect.y + surface->clip_rect.height);
+ }
+
+ if (gm == GM_ADVANCED)
+ SetWorldTransform (surface->dc, &saved_xform);
+
+ return status;
+}
+
+void
+_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header)
+{
+ RGNDATA *rd;
+ unsigned int z;
+
+ if (header)
+ fprintf (stderr, "%s\n", header);
+
+ if (rgn == NULL) {
+ fprintf (stderr, " NULL\n");
+ }
+
+ z = GetRegionData(rgn, 0, NULL);
+ rd = (RGNDATA*) malloc(z);
+ z = GetRegionData(rgn, z, rd);
+
+ fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n",
+ rd->rdh.nCount,
+ rd->rdh.rcBound.left,
+ rd->rdh.rcBound.top,
+ rd->rdh.rcBound.right - rd->rdh.rcBound.left,
+ rd->rdh.rcBound.bottom - rd->rdh.rcBound.top);
+
+ for (z = 0; z < rd->rdh.nCount; z++) {
+ RECT r = ((RECT*)rd->Buffer)[z];
+ fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n",
+ z, r.left, r.top, r.right - r.left, r.bottom - r.top);
+ }
+
+ free(rd);
+ fflush (stderr);
+}
+
+/**
+ * cairo_win32_surface_set_can_convert_to_dib
+ * @surface: a #cairo_surface_t
+ * @can_convert: a #cairo_bool_t indicating whether this surface can
+ * be coverted to a DIB if necessary
+ *
+ * A DDB surface with this flag set can be converted to a DIB if it's
+ * used as a source in a way that GDI can't natively handle; for
+ * example, drawing a RGB24 DDB onto an ARGB32 DIB. Doing this
+ * conversion results in a significant speed optimization, because we
+ * can call on pixman to perform the operation natively, instead of
+ * reading the data from the DC each time.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
+ * changed, or an error otherwise.
+ *
+ */
+cairo_status_t
+cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert)
+{
+ cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
+ if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+ if (surface->bitmap) {
+ if (can_convert)
+ surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
+ else
+ surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_win32_surface_get_can_convert_to_dib
+ * @surface: a #cairo_surface_t
+ * @can_convert: a #cairo_bool_t* that receives the return value
+ *
+ * Returns the value of the flag indicating whether the surface can be
+ * converted to a DIB if necessary, as set by
+ * cairo_win32_surface_set_can_convert_to_dib.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
+ * retreived, or an error otherwise.
+ *
+ */
+cairo_status_t
+cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert)
+{
+ cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
+ if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+ *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+int
+cairo_win32_surface_get_width (cairo_surface_t *asurface)
+{
+ cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
+ if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+ return surface->extents.width;
+}
+
+int
+cairo_win32_surface_get_height (cairo_surface_t *asurface)
+{
+ cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
+ if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
+ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+ return surface->extents.height;
+}
diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h
new file mode 100644
index 000000000..07f7cc8e4
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-win32.h
@@ -0,0 +1,339 @@
+/* -*- 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 <otaylor@redhat.com>
+ */
+
+#ifndef _CAIRO_WIN32_H_
+#define _CAIRO_WIN32_H_
+
+#include "cairo.h"
+
+#if CAIRO_HAS_WIN32_SURFACE
+
+#include <windows.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_win32_surface_create (HDC hdc);
+
+cairo_public cairo_surface_t *
+cairo_win32_surface_create_with_alpha (HDC hdc);
+
+cairo_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_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert);
+
+cairo_public cairo_status_t
+cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert);
+
+BYTE cairo_win32_get_system_text_quality (void);
+
+cairo_public int
+cairo_win32_surface_get_width (cairo_surface_t *surface);
+
+cairo_public int
+cairo_win32_surface_get_height (cairo_surface_t *surface);
+
+#if CAIRO_HAS_WIN32_FONT
+
+/*
+ * 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);
+
+#endif /* CAIRO_HAS_WIN32_FONT */
+
+#if CAIRO_HAS_DWRITE_FONT
+
+/*
+ * Win32 DirectWrite font support
+ */
+cairo_public cairo_font_face_t *
+cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrite_font_face);
+
+void
+cairo_dwrite_scaled_font_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed);
+
+void
+cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force);
+
+cairo_bool_t
+cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font);
+
+void
+cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, int geometry, int mode);
+
+int
+cairo_dwrite_get_cleartype_rendering_mode();
+
+#endif /* CAIRO_HAS_DWRITE_FONT */
+
+struct IDirect3DSurface9;
+cairo_public cairo_surface_t *
+cairo_win32_surface_create_with_d3dsurface9 (struct IDirect3DSurface9 *surface);
+
+
+#if CAIRO_HAS_D2D_SURFACE
+
+struct _cairo_device
+{
+ int type;
+ int refcount;
+};
+
+/**
+ * Create a D2D device
+ *
+ * \return New D2D device, NULL if creation failed.
+ */
+cairo_device_t *
+cairo_d2d_create_device();
+
+cairo_device_t *
+cairo_d2d_create_device_from_d3d10device(struct ID3D10Device1 *device);
+
+/**
+ * Releases a D2D device.
+ *
+ * \return References left to the device
+ */
+int
+cairo_release_device(cairo_device_t *device);
+
+/**
+ * Addrefs a D2D device.
+ *
+ * \return References to the device
+ */
+int
+cairo_addref_device(cairo_device_t *device);
+
+/**
+ * Flushes a D3D device. In most cases the surface backend will do this
+ * internally, but when using a surfaces created from a shared handle this
+ * should be executed manually when a different device is going to be accessing
+ * the same surface data. This will also block until the device is finished
+ * processing all work.
+ */
+void
+cairo_d2d_finish_device(cairo_device_t *device);
+
+/**
+ * Gets the D3D10 device used by a certain cairo_device_t.
+ */
+struct ID3D10Device1*
+cairo_d2d_device_get_device(cairo_device_t *device);
+
+/**
+ * Create a D2D surface for an HWND
+ *
+ * \param device Device used to create the surface
+ * \param wnd Handle for the window
+ * \param content Content of the window, should be COLOR_ALPHA for transparent windows
+ * \return New cairo surface
+ */
+cairo_public cairo_surface_t *
+cairo_d2d_surface_create_for_hwnd(cairo_device_t *device, HWND wnd, cairo_content_t content);
+
+/**
+ * Create a D2D surface of a certain size.
+ *
+ * \param device Device used to create the surface
+ * \param format Cairo format of the surface
+ * \param width Width of the surface
+ * \param height Height of the surface
+ * \return New cairo surface
+ */
+cairo_public cairo_surface_t *
+cairo_d2d_surface_create(cairo_device_t *device,
+ cairo_format_t format,
+ int width,
+ int height);
+
+/**
+ * Create a D3D surface from a Texture SharedHandle, this is obtained from a
+ * CreateTexture call on a D3D9 device. This has to be an A8R8G8B8 format
+ * or an A8 format, the treatment of the alpha channel can be indicated using
+ * the content parameter.
+ *
+ * \param device Device used to create the surface
+ * \param handle Shared handle to the texture we want to wrap
+ * \param content Content of the texture, COLOR_ALPHA for ARGB
+ * \return New cairo surface
+ */
+cairo_public cairo_surface_t *
+cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content);
+
+/**
+ * Create a D3D surface from an ID3D10Texture2D texture, this is obtained from a
+ * CreateTexture2D call on a D3D10 device. This has to be an A8R8G8B8 format
+ * or an A8 format, the treatment of the alpha channel can be indicated using
+ * the content parameter.
+ *
+ * \param device Device used to create the surface
+ * \param texture Texture that we want to wrap
+ * \param content Content of the texture
+ * \return New cairo surface
+ */
+cairo_public cairo_surface_t *
+cairo_d2d_surface_create_for_texture(cairo_device_t *device,
+ struct ID3D10Texture2D *texture,
+ cairo_content_t content);
+
+/**
+ * Get the ID3D10Texture2D used for a surface.
+ */
+cairo_public struct ID3D10Texture2D *cairo_d2d_surface_get_texture(cairo_surface_t *surf);
+
+/**
+ * Present the backbuffer for a surface create for an HWND. This needs
+ * to be called when the owner of the original window surface wants to
+ * actually present the executed drawing operations to the screen.
+ *
+ * \param surface D2D surface.
+ */
+void cairo_d2d_present_backbuffer(cairo_surface_t *surface);
+
+/**
+ * Scroll the surface, this only moves the surface graphics, it does not
+ * actually scroll child windows or anything like that. Nor does it invalidate
+ * that area of the window.
+ *
+ * \param surface The d2d surface this operation should apply to.
+ * \param x The x delta for the movement
+ * \param y The y delta for the movement
+ * \param clip The clip rectangle, the is the 'part' of the surface that needs
+ * scrolling.
+ */
+void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip);
+
+/**
+ * Get a DC for the current render target. When selecting the retention option this
+ * call can be relatively slow, since it may require reading back contents from the
+ * hardware surface.
+ *
+ * \note This must be matched by a call to ReleaseDC!
+ *
+ * \param retain_contents If true the current contents of the RT is copied to the DC,
+ * otherwise the DC is initialized to transparent black.
+ */
+HDC cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents);
+
+/**
+ * Release the DC acquired through GetDC(). Optionally an update region may be specified
+ *
+ * \param updated_rect The area of the DC that was updated, if null the entire dc will
+ * be updated.
+ */
+void cairo_d2d_release_dc(cairo_surface_t *surcace, const cairo_rectangle_int_t *updated_rect);
+
+/**
+ * Get an estimate of the amount of (video) RAM which is currently in use by the D2D
+ * internal image surface cache.
+ */
+int cairo_d2d_get_image_surface_cache_usage();
+
+/**
+ * Get an estimate of the amount of VRAM which is currently used by the d2d
+ * surfaces for a device. This does -not- include the internal image surface
+ * cache.
+ */
+int cairo_d2d_get_surface_vram_usage(cairo_device_t *device);
+
+/**
+ * Get the width of the surface.
+ */
+int cairo_d2d_surface_get_width(cairo_surface_t *surface);
+
+/**
+ * Get the height of the surface.
+ */
+int cairo_d2d_surface_get_height(cairo_surface_t *surface);
+#endif
+
+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-surface.c b/gfx/cairo/cairo/src/cairo-xcb-surface.c
new file mode 100644
index 000000000..da07f609f
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xcb-surface.c
@@ -0,0 +1,1368 @@
+/* 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 <behdad@behdad.org>
+ * Carl D. Worth <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xcb.h"
+#include "cairo-xcb-private.h"
+
+#if CAIRO_HAS_XCB_DRM_FUNCTIONS
+#include <xcb/dri2.h>
+#endif
+
+#define AllPlanes ((unsigned) -1)
+#define CAIRO_ASSUME_PIXMAP 20
+#define XLIB_COORD_MAX 32767
+
+#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
+
+#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS
+#include "drm/cairo-drm-private.h"
+#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.
+ */
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+static cairo_status_t
+_cairo_xcb_surface_create_similar_shm (cairo_xcb_surface_t *other,
+ pixman_format_code_t pixman_format,
+ int width, int height,
+ cairo_surface_t **out)
+{
+ size_t size, stride;
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_status_t status;
+ cairo_surface_t *image;
+
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format));
+ size = stride * height;
+ if (size < CAIRO_XCB_SHM_SMALL_IMAGE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ status = _cairo_xcb_connection_allocate_shm_info (other->connection,
+ size, &shm_info);
+ if (unlikely (status))
+ return status;
+
+ image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
+ pixman_format,
+ width, height,
+ stride);
+ status = image->status;
+ if (unlikely (status)) {
+ _cairo_xcb_shm_info_destroy (shm_info);
+ return status;
+ }
+
+ status = _cairo_user_data_array_set_data (&image->user_data,
+ (const cairo_user_data_key_t *) other->connection,
+ shm_info,
+ (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ _cairo_xcb_shm_info_destroy (shm_info);
+ return status;
+ }
+
+ *out = image;
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+cairo_surface_t *
+_cairo_xcb_surface_create_similar_image (cairo_xcb_surface_t *other,
+ cairo_content_t content,
+ int width, int height)
+{
+ cairo_surface_t *image = NULL;
+ pixman_format_code_t pixman_format;
+
+ /* XXX choose pixman_format from connection->image_formats */
+ switch (content) {
+ case CAIRO_CONTENT_ALPHA:
+ pixman_format = PIXMAN_a8;
+ break;
+ case CAIRO_CONTENT_COLOR:
+ pixman_format = PIXMAN_x8r8g8b8;
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ pixman_format = PIXMAN_a8r8g8b8;
+ break;
+ }
+
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ if (other->flags & CAIRO_XCB_HAS_SHM) {
+ cairo_status_t status;
+
+ status = _cairo_xcb_surface_create_similar_shm (other,
+ pixman_format,
+ width, height,
+ &image);
+ if (_cairo_status_is_error (status))
+ return _cairo_surface_create_in_error (status);
+ }
+#endif
+
+ if (image == NULL) {
+ image = _cairo_image_surface_create_with_pixman_format (NULL,
+ pixman_format,
+ width, height,
+ 0);
+ }
+
+ return image;
+}
+
+cairo_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 (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return NULL;
+
+ if (width <= 0 || height <= 0)
+ return NULL;
+
+#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS
+ if (other->drm != NULL) {
+ cairo_surface_t *drm;
+
+ drm = _cairo_drm_surface_create_similar (other->drm, content, width, height);
+ if (drm != NULL)
+ return drm;
+ }
+#endif
+
+ if ((other->flags & CAIRO_XCB_HAS_RENDER) == 0)
+ return _cairo_xcb_surface_create_similar_image (other, content, width, height);
+
+ connection = other->connection;
+ status = _cairo_xcb_connection_acquire (connection);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ status =_cairo_xcb_connection_take_socket (connection);
+ if (unlikely (status)) {
+ _cairo_xcb_connection_release (connection);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ if (content == other->base.content) {
+ pixmap = _cairo_xcb_connection_create_pixmap (connection,
+ other->depth,
+ 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;
+}
+
+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);
+ cairo_surface_destroy (surface->fallback);
+ }
+
+ cairo_list_del (&surface->link);
+
+#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS
+ if (surface->drm != NULL) {
+ cairo_surface_finish (surface->drm);
+ cairo_surface_destroy (surface->drm);
+
+ xcb_dri2_destroy_drawable (surface->connection->xcb_connection,
+ surface->drawable);
+ }
+#endif
+
+ status = _cairo_xcb_connection_acquire (surface->connection);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ if (_cairo_xcb_connection_take_socket (surface->connection) == CAIRO_STATUS_SUCCESS) {
+ if (surface->picture != XCB_NONE) {
+ _cairo_xcb_connection_render_free_picture (surface->connection,
+ surface->picture);
+ }
+
+ if (surface->owns_pixmap)
+ _cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable);
+ }
+ _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_int_status_t
+_cairo_xcb_surface_create_shm_image (cairo_xcb_surface_t *target,
+ cairo_image_surface_t **image_out,
+ cairo_xcb_shm_info_t **shm_info_out)
+{
+ cairo_image_surface_t *image;
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_status_t status;
+ size_t size, stride;
+
+ if ((target->flags & CAIRO_XCB_HAS_SHM) == 0)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (target->width,
+ PIXMAN_FORMAT_BPP (target->pixman_format));
+ size = stride * target->height;
+ if (size < CAIRO_XCB_SHM_SMALL_IMAGE) {
+ target->flags &= ~CAIRO_XCB_HAS_SHM;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = _cairo_xcb_connection_allocate_shm_info (target->screen->connection,
+ size, &shm_info);
+ if (unlikely (status))
+ return status;
+
+ image = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (shm_info->mem,
+ target->pixman_format,
+ target->width,
+ target->height,
+ stride);
+ status = image->base.status;
+ if (unlikely (status)) {
+ _cairo_xcb_shm_info_destroy (shm_info);
+ return status;
+ }
+
+ status = _cairo_user_data_array_set_data (&image->base.user_data,
+ (const cairo_user_data_key_t *) target->connection,
+ shm_info,
+ (cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&image->base);
+ _cairo_xcb_shm_info_destroy (shm_info);
+ return status;
+ }
+
+ *image_out = image;
+ *shm_info_out = shm_info;
+ return CAIRO_STATUS_SUCCESS;
+}
+#endif
+
+static cairo_status_t
+_get_shm_image (cairo_xcb_surface_t *surface,
+ cairo_image_surface_t **image_out)
+{
+#if CAIRO_HAS_XCB_SHM_FUNCTIONS
+ cairo_image_surface_t *image;
+ cairo_xcb_shm_info_t *shm_info;
+ cairo_status_t status;
+
+ status = _cairo_xcb_surface_create_shm_image (surface, &image, &shm_info);
+ if (unlikely (status))
+ return status;
+
+ if (! surface->base.is_clear) {
+ status = _cairo_xcb_connection_shm_get_image (surface->connection,
+ surface->drawable,
+ 0, 0,
+ surface->width,
+ surface->height,
+ shm_info->shm,
+ shm_info->offset);
+ if (unlikely (status))
+ return status;
+ } else {
+ memset (image->data, 0, image->stride * image->height);
+ }
+
+ *image_out = image;
+ return CAIRO_STATUS_SUCCESS;
+#else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+#endif
+}
+
+static cairo_status_t
+_get_image (cairo_xcb_surface_t *surface,
+ cairo_bool_t use_shm,
+ cairo_image_surface_t **image_out)
+{
+ cairo_image_surface_t *image;
+ cairo_xcb_connection_t *connection;
+ xcb_get_image_reply_t *reply;
+ cairo_status_t status;
+
+ if (surface->base.is_clear || surface->deferred_clear) {
+ image = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_pixman_format (NULL,
+ surface->pixman_format,
+ surface->width,
+ surface->height,
+ 0);
+ *image_out = image;
+ return image->base.status;
+ }
+
+ connection = surface->connection;
+
+ status = _cairo_xcb_connection_acquire (connection);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xcb_connection_take_socket (connection);
+ if (unlikely (status))
+ goto FAIL;
+
+ if (use_shm) {
+ status = _get_shm_image (surface, image_out);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto FAIL;
+ }
+
+ if (surface->use_pixmap == 0) {
+ status = _cairo_xcb_connection_get_image (connection,
+ surface->drawable,
+ 0, 0,
+ surface->width,
+ surface->height,
+ &reply);
+ if (unlikely (status))
+ goto FAIL;
+ } else {
+ surface->use_pixmap--;
+ reply = NULL;
+ }
+
+ 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
+ */
+ 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,
+ surface->width,
+ surface->height);
+
+ /* XXX IncludeInferiors? */
+ _cairo_xcb_connection_copy_area (connection,
+ surface->drawable,
+ pixmap, gc,
+ 0, 0,
+ 0, 0,
+ surface->width,
+ surface->height);
+
+ _cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
+
+ status = _cairo_xcb_connection_get_image (connection,
+ pixmap,
+ 0, 0,
+ surface->width,
+ surface->height,
+ &reply);
+ _cairo_xcb_connection_free_pixmap (connection, pixmap);
+
+ if (unlikely (status))
+ goto FAIL;
+ }
+
+ 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_t *)
+ _cairo_image_surface_create_with_pixman_format
+ (xcb_get_image_data (reply),
+ surface->pixman_format,
+ surface->width,
+ surface->height,
+ CAIRO_STRIDE_FOR_WIDTH_BPP (surface->width,
+ PIXMAN_FORMAT_BPP (surface->pixman_format)));
+ status = image->base.status;
+ if (unlikely (status)) {
+ free (reply);
+ goto FAIL;
+ }
+
+ assert (xcb_get_image_data_length (reply) == image->height * image->stride);
+
+ pixman_image_set_destroy_function (image->pixman_image, _destroy_image, reply);
+
+ _cairo_xcb_connection_release (connection);
+
+ /* synchronisation point */
+ surface->marked_dirty = FALSE;
+
+ *image_out = image;
+ return CAIRO_STATUS_SUCCESS;
+
+FAIL:
+ _cairo_xcb_connection_release (connection);
+ return status;
+}
+
+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_image_surface_t *image;
+ cairo_status_t status;
+
+ if (surface->drm != NULL && ! surface->marked_dirty) {
+ return _cairo_surface_acquire_source_image (surface->drm,
+ image_out, image_extra);
+ }
+
+ if (surface->fallback != NULL) {
+ image = (cairo_image_surface_t *) cairo_surface_reference (surface->fallback);
+ goto DONE;
+ }
+
+ image = (cairo_image_surface_t *)
+ _cairo_surface_has_snapshot (&surface->base,
+ &_cairo_image_surface_backend);
+ if (image != NULL) {
+ image = (cairo_image_surface_t *) cairo_surface_reference (&image->base);
+ goto DONE;
+ }
+
+ status = _get_image (surface, FALSE, &image);
+ if (unlikely (status))
+ return status;
+
+ cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
+
+DONE:
+ *image_out = 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_xcb_surface_t *surface = abstract_surface;
+
+ if (surface->drm != NULL && ! surface->marked_dirty) {
+ return _cairo_surface_release_source_image (surface->drm,
+ image, image_extra);
+ }
+
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_bool_t
+_cairo_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)
+{
+ /* XXX copy from xlib */
+ _cairo_font_options_init_default (options);
+ _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
+}
+
+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;
+
+ shm_info->seqno =
+ _cairo_xcb_connection_shm_put_image (surface->connection,
+ surface->drawable,
+ gc,
+ surface->width, surface->height,
+ 0, 0,
+ image->width, image->height,
+ 0, 0,
+ image->depth,
+ shm_info->shm,
+ shm_info->offset);
+
+ 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_status_t status = CAIRO_STATUS_SUCCESS;
+
+ /* XXX track damaged region? */
+
+ status = _cairo_xcb_connection_acquire (surface->connection);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xcb_connection_take_socket (surface->connection);
+ if (unlikely (status)) {
+ _cairo_xcb_connection_release (surface->connection);
+ return status;
+ }
+
+ if (image->pixman_format == surface->pixman_format) {
+ xcb_gcontext_t gc;
+
+ assert (image->width == surface->width);
+ assert (image->height == surface->height);
+ assert (image->depth == surface->depth);
+ assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)));
+
+ 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,
+ 0, 0,
+ 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_status_t
+_cairo_xcb_surface_flush (void *abstract_surface)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->drm != NULL && ! surface->marked_dirty)
+ return surface->drm->backend->flush (surface->drm);
+
+ if (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.finished) {
+ status = cairo_surface_status (surface->fallback);
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _put_image (surface,
+ (cairo_image_surface_t *) surface->fallback);
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ cairo_surface_attach_snapshot (&surface->base,
+ surface->fallback,
+ cairo_surface_finish);
+ }
+ }
+
+ cairo_surface_destroy (surface->fallback);
+ surface->fallback = NULL;
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xcb_surface_mark_dirty (void *abstract_surface,
+ int x, int y,
+ int width, int height)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ surface->marked_dirty = TRUE;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_xcb_surface_map_to_image (cairo_xcb_surface_t *surface)
+{
+ cairo_status_t status;
+ cairo_image_surface_t *image;
+
+ status = _get_image (surface, TRUE, &image);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ return &image->base;
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->drm != NULL && ! surface->marked_dirty)
+ return _cairo_surface_paint (surface->drm, op, source, clip);
+
+ if (surface->fallback == NULL) {
+ status = _cairo_xcb_surface_cairo_paint (surface, op, source, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_xcb_surface_render_paint (surface, op, source, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ surface->fallback = _cairo_xcb_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_paint (surface->fallback, op, source, clip);
+}
+
+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,
+ cairo_clip_t *clip)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->drm != NULL && ! surface->marked_dirty)
+ return _cairo_surface_mask (surface->drm, op, source, mask, clip);
+
+ if (surface->fallback == NULL) {
+ status = _cairo_xcb_surface_cairo_mask (surface,
+ op, source, mask, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_xcb_surface_render_mask (surface,
+ op, source, mask, clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ surface->fallback = _cairo_xcb_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_mask (surface->fallback,
+ op, source, mask,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->drm != NULL && ! surface->marked_dirty) {
+ return _cairo_surface_stroke (surface->drm,
+ op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+ }
+
+ if (surface->fallback == NULL) {
+ status = _cairo_xcb_surface_cairo_stroke (surface, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_xcb_surface_render_stroke (surface, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ surface->fallback = _cairo_xcb_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_stroke (surface->fallback,
+ op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
+}
+
+static cairo_int_status_t
+_cairo_xcb_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->drm != NULL && ! surface->marked_dirty) {
+ return _cairo_surface_fill (surface->drm,
+ op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ }
+
+ if (surface->fallback == NULL) {
+ status = _cairo_xcb_surface_cairo_fill (surface, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_xcb_surface_render_fill (surface, op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ surface->fallback = _cairo_xcb_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_fill (surface->fallback,
+ op, source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
+}
+
+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,
+ cairo_clip_t *clip,
+ int *num_remaining)
+{
+ cairo_xcb_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ *num_remaining = 0;
+
+ if (surface->drm != NULL && ! surface->marked_dirty) {
+ return _cairo_surface_show_text_glyphs (surface->drm,
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font,
+ clip);
+ }
+
+ if (surface->fallback == NULL) {
+ status = _cairo_xcb_surface_cairo_glyphs (surface,
+ op, source,
+ scaled_font, glyphs, num_glyphs,
+ clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ status = _cairo_xcb_surface_render_glyphs (surface,
+ op, source,
+ scaled_font, glyphs, num_glyphs,
+ clip);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
+ surface->fallback = _cairo_xcb_surface_map_to_image (surface);
+ }
+
+ return _cairo_surface_show_text_glyphs (surface->fallback,
+ op, source,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0, 0,
+ scaled_font,
+ clip);
+}
+
+const cairo_surface_backend_t _cairo_xcb_surface_backend = {
+ CAIRO_SURFACE_TYPE_XCB,
+
+ _cairo_xcb_surface_create_similar,
+ _cairo_xcb_surface_finish,
+ _cairo_xcb_surface_acquire_source_image,
+ _cairo_xcb_surface_release_source_image,
+ NULL, NULL, NULL, /* dest acquire/release/clone */
+
+ NULL, /* composite */
+ NULL, /* fill */
+ NULL, /* trapezoids */
+ NULL, /* span */
+ NULL, /* check-span */
+
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_xcb_surface_get_extents,
+ NULL, /* old-glyphs */
+ _cairo_xcb_surface_get_font_options,
+
+ _cairo_xcb_surface_flush,
+ _cairo_xcb_surface_mark_dirty,
+ _cairo_xcb_surface_scaled_font_fini,
+ _cairo_xcb_surface_scaled_glyph_fini,
+
+ _cairo_xcb_surface_paint,
+ _cairo_xcb_surface_mask,
+ _cairo_xcb_surface_stroke,
+ _cairo_xcb_surface_fill,
+ _cairo_xcb_surface_glyphs,
+};
+
+#if CAIRO_HAS_DRM_SURFACE && CAIRO_HAS_XCB_DRM_FUNCTIONS
+static cairo_surface_t *
+_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection,
+ cairo_xcb_screen_t *screen,
+ xcb_drawable_t drawable,
+ pixman_format_code_t pixman_format,
+ int width, int height)
+{
+ uint32_t attachments[] = { XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT };
+ xcb_dri2_get_buffers_reply_t *buffers;
+ xcb_dri2_dri2_buffer_t *buffer;
+ cairo_surface_t *surface;
+
+ if (! _cairo_drm_size_is_valid (screen->device, width, height))
+ return NULL;
+
+ xcb_dri2_create_drawable (connection->xcb_connection,
+ drawable);
+
+ buffers = xcb_dri2_get_buffers_reply (connection->xcb_connection,
+ xcb_dri2_get_buffers (connection->xcb_connection,
+ drawable, 1,
+ ARRAY_LENGTH (attachments),
+ attachments),
+ 0);
+ if (buffers == NULL) {
+ xcb_dri2_destroy_drawable (connection->xcb_connection,
+ drawable);
+ return NULL;
+ }
+
+ /* If the drawable is a window, we expect to receive an extra fake front,
+ * which would involve copying on each flush - contrary to the user
+ * expectations. But that is likely to be preferable to mixing drm/xcb
+ * operations.
+ */
+ buffer = xcb_dri2_get_buffers_buffers (buffers);
+ if (buffers->count == 1 && buffer[0].attachment == XCB_DRI2_ATTACHMENT_BUFFER_FRONT_LEFT) {
+ assert (buffer[0].cpp == PIXMAN_FORMAT_BPP (pixman_format) / 8);
+ surface = cairo_drm_surface_create_for_name (screen->device,
+ buffer[0].name,
+ _cairo_format_from_pixman_format (pixman_format),
+ width, height,
+ buffer[0].pitch);
+ } else {
+ xcb_dri2_destroy_drawable (connection->xcb_connection,
+ drawable);
+ surface = NULL;
+ }
+ free (buffers);
+
+ return surface;
+}
+
+#else
+
+static cairo_surface_t *
+_xcb_drm_create_surface_for_drawable (cairo_xcb_connection_t *connection,
+ cairo_xcb_screen_t *screen,
+ xcb_drawable_t drawable,
+ pixman_format_code_t pixman_format,
+ int width, int height)
+{
+ return NULL;
+}
+
+#endif
+
+cairo_surface_t *
+_cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen,
+ xcb_drawable_t drawable,
+ 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 = 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));
+
+ surface->connection = _cairo_xcb_connection_reference (screen->connection);
+ surface->screen = screen;
+ cairo_list_add (&surface->link, &screen->surfaces);
+
+ surface->fallback = NULL;
+
+ surface->drawable = drawable;
+ surface->owns_pixmap = owns_pixmap;
+ surface->use_pixmap = 0;
+
+ surface->deferred_clear = FALSE;
+
+ surface->width = width;
+ surface->height = height;
+ surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format);
+
+ surface->picture = XCB_NONE;
+
+ surface->pixman_format = pixman_format;
+ surface->xrender_format = xrender_format;
+
+ surface->flags = screen->connection->flags;
+
+ surface->marked_dirty = FALSE;
+ surface->drm = NULL;
+ if (screen->device != NULL) {
+ surface->drm = _xcb_drm_create_surface_for_drawable (surface->connection,
+ surface->screen,
+ drawable,
+ pixman_format,
+ width, height);
+ }
+
+ 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_surface_t *
+cairo_xcb_surface_create (xcb_connection_t *xcb_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 (xcb_connection))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR);
+
+ if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+ xcb_screen = _cairo_xcb_screen_from_visual (xcb_connection, visual, &depth);
+ if (unlikely (xcb_screen == NULL))
+ return _cairo_surface_create_in_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 > 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 (xcb_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_surface_t *
+cairo_xcb_surface_create_for_bitmap (xcb_connection_t *xcb_connection,
+ xcb_screen_t *xcb_screen,
+ xcb_pixmap_t bitmap,
+ int width,
+ int height)
+{
+ cairo_xcb_screen_t *screen;
+
+ if (xcb_connection_has_error (xcb_connection))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR);
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
+
+ screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen);
+ if (unlikely (screen == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _cairo_xcb_surface_create_internal (screen, bitmap, FALSE,
+ PIXMAN_a1,
+ 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.
+ *
+ * Return value: the newly created surface.
+ **/
+cairo_surface_t *
+cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *xcb_connection,
+ xcb_screen_t *xcb_screen,
+ xcb_drawable_t drawable,
+ xcb_render_pictforminfo_t *format,
+ int width,
+ int height)
+{
+ cairo_xcb_screen_t *screen;
+ cairo_format_masks_t image_masks;
+ pixman_format_code_t pixman_format;
+
+ if (xcb_connection_has_error (xcb_connection))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_WRITE_ERROR);
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return _cairo_surface_create_in_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));
+
+ screen = _cairo_xcb_screen_get (xcb_connection, xcb_screen);
+ if (unlikely (screen == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ return _cairo_xcb_surface_create_internal (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
+
+/**
+ * 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.
+ **/
+void
+cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface,
+ int width,
+ int height)
+{
+ cairo_xcb_surface_t *surface;
+ cairo_status_t status_ignored;
+
+ if (unlikely (abstract_surface->status))
+ return;
+ if (unlikely (abstract_surface->finished)) {
+ status_ignored = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
+ return;
+ }
+
+
+ if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
+ status_ignored = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+ return;
+ }
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) {
+ status_ignored = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ return;
+ }
+
+ surface = (cairo_xcb_surface_t *) abstract_surface;
+ surface->width = width;
+ surface->height = height;
+}
+#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
+slim_hidden_def (cairo_xcb_surface_set_size);
+#endif
diff --git a/gfx/cairo/cairo/src/cairo-xcb-xrender.h b/gfx/cairo/cairo/src/cairo-xcb-xrender.h
new file mode 100644
index 000000000..09c609738
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xcb-xrender.h
@@ -0,0 +1,63 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_XCB_XRENDER_H
+#define CAIRO_XCB_XRENDER_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XCB_SURFACE
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_surface_t *
+cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *c,
+ xcb_drawable_t drawable,
+ xcb_screen_t *screen,
+ xcb_render_pictforminfo_t *format,
+ int width,
+ int height);
+
+CAIRO_END_DECLS
+
+#else /* CAIRO_HAS_XCB_SURFACE */
+# error Cairo was not compiled with support for the xcb backend
+#endif /* CAIRO_HAS_XCB_SURFACE */
+
+#endif /* CAIRO_XCB_XRENDER_H */
diff --git a/gfx/cairo/cairo/src/cairo-xcb.h b/gfx/cairo/cairo/src/cairo-xcb.h
new file mode 100644
index 000000000..3f64dcbdc
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xcb.h
@@ -0,0 +1,96 @@
+/* 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 <cworth@cworth.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_XCB_H
+#define CAIRO_XCB_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XCB_SURFACE
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+
+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);
+
+/* 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);
+
+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-display.c b/gfx/cairo/cairo/src/cairo-xlib-display.c
new file mode 100644
index 000000000..139e63149
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xlib-display.c
@@ -0,0 +1,669 @@
+/* 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 <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+#include "cairo-freelist-private.h"
+#include "cairo-error-private.h"
+
+#include <X11/Xlibint.h> /* For XESetCloseDisplay */
+
+typedef int (*cairo_xlib_error_func_t) (Display *display,
+ XErrorEvent *event);
+
+struct _cairo_xlib_job {
+ cairo_xlib_job_t *next;
+ enum {
+ RESOURCE,
+ WORK
+ } type;
+ union {
+ struct {
+ cairo_xlib_notify_resource_func notify;
+ XID xid;
+ } resource;
+ struct {
+ cairo_xlib_notify_func notify;
+ void *data;
+ void (*destroy) (void *);
+ } work;
+ } func;
+};
+
+static cairo_xlib_display_t *_cairo_xlib_display_list;
+
+static void
+_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
+ cairo_xlib_hook_t *hook);
+
+static void
+_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
+{
+ cairo_xlib_screen_t *screen;
+ cairo_xlib_hook_t *hook;
+
+ cairo_list_foreach_entry (screen, cairo_xlib_screen_t, &display->screens, link)
+ _cairo_xlib_screen_close_display (display, screen);
+
+ while (TRUE) {
+ hook = display->close_display_hooks;
+ if (hook == NULL)
+ break;
+
+ _cairo_xlib_remove_close_display_hook_internal (display, hook);
+
+ hook->func (display, hook);
+ }
+ display->closed = TRUE;
+}
+
+static void
+_cairo_xlib_display_finish (void *abstract_display)
+{
+ cairo_xlib_display_t *display = abstract_display;
+
+ display->display = NULL;
+}
+
+static void
+_cairo_xlib_display_destroy (void *abstract_display)
+{
+ cairo_xlib_display_t *display = abstract_display;
+
+ /* destroy all outstanding notifies */
+ while (display->workqueue != NULL) {
+ cairo_xlib_job_t *job = display->workqueue;
+ display->workqueue = job->next;
+
+ if (job->type == WORK && job->func.work.destroy != NULL)
+ job->func.work.destroy (job->func.work.data);
+
+ _cairo_freelist_free (&display->wq_freelist, job);
+ }
+ _cairo_freelist_fini (&display->wq_freelist);
+
+ while (! cairo_list_is_empty (&display->screens)) {
+ _cairo_xlib_screen_destroy (cairo_list_first_entry (&display->screens,
+ cairo_xlib_screen_t,
+ link));
+ }
+
+ free (display);
+}
+
+static int
+_noop_error_handler (Display *display,
+ XErrorEvent *event)
+{
+ return False; /* return value is ignored */
+}
+
+static void
+_cairo_xlib_display_notify (cairo_xlib_display_t *display)
+{
+ cairo_xlib_job_t *jobs, *job, *freelist;
+ Display *dpy = display->display;
+
+ /* Optimistic atomic pointer read -- don't care if it is wrong due to
+ * contention as we will check again very shortly.
+ */
+ if (display->workqueue == NULL)
+ return;
+
+ jobs = display->workqueue;
+ while (jobs != NULL) {
+ display->workqueue = NULL;
+
+ /* reverse the list to obtain FIFO order */
+ job = NULL;
+ do {
+ cairo_xlib_job_t *next = jobs->next;
+ jobs->next = job;
+ job = jobs;
+ jobs = next;
+ } while (jobs != NULL);
+ freelist = jobs = job;
+
+ do {
+ job = jobs;
+ jobs = job->next;
+
+ switch (job->type){
+ case WORK:
+ job->func.work.notify (dpy, job->func.work.data);
+ if (job->func.work.destroy != NULL)
+ job->func.work.destroy (job->func.work.data);
+ break;
+
+ case RESOURCE:
+ job->func.resource.notify (dpy, job->func.resource.xid);
+ break;
+ }
+ } while (jobs != NULL);
+
+ do {
+ job = freelist;
+ freelist = job->next;
+ _cairo_freelist_free (&display->wq_freelist, job);
+ } while (freelist != NULL);
+
+ jobs = display->workqueue;
+ }
+}
+
+static int
+_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
+{
+ cairo_xlib_display_t *display, **prev, *next;
+ cairo_xlib_error_func_t old_handler;
+
+ CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
+ for (display = _cairo_xlib_display_list; display; display = display->next)
+ if (display->display == dpy)
+ break;
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+ if (display == NULL)
+ return 0;
+
+ if (! cairo_device_acquire (&display->base)) {
+ /* protect the notifies from triggering XErrors */
+ XSync (dpy, False);
+ old_handler = XSetErrorHandler (_noop_error_handler);
+
+ _cairo_xlib_display_notify (display);
+ _cairo_xlib_call_close_display_hooks (display);
+
+ /* catch any that arrived before marking the display as closed */
+ _cairo_xlib_display_notify (display);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+
+ cairo_device_release (&display->base);
+ }
+
+ /*
+ * 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);
+
+ assert (display != NULL);
+
+ cairo_device_finish (&display->base);
+ 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,
+};
+
+/**
+ * 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;
+
+ static int buggy_repeat_force = -1;
+
+ 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 = malloc (sizeof (cairo_xlib_display_t));
+ if (unlikely (display == NULL)) {
+ device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ goto UNLOCK;
+ }
+
+ /* 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;
+ }
+ }
+
+ codes = XAddExtension (dpy);
+ if (unlikely (codes == NULL)) {
+ device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ free (display);
+ goto UNLOCK;
+ }
+
+ _cairo_device_init (&display->base, &_cairo_xlib_device_backend);
+
+ XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
+
+ _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
+
+ cairo_device_reference (&display->base); /* add one for the CloseDisplay */
+ display->display = dpy;
+ cairo_list_init (&display->screens);
+ display->workqueue = NULL;
+ display->close_display_hooks = NULL;
+ display->closed = FALSE;
+
+ memset (display->cached_xrender_formats, 0,
+ sizeof (display->cached_xrender_formats));
+
+ /* 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 carious 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 (strstr (ServerVendor (dpy), "X.Org") != NULL) {
+ 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;
+ }
+
+ /* gradients don't seem to work */
+ display->buggy_gradients = TRUE;
+
+
+ /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */
+ /* If buggy_repeat_force == -1, then initialize.
+ * - set to -2, meaning "nothing was specified", and we trust the above detection.
+ * - if MOZ_CAIRO_BUGGY_REPEAT is '0' (exactly), then force buggy repeat off
+ * - if MOZ_CAIRO_BUGGY_REPEAT is '1' (exactly), then force buggy repeat on
+ */
+ if (buggy_repeat_force == -1) {
+ const char *flag = getenv("MOZ_CAIRO_FORCE_BUGGY_REPEAT");
+
+ buggy_repeat_force = -2;
+
+ if (flag && flag[0] == '0')
+ buggy_repeat_force = 0;
+ else if (flag && flag[0] == '1')
+ buggy_repeat_force = 1;
+ }
+
+ if (buggy_repeat_force != -2)
+ display->buggy_repeat = (buggy_repeat_force == 1);
+
+ display->next = _cairo_xlib_display_list;
+ _cairo_xlib_display_list = display;
+
+ device = &display->base;
+
+UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
+ return device;
+}
+
+void
+_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display,
+ cairo_xlib_hook_t *hook)
+{
+ hook->prev = NULL;
+ hook->next = display->close_display_hooks;
+ if (hook->next != NULL)
+ hook->next->prev = hook;
+ display->close_display_hooks = hook;
+}
+
+static void
+_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display,
+ cairo_xlib_hook_t *hook)
+{
+ if (display->close_display_hooks == hook)
+ display->close_display_hooks = hook->next;
+ else if (hook->prev != NULL)
+ hook->prev->next = hook->next;
+
+ if (hook->next != NULL)
+ hook->next->prev = hook->prev;
+
+ hook->prev = NULL;
+ hook->next = NULL;
+}
+
+void
+_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display,
+ cairo_xlib_hook_t *hook)
+{
+ _cairo_xlib_remove_close_display_hook_internal (display, hook);
+}
+
+cairo_status_t
+_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
+ cairo_xlib_notify_resource_func notify,
+ XID xid)
+{
+ cairo_xlib_job_t *job;
+ cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
+
+ if (display->closed == FALSE) {
+ job = _cairo_freelist_alloc (&display->wq_freelist);
+ if (job != NULL) {
+ job->type = RESOURCE;
+ job->func.resource.xid = xid;
+ job->func.resource.notify = notify;
+
+ job->next = display->workqueue;
+ display->workqueue = job;
+
+ status = CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ return status;
+}
+
+cairo_status_t
+_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
+ cairo_xlib_notify_func notify,
+ void *data,
+ void (*destroy) (void *))
+{
+ cairo_xlib_job_t *job;
+ cairo_status_t status = CAIRO_STATUS_NO_MEMORY;
+
+ if (display->closed == FALSE) {
+ job = _cairo_freelist_alloc (&display->wq_freelist);
+ if (job != NULL) {
+ job->type = WORK;
+ job->func.work.data = data;
+ job->func.work.notify = notify;
+ job->func.work.destroy = destroy;
+
+ job->next = display->workqueue;
+ display->workqueue = job;
+
+ status = CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ return status;
+}
+
+cairo_status_t
+_cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display)
+{
+ cairo_status_t status;
+
+ status = cairo_device_acquire (device);
+ if (status)
+ return status;
+
+ *display = (cairo_xlib_display_t *) device;
+ _cairo_xlib_display_notify (*display);
+ return status;
+}
+
+XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
+ cairo_format_t format)
+{
+ XRenderPictFormat *xrender_format;
+
+#if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER
+ xrender_format = display->cached_xrender_formats[format];
+ if (likely (xrender_format != NULL))
+ return xrender_format;
+#endif
+
+ xrender_format = display->cached_xrender_formats[format];
+ if (xrender_format == NULL) {
+ int pict_format;
+
+ 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: {
+ Visual *visual = NULL;
+ Screen *screen = DefaultScreenOfDisplay(display->display);
+ int j;
+ for (j = 0; j < screen->ndepths; j++) {
+ Depth *d = &screen->depths[j];
+ if (d->depth == 16 && d->nvisuals && &d->visuals[0]) {
+ if (d->visuals[0].red_mask == 0xf800 &&
+ d->visuals[0].green_mask == 0x7e0 &&
+ d->visuals[0].blue_mask == 0x1f)
+ visual = &d->visuals[0];
+ break;
+ }
+ }
+ if (!visual)
+ return NULL;
+ xrender_format = XRenderFindVisualFormat(display->display, visual);
+ break;
+ }
+ case CAIRO_FORMAT_INVALID:
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_FORMAT_ARGB32:
+ pict_format = PictStandardARGB32; break;
+ }
+ if (!xrender_format)
+ xrender_format = XRenderFindStandardFormat (display->display,
+ pict_format);
+ 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;
+}
+
+void
+_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display,
+ int *major, int *minor)
+{
+ *major = display->render_major;
+ *minor = display->render_minor;
+}
+
+cairo_bool_t
+_cairo_xlib_display_has_repeat (cairo_device_t *device)
+{
+ 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;
+}
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 000000000..bd260bc0e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xlib-private.h
@@ -0,0 +1,200 @@
+/* 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 <chris@chris-wilson.co.uk>
+ * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
+ */
+
+#ifndef CAIRO_XLIB_PRIVATE_H
+#define CAIRO_XLIB_PRIVATE_H
+
+#include "cairo-xlib.h"
+#include "cairo-xlib-xrender-private.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"
+
+typedef struct _cairo_xlib_display cairo_xlib_display_t;
+typedef struct _cairo_xlib_screen cairo_xlib_screen_t;
+
+typedef struct _cairo_xlib_hook cairo_xlib_hook_t;
+typedef struct _cairo_xlib_job cairo_xlib_job_t;
+typedef void (*cairo_xlib_notify_func) (Display *, void *);
+typedef void (*cairo_xlib_notify_resource_func) (Display *, XID);
+
+struct _cairo_xlib_hook {
+ cairo_xlib_hook_t *prev, *next; /* private */
+ void (*func) (cairo_xlib_display_t *display, void *data);
+};
+
+/* size of color cube */
+#define CUBE_SIZE 6
+/* size of gray ramp */
+#define RAMP_SIZE 16
+
+struct _cairo_xlib_display {
+ cairo_device_t base;
+
+ cairo_xlib_display_t *next;
+
+ Display *display;
+ cairo_list_t screens;
+
+ int render_major;
+ int render_minor;
+ XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB16_565 + 1];
+
+ cairo_xlib_job_t *workqueue;
+ cairo_freelist_t wq_freelist;
+
+ cairo_xlib_hook_t *close_display_hooks;
+ 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_bool_t has_font_options;
+ cairo_font_options_t font_options;
+
+ GC gc[4];
+ cairo_atomic_int_t gc_depths; /* 4 x uint8_t */
+
+ cairo_list_t visuals;
+};
+
+cairo_private cairo_device_t *
+_cairo_xlib_device_create (Display *display);
+
+cairo_private cairo_xlib_screen_t *
+_cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
+ Screen *screen);
+
+cairo_private void
+_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook);
+
+cairo_private void
+_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook);
+
+cairo_private cairo_status_t
+_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
+ cairo_xlib_notify_func notify,
+ void *data,
+ void (*destroy)(void *));
+cairo_private cairo_status_t
+_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
+ cairo_xlib_notify_resource_func notify,
+ XID resource);
+cairo_private cairo_status_t
+_cairo_xlib_display_acquire (cairo_device_t *device,
+ cairo_xlib_display_t **display);
+
+cairo_private void
+_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display,
+ int *major, int *minor);
+
+cairo_private cairo_bool_t
+_cairo_xlib_display_has_repeat (cairo_device_t *device);
+
+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 XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
+ cairo_format_t format);
+
+cairo_private cairo_status_t
+_cairo_xlib_screen_get (Display *dpy,
+ Screen *screen,
+ cairo_xlib_screen_t **out);
+
+cairo_private void
+_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info);
+
+cairo_private void
+_cairo_xlib_screen_close_display (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info);
+
+cairo_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);
+
+#endif /* CAIRO_XLIB_PRIVATE_H */
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 000000000..a0c3df840
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xlib-screen.c
@@ -0,0 +1,466 @@
+/* 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"
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-xlib-surface-private.h"
+#include "cairo-error-private.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_close_display (cairo_xlib_display_t *display,
+ cairo_xlib_screen_t *info)
+{
+ Display *dpy;
+ int i;
+
+ dpy = display->display;
+
+ for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+ if ((info->gc_depths >> (8*i)) & 0xff)
+ XFreeGC (dpy, info->gc[i]);
+ }
+ info->gc_depths = 0;
+}
+
+void
+_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info)
+{
+ while (! cairo_list_is_empty (&info->visuals)) {
+ _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals,
+ cairo_xlib_visual_info_t,
+ 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 = 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;
+ info->gc_depths = 0;
+ memset (info->gc, 0, sizeof (info->gc));
+
+ 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 >> (8*i)) & 0xff) == depth) {
+ info->gc_depths &= ~(0xff << (8*i));
+ 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 >> (8*i)) & 0xff) == 0)
+ break;
+ }
+
+ if (i == ARRAY_LENGTH (info->gc)) {
+ cairo_status_t status;
+
+ /* perform random substitution to ensure fair caching over depths */
+ i = rand () % ARRAY_LENGTH (info->gc);
+ status =
+ _cairo_xlib_display_queue_work (display,
+ (cairo_xlib_notify_func) XFreeGC,
+ info->gc[i],
+ NULL);
+ if (unlikely (status)) {
+ /* leak the server side resource... */
+ XFree ((char *) info->gc[i]);
+ }
+ }
+
+ info->gc[i] = gc;
+ info->gc_depths &= ~(0xff << (8*i));
+ info->gc_depths |= depth << (8*i);
+}
+
+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;
+}
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 000000000..34732b4f6
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xlib-surface-private.h
@@ -0,0 +1,112 @@
+/* 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.h"
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include "cairo-surface-private.h"
+
+typedef struct _cairo_xlib_surface cairo_xlib_surface_t;
+
+struct _cairo_xlib_surface {
+ cairo_surface_t base;
+
+ cairo_xlib_screen_t *screen;
+ cairo_xlib_hook_t close_display_hook;
+
+ Drawable drawable;
+ cairo_bool_t owns_pixmap;
+ Visual *visual;
+
+ int use_pixmap;
+
+ int render_major;
+ int render_minor;
+
+ /* TRUE if the server has a bug with repeating pictures
+ *
+ * https://bugs.freedesktop.org/show_bug.cgi?id=3566
+ *
+ * We can't test for this because it depends on whether the
+ * picture is in video memory or not.
+ *
+ * We also use this variable as a guard against a second
+ * independent bug with transformed repeating pictures:
+ *
+ * http://lists.freedesktop.org/archives/cairo/2004-September/001839.html
+ *
+ * Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so
+ * we can reuse the test for now.
+ */
+ unsigned int buggy_gradients : 1;
+ unsigned int buggy_pad_reflect : 1;
+ unsigned int buggy_repeat : 1;
+#define CAIRO_XLIB_SURFACE_HAS_BUGGY_GRADIENTS 1
+#define CAIRO_XLIB_SURFACE_HAS_BUGGY_PAD_REFLECT 1
+#define CAIRO_XLIB_SURFACE_HAS_BUGGY_REPEAT 1
+
+ int width;
+ int height;
+ int depth;
+
+ Picture dst_picture, src_picture;
+
+ unsigned int clip_dirty;
+ XRectangle embedded_clip_rects[8];
+ XRectangle *clip_rects;
+ int num_clip_rects;
+ cairo_region_t *clip_region;
+
+ XRenderPictFormat *xrender_format;
+ cairo_filter_t filter;
+ cairo_extend_t extend;
+ cairo_bool_t has_component_alpha;
+ int precision;
+ XTransform xtransform;
+
+ uint32_t a_mask;
+ uint32_t r_mask;
+ uint32_t g_mask;
+ uint32_t b_mask;
+};
+
+enum {
+ CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC = 0x01,
+ CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE = 0x02,
+ CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL = 0x03
+};
+
+#endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */
diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c
new file mode 100644
index 000000000..e24c9627a
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c
@@ -0,0 +1,4933 @@
+/* -*- 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 <cworth@cworth.org>
+ * Behdad Esfahbod <behdad@behdad.org>
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ * Karl Tomlinson <karlt+@karlt.net>, 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"
+
+#include "cairo-xlib-private.h"
+#include "cairo-xlib-surface-private.h"
+#include "cairo-clip-private.h"
+#include "cairo-error-private.h"
+#include "cairo-scaled-font-private.h"
+#include "cairo-surface-snapshot-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-region-private.h"
+#include "cairo-xlib-xrender-private.h"
+
+#include <X11/Xutil.h> /* for XDestroyImage */
+#include <X11/Xlibint.h> /* for access to XDisplay's innards */
+
+#define XLIB_COORD_MAX 32767
+
+#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
+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.
+ */
+
+/**
+ * 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.
+ */
+
+/* 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);
+
+static cairo_bool_t
+_native_byte_order_lsb (void);
+
+static cairo_int_status_t
+_cairo_xlib_surface_show_glyphs (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs);
+
+/*
+ * Instead of taking two round trips for each blending request,
+ * assume that if a particular drawable fails GetImage that it will
+ * fail for a "while"; use temporary pixmaps to avoid the errors
+ */
+
+#define CAIRO_ASSUME_PIXMAP 20
+
+static const XTransform identity = { {
+ { 1 << 16, 0x00000, 0x00000 },
+ { 0x00000, 1 << 16, 0x00000 },
+ { 0x00000, 0x00000, 1 << 16 },
+} };
+
+#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \
+ (((surface)->render_major > major) || \
+ (((surface)->render_major == major) && ((surface)->render_minor >= minor)))
+
+#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
+#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
+#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
+
+#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1)
+
+#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2)
+#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2)
+
+#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
+#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
+
+#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
+#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
+
+#define CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10)
+#define CAIRO_SURFACE_RENDER_HAS_GRADIENTS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10)
+
+#define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11)
+
+#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \
+ ((op) <= CAIRO_OPERATOR_SATURATE || \
+ (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \
+ (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY))
+
+static Visual *
+_visual_for_xrender_format(Screen *screen,
+ XRenderPictFormat *xrender_format)
+{
+ 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_status_t
+_cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface,
+ cairo_region_t *region)
+{
+ cairo_bool_t had_clip_rects = surface->clip_region != NULL;
+
+ if (had_clip_rects == FALSE && region == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (surface->clip_region == region)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (cairo_region_equal (surface->clip_region, region))
+ return CAIRO_STATUS_SUCCESS;
+
+ cairo_region_destroy (surface->clip_region);
+ surface->clip_region = cairo_region_reference (region);
+
+ if (surface->clip_rects != surface->embedded_clip_rects) {
+ free (surface->clip_rects);
+ surface->clip_rects = surface->embedded_clip_rects;
+ }
+ surface->num_clip_rects = 0;
+
+ if (region != NULL) {
+ XRectangle *rects = NULL;
+ int n_rects, i;
+
+ n_rects = cairo_region_num_rectangles (region);
+ if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) {
+ rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
+ if (unlikely (rects == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ } else {
+ rects = surface->embedded_clip_rects;
+ }
+
+ for (i = 0; i < n_rects; i++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (region, i, &rect);
+
+ rects[i].x = rect.x;
+ rects[i].y = rect.y;
+ rects[i].width = rect.width;
+ rects[i].height = rect.height;
+ }
+
+ surface->clip_rects = rects;
+ surface->num_clip_rects = n_rects;
+ }
+
+ surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_content_t
+_xrender_format_to_content (XRenderPictFormat *xrender_format)
+{
+ cairo_bool_t xrender_format_has_alpha;
+ cairo_bool_t xrender_format_has_color;
+
+ /* This only happens when using a non-Render server. Let's punt
+ * and say there's no alpha here. */
+ if (xrender_format == NULL)
+ return CAIRO_CONTENT_COLOR;
+
+ xrender_format_has_alpha = (xrender_format->direct.alphaMask != 0);
+ xrender_format_has_color = (xrender_format->direct.redMask != 0 ||
+ xrender_format->direct.greenMask != 0 ||
+ xrender_format->direct.blueMask != 0);
+
+ if (xrender_format_has_alpha)
+ if (xrender_format_has_color)
+ return CAIRO_CONTENT_COLOR_ALPHA;
+ else
+ return CAIRO_CONTENT_ALPHA;
+ else
+ return CAIRO_CONTENT_COLOR;
+}
+
+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 (! CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (src))
+ 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 = src->xrender_format;
+ if ((xrender_format != NULL &&
+ _xrender_format_to_content (xrender_format) == content) ||
+ (xrender_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ _cairo_format_from_content (content))))
+ {
+ Visual *visual;
+
+ /* We've got a compatible XRenderFormat now, which means the
+ * similar surface will match the existing surface as closely in
+ * visual/depth etc. as possible. */
+ pix = XCreatePixmap (display->display, src->drawable,
+ width <= 0 ? 1 : width, height <= 0 ? 1 : height,
+ xrender_format->depth);
+
+ 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
+ {
+#ifdef DEBUG_FORCE_FALLBACKS
+ Screen *screen = src->screen->screen;
+ int depth;
+
+ /* No compatabile 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);
+#else
+ /* No compatabile XRenderFormat, just say no. */
+ cairo_device_release (&display->base);
+ return NULL;
+#endif
+ }
+
+ if (unlikely (surface->base.status)) {
+ XFreePixmap (display->display, pix);
+ cairo_device_release (&display->base);
+ return &surface->base;
+ }
+
+ surface->owns_pixmap = TRUE;
+
+ cairo_device_release (&display->base);
+
+ return &surface->base;
+}
+
+static 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;
+
+ X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable));
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ if (surface->owns_pixmap) {
+ cairo_status_t status2;
+
+ if (surface->dst_picture != None) {
+ status2 = _cairo_xlib_display_queue_resource (display,
+ XRenderFreePicture,
+ surface->dst_picture);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+ }
+
+ if (surface->src_picture != None) {
+ status2 = _cairo_xlib_display_queue_resource (display,
+ XRenderFreePicture,
+ surface->src_picture);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+ }
+
+ status2 = _cairo_xlib_display_queue_resource (display,
+ (cairo_xlib_notify_resource_func) XFreePixmap,
+ surface->drawable);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+ } else {
+ if (surface->dst_picture != None)
+ XRenderFreePicture (display->display, surface->dst_picture);
+
+ if (surface->src_picture != None)
+ XRenderFreePicture (display->display, surface->src_picture);
+ }
+
+ if (surface->clip_rects != surface->embedded_clip_rects)
+ free (surface->clip_rects);
+
+ if (display->display != NULL)
+ _cairo_xlib_remove_close_display_hook (display,
+ &surface->close_display_hook);
+
+ cairo_device_release (&display->base);
+
+ cairo_region_destroy (surface->clip_region);
+
+ return status;
+}
+
+static 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 void
+_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ GC gc)
+{
+ _cairo_xlib_screen_put_gc (display,
+ surface->screen,
+ surface->depth,
+ gc);
+}
+
+static int
+_noop_error_handler (Display *display,
+ XErrorEvent *event)
+{
+ 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 = _native_byte_order_lsb () ? 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 cairo_status_t
+_get_image_surface (cairo_xlib_surface_t *surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect)
+{
+ cairo_int_status_t status;
+ cairo_image_surface_t *image = NULL;
+ XImage *ximage;
+ cairo_rectangle_int_t extents;
+ pixman_format_code_t pixman_format;
+ cairo_format_masks_t xlib_masks;
+ cairo_xlib_display_t *display;
+
+ extents.x = 0;
+ extents.y = 0;
+ extents.width = surface->width;
+ extents.height = surface->height;
+
+ if (interest_rect) {
+ if (! _cairo_rectangle_intersect (&extents, interest_rect)) {
+ *image_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (status)
+ return status;
+
+ if (image_rect)
+ *image_rect = extents;
+
+ /* XXX: This should try to use the XShm extension if available */
+
+ if (surface->use_pixmap == 0)
+ {
+ cairo_xlib_error_func_t old_handler;
+
+ 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 <= 0 ? 1 : extents.width,
+ extents.height <= 0 ? 1 : extents.height,
+ surface->depth);
+ if (pixmap) {
+ XCopyArea (display->display, surface->drawable, pixmap, gc,
+ extents.x, extents.y,
+ extents.width, extents.height,
+ 0, 0);
+
+ ximage = XGetImage (display->display,
+ pixmap,
+ 0, 0,
+ extents.width <= 0 ? 1 : extents.width,
+ extents.height <= 0 ? 1 : 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);
+
+ xlib_masks.bpp = ximage->bits_per_pixel;
+ xlib_masks.alpha_mask = surface->a_mask;
+ xlib_masks.red_mask = surface->r_mask;
+ xlib_masks.green_mask = surface->g_mask;
+ xlib_masks.blue_mask = surface->b_mask;
+
+ /* We can't use pixman to simply write to image if:
+ * (a) the pixels are not appropriately aligned,
+ * (b) pixman does not the pixel format, or
+ * (c) if the image is palettized and we need to convert.
+ */
+ if (ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 &&
+ _pixman_format_from_masks (&xlib_masks, &pixman_format) &&
+ (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 = (
+ _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);
+ image = NULL;
+ }
+ }
+ *image_out = image;
+ return status;
+}
+
+static void
+_cairo_xlib_surface_ensure_src_picture (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface)
+{
+ if (!surface->src_picture) {
+ XRenderPictureAttributes pa;
+ int mask = 0;
+
+ pa.subwindow_mode = IncludeInferiors;
+ mask |= CPSubwindowMode;
+
+ surface->src_picture = XRenderCreatePicture (display->display,
+ surface->drawable,
+ surface->xrender_format,
+ mask, &pa);
+ }
+}
+
+static void
+_cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface)
+{
+ if (surface->clip_region != NULL) {
+ XRenderSetPictureClipRectangles (display->display, surface->dst_picture,
+ 0, 0,
+ surface->clip_rects,
+ surface->num_clip_rects);
+ } else {
+ XRenderPictureAttributes pa;
+ pa.clip_mask = None;
+ XRenderChangePicture (display->display, surface->dst_picture,
+ CPClipMask, &pa);
+ }
+
+ surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE;
+}
+
+static void
+_cairo_xlib_surface_set_precision (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ cairo_antialias_t antialias)
+{
+ int precision;
+
+ switch (antialias) {
+ case CAIRO_ANTIALIAS_DEFAULT:
+ case CAIRO_ANTIALIAS_GRAY:
+ precision = PolyModeImprecise;
+ break;
+ case CAIRO_ANTIALIAS_NONE:
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ precision = PolyModePrecise;
+ break;
+ }
+
+ /* NVidia's driver version 190.42 is much slower when using PolyModeInprecise */
+ precision = PolyModePrecise;
+
+ if (surface->precision != precision) {
+ XRenderPictureAttributes pa;
+
+ pa.poly_mode = precision;
+ XRenderChangePicture (display->display, surface->dst_picture,
+ CPPolyMode, &pa);
+
+ surface->precision = precision;
+ }
+}
+
+static void
+_cairo_xlib_surface_ensure_dst_picture (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface)
+{
+ if (!surface->dst_picture) {
+ surface->dst_picture = XRenderCreatePicture (display->display,
+ surface->drawable,
+ surface->xrender_format,
+ 0, NULL);
+ }
+
+ if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE)
+ _cairo_xlib_surface_set_picture_clip_rects (display, surface);
+}
+
+static cairo_status_t
+_draw_image_surface (cairo_xlib_surface_t *surface,
+ cairo_image_surface_t *image,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int dst_x,
+ int dst_y)
+{
+ cairo_xlib_display_t *display;
+ XImage ximage;
+ cairo_format_masks_t image_masks;
+ int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
+ pixman_image_t *pixman_image = NULL;
+ cairo_status_t status;
+ cairo_bool_t own_data;
+ 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;
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ if (!_pixman_format_to_masks (image->pixman_format, &image_masks))
+ {
+ 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 = surface->depth;
+ ret = _pixman_format_from_masks (&image_masks, &intermediate_format);
+ assert (ret);
+
+ own_data = FALSE;
+
+ pixman_image = pixman_image_create_bits (intermediate_format,
+ image->width,
+ image->height,
+ NULL,
+ 0);
+ if (pixman_image == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ pixman_image_composite32 (PIXMAN_OP_SRC,
+ image->pixman_image,
+ NULL,
+ pixman_image,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ image->width, image->height);
+
+ ximage.bits_per_pixel = image_masks.bpp;
+ ximage.data = (char *) pixman_image_get_data (pixman_image);
+ ximage.bytes_per_line = pixman_image_get_stride (pixman_image);
+
+ ret = XInitImage (&ximage);
+ assert (ret != 0);
+ }
+ else if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
+ (image_masks.red_mask == surface->r_mask || surface->r_mask == 0) &&
+ (image_masks.green_mask == surface->g_mask || surface->g_mask == 0) &&
+ (image_masks.blue_mask == surface->b_mask || surface->b_mask == 0))
+ {
+ int ret;
+
+ ximage.bits_per_pixel = image_masks.bpp;
+ ximage.bytes_per_line = image->stride;
+ ximage.data = (char *)image->data;
+ own_data = FALSE;
+
+ ret = XInitImage (&ximage);
+ assert (ret != 0);
+ }
+ 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;
+
+ if (surface->depth > 16)
+ ximage.bits_per_pixel = 32;
+ else if (surface->depth > 8)
+ ximage.bits_per_pixel = 16;
+ else if (surface->depth > 1)
+ ximage.bits_per_pixel = 8;
+ else
+ ximage.bits_per_pixel = 1;
+ stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width,
+ ximage.bits_per_pixel);
+ ximage.bytes_per_line = stride;
+ ximage.data = _cairo_malloc_ab (stride, ximage.height);
+ if (unlikely (ximage.data == NULL)) {
+ own_data = FALSE;
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+
+ 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;
+
+ 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 (pixman_image)
+ pixman_image_unref (pixman_image);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+
+ status = _get_image_surface (surface, NULL, &image, NULL);
+ if (unlikely (status))
+ return status;
+
+ *image_out = image;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_snapshot (void *abstract_surface)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+
+ status = _get_image_surface (surface, NULL, &image, NULL);
+ if (unlikely (status))
+ return _cairo_surface_create_in_error (status);
+
+ return &image->base;
+}
+
+static void
+_cairo_xlib_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_xlib_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
+
+ status = _get_image_surface (surface, interest_rect, &image, image_rect_out);
+ if (unlikely (status))
+ return status;
+
+ *image_out = image;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xlib_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ status = _draw_image_surface (surface, image,
+ 0, 0, image->width, image->height,
+ image_rect->x, image_rect->y);
+ status = _cairo_surface_set_error (&surface->base, status);
+
+ cairo_surface_destroy (&image->base);
+}
+
+/*
+ * Return whether two xlib surfaces share the same
+ * screen. Both core and Render drawing require this
+ * when using multiple drawables in an operation.
+ */
+static inline cairo_bool_t
+_cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst,
+ cairo_xlib_surface_t *src)
+{
+ return dst->screen == src->screen;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_xlib_surface_t *clone;
+ cairo_status_t status;
+
+ if (src->backend == surface->base.backend ) {
+ cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src;
+
+ if (_cairo_xlib_surface_same_screen (surface, xlib_src)) {
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+ } else if (_cairo_surface_is_image (src)) {
+ cairo_image_surface_t *image_src = (cairo_image_surface_t *)src;
+
+ if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return UNSUPPORTED ("roi too large for xlib");
+
+ clone = (cairo_xlib_surface_t *)
+ _cairo_xlib_surface_create_similar (surface,
+ image_src->base.content,
+ width, height);
+ if (clone == NULL)
+ return UNSUPPORTED ("unhandled image format, no similar surface");
+
+ if (unlikely (clone->base.status))
+ return clone->base.status;
+
+ status = _draw_image_surface (clone, image_src,
+ src_x, src_y,
+ width, height,
+ 0, 0);
+ if (unlikely (status)) {
+ cairo_surface_destroy (&clone->base);
+ return status;
+ }
+
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+ *clone_out = &clone->base;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_solid_pattern_surface (void *abstract_surface,
+ const cairo_solid_pattern_t *solid_pattern)
+{
+ /* This function's only responsibility is to create a proper surface
+ * for when XRender is not available. The proper surface is a xlib
+ * surface (as opposed to image surface which is what create_similar
+ * returns in those cases) and the size of the dithering pattern, not
+ * 1x1. This surface can then be used in
+ * _cairo_xlib_surface_solid_fill_rectangles() to do dithered "solid"
+ * fills using core protocol */
+
+ cairo_xlib_surface_t *other = abstract_surface;
+ cairo_image_surface_t *image;
+ cairo_xlib_surface_t *surface = NULL;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_xlib_display_t *display;
+
+ int width = ARRAY_LENGTH (dither_pattern[0]);
+ int height = ARRAY_LENGTH (dither_pattern);
+
+ Pixmap pixmap = None;
+
+ if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other))
+ return NULL;
+
+ image = (cairo_image_surface_t *)
+ _cairo_image_surface_create_with_content (_cairo_color_get_content (&solid_pattern->color),
+ width, height);
+ status = image->base.status;
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _cairo_xlib_display_acquire (other->base.device, &display);
+ if (unlikely (status))
+ goto BAIL;
+
+ pixmap = XCreatePixmap (display->display,
+ other->drawable,
+ width, height,
+ other->depth);
+ cairo_device_release (&display->base);
+
+ surface = (cairo_xlib_surface_t *)
+ _cairo_xlib_surface_create_internal (other->screen,
+ pixmap,
+ other->visual,
+ other->xrender_format,
+ width, height,
+ other->depth);
+ status = surface->base.status;
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _cairo_surface_paint (&image->base,
+ CAIRO_OPERATOR_SOURCE,
+ &solid_pattern->base,
+ NULL);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _draw_image_surface (surface, image,
+ 0, 0,
+ width, height,
+ 0, 0);
+ if (unlikely (status))
+ goto BAIL;
+
+ BAIL:
+ cairo_surface_destroy (&image->base);
+
+ if (status) {
+ if (pixmap != None) {
+ if (!_cairo_xlib_display_acquire (other->base.device, &display)) {
+ XFreePixmap (display->display, pixmap);
+ cairo_device_release (&display->base);
+ }
+ }
+ cairo_surface_destroy (&surface->base);
+
+ return _cairo_surface_create_in_error (status);
+ }
+
+ surface->owns_pixmap = TRUE;
+ return &surface->base;
+}
+
+static cairo_bool_t
+_cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface,
+ const cairo_solid_pattern_t *solid_pattern)
+{
+ cairo_xlib_surface_t *other = abstract_surface;
+ return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other);
+}
+
+static cairo_status_t
+_cairo_xlib_surface_set_matrix (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ const cairo_matrix_t *matrix,
+ double xc,
+ double yc)
+{
+ XTransform xtransform;
+
+ /* Casting between pixman_transform_t and XTransform is safe because
+ * they happen to be the exact same type.
+ */
+ _cairo_matrix_to_pixman_matrix (matrix,
+ (pixman_transform_t *) &xtransform,
+ xc, yc);
+
+ if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (! CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface))
+ return UNSUPPORTED ("XRender does not support picture transforms");
+
+ XRenderSetPictureTransform (display->display, surface->src_picture, &xtransform);
+ surface->xtransform = xtransform;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_set_filter (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ cairo_filter_t filter)
+{
+ const char *render_filter;
+
+ if (surface->filter == filter)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) {
+ if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST)
+ return CAIRO_STATUS_SUCCESS;
+
+ return UNSUPPORTED ("XRender does not support filter");
+ }
+
+ switch (filter) {
+ case CAIRO_FILTER_FAST:
+ render_filter = FilterFast;
+ break;
+ case CAIRO_FILTER_GOOD:
+ render_filter = FilterGood;
+ break;
+ case CAIRO_FILTER_BEST:
+ render_filter = FilterBest;
+ break;
+ case CAIRO_FILTER_NEAREST:
+ render_filter = FilterNearest;
+ break;
+ case CAIRO_FILTER_BILINEAR:
+ render_filter = FilterBilinear;
+ break;
+ case CAIRO_FILTER_GAUSSIAN:
+ /* XXX: The GAUSSIAN value has no implementation in cairo
+ * whatsoever, so it was really a mistake to have it in the
+ * API. We could fix this by officially deprecating it, or
+ * else inventing semantics and providing an actual
+ * implementation for it. */
+ default:
+ render_filter = FilterBest;
+ break;
+ }
+
+ XRenderSetPictureFilter (display->display, surface->src_picture,
+ (char *) render_filter, NULL, 0);
+ surface->filter = filter;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface,
+ cairo_extend_t extend,
+ unsigned long *mask,
+ XRenderPictureAttributes *pa)
+{
+ int repeat;
+
+ if (surface->extend == extend)
+ return CAIRO_STATUS_SUCCESS;
+
+ switch (extend) {
+ case CAIRO_EXTEND_NONE:
+ repeat = RepeatNone;
+ break;
+ case CAIRO_EXTEND_REPEAT:
+ repeat = RepeatNormal;
+ break;
+ case CAIRO_EXTEND_REFLECT:
+ if (surface->buggy_pad_reflect)
+ return UNSUPPORTED ("buggy reflect");
+
+ repeat = RepeatReflect;
+ break;
+ case CAIRO_EXTEND_PAD:
+ if (surface->buggy_pad_reflect)
+ return UNSUPPORTED ("buggy pad");
+
+ repeat = RepeatPad;
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ *mask |= CPRepeat;
+ pa->repeat = repeat;
+
+ surface->extend = extend;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_set_component_alpha (cairo_xlib_surface_t *surface,
+ cairo_bool_t ca,
+ unsigned long *mask,
+ XRenderPictureAttributes *pa)
+{
+ if (surface->has_component_alpha == ca)
+ return CAIRO_STATUS_SUCCESS;
+
+ *mask |= CPComponentAlpha;
+ pa->component_alpha = ca;
+
+ surface->has_component_alpha = ca;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *surface,
+ const cairo_surface_attributes_t *attributes,
+ double xc,
+ double yc)
+{
+ cairo_int_status_t status;
+ XRenderPictureAttributes pa;
+ unsigned long mask = 0;
+
+ _cairo_xlib_surface_ensure_src_picture (display, surface);
+
+ status = _cairo_xlib_surface_set_matrix (display, surface,
+ &attributes->matrix, xc, yc);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xlib_surface_set_repeat (surface, attributes->extend,
+ &mask, &pa);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xlib_surface_set_component_alpha (surface,
+ attributes->has_component_alpha,
+ &mask, &pa);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xlib_surface_set_filter (display, surface, attributes->filter);
+ if (unlikely (status))
+ return status;
+
+ if (mask)
+ XRenderChangePicture (display->display, surface->src_picture, mask, &pa);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Checks whether we can can directly draw from src to dst with
+ * the core protocol: either with CopyArea or using src as a
+ * a tile in a GC.
+ */
+static cairo_bool_t
+_surfaces_compatible (cairo_xlib_surface_t *dst,
+ cairo_xlib_surface_t *src)
+{
+ /* same screen */
+ if (! _cairo_xlib_surface_same_screen (dst, src))
+ return FALSE;
+
+ /* same depth (for core) */
+ if (src->depth != dst->depth)
+ return FALSE;
+
+ /* if Render is supported, match picture formats */
+ if (src->xrender_format != dst->xrender_format)
+ return FALSE;
+ else if (src->xrender_format != NULL)
+ return TRUE;
+
+ /* Without Render, match visuals instead */
+ if (src->visual == dst->visual)
+ return TRUE;
+
+ return FALSE;
+}
+
+static cairo_bool_t
+_surface_has_alpha (cairo_xlib_surface_t *surface)
+{
+ if (surface->xrender_format) {
+ if (surface->xrender_format->type == PictTypeDirect &&
+ surface->xrender_format->direct.alphaMask != 0)
+ return TRUE;
+ else
+ return FALSE;
+ } else {
+ /* In the no-render case, we never have alpha */
+ return FALSE;
+ }
+}
+
+/* Returns true if the given operator and alpha combination requires alpha
+ * compositing to complete on source and destination surfaces with the same
+ * format. i.e. if a simple bitwise copy is not appropriate.
+ */
+static cairo_bool_t
+_operator_needs_alpha_composite (cairo_operator_t op,
+ cairo_bool_t surfaces_have_alpha)
+{
+ if (op == CAIRO_OPERATOR_SOURCE)
+ return FALSE;
+
+ if (op == CAIRO_OPERATOR_OVER ||
+ op == CAIRO_OPERATOR_IN ||
+ op == CAIRO_OPERATOR_ATOP)
+ return surfaces_have_alpha;
+
+ return TRUE;
+}
+
+/* There is a bug in most older X servers with compositing using a
+ * untransformed repeating source pattern when the source is in off-screen
+ * video memory, and another with repeated transformed images using a
+ * general transform matrix. When these bugs could be triggered, we need a
+ * fallback: in the common case where we have no transformation and the
+ * source and destination have the same format/visual, we can do the
+ * operation using the core protocol for the first bug, otherwise, we need
+ * a software fallback.
+ *
+ * We can also often optimize a compositing operation by calling XCopyArea
+ * for some common cases where there is no alpha compositing to be done.
+ * We figure that out here as well.
+ */
+typedef enum {
+ DO_RENDER, /* use render */
+ DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */
+ DO_XTILE, /* core protocol XSetTile optimization/fallback */
+ DO_UNSUPPORTED /* software fallback */
+} composite_operation_t;
+
+/* Initial check for the render bugs; we need to recheck for the
+ * offscreen-memory bug after we turn patterns into surfaces, since that
+ * may introduce a repeating pattern for gradient patterns. We don't need
+ * to check for the repeat+transform bug because gradient surfaces aren't
+ * transformed.
+ *
+ * All we do here is reject cases where we *know* are going to
+ * hit the bug and won't be able to use a core protocol fallback.
+ */
+static composite_operation_t
+_categorize_composite_operation (cairo_xlib_surface_t *dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ cairo_bool_t have_mask)
+
+{
+ if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (dst, op))
+ return DO_UNSUPPORTED;
+
+ if (! dst->buggy_repeat)
+ return DO_RENDER;
+
+ if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID &&
+ src_pattern->extend == CAIRO_EXTEND_REPEAT)
+ {
+ /* Check for the bug with repeat patterns nad general transforms. */
+ if (! _cairo_matrix_is_integer_translation (&src_pattern->matrix,
+ NULL, NULL))
+ {
+ return DO_UNSUPPORTED;
+ }
+
+ if (have_mask ||
+ !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER))
+ {
+ return DO_UNSUPPORTED;
+ }
+
+ if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) src_pattern;
+
+ /* This is the case where we have the bug involving
+ * untransformed repeating source patterns with off-screen
+ * video memory; reject some cases where a core protocol
+ * fallback is impossible.
+ */
+ if (_cairo_surface_is_xlib (surface_pattern->surface)) {
+ cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) surface_pattern->surface;
+
+ if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src))
+ return DO_UNSUPPORTED;
+
+ /* If these are on the same screen but otherwise incompatible,
+ * make a copy as core drawing can't cross depths and doesn't
+ * work right across visuals of the same depth
+ */
+ if (_cairo_xlib_surface_same_screen (dst, src) &&
+ !_surfaces_compatible (dst, src))
+ {
+ return DO_UNSUPPORTED;
+ }
+ }
+ }
+ }
+
+ return DO_RENDER;
+}
+
+/* Recheck for composite-repeat once we've turned patterns into Xlib surfaces
+ * If we end up returning DO_UNSUPPORTED here, we're throwing away work we
+ * did to turn gradients into a pattern, but most of the time we can handle
+ * that case with core protocol fallback.
+ *
+ * Also check here if we can just use XCopyArea, instead of going through
+ * Render.
+ */
+static composite_operation_t
+_recategorize_composite_operation (cairo_xlib_surface_t *dst,
+ cairo_operator_t op,
+ cairo_xlib_surface_t *src,
+ cairo_surface_attributes_t *src_attr,
+ cairo_bool_t have_mask)
+{
+ /* Can we use the core protocol? */
+ if (! have_mask &&
+ _surfaces_compatible (src, dst) &&
+ _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) &&
+ ! _operator_needs_alpha_composite (op, _surface_has_alpha (dst)))
+ {
+ if (src_attr->extend == CAIRO_EXTEND_NONE)
+ return DO_XCOPYAREA;
+
+ if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT)
+ return DO_XTILE;
+ }
+
+ if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT &&
+ (src->width != 1 || src->height != 1))
+ return DO_UNSUPPORTED;
+
+ if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src))
+ return DO_UNSUPPORTED;
+
+ if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))
+ return DO_UNSUPPORTED;
+
+ return DO_RENDER;
+}
+
+static int
+_render_operator (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_CLEAR:
+ return PictOpClear;
+
+ case CAIRO_OPERATOR_SOURCE:
+ return PictOpSrc;
+ case CAIRO_OPERATOR_OVER:
+ return PictOpOver;
+ case CAIRO_OPERATOR_IN:
+ return PictOpIn;
+ case CAIRO_OPERATOR_OUT:
+ return PictOpOut;
+ case CAIRO_OPERATOR_ATOP:
+ return PictOpAtop;
+
+ case CAIRO_OPERATOR_DEST:
+ return PictOpDst;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return PictOpOverReverse;
+ case CAIRO_OPERATOR_DEST_IN:
+ return PictOpInReverse;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return PictOpOutReverse;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return PictOpAtopReverse;
+
+ case CAIRO_OPERATOR_XOR:
+ return PictOpXor;
+ case CAIRO_OPERATOR_ADD:
+ return PictOpAdd;
+ case CAIRO_OPERATOR_SATURATE:
+ return PictOpSaturate;
+
+ case CAIRO_OPERATOR_MULTIPLY:
+ return PictOpMultiply;
+ case CAIRO_OPERATOR_SCREEN:
+ return PictOpScreen;
+ case CAIRO_OPERATOR_OVERLAY:
+ return PictOpOverlay;
+ case CAIRO_OPERATOR_DARKEN:
+ return PictOpDarken;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return PictOpLighten;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return PictOpColorDodge;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return PictOpColorBurn;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return PictOpHardLight;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return PictOpSoftLight;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return PictOpDifference;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return PictOpExclusion;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return PictOpHSLHue;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return PictOpHSLSaturation;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return PictOpHSLColor;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return PictOpHSLLuminosity;
+
+ default:
+ ASSERT_NOT_REACHED;
+ return PictOpOver;
+ }
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *dst,
+ const cairo_pattern_t *pattern,
+ int x, int y,
+ int width, int height,
+ cairo_xlib_surface_t **surface_out,
+ cairo_surface_attributes_t *attributes)
+{
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ {
+ cairo_gradient_pattern_t *gradient =
+ (cairo_gradient_pattern_t *) pattern;
+ cairo_matrix_t matrix = pattern->matrix;
+ cairo_xlib_surface_t *surface;
+ char buf[CAIRO_STACK_BUFFER_SIZE];
+ XFixed *stops;
+ XRenderColor *colors;
+ XRenderPictFormat *format;
+ Picture picture;
+ unsigned int i;
+
+ if (dst->buggy_gradients)
+ break;
+
+ if (gradient->n_stops < 2) /* becomes a solid */
+ break;
+
+ if (gradient->n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor)))
+ {
+ stops = (XFixed *) buf;
+ }
+ else
+ {
+ stops =
+ _cairo_malloc_ab (gradient->n_stops,
+ sizeof (XFixed) + sizeof (XRenderColor));
+ if (unlikely (stops == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ colors = (XRenderColor *) (stops + gradient->n_stops);
+ for (i = 0; i < gradient->n_stops; i++) {
+ stops[i] =
+ _cairo_fixed_16_16_from_double (gradient->stops[i].offset);
+
+ colors[i].red = gradient->stops[i].color.red_short;
+ colors[i].green = gradient->stops[i].color.green_short;
+ colors[i].blue = gradient->stops[i].color.blue_short;
+ colors[i].alpha = gradient->stops[i].color.alpha_short;
+ }
+
+#if 0
+ /* For some weird reason the X server is sometimes getting
+ * CreateGradient requests with bad length. So far I've only seen
+ * XRenderCreateLinearGradient request with 4 stops sometime end up
+ * with length field matching 0 stops at the server side. I've
+ * looked at the libXrender code and I can't see anything that
+ * could cause this behavior. However, for some reason having a
+ * XSync call here seems to avoid the issue so I'll keep it here
+ * until it's solved.
+ */
+ XSync (display->display, False);
+#endif
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
+ XLinearGradient grad;
+
+ cairo_fixed_t xdim, ydim;
+
+ xdim = linear->p2.x - linear->p1.x;
+ ydim = linear->p2.y - linear->p1.y;
+
+ /*
+ * Transform the matrix to avoid overflow when converting between
+ * cairo_fixed_t and pixman_fixed_t (without incurring performance
+ * loss when the transformation is unnecessary).
+ *
+ * XXX: Consider converting out-of-range co-ordinates and transforms.
+ * Having a function to compute the required transformation to
+ * "normalize" a given bounding box would be generally useful -
+ * cf linear patterns, gradient patterns, surface patterns...
+ */
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
+ if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT ||
+ _cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)
+ {
+ double sf;
+
+ if (xdim > ydim)
+ sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
+ else
+ sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
+
+ grad.p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
+ grad.p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
+ grad.p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
+ grad.p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
+
+ cairo_matrix_scale (&matrix, sf, sf);
+ }
+ else
+ {
+ grad.p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
+ grad.p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
+ grad.p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
+ grad.p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
+ }
+
+ picture = XRenderCreateLinearGradient (display->display, &grad,
+ stops, colors,
+ gradient->n_stops);
+ } else {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
+ XRadialGradient grad;
+
+ grad.inner.x = _cairo_fixed_to_16_16 (radial->c1.x);
+ grad.inner.y = _cairo_fixed_to_16_16 (radial->c1.y);
+ grad.inner.radius = _cairo_fixed_to_16_16 (radial->r1);
+
+ grad.outer.x = _cairo_fixed_to_16_16 (radial->c2.x);
+ grad.outer.y = _cairo_fixed_to_16_16 (radial->c2.y);
+ grad.outer.radius = _cairo_fixed_to_16_16 (radial->r2);
+
+ picture = XRenderCreateRadialGradient (display->display, &grad,
+ stops, colors,
+ gradient->n_stops);
+
+ }
+
+ if (stops != (XFixed *) buf)
+ free (stops);
+
+ if (unlikely (picture == None))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ /* Wrap the remote Picture in an xlib surface. */
+ format = _cairo_xlib_display_get_xrender_format (display,
+ CAIRO_FORMAT_ARGB32);
+
+ surface = (cairo_xlib_surface_t *)
+ _cairo_xlib_surface_create_internal (dst->screen, None,
+ NULL, format,
+ /* what could possibly go wrong? */
+ XLIB_COORD_MAX, XLIB_COORD_MAX, 32);
+ if (unlikely (surface->base.status)) {
+ XRenderFreePicture (display->display, picture);
+ return surface->base.status;
+ }
+
+ surface->src_picture = picture;
+
+ attributes->matrix = matrix;
+ attributes->extend = pattern->extend;
+ attributes->filter = CAIRO_FILTER_NEAREST;
+ attributes->x_offset = 0;
+ attributes->y_offset = 0;
+ attributes->has_component_alpha = FALSE;
+
+ *surface_out = surface;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ default:
+ ASSERT_NOT_REACHED;
+ case CAIRO_PATTERN_TYPE_SOLID:
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ break;
+ }
+
+ return _cairo_pattern_acquire_surface (pattern, &dst->base,
+ x, y, width, height,
+ dst->buggy_pad_reflect ?
+ CAIRO_PATTERN_ACQUIRE_NO_REFLECT :
+ CAIRO_PATTERN_ACQUIRE_NONE,
+ (cairo_surface_t **) surface_out,
+ attributes);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_acquire_pattern_surfaces (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *dst,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_xlib_surface_t **src_out,
+ cairo_xlib_surface_t **mask_out,
+ cairo_surface_attributes_t *src_attr,
+ cairo_surface_attributes_t *mask_attr)
+{
+ if (! dst->buggy_gradients &&
+ (src->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ src->type == CAIRO_PATTERN_TYPE_RADIAL ||
+ (mask && (mask->type == CAIRO_PATTERN_TYPE_LINEAR ||
+ mask->type == CAIRO_PATTERN_TYPE_RADIAL))))
+ {
+ cairo_int_status_t status;
+
+ status = _cairo_xlib_surface_acquire_pattern_surface (display,
+ dst, src,
+ src_x, src_y,
+ width, height,
+ src_out,
+ src_attr);
+ if (unlikely (status))
+ return status;
+
+ if (mask) {
+ status = _cairo_xlib_surface_acquire_pattern_surface (display,
+ dst, mask,
+ mask_x,
+ mask_y,
+ width,
+ height,
+ mask_out,
+ mask_attr);
+ if (unlikely (status)) {
+ _cairo_pattern_release_surface (src, &(*src_out)->base,
+ src_attr);
+ return status;
+ }
+ } else {
+ *mask_out = NULL;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return _cairo_pattern_acquire_surfaces (src, mask,
+ &dst->base,
+ src_x, src_y,
+ mask_x, mask_y,
+ width, height,
+ dst->buggy_pad_reflect ?
+ CAIRO_PATTERN_ACQUIRE_NO_REFLECT :
+ CAIRO_PATTERN_ACQUIRE_NONE,
+ (cairo_surface_t **) src_out,
+ (cairo_surface_t **) mask_out,
+ src_attr, mask_attr);
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_upload(cairo_xlib_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ int src_x, int src_y,
+ int dst_x, int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_image_surface_t *image;
+ cairo_rectangle_int_t extents;
+ cairo_status_t status;
+ int tx, ty;
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ image = (cairo_image_surface_t *) ((cairo_surface_pattern_t *) pattern)->surface;
+ if (image->base.type != CAIRO_SURFACE_TYPE_IMAGE)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! (op == CAIRO_OPERATOR_SOURCE ||
+ (op == CAIRO_OPERATOR_OVER &&
+ (image->base.content & CAIRO_CONTENT_ALPHA) == 0)))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (image->base.backend->type != CAIRO_SURFACE_TYPE_IMAGE) {
+ if (image->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
+ image = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) image)->target;
+ extents.x = extents.y = 0;
+ extents.width = image->width;
+ extents.height = image->height;
+ } else if (image->base.backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
+ cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) image;
+ image = (cairo_image_surface_t *) sub->target;
+ src_x += sub->extents.x;
+ src_y += sub->extents.y;
+ extents = sub->extents;
+ } else {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ } else {
+ extents.x = extents.y = 0;
+ extents.width = image->width;
+ extents.height = image->height;
+ }
+
+ if (image->format == CAIRO_FORMAT_INVALID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (image->depth != surface->depth)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (! _cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ src_x += tx;
+ src_y += ty;
+
+ /* XXX for EXTEND_NONE perform unbounded fixups? */
+ if (src_x < extents.x ||
+ src_y < extents.y ||
+ src_x + width > (unsigned) extents.width ||
+ src_y + height > (unsigned) extents.height)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ status = cairo_device_acquire (surface->base.device);
+ if (unlikely (status))
+ return status;
+
+ if (clip_region != NULL) {
+ int n, num_rect;
+
+ src_x -= dst_x;
+ src_y -= dst_y;
+
+ num_rect = cairo_region_num_rectangles (clip_region);
+ for (n = 0; n < num_rect; n++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, n, &rect);
+ status = _draw_image_surface (surface, image,
+ rect.x + src_x, rect.y + src_y,
+ rect.width, rect.height,
+ rect.x, rect.y);
+ if (unlikely (status))
+ break;
+ }
+ } else {
+ status = _draw_image_surface (surface, image,
+ src_x, src_y,
+ width, height,
+ dst_x, dst_y);
+ }
+
+ cairo_device_release (surface->base.device);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ const cairo_pattern_t *mask_pattern,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region)
+{
+ cairo_surface_attributes_t src_attr, mask_attr;
+ cairo_xlib_surface_t *dst = abstract_dst;
+ cairo_xlib_surface_t *src;
+ cairo_xlib_surface_t *mask;
+ cairo_xlib_display_t *display;
+ cairo_int_status_t status;
+ composite_operation_t operation;
+ int itx, ity;
+ cairo_bool_t is_integer_translation;
+ GC gc;
+
+ if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))
+ return UNSUPPORTED ("no support for masks");
+
+ operation = _categorize_composite_operation (dst, op, src_pattern,
+ mask_pattern != NULL);
+ if (operation == DO_UNSUPPORTED)
+ return UNSUPPORTED ("unsupported operation");
+
+ X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable));
+
+ if (mask_pattern == NULL) {
+ /* Can we do a simple upload in-place? */
+ status = _cairo_xlib_surface_upload(dst, op, src_pattern,
+ src_x, src_y,
+ dst_x, dst_y,
+ width, height,
+ clip_region);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+ }
+
+ status = _cairo_xlib_display_acquire (dst-> base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ status =
+ _cairo_xlib_surface_acquire_pattern_surfaces (display, dst,
+ src_pattern, mask_pattern,
+ src_x, src_y,
+ mask_x, mask_y,
+ width, height,
+ &src, &mask,
+ &src_attr, &mask_attr);
+ if (unlikely (status))
+ goto BAIL0;
+
+ /* check for fallback surfaces that we cannot handle ... */
+ assert (_cairo_surface_is_xlib (&src->base));
+ assert (mask == NULL || _cairo_surface_is_xlib (&mask->base));
+
+ if (mask != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (mask)) {
+ status = UNSUPPORTED ("unsupported mask");
+ goto BAIL;
+ }
+
+ operation = _recategorize_composite_operation (dst, op, src, &src_attr,
+ mask_pattern != NULL);
+ if (operation == DO_UNSUPPORTED) {
+ status = UNSUPPORTED ("unsupported operation");
+ goto BAIL;
+ }
+
+ switch (operation)
+ {
+ case DO_RENDER:
+ status = _cairo_xlib_surface_set_attributes (display,
+ src, &src_attr,
+ dst_x + width / 2.,
+ dst_y + height / 2.);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _cairo_xlib_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_xlib_surface_ensure_dst_picture (display, dst);
+ if (mask) {
+ status = _cairo_xlib_surface_set_attributes (display,
+ mask, &mask_attr,
+ dst_x + width / 2.,
+ dst_y + height/ 2.);
+ if (unlikely (status))
+ goto BAIL;
+
+ XRenderComposite (display->display,
+ _render_operator (op),
+ src->src_picture,
+ mask->src_picture,
+ dst->dst_picture,
+ src_x + src_attr.x_offset,
+ src_y + src_attr.y_offset,
+ mask_x + mask_attr.x_offset,
+ mask_y + mask_attr.y_offset,
+ dst_x, dst_y,
+ width, height);
+ } else {
+ XRenderComposite (display->display,
+ _render_operator (op),
+ src->src_picture,
+ 0,
+ dst->dst_picture,
+ src_x + src_attr.x_offset,
+ src_y + src_attr.y_offset,
+ 0, 0,
+ dst_x, dst_y,
+ width, height);
+ }
+
+ break;
+
+ case DO_XCOPYAREA:
+ status = _cairo_xlib_surface_get_gc (display, dst, &gc);
+ if (unlikely (status))
+ goto BAIL;
+
+ is_integer_translation =
+ _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity);
+ /* This is a pre-condition for DO_XCOPYAREA. */
+ assert (is_integer_translation);
+
+ if (clip_region == NULL) {
+ XCopyArea (display->display, src->drawable, dst->drawable, gc,
+ src_x + src_attr.x_offset + itx,
+ src_y + src_attr.y_offset + ity,
+ width, height,
+ dst_x, dst_y);
+ } else {
+ int n, num_rects, x, y;
+
+ x = src_x + src_attr.x_offset + itx - dst_x;
+ y = src_y + src_attr.y_offset + ity - dst_y;
+
+ num_rects = cairo_region_num_rectangles (clip_region);
+ for (n = 0; n < num_rects; n++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, n, &rect);
+ XCopyArea (display->display, src->drawable, dst->drawable, gc,
+ rect.x + x, rect.y + y,
+ rect.width, rect.height,
+ rect.x, rect.y);
+ }
+ }
+
+ _cairo_xlib_surface_put_gc (display, dst, gc);
+ break;
+
+ case DO_XTILE:
+ /* This case is only used for bug fallbacks, though we also use it for
+ * the case where we don't have the RENDER extension, by forcing
+ * buggy_repeat to TRUE.
+ *
+ * We've checked that we have a repeating unscaled source in
+ * _recategorize_composite_operation.
+ */
+
+ status = _cairo_xlib_surface_get_gc (display, dst, &gc);
+ if (unlikely (status))
+ goto BAIL;
+
+ is_integer_translation =
+ _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity);
+ /* This is a pre-condition for DO_XTILE. */
+ assert (is_integer_translation);
+
+ XSetTSOrigin (display->display, gc,
+ - (itx + src_attr.x_offset), - (ity + src_attr.y_offset));
+ XSetTile (display->display, gc, src->drawable);
+
+ if (clip_region == NULL) {
+ XFillRectangle (display->display, dst->drawable, gc,
+ dst_x, dst_y, width, height);
+ } else {
+ int n, num_rects;
+
+ num_rects = cairo_region_num_rectangles (clip_region);
+ for (n = 0; n < num_rects; n++) {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (clip_region, n, &rect);
+ XFillRectangle (display->display, dst->drawable, gc,
+ rect.x, rect.y, rect.width, rect.height);
+ }
+ }
+
+ _cairo_xlib_surface_put_gc (display, dst, gc);
+ break;
+
+ case DO_UNSUPPORTED:
+ default:
+ ASSERT_NOT_REACHED;
+ }
+
+ if (!_cairo_operator_bounded_by_source (op))
+ status = _cairo_surface_composite_fixup_unbounded (&dst->base,
+ &src_attr, src->width, src->height,
+ mask ? &mask_attr : NULL,
+ mask ? mask->width : 0,
+ mask ? mask->height : 0,
+ src_x, src_y,
+ mask_x, mask_y,
+ dst_x, dst_y, width, height,
+ clip_region);
+
+ BAIL:
+ if (mask)
+ _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr);
+
+ _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr);
+
+ BAIL0:
+ cairo_device_release (&display->base);
+
+ return status;
+}
+
+/* XXX move this out of core and into acquire_pattern_surface() above. */
+static cairo_int_status_t
+_cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_status_t status;
+ cairo_solid_pattern_t solid;
+ cairo_surface_t *solid_surface = NULL;
+ cairo_surface_attributes_t attrs;
+ cairo_xlib_display_t *display;
+ GC gc;
+ int i;
+
+ _cairo_pattern_init_solid (&solid, color);
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xlib_surface_get_gc (display, surface, &gc);
+ if (unlikely (status))
+ return status;
+
+ X_DEBUG ((display->display, "solid_fill_rectangles (dst=%x)", (unsigned int) surface->drawable));
+
+ status = _cairo_pattern_acquire_surface (&solid.base, &surface->base,
+ 0, 0,
+ ARRAY_LENGTH (dither_pattern[0]),
+ ARRAY_LENGTH (dither_pattern),
+ CAIRO_PATTERN_ACQUIRE_NONE,
+ &solid_surface,
+ &attrs);
+ if (unlikely (status)) {
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+ cairo_device_release (&display->base);
+ return status;
+ }
+
+ assert (_cairo_surface_is_xlib (solid_surface));
+
+ XSetTSOrigin (display->display, gc,
+ - (surface->base.device_transform.x0 + attrs.x_offset),
+ - (surface->base.device_transform.y0 + attrs.y_offset));
+ XSetTile (display->display, gc,
+ ((cairo_xlib_surface_t *) solid_surface)->drawable);
+
+ for (i = 0; i < num_rects; i++) {
+ XFillRectangle (display->display, surface->drawable, gc,
+ rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height);
+ }
+
+ _cairo_xlib_surface_put_gc (display, surface, gc);
+
+ _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs);
+
+ cairo_device_release (&display->base);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_fill_rectangles (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects)
+{
+ cairo_xlib_surface_t *surface = abstract_surface;
+ cairo_xlib_display_t *display;
+ XRenderColor render_color;
+ cairo_status_t status;
+ int i;
+
+ if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (surface, op))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) {
+ if (op == CAIRO_OPERATOR_CLEAR ||
+ ((op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER) &&
+ CAIRO_COLOR_IS_OPAQUE (color)))
+ {
+ return _cairo_xlib_surface_solid_fill_rectangles (surface, color,
+ rects, num_rects);
+ }
+
+ return UNSUPPORTED ("no support for FillRectangles with this op");
+ }
+
+ status = _cairo_xlib_display_acquire (surface->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable));
+
+ render_color.red = color->red_short;
+ render_color.green = color->green_short;
+ render_color.blue = color->blue_short;
+ render_color.alpha = color->alpha_short;
+
+ status = _cairo_xlib_surface_set_clip_region (surface, NULL);
+ assert (status == CAIRO_STATUS_SUCCESS);
+
+ _cairo_xlib_surface_ensure_dst_picture (display, surface);
+ if (num_rects == 1) {
+ /* Take advantage of the protocol compaction that libXrender performs
+ * to amalgamate sequences of XRenderFillRectangle().
+ */
+ XRenderFillRectangle (display->display,
+ _render_operator (op),
+ surface->dst_picture,
+ &render_color,
+ rects->x,
+ rects->y,
+ rects->width,
+ rects->height);
+ } else {
+ XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
+ XRectangle *xrects = static_xrects;
+
+ if (num_rects > ARRAY_LENGTH (static_xrects)) {
+ xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle));
+ if (unlikely (xrects == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ }
+
+ for (i = 0; i < num_rects; i++) {
+ xrects[i].x = rects[i].x;
+ xrects[i].y = rects[i].y;
+ xrects[i].width = rects[i].width;
+ xrects[i].height = rects[i].height;
+ }
+
+ XRenderFillRectangles (display->display,
+ _render_operator (op),
+ surface->dst_picture,
+ &render_color, xrects, num_rects);
+
+ if (xrects != static_xrects)
+ free (xrects);
+ }
+
+BAIL:
+ cairo_device_release (&display->base);
+ return status;
+}
+
+#define CAIRO_FIXED_16_16_MIN -32768
+#define CAIRO_FIXED_16_16_MAX 32767
+
+static cairo_bool_t
+_line_exceeds_16_16 (const cairo_line_t *line)
+{
+ return
+ line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+ line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+ line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+ line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+ line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+ line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
+ line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
+ line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX);
+}
+
+static void
+_project_line_x_onto_16_16 (const cairo_line_t *line,
+ cairo_fixed_t top,
+ cairo_fixed_t bottom,
+ XLineFixed *out)
+{
+ cairo_point_double_t p1, p2;
+ double m;
+
+ p1.x = _cairo_fixed_to_double (line->p1.x);
+ p1.y = _cairo_fixed_to_double (line->p1.y);
+
+ p2.x = _cairo_fixed_to_double (line->p2.x);
+ p2.y = _cairo_fixed_to_double (line->p2.y);
+
+ m = (p2.x - p1.x) / (p2.y - p1.y);
+ out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
+ out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *abstract_dst,
+ cairo_antialias_t antialias,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *clip_region)
+{
+ cairo_surface_attributes_t attributes;
+ cairo_xlib_surface_t *dst = abstract_dst;
+ cairo_xlib_surface_t *src;
+ cairo_xlib_display_t *display;
+ cairo_int_status_t status;
+ composite_operation_t operation;
+ int render_reference_x, render_reference_y;
+ int render_src_x, render_src_y;
+ XRenderPictFormat *pict_format;
+ XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)];
+ XTrapezoid *xtraps = xtraps_stack;
+ int i;
+
+ if (! CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst))
+ return UNSUPPORTED ("XRender does not support CompositeTrapezoids");
+
+ operation = _categorize_composite_operation (dst, op, pattern, TRUE);
+ if (operation == DO_UNSUPPORTED)
+ return UNSUPPORTED ("unsupported operation");
+
+ status = _cairo_xlib_display_acquire (dst->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
+
+ status = _cairo_xlib_surface_acquire_pattern_surface (display,
+ dst,
+ pattern,
+ src_x, src_y,
+ width, height,
+ &src, &attributes);
+ if (unlikely (status))
+ goto BAIL0;
+
+ operation = _recategorize_composite_operation (dst, op, src,
+ &attributes, TRUE);
+ if (operation == DO_UNSUPPORTED) {
+ status = UNSUPPORTED ("unsupported operation");
+ goto BAIL;
+ }
+
+ switch (antialias) {
+ case CAIRO_ANTIALIAS_NONE:
+ pict_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ CAIRO_FORMAT_A1);
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ case CAIRO_ANTIALIAS_DEFAULT:
+ default:
+ pict_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ CAIRO_FORMAT_A8);
+ break;
+ }
+
+ status = _cairo_xlib_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_xlib_surface_ensure_dst_picture (display, dst);
+ _cairo_xlib_surface_set_precision (display, dst, antialias);
+
+ status = _cairo_xlib_surface_set_attributes (display,
+ src, &attributes,
+ dst_x + width / 2.,
+ dst_y + height / 2.);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (num_traps > ARRAY_LENGTH (xtraps_stack)) {
+ xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid));
+ if (unlikely (xtraps == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ }
+
+ for (i = 0; i < num_traps; i++) {
+ /* top/bottom will be clamped to surface bounds */
+ xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top);
+ xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom);
+
+ /* However, all the other coordinates will have been left untouched so
+ * as not to introduce numerical error. Recompute them if they
+ * exceed the 16.16 limits.
+ */
+ if (unlikely (_line_exceeds_16_16 (&traps[i].left))) {
+ _project_line_x_onto_16_16 (&traps[i].left,
+ traps[i].top,
+ traps[i].bottom,
+ &xtraps[i].left);
+ xtraps[i].left.p1.y = xtraps[i].top;
+ xtraps[i].left.p2.y = xtraps[i].bottom;
+ } else {
+ xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x);
+ xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y);
+ xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x);
+ xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y);
+ }
+
+ if (unlikely (_line_exceeds_16_16 (&traps[i].right))) {
+ _project_line_x_onto_16_16 (&traps[i].right,
+ traps[i].top,
+ traps[i].bottom,
+ &xtraps[i].right);
+ xtraps[i].right.p1.y = xtraps[i].top;
+ xtraps[i].right.p2.y = xtraps[i].bottom;
+ } else {
+ xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x);
+ xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y);
+ xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x);
+ xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y);
+ }
+ }
+
+ if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
+ render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p1.x);
+ render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p1.y);
+ } else {
+ render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p2.x);
+ render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p2.y);
+ }
+
+ render_src_x = src_x + render_reference_x - dst_x;
+ render_src_y = src_y + render_reference_y - dst_y;
+
+ XRenderCompositeTrapezoids (display->display,
+ _render_operator (op),
+ src->src_picture, dst->dst_picture,
+ pict_format,
+ render_src_x + attributes.x_offset,
+ render_src_y + attributes.y_offset,
+ xtraps, num_traps);
+
+ if (xtraps != xtraps_stack)
+ free (xtraps);
+
+ if (! _cairo_operator_bounded_by_mask (op)) {
+ cairo_traps_t _traps;
+ cairo_box_t box;
+ cairo_rectangle_int_t extents;
+
+ /* XRenderCompositeTrapezoids() creates a mask only large enough for the
+ * trapezoids themselves, but if the operator is unbounded, then we need
+ * to actually composite all the way out to the bounds.
+ */
+ /* XXX: update the interface to pass composite rects */
+ _traps.traps = traps;
+ _traps.num_traps = num_traps;
+ _cairo_traps_extents (&_traps, &box);
+ _cairo_box_round_to_rectangle (&box, &extents);
+
+ status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base,
+ &attributes,
+ src->width, src->height,
+ extents.width, extents.height,
+ src_x, src_y,
+ -extents.x + dst_x, -extents.y + dst_y,
+ dst_x, dst_y,
+ width, height,
+ clip_region);
+ }
+
+ BAIL:
+ _cairo_pattern_release_surface (pattern, &src->base, &attributes);
+ BAIL0:
+ cairo_device_release (&display->base);
+
+ return status;
+}
+
+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 void
+_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
+
+static void
+_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font);
+
+static cairo_bool_t
+_cairo_xlib_surface_is_similar (void *surface_a,
+ void *surface_b)
+{
+ return _cairo_xlib_surface_same_screen (surface_a, surface_b);
+}
+
+static const cairo_surface_backend_t cairo_xlib_surface_backend = {
+ CAIRO_SURFACE_TYPE_XLIB,
+ _cairo_xlib_surface_create_similar,
+ _cairo_xlib_surface_finish,
+ _cairo_xlib_surface_acquire_source_image,
+ _cairo_xlib_surface_release_source_image,
+ _cairo_xlib_surface_acquire_dest_image,
+ _cairo_xlib_surface_release_dest_image,
+ _cairo_xlib_surface_clone_similar,
+ _cairo_xlib_surface_composite,
+ _cairo_xlib_surface_fill_rectangles,
+ _cairo_xlib_surface_composite_trapezoids,
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_xlib_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ _cairo_xlib_surface_get_font_options,
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ _cairo_xlib_surface_scaled_font_fini,
+ _cairo_xlib_surface_scaled_glyph_fini,
+
+ NULL, /* paint */
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+ _cairo_xlib_surface_show_glyphs,
+
+ _cairo_xlib_surface_snapshot,
+ _cairo_xlib_surface_is_similar,
+
+ NULL, /* fill_stroke */
+
+ _cairo_xlib_surface_create_solid_pattern_surface,
+ _cairo_xlib_surface_can_repaint_solid_pattern_surface
+};
+
+/**
+ * _cairo_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;
+}
+
+/* callback from CloseDisplay */
+static void
+_cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data)
+{
+ cairo_xlib_surface_t *surface = cairo_container_of (data,
+ cairo_xlib_surface_t,
+ close_display_hook);
+ Display *dpy;
+
+ dpy = display->display;
+
+ X_DEBUG ((dpy, "detach (drawable=%x)", (unsigned int) surface->drawable));
+
+ if (surface->dst_picture != None) {
+ XRenderFreePicture (dpy, surface->dst_picture);
+ surface->dst_picture = None;
+ }
+
+ if (surface->src_picture != None) {
+ XRenderFreePicture (dpy, surface->src_picture);
+ surface->src_picture = None;
+ }
+
+ if (surface->owns_pixmap) {
+ XFreePixmap (dpy, surface->drawable);
+ surface->drawable = None;
+ surface->owns_pixmap = FALSE;
+ }
+}
+
+static cairo_surface_t *
+_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen,
+ Drawable drawable,
+ 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 = 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));
+ }
+
+ _cairo_xlib_display_get_xrender_version (display,
+ &surface->render_major,
+ &surface->render_minor);
+ if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) {
+ 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);
+ }
+ }
+ } else {
+ /* we cannot use XRender for this surface, so ensure we don't try */
+ surface->render_major = -1;
+ surface->render_minor = -1;
+ }
+
+ /* initialize and hook into the CloseDisplay callback */
+ surface->close_display_hook.func = _cairo_xlib_surface_detach_display;
+ _cairo_xlib_add_close_display_hook (display,
+ &surface->close_display_hook);
+
+ cairo_device_release (&display->base);
+
+ _cairo_surface_init (&surface->base,
+ &cairo_xlib_surface_backend,
+ screen->device,
+ _xrender_format_to_content (xrender_format));
+
+ surface->screen = screen;
+
+ surface->drawable = drawable;
+ surface->owns_pixmap = FALSE;
+ surface->use_pixmap = 0;
+ surface->width = width;
+ surface->height = height;
+
+ surface->buggy_repeat = ! _cairo_xlib_display_has_repeat (screen->device);
+ if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) {
+ /* so we can use the XTile fallback */
+ surface->buggy_repeat = TRUE;
+ }
+
+ surface->buggy_pad_reflect = ! _cairo_xlib_display_has_reflect (screen->device);
+ if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface))
+ surface->buggy_pad_reflect = TRUE;
+
+ surface->buggy_gradients = ! _cairo_xlib_display_has_gradients (screen->device);
+ if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface))
+ surface->buggy_gradients = TRUE;
+
+ surface->dst_picture = None;
+ surface->src_picture = None;
+
+ surface->visual = visual;
+ surface->xrender_format = xrender_format;
+ surface->depth = depth;
+ surface->filter = CAIRO_FILTER_NEAREST;
+ surface->extend = CAIRO_EXTEND_NONE;
+ surface->has_component_alpha = FALSE;
+ surface->precision = PolyModePrecise;
+ surface->xtransform = identity;
+
+ surface->clip_region = NULL;
+ surface->clip_rects = surface->embedded_clip_rects;
+ surface->num_clip_rects = 0;
+ surface->clip_dirty = 0;
+
+ /*
+ * Compute the pixel format masks from either a XrenderFormat or
+ * 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;
+ }
+
+ 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_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
+ **/
+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 (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) {
+ /* you're lying, and you know it! */
+ return _cairo_surface_create_in_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
+ **/
+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 (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return _cairo_surface_create_in_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
+ **/
+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 (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
+ return _cairo_surface_create_in_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.
+ **/
+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)) {
+ 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 (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) {
+ status = _cairo_surface_set_error (abstract_surface,
+ _cairo_error (CAIRO_STATUS_INVALID_SIZE));
+ return;
+ }
+
+ 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.
+ **/
+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 (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) {
+ 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;
+
+ 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->dst_picture != None) {
+ status = _cairo_xlib_display_queue_resource (
+ display,
+ XRenderFreePicture,
+ surface->dst_picture);
+ if (unlikely (status)) {
+ status = _cairo_surface_set_error (&surface->base, status);
+ return;
+ }
+
+ surface->dst_picture = None;
+ }
+
+ if (surface->src_picture != None) {
+ status = _cairo_xlib_display_queue_resource (
+ display,
+ XRenderFreePicture,
+ surface->src_picture);
+ if (unlikely (status)) {
+ status = _cairo_surface_set_error (&surface->base, status);
+ return;
+ }
+
+ surface->src_picture = None;
+ }
+
+ cairo_device_release (&display->base);
+
+ surface->drawable = drawable;
+ }
+ 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;
+}
+
+enum {
+ GLYPHSET_INDEX_ARGB32,
+ GLYPHSET_INDEX_A8,
+ GLYPHSET_INDEX_A1,
+ NUM_GLYPHSETS
+};
+
+typedef struct _cairo_xlib_font_glyphset_free_glyphs {
+ GlyphSet glyphset;
+ int glyph_count;
+ unsigned long glyph_indices[128];
+} cairo_xlib_font_glyphset_free_glyphs_t;
+
+typedef struct _cairo_xlib_font_glyphset_info {
+ GlyphSet glyphset;
+ cairo_format_t format;
+ XRenderPictFormat *xrender_format;
+ cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs;
+} cairo_xlib_font_glyphset_info_t;
+
+typedef struct _cairo_xlib_surface_font_private {
+ cairo_scaled_font_t *scaled_font;
+ cairo_scaled_font_t *grayscale_font;
+ cairo_xlib_hook_t close_display_hook;
+ cairo_device_t *device;
+ cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS];
+} cairo_xlib_surface_font_private_t;
+
+/* callback from CloseDisplay */
+static void
+_cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display,
+ void *data)
+{
+ cairo_xlib_surface_font_private_t *font_private;
+ cairo_scaled_font_t *scaled_font;
+
+ font_private = cairo_container_of (data,
+ cairo_xlib_surface_font_private_t,
+ close_display_hook);
+ scaled_font = font_private->scaled_font;
+
+ CAIRO_MUTEX_LOCK (scaled_font->mutex);
+ font_private = scaled_font->surface_private;
+ scaled_font->surface_private = NULL;
+
+ _cairo_scaled_font_reset_cache (scaled_font);
+ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+
+ if (font_private != NULL) {
+ int i;
+
+ if (font_private->grayscale_font) {
+ cairo_scaled_font_destroy (font_private->grayscale_font);
+ }
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xlib_font_glyphset_info_t *glyphset_info;
+
+ glyphset_info = &font_private->glyphset_info[i];
+ if (glyphset_info->glyphset)
+ XRenderFreeGlyphSet (display->display, glyphset_info->glyphset);
+
+ if (glyphset_info->pending_free_glyphs != NULL)
+ free (glyphset_info->pending_free_glyphs);
+ }
+
+ cairo_device_destroy (font_private->device);
+ free (font_private);
+ }
+}
+
+static cairo_status_t
+_cairo_xlib_surface_font_init (cairo_xlib_display_t *display,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_xlib_surface_font_private_t *font_private;
+ int i;
+
+ font_private = malloc (sizeof (cairo_xlib_surface_font_private_t));
+ if (unlikely (font_private == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font_private->scaled_font = scaled_font;
+ font_private->grayscale_font = NULL;
+ font_private->device = cairo_device_reference (&display->base);
+
+ /* initialize and hook into the CloseDisplay callback */
+ font_private->close_display_hook.func =
+ _cairo_xlib_surface_remove_scaled_font;
+ _cairo_xlib_add_close_display_hook (display,
+ &font_private->close_display_hook);
+
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i];
+ switch (i) {
+ case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break;
+ case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break;
+ case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break;
+ default: ASSERT_NOT_REACHED; break;
+ }
+ glyphset_info->xrender_format = NULL;
+ glyphset_info->glyphset = None;
+ glyphset_info->pending_free_glyphs = NULL;
+ }
+
+ scaled_font->surface_private = font_private;
+ scaled_font->surface_backend = &cairo_xlib_surface_backend;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
+{
+ cairo_xlib_surface_font_private_t *font_private;
+ cairo_status_t status;
+
+ font_private = scaled_font->surface_private;
+ if (font_private != NULL) {
+ cairo_xlib_display_t *display;
+ int i;
+
+ if (font_private->grayscale_font) {
+ cairo_scaled_font_destroy (font_private->grayscale_font);
+ }
+ status = _cairo_xlib_display_acquire (font_private->device, &display);
+ if (status)
+ goto BAIL;
+
+ _cairo_xlib_remove_close_display_hook (display,
+ &font_private->close_display_hook);
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xlib_font_glyphset_info_t *glyphset_info;
+
+ glyphset_info = &font_private->glyphset_info[i];
+
+ if (glyphset_info->pending_free_glyphs != NULL)
+ free (glyphset_info->pending_free_glyphs);
+
+ if (glyphset_info->glyphset) {
+ status = _cairo_xlib_display_queue_resource (display,
+ XRenderFreeGlyphSet,
+ glyphset_info->glyphset);
+ (void) status; /* XXX cannot propagate failure */
+ }
+ }
+
+ cairo_device_release (&display->base);
+BAIL:
+ cairo_device_destroy (&display->base);
+ free (font_private);
+ }
+}
+
+static void
+_cairo_xlib_render_free_glyphs (Display *dpy,
+ cairo_xlib_font_glyphset_free_glyphs_t *to_free)
+{
+ XRenderFreeGlyphs (dpy,
+ to_free->glyphset,
+ to_free->glyph_indices,
+ to_free->glyph_count);
+}
+
+static cairo_xlib_font_glyphset_info_t *
+_cairo_xlib_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph)
+{
+ return scaled_glyph->surface_private;
+}
+
+static void
+_cairo_xlib_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_xlib_font_glyphset_info_t *glyphset_info)
+{
+ scaled_glyph->surface_private = glyphset_info;
+}
+
+static void
+_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_xlib_surface_font_private_t *font_private;
+ cairo_xlib_font_glyphset_info_t *glyphset_info;
+
+ if (scaled_font->finished)
+ return;
+
+ font_private = scaled_font->surface_private;
+ glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph);
+ if (font_private != NULL && glyphset_info != NULL) {
+ cairo_xlib_font_glyphset_free_glyphs_t *to_free;
+ cairo_status_t status;
+
+ to_free = glyphset_info->pending_free_glyphs;
+ if (to_free != NULL &&
+ to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices))
+ {
+ cairo_xlib_display_t *display;
+
+ status = _cairo_xlib_display_acquire (font_private->device, &display);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _cairo_xlib_display_queue_work (display,
+ (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs,
+ to_free,
+ free);
+ cairo_device_release (&display->base);
+ }
+ /* XXX cannot propagate failure */
+ if (unlikely (status))
+ free (to_free);
+
+ to_free = glyphset_info->pending_free_glyphs = NULL;
+ }
+
+ if (to_free == NULL) {
+ to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t));
+ if (unlikely (to_free == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return; /* XXX cannot propagate failure */
+ }
+
+ to_free->glyphset = glyphset_info->glyphset;
+ to_free->glyph_count = 0;
+ glyphset_info->pending_free_glyphs = to_free;
+ }
+
+ to_free->glyph_indices[to_free->glyph_count++] =
+ _cairo_scaled_glyph_index (scaled_glyph);
+ }
+}
+
+static cairo_bool_t
+_native_byte_order_lsb (void)
+{
+ int x = 1;
+
+ return *((char *) &x) == 1;
+}
+
+static int
+_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format)
+{
+ if (format == CAIRO_FORMAT_A8)
+ return GLYPHSET_INDEX_A8;
+ if (format == CAIRO_FORMAT_A1)
+ return GLYPHSET_INDEX_A1;
+
+ assert (format == CAIRO_FORMAT_ARGB32);
+ return GLYPHSET_INDEX_ARGB32;
+}
+
+static cairo_xlib_font_glyphset_info_t *
+_cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font,
+ cairo_format_t format)
+{
+ cairo_xlib_surface_font_private_t *font_private;
+ cairo_xlib_font_glyphset_info_t *glyphset_info;
+ int glyphset_index;
+
+ glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format);
+ font_private = scaled_font->surface_private;
+ glyphset_info = &font_private->glyphset_info[glyphset_index];
+ if (glyphset_info->glyphset == None) {
+ cairo_xlib_display_t *display;
+
+ if (_cairo_xlib_display_acquire (font_private->device, &display))
+ return NULL;
+
+ glyphset_info->xrender_format =
+ _cairo_xlib_display_get_xrender_format (display,
+ glyphset_info->format);
+ glyphset_info->glyphset = XRenderCreateGlyphSet (display->display,
+ glyphset_info->xrender_format);
+
+ cairo_device_release (&display->base);
+ }
+
+ return glyphset_info;
+}
+
+static cairo_bool_t
+_cairo_xlib_glyphset_info_has_pending_free_glyph (
+ cairo_xlib_font_glyphset_info_t *glyphset_info,
+ unsigned long glyph_index)
+{
+ if (glyphset_info->pending_free_glyphs != NULL) {
+ cairo_xlib_font_glyphset_free_glyphs_t *to_free;
+ int i;
+
+ to_free = glyphset_info->pending_free_glyphs;
+ for (i = 0; i < to_free->glyph_count; i++) {
+ if (to_free->glyph_indices[i] == glyph_index) {
+ to_free->glyph_count--;
+ memmove (&to_free->glyph_indices[i],
+ &to_free->glyph_indices[i+1],
+ (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0]));
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static cairo_xlib_font_glyphset_info_t *
+_cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (
+ cairo_scaled_font_t *scaled_font,
+ unsigned long glyph_index,
+ cairo_image_surface_t *surface)
+{
+ cairo_xlib_surface_font_private_t *font_private;
+ int i;
+
+ font_private = scaled_font->surface_private;
+ if (font_private == NULL)
+ return NULL;
+
+ if (surface != NULL) {
+ i = _cairo_xlib_get_glyphset_index_for_format (surface->format);
+ if (_cairo_xlib_glyphset_info_has_pending_free_glyph (
+ &font_private->glyphset_info[i],
+ glyph_index))
+ {
+ return &font_private->glyphset_info[i];
+ }
+ } else {
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ if (_cairo_xlib_glyphset_info_has_pending_free_glyph (
+ &font_private->glyphset_info[i],
+ glyph_index))
+ {
+ return &font_private->glyphset_info[i];
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static cairo_status_t
+_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
+ cairo_scaled_font_t *scaled_font,
+ cairo_scaled_glyph_t **pscaled_glyph)
+{
+ XGlyphInfo glyph_info;
+ unsigned long glyph_index;
+ unsigned char *data;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_scaled_glyph_t *scaled_glyph = *pscaled_glyph;
+ cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+ cairo_bool_t already_had_glyph_surface;
+ cairo_xlib_font_glyphset_info_t *glyphset_info;
+
+ glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
+
+ /* check to see if we have a pending XRenderFreeGlyph for this glyph */
+ glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface);
+ if (glyphset_info != NULL) {
+ _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (!glyph_surface) {
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyph_index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS |
+ CAIRO_SCALED_GLYPH_INFO_SURFACE,
+ pscaled_glyph);
+ if (unlikely (status))
+ return status;
+
+ scaled_glyph = *pscaled_glyph;
+ glyph_surface = scaled_glyph->surface;
+ already_had_glyph_surface = FALSE;
+ } else {
+ already_had_glyph_surface = TRUE;
+ }
+
+ if (scaled_font->surface_private == NULL) {
+ status = _cairo_xlib_surface_font_init (display, scaled_font);
+ if (unlikely (status))
+ return status;
+ }
+
+ glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font,
+ glyph_surface->format);
+
+ /* XRenderAddGlyph does not handle a glyph surface larger than the extended maximum XRequest size. */
+ {
+ int len = cairo_format_stride_for_width (glyphset_info->format, glyph_surface->width) * glyph_surface->height;
+ int max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display)
+ : XMaxRequestSize (display->display)) * 4 -
+ sz_xRenderAddGlyphsReq -
+ sz_xGlyphInfo -
+ 8;
+ if (len >= max_request_size)
+ return UNSUPPORTED ("glyph too large for XRequest");
+ }
+
+ /* If the glyph surface has zero height or width, we create
+ * a clear 1x1 surface, to avoid various X server bugs.
+ */
+ if (glyph_surface->width == 0 || glyph_surface->height == 0) {
+ cairo_surface_t *tmp_surface;
+
+ tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1);
+ status = tmp_surface->status;
+ if (unlikely (status))
+ goto BAIL;
+
+ tmp_surface->device_transform = glyph_surface->base.device_transform;
+ tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
+
+ glyph_surface = (cairo_image_surface_t *) tmp_surface;
+ }
+
+ /* If the glyph format does not match the font format, then we
+ * create a temporary surface for the glyph image with the font's
+ * format.
+ */
+ if (glyph_surface->format != glyphset_info->format) {
+ cairo_surface_pattern_t pattern;
+ cairo_surface_t *tmp_surface;
+
+ tmp_surface = cairo_image_surface_create (glyphset_info->format,
+ glyph_surface->width,
+ glyph_surface->height);
+ status = tmp_surface->status;
+ if (unlikely (status))
+ goto BAIL;
+
+ tmp_surface->device_transform = glyph_surface->base.device_transform;
+ tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
+
+ _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base);
+ status = _cairo_surface_paint (tmp_surface,
+ CAIRO_OPERATOR_SOURCE, &pattern.base,
+ NULL);
+ _cairo_pattern_fini (&pattern.base);
+
+ glyph_surface = (cairo_image_surface_t *) tmp_surface;
+
+ if (unlikely (status))
+ goto BAIL;
+ }
+
+ /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */
+ glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0);
+ glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0);
+ glyph_info.width = glyph_surface->width;
+ glyph_info.height = glyph_surface->height;
+ glyph_info.xOff = scaled_glyph->x_advance;
+ glyph_info.yOff = scaled_glyph->y_advance;
+
+ data = glyph_surface->data;
+
+ /* flip formats around */
+ switch (_cairo_xlib_get_glyphset_index_for_format (scaled_glyph->surface->format)) {
+ case GLYPHSET_INDEX_A1:
+ /* local bitmaps are always stored with bit == byte */
+ if (_native_byte_order_lsb() != (BitmapBitOrder (display->display) == LSBFirst)) {
+ int c = glyph_surface->stride * glyph_surface->height;
+ unsigned char *d;
+ unsigned char *new, *n;
+
+ new = malloc (c);
+ if (!new) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ n = new;
+ d = data;
+ do {
+ char b = *d++;
+ b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
+ b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
+ b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
+ *n++ = b;
+ } while (--c);
+ data = new;
+ }
+ break;
+ case GLYPHSET_INDEX_A8:
+ break;
+ case GLYPHSET_INDEX_ARGB32:
+ if (_native_byte_order_lsb() != (ImageByteOrder (display->display) == LSBFirst)) {
+ unsigned int c = glyph_surface->stride * glyph_surface->height / 4;
+ const uint32_t *d;
+ uint32_t *new, *n;
+
+ new = malloc (4 * c);
+ if (unlikely (new == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
+ n = new;
+ d = (uint32_t *) data;
+ do {
+ *n++ = bswap_32 (*d);
+ d++;
+ } while (--c);
+ data = (uint8_t *) new;
+ }
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+ /* XXX assume X server wants pixman padding. Xft assumes this as well */
+
+ struct _XDisplay *dpy = (struct _XDisplay *) display->display;
+ int req_length = sz_xRenderAddGlyphsReq + 4;
+ if (req_length & 3)
+ req_length += 4 - (req_length & 3);
+ if (dpy->bufptr + req_length > dpy->bufmax)
+ XFlush (display->display);
+
+ XRenderAddGlyphs (display->display, glyphset_info->glyphset,
+ &glyph_index, &glyph_info, 1,
+ (char *) data,
+ glyph_surface->stride * glyph_surface->height);
+
+ if (data != glyph_surface->data)
+ free (data);
+
+ _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info);
+
+ BAIL:
+ if (glyph_surface != scaled_glyph->surface)
+ cairo_surface_destroy (&glyph_surface->base);
+
+ /* if the scaled glyph didn't already have a surface attached
+ * to it, release the created surface now that we have it
+ * uploaded to the X server. If the surface has already been
+ * there (eg. because image backend requested it), leave it in
+ * the cache
+ */
+ if (!already_had_glyph_surface)
+ _cairo_scaled_glyph_set_surface (scaled_glyph, scaled_font, NULL);
+
+ return status;
+}
+
+typedef void (*cairo_xrender_composite_text_func_t)
+ (Display *dpy,
+ int op,
+ Picture src,
+ Picture dst,
+ _Xconst XRenderPictFormat *maskFormat,
+ int xSrc,
+ int ySrc,
+ int xDst,
+ int yDst,
+ _Xconst XGlyphElt8 *elts,
+ int nelt);
+
+/* Build a struct of the same size of #cairo_glyph_t that can be used both as
+ * an input glyph with double coordinates, and as "working" glyph with
+ * integer from-current-point offsets. */
+typedef union {
+ cairo_glyph_t d;
+ unsigned long index;
+ struct {
+ unsigned long index;
+ int x;
+ int y;
+ } i;
+} cairo_xlib_glyph_t;
+
+/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */
+COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t));
+
+/* Start a new element for the first glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs
+ * (Xrender limits each element to 252 glyphs, we limit them to 128)
+ *
+ * These same conditions need to be mirrored between
+ * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks
+ */
+#define _start_new_glyph_elt(count, glyph) \
+ (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
+
+static cairo_status_t
+_emit_glyphs_chunk (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *dst,
+ cairo_xlib_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_operator_t op,
+ cairo_xlib_surface_t *src,
+ cairo_surface_attributes_t *attributes,
+ /* info for this chunk */
+ int num_elts,
+ int width,
+ cairo_xlib_font_glyphset_info_t *glyphset_info)
+{
+ /* Which XRenderCompositeText function to use */
+ cairo_xrender_composite_text_func_t composite_text_func;
+ int size;
+
+ /* Element buffer stuff */
+ XGlyphElt8 *elts;
+ XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)];
+
+ /* Reuse the input glyph array for output char generation */
+ char *char8 = (char *) glyphs;
+ unsigned short *char16 = (unsigned short *) glyphs;
+ unsigned int *char32 = (unsigned int *) glyphs;
+
+ int i;
+ int nelt; /* Element index */
+ int n; /* Num output glyphs in current element */
+ int j; /* Num output glyphs so far */
+
+ switch (width) {
+ case 1:
+ /* don't cast the 8-variant, to catch possible mismatches */
+ composite_text_func = XRenderCompositeText8;
+ size = sizeof (char);
+ break;
+ case 2:
+ composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16;
+ size = sizeof (unsigned short);
+ break;
+ default:
+ case 4:
+ composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32;
+ size = sizeof (unsigned int);
+ }
+
+ /* Allocate element array */
+ if (num_elts <= ARRAY_LENGTH (stack_elts)) {
+ elts = stack_elts;
+ } else {
+ elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8));
+ if (unlikely (elts == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ /* Fill them in */
+ nelt = 0;
+ n = 0;
+ j = 0;
+ for (i = 0; i < num_glyphs; i++) {
+
+ /* Start a new element for first output glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs.
+ *
+ * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs()
+ */
+ if (_start_new_glyph_elt (j, &glyphs[i])) {
+ if (j) {
+ elts[nelt].nchars = n;
+ nelt++;
+ n = 0;
+ }
+ elts[nelt].chars = char8 + size * j;
+ elts[nelt].glyphset = glyphset_info->glyphset;
+ elts[nelt].xOff = glyphs[i].i.x;
+ elts[nelt].yOff = glyphs[i].i.y;
+ }
+
+ switch (width) {
+ case 1: char8 [j] = (char) glyphs[i].index; break;
+ case 2: char16[j] = (unsigned short) glyphs[i].index; break;
+ default:
+ case 4: char32[j] = (unsigned int) glyphs[i].index; break;
+ }
+
+ n++;
+ j++;
+ }
+
+ if (n) {
+ elts[nelt].nchars = n;
+ nelt++;
+ }
+
+ /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the
+ * expected number of xGlyphElts. */
+ assert (nelt == num_elts);
+
+ composite_text_func (display->display,
+ _render_operator (op),
+ src->src_picture,
+ dst->dst_picture,
+ glyphset_info->xrender_format,
+ attributes->x_offset + elts[0].xOff,
+ attributes->y_offset + elts[0].yOff,
+ elts[0].xOff, elts[0].yOff,
+ (XGlyphElt8 *) elts, nelt);
+
+ if (elts != stack_elts)
+ free (elts);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
+/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
+ * enough room for padding */
+#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4)
+
+static cairo_status_t
+_cairo_xlib_surface_emit_glyphs (cairo_xlib_display_t *display,
+ cairo_xlib_surface_t *dst,
+ cairo_xlib_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_operator_t op,
+ cairo_xlib_surface_t *src,
+ cairo_surface_attributes_t *attributes,
+ int *remaining_glyphs)
+{
+ int i;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_scaled_glyph_t *scaled_glyph;
+ cairo_fixed_t x = 0, y = 0;
+ cairo_xlib_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info;
+
+ unsigned long max_index = 0;
+ int width = 1;
+ int num_elts = 0;
+ int num_out_glyphs = 0;
+
+ int max_request_size = XMaxRequestSize (display->display) * 4
+ - MAX (sz_xRenderCompositeGlyphs8Req,
+ MAX(sz_xRenderCompositeGlyphs16Req,
+ sz_xRenderCompositeGlyphs32Req));
+ int request_size = 0;
+
+ _cairo_xlib_surface_ensure_dst_picture (display, dst);
+
+ for (i = 0; i < num_glyphs; i++) {
+ int this_x, this_y;
+ int old_width;
+
+ status = _cairo_scaled_glyph_lookup (scaled_font,
+ glyphs[i].index,
+ CAIRO_SCALED_GLYPH_INFO_METRICS,
+ &scaled_glyph);
+ if (unlikely (status))
+ return status;
+
+ this_x = _cairo_lround (glyphs[i].d.x);
+ this_y = _cairo_lround (glyphs[i].d.y);
+
+ /* Glyph skipping:
+ *
+ * We skip any glyphs that have troublesome coordinates. We want
+ * to make sure that (glyph2.x - (glyph1.x + glyph1.width)) fits in
+ * a signed 16bit integer, otherwise it will overflow in the render
+ * protocol.
+ * To ensure this, we'll make sure that (glyph2.x - glyph1.x) fits in
+ * a signed 15bit integer. The trivial option would be to allow
+ * coordinates -8192..8192, but that's kinda dull. It probably will
+ * take a decade or so to get monitors 8192x4096 or something. A
+ * negative value of -8192 on the other hand, is absolutely useless.
+ * Note that we do want to allow some negative positions. The glyph
+ * may start off the screen but part of it make it to the screen.
+ * Anyway, we will allow positions in the range -4096..122887. That
+ * will buy us a few more years before this stops working.
+ *
+ * Update: upon seeing weird glyphs, we just return and let fallback
+ * code do the job.
+ */
+ if (((this_x+4096)|(this_y+4096))&~0x3fffu)
+ break;
+
+ /* Send unsent glyphs to the server */
+ if (_cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) {
+ status = _cairo_xlib_surface_add_glyph (display,
+ scaled_font,
+ &scaled_glyph);
+ if (unlikely (status)) {
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+ /* Break so we flush glyphs so far and let fallback code
+ * handle the rest */
+ break;
+
+ return status;
+ }
+ }
+
+ this_glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph);
+ if (!glyphset_info)
+ glyphset_info = this_glyphset_info;
+
+ /* The invariant here is that we can always flush the glyphs
+ * accumulated before this one, using old_width, and they
+ * would fit in the request.
+ */
+ old_width = width;
+
+ /* Update max glyph index */
+ if (glyphs[i].index > max_index) {
+ max_index = glyphs[i].index;
+ if (max_index >= 65536)
+ width = 4;
+ else if (max_index >= 256)
+ width = 2;
+ if (width != old_width)
+ request_size += (width - old_width) * num_out_glyphs;
+ }
+
+ /* If we will pass the max request size by adding this glyph,
+ * flush current glyphs. Note that we account for a
+ * possible element being added below.
+ *
+ * Also flush if changing glyphsets, as Xrender limits one mask
+ * format per request, so we can either break up, or use a
+ * wide-enough mask format. We do the former. One reason to
+ * prefer the latter is the fact that Xserver ADDs all glyphs
+ * to the mask first, and then composes that to final surface,
+ * though it's not a big deal.
+ */
+ if (request_size + width > max_request_size - _cairo_sz_xGlyphElt ||
+ (this_glyphset_info != glyphset_info)) {
+ status = _emit_glyphs_chunk (display, dst, glyphs, i,
+ scaled_font, op, src, attributes,
+ num_elts, old_width, glyphset_info);
+ if (unlikely (status))
+ return status;
+
+ glyphs += i;
+ num_glyphs -= i;
+ i = 0;
+ max_index = glyphs[i].index;
+ width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
+ request_size = 0;
+ num_elts = 0;
+ num_out_glyphs = 0;
+ x = y = 0;
+ glyphset_info = this_glyphset_info;
+ }
+
+ /* Convert absolute glyph position to relative-to-current-point
+ * position */
+ glyphs[i].i.x = this_x - x;
+ glyphs[i].i.y = this_y - y;
+
+ /* Start a new element for the first glyph,
+ * or for any glyph that has unexpected position,
+ * or if current element has too many glyphs.
+ *
+ * These same conditions are mirrored in _emit_glyphs_chunk().
+ */
+ if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) {
+ num_elts++;
+ request_size += _cairo_sz_xGlyphElt;
+ }
+
+ /* adjust current-position */
+ x = this_x + scaled_glyph->x_advance;
+ y = this_y + scaled_glyph->y_advance;
+
+ num_out_glyphs++;
+ request_size += width;
+ }
+
+ if (num_elts) {
+ status = _emit_glyphs_chunk (display, dst, glyphs, i,
+ scaled_font, op, src, attributes,
+ num_elts, width, glyphset_info);
+ }
+
+ *remaining_glyphs = num_glyphs - i;
+ if (*remaining_glyphs != 0 && status == CAIRO_STATUS_SUCCESS)
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+ return status;
+}
+
+static cairo_bool_t
+_cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_xlib_surface_font_private_t *font_private;
+
+ font_private = scaled_font->surface_private;
+ if ((scaled_font->surface_backend != NULL &&
+ scaled_font->surface_backend != &cairo_xlib_surface_backend) ||
+ (font_private != NULL && font_private->device != dst->base.device))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Gets a grayscale version of scaled_font. The grayscale version is cached
+ * in our surface_private data.
+ */
+static cairo_scaled_font_t *
+_cairo_xlib_get_grayscale_font (cairo_xlib_display_t *display,
+ cairo_scaled_font_t *scaled_font)
+{
+ cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private;
+ cairo_bool_t needs_font;
+
+ if (font_private == NULL) {
+ cairo_status_t status = _cairo_xlib_surface_font_init (display, scaled_font);
+ if (unlikely (status))
+ return _cairo_scaled_font_create_in_error (status);
+ font_private = scaled_font->surface_private;
+ }
+
+ CAIRO_MUTEX_LOCK (scaled_font->mutex);
+ needs_font = !font_private->grayscale_font;
+ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+
+ if (needs_font) {
+ cairo_font_options_t options;
+ cairo_scaled_font_t *new_font;
+
+ options = scaled_font->options;
+ options.antialias = CAIRO_ANTIALIAS_GRAY;
+ new_font = cairo_scaled_font_create (scaled_font->font_face,
+ &scaled_font->font_matrix,
+ &scaled_font->ctm, &options);
+
+ CAIRO_MUTEX_LOCK (scaled_font->mutex);
+ if (!font_private->grayscale_font) {
+ font_private->grayscale_font = new_font;
+ new_font = NULL;
+ }
+ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+
+ if (new_font) {
+ cairo_scaled_font_destroy (new_font);
+ }
+ }
+
+ return font_private->grayscale_font;
+}
+
+static cairo_int_status_t
+_cairo_xlib_surface_show_glyphs (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst;
+ composite_operation_t operation;
+ cairo_surface_attributes_t attributes;
+ cairo_xlib_surface_t *src = NULL;
+ cairo_region_t *clip_region = NULL;
+ cairo_xlib_display_t *display;
+
+ if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst))
+ return UNSUPPORTED ("XRender does not support CompositeText");
+
+ /* Just let unbounded operators go through the fallback code
+ * instead of trying to do the fixups here */
+ if (! _cairo_operator_bounded_by_mask (op))
+ return UNSUPPORTED ("unsupported unbounded op");
+
+ /* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs --
+ * the solid source seems to be multiplied by the glyph mask, and
+ * then the entire thing is copied to the destination surface,
+ * including the fully transparent "background" of the rectangular
+ * glyph surface. */
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ ! CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11))
+ {
+ return UNSUPPORTED ("known bug in Render");
+ }
+
+ /* We can only use our code if we either have no clip or
+ * have a real native clip region set. If we're using
+ * fallback clip masking, we have to go through the full
+ * fallback path.
+ */
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
+ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+ if (status)
+ return status;
+ }
+
+ operation = _categorize_composite_operation (dst, op, src_pattern, TRUE);
+ if (operation == DO_UNSUPPORTED)
+ return UNSUPPORTED ("unsupported op");
+
+ if (! _cairo_xlib_surface_owns_font (dst, scaled_font))
+ return UNSUPPORTED ("unowned font");
+
+
+ status = _cairo_xlib_display_acquire (dst->base.device, &display);
+ if (unlikely (status))
+ return status;
+
+ if (!dst->base.permit_subpixel_antialiasing &&
+ scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
+ scaled_font = _cairo_xlib_get_grayscale_font (display, scaled_font);
+ }
+
+ X_DEBUG ((display->display, "show_glyphs (dst=%x)", (unsigned int) dst->drawable));
+
+ if (clip_region != NULL &&
+ cairo_region_num_rectangles (clip_region) == 1)
+ {
+ cairo_rectangle_int_t glyph_extents;
+ cairo_rectangle_int_t clip_extents;
+
+ /* Can we do without the clip?
+ * Around 50% of the time the clip is redundant (firefox).
+ */
+ _cairo_scaled_font_glyph_approximate_extents (scaled_font,
+ glyphs, num_glyphs,
+ &glyph_extents);
+
+ cairo_region_get_extents(clip_region, &clip_extents);
+ if (clip_extents.x <= glyph_extents.x &&
+ clip_extents.y <= glyph_extents.y &&
+ clip_extents.x + clip_extents.width >= glyph_extents.x + glyph_extents.width &&
+ clip_extents.y + clip_extents.height >= glyph_extents.y + glyph_extents.height)
+ {
+ clip_region = NULL;
+ }
+ }
+
+ status = _cairo_xlib_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ goto BAIL0;
+
+ /* After passing all those tests, we're now committed to rendering
+ * these glyphs or to fail trying. We first upload any glyphs to
+ * the X server that it doesn't have already, then we draw
+ * them.
+ */
+
+ /* PictOpClear doesn't seem to work with CompositeText; it seems to ignore
+ * the mask (the glyphs). This code below was executed as a side effect
+ * of going through the _clip_and_composite fallback code for old_show_glyphs,
+ * so PictOpClear was never used with CompositeText before.
+ */
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ src_pattern = &_cairo_pattern_white.base;
+ op = CAIRO_OPERATOR_DEST_OUT;
+ }
+
+ if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
+ 0, 0, 1, 1,
+ CAIRO_PATTERN_ACQUIRE_NONE,
+ (cairo_surface_t **) &src,
+ &attributes);
+ if (unlikely (status))
+ goto BAIL0;
+ } else {
+ cairo_rectangle_int_t glyph_extents;
+
+ status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+ glyphs,
+ num_glyphs,
+ &glyph_extents,
+ NULL);
+ if (unlikely (status))
+ goto BAIL0;
+
+ if (clip != NULL) {
+ if (! _cairo_rectangle_intersect (&glyph_extents,
+ _cairo_clip_get_extents (clip)))
+ {
+ goto BAIL0;
+ }
+ }
+
+ status = _cairo_xlib_surface_acquire_pattern_surface (display,
+ dst, src_pattern,
+ glyph_extents.x,
+ glyph_extents.y,
+ glyph_extents.width,
+ glyph_extents.height,
+ &src, &attributes);
+ if (unlikely (status))
+ goto BAIL0;
+ }
+
+ operation = _recategorize_composite_operation (dst, op, src,
+ &attributes, TRUE);
+ if (operation == DO_UNSUPPORTED) {
+ status = UNSUPPORTED ("unsupported op");
+ goto BAIL1;
+ }
+
+ status = _cairo_xlib_surface_set_attributes (display, src, &attributes, 0, 0);
+ if (unlikely (status))
+ goto BAIL1;
+
+ _cairo_scaled_font_freeze_cache (scaled_font);
+ if (_cairo_xlib_surface_owns_font (dst, scaled_font)) {
+ status = _cairo_xlib_surface_emit_glyphs (display,
+ dst,
+ (cairo_xlib_glyph_t *) glyphs,
+ num_glyphs,
+ scaled_font,
+ op,
+ src,
+ &attributes,
+ remaining_glyphs);
+ } else {
+ status = UNSUPPORTED ("unowned font");
+ }
+ _cairo_scaled_font_thaw_cache (scaled_font);
+
+ BAIL1:
+ if (src)
+ _cairo_pattern_release_surface (src_pattern, &src->base, &attributes);
+ BAIL0:
+ cairo_device_release (&display->base);
+
+ return status;
+}
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 000000000..e076ed01e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xlib-visual.c
@@ -0,0 +1,187 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xlib-private.h"
+
+#include "cairo-error-private.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 = malloc (sizeof (cairo_xlib_visual_info_t));
+ if (unlikely (info == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ 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 */
+ free (info);
+}
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 000000000..52f415915
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xlib-xrender-private.h
@@ -0,0 +1,1164 @@
+/* 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 <X11/Xlib.h>
+
+/* 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 <X11/extensions/Xrender.h>
+#include <X11/extensions/renderproto.h>
+
+/* 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
+
+/* There doesn't appear to be a simple #define that we can conditionalize
+ * on. Instead, use the version; gradients were introdiced in 0.10. */
+#if RENDER_MAJOR == 0 && RENDER_MINOR < 10
+#define XRenderCreateLinearGradient _int_consume
+#define XRenderCreateRadialGradient _int_consume
+#define XRenderCreateConicalGradient _int_consume
+typedef struct _XCircle {
+ XFixed x;
+ XFixed y;
+ XFixed radius;
+} XCircle;
+typedef struct _XLinearGradient {
+ XPointFixed p1;
+ XPointFixed p2;
+} XLinearGradient;
+
+typedef struct _XRadialGradient {
+ XCircle inner;
+ XCircle outer;
+} XRadialGradient;
+
+typedef struct _XConicalGradient {
+ XPointFixed center;
+ XFixed angle; /* in degrees */
+} XConicalGradient;
+#endif
+
+
+#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 <X11/Xmd.h>
+
+#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 000000000..b34b057de
--- /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 <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_XLIB_XRENDER_H
+#define CAIRO_XLIB_XRENDER_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
+
+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 000000000..4ee592ce4
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xlib.h
@@ -0,0 +1,100 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#ifndef CAIRO_XLIB_H
+#define CAIRO_XLIB_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_XLIB_SURFACE
+
+#include <X11/Xlib.h>
+
+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);
+
+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 000000000..5583829e7
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo-xml-surface.c
@@ -0,0 +1,1152 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ *
+ * Contributor(s):
+ * Chris Wilson <chris@chris-wilson.co.uk>
+ */
+
+/* This surface is intended to produce a verbose, hierarchical, DAG XML file
+ * representing a single surface. It is intended to be used by debuggers,
+ * such as cairo-sphinx, or by application test-suites that what a log of
+ * operations.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-xml.h"
+
+#include "cairo-clip-private.h"
+#include "cairo-device-private.h"
+#include "cairo-error-private.h"
+#include "cairo-output-stream-private.h"
+#include "cairo-recording-surface-private.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 */
+ "ExtendMode::REPEAT", /* CAIRO_EXTEND_REPEAT */
+ "ExtendMode::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 */
+ "SamplingFilter::GOOD", /* CAIRO_FILTER_GOOD */
+ "FILTER_BEST", /* CAIRO_FILTER_BEST */
+ "FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */
+ "FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */
+ "FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */
+ };
+ assert (filter < ARRAY_LENGTH (names));
+ return names[filter];
+}
+
+static const char *
+_fill_rule_to_string (cairo_fill_rule_t rule)
+{
+ static const char *names[] = {
+ "WINDING", /* CAIRO_FILL_RULE_WINDING */
+ "EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */
+ };
+ assert (rule < ARRAY_LENGTH (names));
+ return names[rule];
+}
+
+static const char *
+_antialias_to_string (cairo_antialias_t antialias)
+{
+ static const char *names[] = {
+ "ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */
+ "ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */
+ "ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */
+ "ANTIALIAS_SUBPIXEL" /* CAIRO_ANTIALIAS_SUBPIXEL */
+ };
+ assert (antialias < ARRAY_LENGTH (names));
+ return names[antialias];
+}
+
+static const char *
+_line_cap_to_string (cairo_line_cap_t line_cap)
+{
+ static const char *names[] = {
+ "LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */
+ "LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */
+ "LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */
+ };
+ assert (line_cap < ARRAY_LENGTH (names));
+ return names[line_cap];
+}
+
+static const char *
+_line_join_to_string (cairo_line_join_t line_join)
+{
+ static const char *names[] = {
+ "LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */
+ "LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */
+ "LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */
+ };
+ assert (line_join < ARRAY_LENGTH (names));
+ return names[line_join];
+}
+
+static 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_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 = 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,
+ cairo_path_fixed_t *path)
+{
+ cairo_status_t status;
+
+ _cairo_xml_printf_start (xml, "<path>");
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_xml_move_to,
+ _cairo_xml_line_to,
+ _cairo_xml_curve_to,
+ _cairo_xml_close_path,
+ xml);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ _cairo_xml_printf_end (xml, "</path>");
+}
+
+static void
+_cairo_xml_emit_string (cairo_xml_t *xml,
+ const char *node,
+ const char *data)
+{
+ _cairo_xml_printf (xml, "<%s>%s</%s>", node, data, node);
+}
+
+static void
+_cairo_xml_emit_double (cairo_xml_t *xml,
+ const char *node,
+ double data)
+{
+ _cairo_xml_printf (xml, "<%s>%f</%s>", node, data, node);
+}
+
+static cairo_xml_t *
+to_xml (cairo_xml_surface_t *surface)
+{
+ return (cairo_xml_t *) surface->base.device;
+}
+
+static cairo_status_t
+_cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface,
+ cairo_clip_path_t *clip_path)
+{
+ cairo_box_t box;
+ cairo_status_t status;
+ cairo_xml_t *xml;
+
+ if (clip_path->prev != NULL) {
+ status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev);
+ if (unlikely (status))
+ return status;
+ }
+
+
+ /* skip the trivial clip covering the surface extents */
+ if (surface->width >= 0 && surface->height >= 0 &&
+ _cairo_path_fixed_is_box (&clip_path->path, &box))
+ {
+ if (box.p1.x <= 0 && box.p1.y <= 0 &&
+ box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) &&
+ box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height))
+ {
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ xml = to_xml (surface);
+
+ _cairo_xml_printf_start (xml, "<clip>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_path (xml, &clip_path->path);
+ _cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance);
+ _cairo_xml_emit_string (xml, "antialias",
+ _antialias_to_string (clip_path->antialias));
+ _cairo_xml_emit_string (xml, "fill-rule",
+ _fill_rule_to_string (clip_path->fill_rule));
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf_end (xml, "</clip>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface,
+ cairo_clip_t *clip)
+{
+ if (clip == NULL)
+ return CAIRO_STATUS_SUCCESS;
+
+ return _cairo_xml_surface_emit_clip_path (surface, clip->path);
+}
+
+static cairo_status_t
+_cairo_xml_emit_solid (cairo_xml_t *xml,
+ const cairo_solid_pattern_t *solid)
+{
+ _cairo_xml_printf (xml, "<solid>%f %f %f %f</solid>",
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_xml_emit_matrix (cairo_xml_t *xml,
+ const cairo_matrix_t *matrix)
+{
+ if (! _cairo_matrix_is_identity (matrix)) {
+ _cairo_xml_printf (xml, "<matrix>%f %f %f %f %f %f</matrix>",
+ matrix->xx, matrix->yx,
+ matrix->xy, matrix->yy,
+ matrix->x0, matrix->y0);
+ }
+}
+
+static void
+_cairo_xml_emit_gradient (cairo_xml_t *xml,
+ const cairo_gradient_pattern_t *gradient)
+{
+ unsigned int i;
+
+ for (i = 0; i < gradient->n_stops; i++) {
+ _cairo_xml_printf (xml,
+ "<color-stop>%f %f %f %f %f</color-stop>",
+ gradient->stops[i].offset,
+ gradient->stops[i].color.red,
+ gradient->stops[i].color.green,
+ gradient->stops[i].color.blue,
+ gradient->stops[i].color.alpha);
+ }
+}
+
+static cairo_status_t
+_cairo_xml_emit_linear (cairo_xml_t *xml,
+ const cairo_linear_pattern_t *linear)
+{
+ _cairo_xml_printf (xml,
+ "<linear x1='%f' y1='%f' x2='%f' y2='%f'>",
+ _cairo_fixed_to_double (linear->p1.x),
+ _cairo_fixed_to_double (linear->p1.y),
+ _cairo_fixed_to_double (linear->p2.x),
+ _cairo_fixed_to_double (linear->p2.y));
+ _cairo_xml_indent (xml, 2);
+ _cairo_xml_emit_gradient (xml, &linear->base);
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</linear>");
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_radial (cairo_xml_t *xml,
+ const cairo_radial_pattern_t *radial)
+{
+ _cairo_xml_printf (xml,
+ "<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>",
+ _cairo_fixed_to_double (radial->c1.x),
+ _cairo_fixed_to_double (radial->c1.y),
+ _cairo_fixed_to_double (radial->r1),
+ _cairo_fixed_to_double (radial->c2.x),
+ _cairo_fixed_to_double (radial->c2.y),
+ _cairo_fixed_to_double (radial->r2));
+ _cairo_xml_indent (xml, 2);
+ _cairo_xml_emit_gradient (xml, &radial->base);
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</radial>");
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_write_func (void *closure, const unsigned char *data, unsigned len)
+{
+ _cairo_output_stream_write (closure, data, len);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_image (cairo_xml_t *xml,
+ cairo_image_surface_t *image)
+{
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ _cairo_xml_printf_start (xml,
+ "<image width='%d' height='%d' format='%s'>",
+ image->width, image->height,
+ _format_to_string (image->format));
+
+ stream = _cairo_base64_stream_create (xml->stream);
+ status = cairo_surface_write_to_png_stream (&image->base,
+ _write_func, stream);
+ assert (status == CAIRO_STATUS_SUCCESS);
+ status = _cairo_output_stream_destroy (stream);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xml_printf_end (xml, "</image>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_surface (cairo_xml_t *xml,
+ const cairo_surface_pattern_t *pattern)
+{
+ cairo_surface_t *source = pattern->surface;
+ cairo_status_t status;
+
+ if (_cairo_surface_is_recording (source)) {
+ status = cairo_xml_for_recording_surface (&xml->base, source);
+ } else {
+ cairo_image_surface_t *image;
+ void *image_extra;
+
+ status = _cairo_surface_acquire_source_image (source,
+ &image, &image_extra);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_image (xml, image);
+
+ _cairo_surface_release_source_image (source, image, image_extra);
+ }
+
+ return status;
+}
+
+static cairo_status_t
+_cairo_xml_emit_pattern (cairo_xml_t *xml,
+ const char *source_or_mask,
+ const cairo_pattern_t *pattern)
+{
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<%s-pattern>", source_or_mask);
+ _cairo_xml_indent (xml, 2);
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID:
+ status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_RADIAL:
+ status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern);
+ break;
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern);
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ break;
+ }
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
+ _cairo_xml_emit_matrix (xml, &pattern->matrix);
+ _cairo_xml_printf (xml,
+ "<extend>%s</extend>",
+ _extend_to_string (pattern->extend));
+ _cairo_xml_printf (xml,
+ "<filter>%s</filter>",
+ _filter_to_string (pattern->filter));
+ }
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</%s-pattern>", source_or_mask);
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<paint>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</paint>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<mask>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "mask", mask);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</mask>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<stroke>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+ _cairo_xml_emit_double (xml, "line-width", style->line_width);
+ _cairo_xml_emit_double (xml, "miter-limit", style->miter_limit);
+ _cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap));
+ _cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ if (style->num_dashes) {
+ unsigned int i;
+
+ _cairo_xml_printf_start (xml, "<dash offset='%f'>",
+ style->dash_offset);
+ for (i = 0; i < style->num_dashes; i++)
+ _cairo_xml_printf_continue (xml, "%f ", style->dash[i]);
+
+ _cairo_xml_printf_end (xml, "</dash>");
+ }
+
+ _cairo_xml_emit_path (xml, path);
+ _cairo_xml_emit_double (xml, "tolerance", tolerance);
+ _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
+
+ _cairo_xml_emit_matrix (xml, ctm);
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</stroke>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<fill>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ _cairo_xml_emit_path (xml, path);
+ _cairo_xml_emit_double (xml, "tolerance", tolerance);
+ _cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
+ _cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule));
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</fill>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+#if CAIRO_HAS_FT_FONT
+#include "cairo-ft-private.h"
+static cairo_status_t
+_cairo_xml_emit_type42_font (cairo_xml_t *xml,
+ cairo_scaled_font_t *scaled_font)
+{
+ const cairo_scaled_font_backend_t *backend;
+ cairo_output_stream_t *base64_stream;
+ cairo_output_stream_t *zlib_stream;
+ cairo_status_t status, status2;
+ unsigned long size;
+ uint32_t len;
+ uint8_t *buf;
+
+ backend = scaled_font->backend;
+ if (backend->load_truetype_table == NULL)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ size = 0;
+ status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
+ if (unlikely (status))
+ return status;
+
+ buf = malloc (size);
+ if (unlikely (buf == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ status = backend->load_truetype_table (scaled_font, 0, 0, buf, NULL);
+ if (unlikely (status)) {
+ free (buf);
+ return status;
+ }
+
+ _cairo_xml_printf_start (xml, "<font type='42' flags='%d' index='0'>",
+ _cairo_ft_scaled_font_get_load_flags (scaled_font));
+
+
+ base64_stream = _cairo_base64_stream_create (xml->stream);
+ len = size;
+ _cairo_output_stream_write (base64_stream, &len, sizeof (len));
+
+ zlib_stream = _cairo_deflate_stream_create (base64_stream);
+
+ _cairo_output_stream_write (zlib_stream, buf, size);
+ free (buf);
+
+ status2 = _cairo_output_stream_destroy (zlib_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ status2 = _cairo_output_stream_destroy (base64_stream);
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = status2;
+
+ _cairo_xml_printf_end (xml, "</font>");
+
+ return status;
+}
+#else
+static cairo_status_t
+_cairo_xml_emit_type42_font (cairo_xml_t *xml,
+ cairo_scaled_font_t *scaled_font)
+{
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+#endif
+
+static cairo_status_t
+_cairo_xml_emit_type3_font (cairo_xml_t *xml,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ _cairo_xml_printf_start (xml, "<font type='3'>");
+ _cairo_xml_printf_end (xml, "</font>");
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_xml_emit_scaled_font (cairo_xml_t *xml,
+ cairo_scaled_font_t *scaled_font,
+ cairo_glyph_t *glyphs,
+ int num_glyphs)
+{
+ cairo_status_t status;
+
+ _cairo_xml_printf (xml, "<scaled-font>");
+ _cairo_xml_indent (xml, 2);
+
+ status = _cairo_xml_emit_type42_font (xml, scaled_font);
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_xml_emit_type3_font (xml, scaled_font,
+ glyphs, num_glyphs);
+ }
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "<scaled-font>");
+
+ return status;
+}
+
+static cairo_int_status_t
+_cairo_xml_surface_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+{
+ cairo_xml_surface_t *surface = abstract_surface;
+ cairo_xml_t *xml = to_xml (surface);
+ cairo_status_t status;
+ int i;
+
+ _cairo_xml_printf (xml, "<glyphs>");
+ _cairo_xml_indent (xml, 2);
+
+ _cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
+
+ status = _cairo_xml_surface_emit_clip (surface, clip);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_pattern (xml, "source", source);
+ if (unlikely (status))
+ return status;
+
+ status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs);
+ if (unlikely (status))
+ return status;
+
+ for (i = 0; i < num_glyphs; i++) {
+ _cairo_xml_printf (xml, "<glyph index='%lu'>%f %f</glyph>",
+ glyphs[i].index,
+ glyphs[i].x,
+ glyphs[i].y);
+ }
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</glyphs>");
+
+ *remaining_glyphs = 0;
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static const cairo_surface_backend_t
+_cairo_xml_surface_backend = {
+ CAIRO_SURFACE_TYPE_XML,
+ _cairo_xml_surface_create_similar,
+ NULL,
+ NULL, NULL, /* source image */
+ NULL, NULL, /* dst image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, NULL, /* copy/show page */
+ _cairo_xml_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* font fini */
+ NULL, /* scaled_glyph_fini */
+
+ /* The 5 high level operations */
+ _cairo_xml_surface_paint,
+ _cairo_xml_surface_mask,
+ _cairo_xml_surface_stroke,
+ _cairo_xml_surface_fill,
+ _cairo_xml_surface_glyphs,
+
+ NULL, /* snapshot */
+
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+
+ /* The alternate high-level text operation */
+ NULL, NULL, /* has, show_text_glyphs */
+};
+
+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 = 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);
+
+ surface->width = width;
+ surface->height = height;
+
+ return &surface->base;
+}
+
+cairo_device_t *
+cairo_xml_create (const char *filename)
+{
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ stream = _cairo_output_stream_create_for_filename (filename);
+ if ((status = _cairo_output_stream_get_status (stream)))
+ return _cairo_device_create_in_error (status);
+
+ return _cairo_xml_create_internal (stream);
+}
+
+cairo_device_t *
+cairo_xml_create_for_stream (cairo_write_func_t write_func,
+ void *closure)
+{
+ cairo_output_stream_t *stream;
+ cairo_status_t status;
+
+ stream = _cairo_output_stream_create (write_func, NULL, closure);
+ if ((status = _cairo_output_stream_get_status (stream)))
+ return _cairo_device_create_in_error (status);
+
+ return _cairo_xml_create_internal (stream);
+}
+
+cairo_surface_t *
+cairo_xml_surface_create (cairo_device_t *device,
+ cairo_content_t content,
+ double width, double height)
+{
+ if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
+ return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (device->status))
+ return _cairo_surface_create_in_error (device->status);
+
+ return _cairo_xml_surface_create_internal (device, content, width, height);
+}
+
+cairo_status_t
+cairo_xml_for_recording_surface (cairo_device_t *device,
+ cairo_surface_t *recording_surface)
+{
+ cairo_box_t bbox;
+ cairo_rectangle_int_t extents;
+ cairo_surface_t *surface;
+ cairo_xml_t *xml;
+ cairo_status_t status;
+
+ if (unlikely (device->status))
+ return device->status;
+
+ if (unlikely (recording_surface->status))
+ return recording_surface->status;
+
+ if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
+ return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
+
+ if (unlikely (! _cairo_surface_is_recording (recording_surface)))
+ return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+
+ status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
+ &bbox, NULL);
+ if (unlikely (status))
+ return status;
+
+ _cairo_box_round_to_rectangle (&bbox, &extents);
+ surface = _cairo_xml_surface_create_internal (device,
+ recording_surface->content,
+ extents.width,
+ extents.height);
+ if (unlikely (surface->status))
+ return surface->status;
+
+ xml = (cairo_xml_t *) device;
+
+ _cairo_xml_printf (xml,
+ "<surface content='%s' width='%d' height='%d'>",
+ _content_to_string (recording_surface->content),
+ extents.width, extents.height);
+ _cairo_xml_indent (xml, 2);
+
+ cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
+ status = _cairo_recording_surface_replay (recording_surface, surface);
+ cairo_surface_destroy (surface);
+
+ _cairo_xml_indent (xml, -2);
+ _cairo_xml_printf (xml, "</surface>");
+
+ return status;
+}
+slim_hidden_def (cairo_xml_for_recording_surface);
diff --git a/gfx/cairo/cairo/src/cairo-xml.h b/gfx/cairo/cairo/src/cairo-xml.h
new file mode 100644
index 000000000..9ae76e90a
--- /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 <chris@chris-wilson.co.uk>
+ */
+
+#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 000000000..eeee0206e
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo.c
@@ -0,0 +1,4201 @@
+/* -*- 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 <cworth@cworth.org>
+ */
+
+#include "cairoint.h"
+#include "cairo-private.h"
+
+#include "cairo-arc-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-private.h"
+
+/**
+ * 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 loosing 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 <emphasis>text</emphasis> in their name form cairo's
+ * <firstterm>toy</firstterm> 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 <emphasis>glyphs</emphasis> in their name form cairo's
+ * <firstterm>low-level</firstterm> 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 <ulink
+ * url="http://www.pango.org/">http://www.pango.org/</ulink>.
+ */
+
+/**
+ * SECTION:cairo-transforms
+ * @Title: Transformations
+ * @Short_Description: Manipulating the current transformation matrix
+ * @See_Also: #cairo_matrix_t
+ *
+ * The current transformation matrix, <firstterm>ctm</firstterm>, is a
+ * two-dimensional affine transformation that maps all coordinates and other
+ * drawing instruments from the <firstterm>user space</firstterm> into the
+ * surface's canonical coordinate system, also known as the <firstterm>device
+ * space</firstterm>.
+ */
+
+#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1)
+
+#if !defined(INFINITY)
+#define INFINITY HUGE_VAL
+#endif
+
+static const cairo_t _cairo_nil = {
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_NO_MEMORY, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ NULL, /* gstate */
+ {{ 0 }, { 0 }}, /* gstate_tail */
+ NULL, /* gstate_freelist */
+ {{ /* path */
+ { 0, 0 }, /* last_move_point */
+ { 0, 0 }, /* current point */
+ FALSE, /* has_current_point */
+ FALSE, /* has_last_move_point */
+ FALSE, /* has_curve_to */
+ FALSE, /* is_box */
+ FALSE, /* maybe_fill_region */
+ TRUE, /* is_empty_fill */
+ { {0, 0}, {0, 0}}, /* extents */
+ {{{NULL,NULL}}} /* link */
+ }}
+};
+
+static const cairo_t _cairo_nil__null_pointer = {
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
+ CAIRO_STATUS_NULL_POINTER, /* status */
+ { 0, 0, 0, NULL }, /* user_data */
+ NULL, /* gstate */
+ {{ 0 }, { 0 }}, /* gstate_tail */
+ NULL, /* gstate_freelist */
+ {{ /* path */
+ { 0, 0 }, /* last_move_point */
+ { 0, 0 }, /* current point */
+ FALSE, /* has_current_point */
+ FALSE, /* has_last_move_point */
+ FALSE, /* has_curve_to */
+ FALSE, /* is_box */
+ FALSE, /* maybe_fill_region */
+ TRUE, /* is_empty_fill */
+ { {0, 0}, {0, 0}}, /* extents */
+ {{{NULL,NULL}}} /* link */
+ }}
+};
+#include <assert.h>
+
+/**
+ * _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));
+
+#ifdef MOZILLA_VERSION
+ static int abort_on_error = -1;
+ if (abort_on_error < 0) {
+ abort_on_error = (getenv("MOZ_CAIRO_ERROR_ABORT") != NULL) ? 1 : 0;
+ }
+ if (abort_on_error) {
+ abort();
+ }
+#endif
+ return status;
+}
+
+/**
+ * _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));
+}
+
+/* We keep a small stash of contexts to reduce malloc pressure */
+#define CAIRO_STASH_SIZE 4
+#if CAIRO_NO_MUTEX
+static struct {
+ cairo_t pool[CAIRO_STASH_SIZE];
+ int occupied;
+} _context_stash;
+
+static cairo_t *
+_context_get (void)
+{
+ int avail;
+
+ avail = ffs (~_context_stash.occupied) - 1;
+ if (avail >= CAIRO_STASH_SIZE)
+ return malloc (sizeof (cairo_t));
+
+ _context_stash.occupied |= 1 << avail;
+ return &_context_stash.pool[avail];
+}
+
+static void
+_context_put (cairo_t *cr)
+{
+ if (cr < &_context_stash.pool[0] ||
+ cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
+ {
+ free (cr);
+ return;
+ }
+
+ _context_stash.occupied &= ~(1 << (cr - &_context_stash.pool[0]));
+}
+#elif HAS_ATOMIC_OPS
+static struct {
+ cairo_t pool[CAIRO_STASH_SIZE];
+ cairo_atomic_int_t occupied;
+} _context_stash;
+
+static cairo_t *
+_context_get (void)
+{
+ cairo_atomic_int_t avail, old, new;
+
+ do {
+ old = _cairo_atomic_int_get (&_context_stash.occupied);
+ avail = ffs (~old) - 1;
+ if (avail >= CAIRO_STASH_SIZE)
+ return malloc (sizeof (cairo_t));
+
+ new = old | (1 << avail);
+ } while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new));
+
+ return &_context_stash.pool[avail];
+}
+
+static void
+_context_put (cairo_t *cr)
+{
+ cairo_atomic_int_t old, new, avail;
+
+ if (cr < &_context_stash.pool[0] ||
+ cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
+ {
+ free (cr);
+ return;
+ }
+
+ avail = ~(1 << (cr - &_context_stash.pool[0]));
+ do {
+ old = _cairo_atomic_int_get (&_context_stash.occupied);
+ new = old & avail;
+ } while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new));
+}
+#else
+#define _context_get() malloc (sizeof (cairo_t))
+#define _context_put(cr) free (cr)
+#endif
+
+/* XXX This should disappear in favour of a common pool of error objects. */
+static cairo_t *_cairo_nil__objects[CAIRO_STATUS_LAST_STATUS + 1];
+
+static cairo_t *
+_cairo_create_in_error (cairo_status_t status)
+{
+ cairo_t *cr;
+
+ assert (status != CAIRO_STATUS_SUCCESS);
+
+ /* Sanity check */
+ if (status < 0 || status > CAIRO_STATUS_LAST_STATUS) {
+ abort();
+ }
+
+ /* special case OOM in order to avoid another allocation */
+ switch ((int) status) {
+ case CAIRO_STATUS_NO_MEMORY:
+ return (cairo_t *) &_cairo_nil;
+ case CAIRO_STATUS_NULL_POINTER:
+ return (cairo_t *) &_cairo_nil__null_pointer;
+ }
+
+ CAIRO_MUTEX_LOCK (_cairo_error_mutex);
+ cr = _cairo_nil__objects[status];
+ if (cr == NULL) {
+ cr = malloc (sizeof (cairo_t));
+ if (unlikely (cr == NULL)) {
+ CAIRO_MUTEX_UNLOCK (_cairo_error_mutex);
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_t *) &_cairo_nil;
+ }
+
+ *cr = _cairo_nil;
+ cr->status = status;
+ _cairo_nil__objects[status] = cr;
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_error_mutex);
+
+ return cr;
+}
+
+void
+_cairo_reset_static_data (void)
+{
+ int status;
+
+ CAIRO_MUTEX_LOCK (_cairo_error_mutex);
+ for (status = CAIRO_STATUS_SUCCESS;
+ status <= CAIRO_STATUS_LAST_STATUS;
+ status++)
+ {
+ if (_cairo_nil__objects[status] != NULL) {
+ free (_cairo_nil__objects[status]);
+ _cairo_nil__objects[status] = NULL;
+ }
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_error_mutex);
+}
+
+/**
+ * cairo_create:
+ * @target: target surface for the context
+ *
+ * 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_<emphasis>backend</emphasis>_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.
+ * You can use this object normally, but no drawing will
+ * be done.
+ **/
+cairo_t *
+cairo_create (cairo_surface_t *target)
+{
+ cairo_t *cr;
+ cairo_status_t status;
+
+ if (unlikely (target == NULL))
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
+ if (unlikely (target->status))
+ return _cairo_create_in_error (target->status);
+
+ cr = _context_get ();
+ if (unlikely (cr == NULL))
+ return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1);
+
+ cr->status = CAIRO_STATUS_SUCCESS;
+
+ _cairo_user_data_array_init (&cr->user_data);
+ _cairo_path_fixed_init (cr->path);
+
+ cr->gstate = &cr->gstate_tail[0];
+ cr->gstate_freelist = &cr->gstate_tail[1];
+ cr->gstate_tail[1].next = NULL;
+
+ status = _cairo_gstate_init (cr->gstate, target);
+ if (unlikely (status)) {
+ _context_put (cr);
+ cr = _cairo_create_in_error (status);
+ }
+
+ return cr;
+}
+slim_hidden_def (cairo_create);
+
+/**
+ * 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.
+ *
+ * The number of references to a #cairo_t can be get using
+ * cairo_get_reference_count().
+ *
+ * Return value: the referenced #cairo_t.
+ **/
+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;
+}
+
+/**
+ * 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().
+ **/
+void
+cairo_destroy (cairo_t *cr)
+{
+ cairo_surface_t *surface;
+
+ 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;
+
+ while (cr->gstate != &cr->gstate_tail[0]) {
+ if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist))
+ break;
+ }
+
+ /* The context is expected (>99% of all use cases) to be held for the
+ * duration of a single expose event/sequence of graphic operations.
+ * Therefore, on destroy we explicitly flush the Cairo pipeline of any
+ * pending operations.
+ */
+ surface = _cairo_gstate_get_original_target (cr->gstate);
+ if (surface != NULL)
+ cairo_surface_flush (surface);
+
+ _cairo_gstate_fini (cr->gstate);
+ cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */
+ while (cr->gstate_freelist != NULL) {
+ cairo_gstate_t *gstate = cr->gstate_freelist;
+ cr->gstate_freelist = gstate->next;
+ free (gstate);
+ }
+
+ _cairo_path_fixed_fini (cr->path);
+
+ _cairo_user_data_array_fini (&cr->user_data);
+
+ /* mark the context as invalid to protect against misuse */
+ cr->status = CAIRO_STATUS_NULL_POINTER;
+
+ _context_put (cr);
+}
+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.
+ **/
+void
+cairo_save (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
+ 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.
+ **/
+void
+cairo_restore (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist);
+ 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:
+ *
+ * <informalexample><programlisting>
+ * 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);
+ * </programlisting></informalexample>
+ *
+ * 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_surface_t *group_surface;
+ cairo_clip_t *clip;
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ clip = _cairo_gstate_get_clip (cr->gstate);
+ if (clip->all_clipped) {
+ group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
+ status = group_surface->status;
+ if (unlikely (status))
+ goto bail;
+ } else {
+ cairo_surface_t *parent_surface;
+ const cairo_rectangle_int_t *clip_extents;
+ cairo_rectangle_int_t extents;
+ cairo_matrix_t matrix;
+ cairo_bool_t is_empty;
+
+ parent_surface = _cairo_gstate_get_target (cr->gstate);
+
+ /* Get the extents that we'll use in creating our new group surface */
+ is_empty = _cairo_surface_get_extents (parent_surface, &extents);
+ clip_extents = _cairo_clip_get_extents (_cairo_gstate_get_clip (cr->gstate));
+ if (clip_extents != NULL)
+ is_empty = _cairo_rectangle_intersect (&extents, clip_extents);
+
+ group_surface = _cairo_surface_create_similar_solid (parent_surface,
+ content,
+ extents.width,
+ extents.height,
+ CAIRO_COLOR_TRANSPARENT,
+ TRUE);
+ status = group_surface->status;
+ if (unlikely (status))
+ goto bail;
+
+ /* Set device offsets on the new surface so that logically it appears at
+ * the same location on the parent surface -- when we pop_group this,
+ * the source pattern will get fixed up for the appropriate target surface
+ * device offsets, so we want to set our own surface offsets from /that/,
+ * and not from the device origin. */
+ cairo_surface_set_device_offset (group_surface,
+ parent_surface->device_transform.x0 - extents.x,
+ parent_surface->device_transform.y0 - extents.y);
+
+ /* If we have a current path, we need to adjust it to compensate for
+ * the device offset just applied. */
+ cairo_matrix_init_translate (&matrix, -extents.x, -extents.y);
+ _cairo_path_fixed_transform (cr->path, &matrix);
+ }
+
+ /* create a new gstate for the redirect */
+ cairo_save (cr);
+ if (unlikely (cr->status))
+ goto bail;
+
+ status = _cairo_gstate_redirect_target (cr->gstate, group_surface);
+
+bail:
+ cairo_surface_destroy (group_surface);
+ 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_surface_t *group_surface, *parent_target;
+ cairo_pattern_t *group_pattern;
+ cairo_matrix_t group_matrix, device_transform_matrix;
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return _cairo_pattern_create_in_error (cr->status);
+
+ /* Grab the active surfaces */
+ group_surface = _cairo_gstate_get_target (cr->gstate);
+ parent_target = _cairo_gstate_get_parent_target (cr->gstate);
+
+ /* Verify that we are at the right nesting level */
+ if (parent_target == NULL) {
+ _cairo_set_error (cr, CAIRO_STATUS_INVALID_POP_GROUP);
+ return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP);
+ }
+
+ /* We need to save group_surface before we restore; we don't need
+ * to reference parent_target and original_target, since the
+ * gstate will still hold refs to them once we restore. */
+ group_surface = cairo_surface_reference (group_surface);
+
+ cairo_restore (cr);
+
+ if (unlikely (cr->status)) {
+ group_pattern = _cairo_pattern_create_in_error (cr->status);
+ goto done;
+ }
+
+ group_pattern = cairo_pattern_create_for_surface (group_surface);
+ status = group_pattern->status;
+ if (unlikely (status)) {
+ _cairo_set_error (cr, status);
+ goto done;
+ }
+
+ _cairo_gstate_get_matrix (cr->gstate, &group_matrix);
+ /* Transform by group_matrix centered around device_transform so that when
+ * we call _cairo_gstate_copy_transformed_pattern the result is a pattern
+ * with a matrix equivalent to the device_transform of group_surface. */
+ if (_cairo_surface_has_device_transform (group_surface)) {
+ cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform);
+ _cairo_pattern_transform (group_pattern, &group_matrix);
+ _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse);
+ } else {
+ cairo_pattern_set_matrix (group_pattern, &group_matrix);
+ }
+
+ /* If we have a current path, we need to adjust it to compensate for
+ * the device offset just removed. */
+ cairo_matrix_multiply (&device_transform_matrix,
+ &_cairo_gstate_get_target (cr->gstate)->device_transform,
+ &group_surface->device_transform_inverse);
+ _cairo_path_fixed_transform (cr->path, &device_transform_matrix);
+
+done:
+ cairo_surface_destroy (group_surface);
+
+ 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:
+ *
+ * <informalexample><programlisting>
+ * #cairo_pattern_t *group = cairo_pop_group (cr);
+ * cairo_set_source (cr, group);
+ * cairo_pattern_destroy (group);
+ * </programlisting></informalexample>
+ *
+ * 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.
+ **/
+void
+cairo_set_operator (cairo_t *cr, cairo_operator_t op)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_operator (cr->gstate, op);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def (cairo_set_operator);
+
+
+static cairo_bool_t
+_current_source_matches_solid (cairo_t *cr,
+ double red,
+ double green,
+ double blue,
+ double alpha)
+{
+ const cairo_pattern_t *current;
+ cairo_color_t color;
+
+ current = cr->gstate->source;
+ if (current->type != CAIRO_PATTERN_TYPE_SOLID)
+ return FALSE;
+
+ red = _cairo_restrict_value (red, 0.0, 1.0);
+ green = _cairo_restrict_value (green, 0.0, 1.0);
+ blue = _cairo_restrict_value (blue, 0.0, 1.0);
+ alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
+
+ _cairo_color_init_rgba (&color, red, green, blue, alpha);
+ return _cairo_color_equal (&color,
+ &((cairo_solid_pattern_t *) current)->color);
+}
+/**
+ * cairo_set_source_rgb
+ * @cr: a cairo context
+ * @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)).
+ **/
+void
+cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue)
+{
+ cairo_pattern_t *pattern;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (_current_source_matches_solid (cr, red, green, blue, 1.))
+ return;
+
+ /* push the current pattern to the freed lists */
+ cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
+
+ pattern = cairo_pattern_create_rgb (red, green, blue);
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+}
+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)).
+ **/
+void
+cairo_set_source_rgba (cairo_t *cr,
+ double red, double green, double blue,
+ double alpha)
+{
+ cairo_pattern_t *pattern;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (_current_source_matches_solid (cr, red, green, blue, alpha))
+ return;
+
+ /* push the current pattern to the freed lists */
+ cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
+
+ pattern = cairo_pattern_create_rgba (red, green, blue, alpha);
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+}
+
+/**
+ * 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()).
+ **/
+void
+cairo_set_source_surface (cairo_t *cr,
+ cairo_surface_t *surface,
+ double x,
+ double y)
+{
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+
+ if (unlikely (cr->status))
+ return;
+
+ /* push the current pattern to the freed lists */
+ cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
+
+ pattern = cairo_pattern_create_for_surface (surface);
+
+ cairo_matrix_init_translate (&matrix, -x, -y);
+ cairo_pattern_set_matrix (pattern, &matrix);
+
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+}
+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)).
+ **/
+void
+cairo_set_source (cairo_t *cr, cairo_pattern_t *source)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (source == NULL) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ if (source->status) {
+ _cairo_set_error (cr, source->status);
+ return;
+ }
+
+ status = _cairo_gstate_set_source (cr->gstate, 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().
+ **/
+cairo_pattern_t *
+cairo_get_source (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_pattern_create_in_error (cr->status);
+
+ return _cairo_gstate_get_source (cr->gstate);
+}
+
+/**
+ * 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.
+ **/
+void
+cairo_set_tolerance (cairo_t *cr, double tolerance)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (tolerance < CAIRO_TOLERANCE_MINIMUM)
+ tolerance = CAIRO_TOLERANCE_MINIMUM;
+
+ status = _cairo_gstate_set_tolerance (cr->gstate, 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().
+ **/
+void
+cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_antialias (cr->gstate, 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.
+ **/
+void
+cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_fill_rule (cr->gstate, 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.
+ **/
+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 = _cairo_gstate_set_line_width (cr->gstate, 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.
+ **/
+void
+cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_line_cap (cr->gstate, 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.
+ **/
+void
+cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_line_join (cr->gstate, 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.
+ **/
+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 = _cairo_gstate_set_dash (cr->gstate,
+ 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;
+
+ _cairo_gstate_get_dash (cr->gstate, 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;
+
+ _cairo_gstate_get_dash (cr->gstate, 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)
+ **/
+void
+cairo_set_miter_limit (cairo_t *cr, double limit)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_miter_limit (cr->gstate, 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.
+ **/
+void
+cairo_translate (cairo_t *cr, double tx, double ty)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_translate (cr->gstate, 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.
+ **/
+void
+cairo_scale (cairo_t *cr, double sx, double sy)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_scale (cr->gstate, 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.
+ **/
+void
+cairo_rotate (cairo_t *cr, double angle)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_rotate (cr->gstate, 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.
+ **/
+void
+cairo_transform (cairo_t *cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_transform (cr->gstate, 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.
+ **/
+void
+cairo_set_matrix (cairo_t *cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_matrix (cr->gstate, 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.
+ **/
+void
+cairo_identity_matrix (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_identity_matrix (cr->gstate);
+}
+
+/**
+ * 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).
+ **/
+void
+cairo_user_to_device (cairo_t *cr, double *x, double *y)
+{
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_user_to_device (cr->gstate, x, y);
+}
+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).
+ **/
+void
+cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy)
+{
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_user_to_device_distance (cr->gstate, dx, dy);
+}
+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).
+ **/
+void
+cairo_device_to_user (cairo_t *cr, double *x, double *y)
+{
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_device_to_user (cr->gstate, x, y);
+}
+
+/**
+ * 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).
+ **/
+void
+cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy)
+{
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_device_to_user_distance (cr->gstate, dx, dy);
+}
+
+/**
+ * cairo_new_path:
+ * @cr: a cairo context
+ *
+ * Clears the current path. After this call there will be no path and
+ * no current point.
+ **/
+void
+cairo_new_path (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_path_fixed_fini (cr->path);
+ _cairo_path_fixed_init (cr->path);
+}
+slim_hidden_def(cairo_new_path);
+
+/**
+ * cairo_move_to:
+ * @cr: a cairo context
+ * @x: the X coordinate of the new position
+ * @y: the Y coordinate of the new position
+ *
+ * Begin a new sub-path. After this call the current point will be (@x,
+ * @y).
+ **/
+void
+cairo_move_to (cairo_t *cr, double x, double y)
+{
+ cairo_status_t status;
+ cairo_fixed_t x_fixed, y_fixed;
+
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_user_to_backend (cr->gstate, &x, &y);
+ x_fixed = _cairo_fixed_from_double (x);
+ y_fixed = _cairo_fixed_from_double (y);
+
+ status = _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+slim_hidden_def(cairo_move_to);
+
+/**
+ * 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)
+{
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_path_fixed_new_sub_path (cr->path);
+}
+
+/**
+ * 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).
+ **/
+void
+cairo_line_to (cairo_t *cr, double x, double y)
+{
+ cairo_status_t status;
+ cairo_fixed_t x_fixed, y_fixed;
+
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_user_to_backend (cr->gstate, &x, &y);
+ x_fixed = _cairo_fixed_from_double (x);
+ y_fixed = _cairo_fixed_from_double (y);
+
+ status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
+ 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).
+ **/
+void
+cairo_curve_to (cairo_t *cr,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3)
+{
+ cairo_status_t status;
+ cairo_fixed_t x1_fixed, y1_fixed;
+ cairo_fixed_t x2_fixed, y2_fixed;
+ cairo_fixed_t x3_fixed, y3_fixed;
+
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_user_to_backend (cr->gstate, &x1, &y1);
+ _cairo_gstate_user_to_backend (cr->gstate, &x2, &y2);
+ _cairo_gstate_user_to_backend (cr->gstate, &x3, &y3);
+
+ x1_fixed = _cairo_fixed_from_double (x1);
+ y1_fixed = _cairo_fixed_from_double (y1);
+
+ x2_fixed = _cairo_fixed_from_double (x2);
+ y2_fixed = _cairo_fixed_from_double (y2);
+
+ x3_fixed = _cairo_fixed_from_double (x3);
+ y3_fixed = _cairo_fixed_from_double (y3);
+
+ status = _cairo_path_fixed_curve_to (cr->path,
+ x1_fixed, y1_fixed,
+ x2_fixed, y2_fixed,
+ x3_fixed, y3_fixed);
+ 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 <literal>degrees * (M_PI /
+ * 180.)</literal>.)
+ *
+ * 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:
+ *
+ * <informalexample><programlisting>
+ * 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);
+ * </programlisting></informalexample>
+ **/
+void
+cairo_arc (cairo_t *cr,
+ double xc, double yc,
+ double radius,
+ double angle1, double angle2)
+{
+ if (unlikely (cr->status))
+ return;
+
+ /* Do nothing, successfully, if radius is <= 0 */
+ if (radius <= 0.0) {
+ cairo_line_to (cr, xc, yc);
+ return;
+ }
+
+ while (angle2 < angle1)
+ angle2 += 2 * M_PI;
+
+ cairo_line_to (cr,
+ xc + radius * cos (angle1),
+ yc + radius * sin (angle1));
+
+ _cairo_arc_path (cr, xc, yc, radius,
+ angle1, angle2);
+}
+
+/**
+ * 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.
+ **/
+void
+cairo_arc_negative (cairo_t *cr,
+ double xc, double yc,
+ double radius,
+ double angle1, double angle2)
+{
+ if (unlikely (cr->status))
+ return;
+
+ /* Do nothing, successfully, if radius is <= 0 */
+ if (radius <= 0.0)
+ return;
+
+ while (angle2 > angle1)
+ angle2 -= 2 * M_PI;
+
+ cairo_line_to (cr,
+ xc + radius * cos (angle1),
+ yc + radius * sin (angle1));
+
+ _cairo_arc_path_negative (cr, xc, yc, radius,
+ angle1, angle2);
+}
+
+/* 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 = _cairo_gstate_arc_to (cr->gstate,
+ x1, y1,
+ x2, y2,
+ 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.
+ **/
+void
+cairo_rel_move_to (cairo_t *cr, double dx, double dy)
+{
+ cairo_fixed_t dx_fixed, dy_fixed;
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy);
+
+ dx_fixed = _cairo_fixed_from_double (dx);
+ dy_fixed = _cairo_fixed_from_double (dy);
+
+ status = _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed);
+ 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.
+ **/
+void
+cairo_rel_line_to (cairo_t *cr, double dx, double dy)
+{
+ cairo_fixed_t dx_fixed, dy_fixed;
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy);
+
+ dx_fixed = _cairo_fixed_from_double (dx);
+ dy_fixed = _cairo_fixed_from_double (dy);
+
+ status = _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed);
+ 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.
+ **/
+void
+cairo_rel_curve_to (cairo_t *cr,
+ double dx1, double dy1,
+ double dx2, double dy2,
+ double dx3, double dy3)
+{
+ cairo_fixed_t dx1_fixed, dy1_fixed;
+ cairo_fixed_t dx2_fixed, dy2_fixed;
+ cairo_fixed_t dx3_fixed, dy3_fixed;
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ _cairo_gstate_user_to_device_distance (cr->gstate, &dx1, &dy1);
+ _cairo_gstate_user_to_device_distance (cr->gstate, &dx2, &dy2);
+ _cairo_gstate_user_to_device_distance (cr->gstate, &dx3, &dy3);
+
+ dx1_fixed = _cairo_fixed_from_double (dx1);
+ dy1_fixed = _cairo_fixed_from_double (dy1);
+
+ dx2_fixed = _cairo_fixed_from_double (dx2);
+ dy2_fixed = _cairo_fixed_from_double (dy2);
+
+ dx3_fixed = _cairo_fixed_from_double (dx3);
+ dy3_fixed = _cairo_fixed_from_double (dy3);
+
+ status = _cairo_path_fixed_rel_curve_to (cr->path,
+ dx1_fixed, dy1_fixed,
+ dx2_fixed, dy2_fixed,
+ dx3_fixed, dy3_fixed);
+ 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:
+ * <informalexample><programlisting>
+ * 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);
+ * </programlisting></informalexample>
+ **/
+void
+cairo_rectangle (cairo_t *cr,
+ double x, double y,
+ double width, double height)
+{
+ if (unlikely (cr->status))
+ return;
+
+ cairo_move_to (cr, x, y);
+ cairo_rel_line_to (cr, width, 0);
+ cairo_rel_line_to (cr, 0, height);
+ cairo_rel_line_to (cr, -width, 0);
+ cairo_close_path (cr);
+}
+
+#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.
+ **/
+void
+cairo_close_path (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_path_fixed_close_path (cr->path);
+ 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;
+ }
+
+ _cairo_gstate_path_extents (cr->gstate,
+ cr->path,
+ x1, y1, x2, y2);
+}
+
+/**
+ * cairo_paint:
+ * @cr: a cairo context
+ *
+ * A drawing operator that paints the current source everywhere within
+ * the current clip region.
+ **/
+void
+cairo_paint (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_paint (cr->gstate);
+ 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.
+ **/
+void
+cairo_paint_with_alpha (cairo_t *cr,
+ double alpha)
+{
+ cairo_status_t status;
+ cairo_color_t color;
+ cairo_solid_pattern_t pattern;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (CAIRO_ALPHA_IS_OPAQUE (alpha)) {
+ cairo_paint (cr);
+ return;
+ }
+
+ if (CAIRO_ALPHA_IS_ZERO (alpha) &&
+ _cairo_operator_bounded_by_mask (cr->gstate->op)) {
+ return;
+ }
+
+ _cairo_color_init_rgba (&color, 0., 0., 0., alpha);
+ _cairo_pattern_init_solid (&pattern, &color);
+
+ status = _cairo_gstate_mask (cr->gstate, &pattern.base);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+
+ _cairo_pattern_fini (&pattern.base);
+}
+
+/**
+ * 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.)
+ */
+void
+cairo_mask (cairo_t *cr,
+ cairo_pattern_t *pattern)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (pattern == NULL) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ if (pattern->status) {
+ _cairo_set_error (cr, pattern->status);
+ return;
+ }
+
+ status = _cairo_gstate_mask (cr->gstate, 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.)
+ */
+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.
+ **/
+void
+cairo_stroke (cairo_t *cr)
+{
+ cairo_stroke_preserve (cr);
+
+ cairo_new_path (cr);
+}
+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().
+ **/
+void
+cairo_stroke_preserve (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_stroke (cr->gstate, cr->path);
+ 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().
+ **/
+void
+cairo_fill (cairo_t *cr)
+{
+ cairo_fill_preserve (cr);
+
+ cairo_new_path (cr);
+}
+
+/**
+ * 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().
+ **/
+void
+cairo_fill_preserve (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_fill (cr->gstate, cr->path);
+ 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.
+ **/
+void
+cairo_copy_page (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_copy_page (cr->gstate);
+ 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.
+ **/
+void
+cairo_show_page (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_show_page (cr->gstate);
+ 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.
+ **/
+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 = _cairo_gstate_in_stroke (cr->gstate,
+ cr->path,
+ 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.
+ **/
+cairo_bool_t
+cairo_in_fill (cairo_t *cr, double x, double y)
+{
+ if (unlikely (cr->status))
+ return FALSE;
+
+ return _cairo_gstate_in_fill (cr->gstate, cr->path, x, y);
+}
+
+/**
+ * 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().
+ **/
+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 = _cairo_gstate_stroke_extents (cr->gstate,
+ cr->path,
+ 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().
+ **/
+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 = _cairo_gstate_fill_extents (cr->gstate,
+ cr->path,
+ 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().
+ **/
+void
+cairo_clip (cairo_t *cr)
+{
+ cairo_clip_preserve (cr);
+
+ cairo_new_path (cr);
+}
+
+/**
+ * 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().
+ **/
+void
+cairo_clip_preserve (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_clip (cr->gstate, cr->path);
+ 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.
+ **/
+void
+cairo_reset_clip (cairo_t *cr)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_reset_clip (cr->gstate);
+ 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)
+{
+ 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;
+ }
+
+ if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) {
+ *x1 = -INFINITY;
+ *y1 = -INFINITY;
+ *x2 = +INFINITY;
+ *y2 = +INFINITY;
+ }
+}
+
+/**
+ * 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)
+{
+ if (unlikely (cr->status))
+ return FALSE;
+
+ return _cairo_gstate_in_clip (cr->gstate, x, y);
+}
+
+static cairo_rectangle_list_t *
+_cairo_rectangle_list_create_in_error (cairo_status_t status)
+{
+ cairo_rectangle_list_t *list;
+
+ if (status == CAIRO_STATUS_NO_MEMORY)
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+
+ list = malloc (sizeof (cairo_rectangle_list_t));
+ if (unlikely (list == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
+ }
+
+ list->status = status;
+ list->rectangles = NULL;
+ list->num_rectangles = 0;
+ return list;
+}
+
+/**
+ * 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 _cairo_gstate_copy_clip_rectangle_list (cr->gstate);
+}
+
+/**
+ * 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().
+ **/
+void
+cairo_select_font_face (cairo_t *cr,
+ const char *family,
+ cairo_font_slant_t slant,
+ cairo_font_weight_t weight)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_select_font_face (cr->gstate, family, slant, weight);
+ 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.
+ **/
+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 = _cairo_gstate_get_font_extents (cr->gstate, 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.
+ **/
+void
+cairo_set_font_face (cairo_t *cr,
+ cairo_font_face_t *font_face)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_font_face (cr->gstate, 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).
+ **/
+cairo_font_face_t *
+cairo_get_font_face (cairo_t *cr)
+{
+ cairo_status_t status;
+ cairo_font_face_t *font_face;
+
+ if (unlikely (cr->status))
+ return (cairo_font_face_t*) &_cairo_font_face_nil;
+
+ status = _cairo_gstate_get_font_face (cr->gstate, &font_face);
+ if (unlikely (status)) {
+ _cairo_set_error (cr, status);
+ return (cairo_font_face_t*) &_cairo_font_face_nil;
+ }
+
+ return font_face;
+}
+
+/**
+ * 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.
+ **/
+void
+cairo_set_font_size (cairo_t *cr, double size)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_font_size (cr->gstate, 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
+ **/
+void
+cairo_set_font_matrix (cairo_t *cr,
+ const cairo_matrix_t *matrix)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ status = _cairo_gstate_set_font_matrix (cr->gstate, matrix);
+ if (unlikely (status))
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * 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().
+ **/
+void
+cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix)
+{
+ if (unlikely (cr->status)) {
+ cairo_matrix_init_identity (matrix);
+ return;
+ }
+
+ _cairo_gstate_get_font_matrix (cr->gstate, 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.
+ **/
+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;
+ }
+
+ _cairo_gstate_set_font_options (cr->gstate, options);
+}
+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().
+ **/
+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;
+ }
+
+ _cairo_gstate_get_font_options (cr->gstate, 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;
+ cairo_bool_t was_previous;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (scaled_font == NULL) {
+ status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
+ goto BAIL;
+ }
+
+ status = scaled_font->status;
+ if (unlikely (status))
+ goto BAIL;
+
+ if (scaled_font == cr->gstate->scaled_font)
+ return;
+
+ was_previous = scaled_font == cr->gstate->previous_scaled_font;
+
+ status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face);
+ if (unlikely (status))
+ goto BAIL;
+
+ status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options);
+
+ /* XXX: Mozilla code assumes that the ctm of a scaled font doesn't need to
+ * match the context ctm. This assumption breaks the previous_scaled_font
+ * cache. So we avoid using the cache for now.
+ if (was_previous)
+ cr->gstate->scaled_font = cairo_scaled_font_reference ((cairo_scaled_font_t *) scaled_font);
+ */
+
+ return;
+
+BAIL:
+ _cairo_set_error (cr, status);
+}
+
+/**
+ * cairo_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)
+{
+ cairo_status_t status;
+ cairo_scaled_font_t *scaled_font;
+
+ if (unlikely (cr->status))
+ return _cairo_scaled_font_create_in_error (cr->status);
+
+ status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font);
+ if (unlikely (status)) {
+ _cairo_set_error (cr, status);
+ return _cairo_scaled_font_create_in_error (status);
+ }
+
+ return scaled_font;
+}
+
+/**
+ * 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.
+ **/
+void
+cairo_text_extents (cairo_t *cr,
+ const char *utf8,
+ cairo_text_extents_t *extents)
+{
+ cairo_status_t status;
+ cairo_glyph_t *glyphs = NULL;
+ int num_glyphs;
+ 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;
+
+ cairo_get_current_point (cr, &x, &y);
+
+ status = _cairo_gstate_text_to_glyphs (cr->gstate,
+ x, y,
+ utf8, strlen (utf8),
+ &glyphs, &num_glyphs,
+ NULL, NULL,
+ NULL);
+
+ if (status == CAIRO_STATUS_SUCCESS)
+ status = _cairo_gstate_glyph_extents (cr->gstate,
+ glyphs, num_glyphs,
+ extents);
+ 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).
+ **/
+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 (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 = _cairo_gstate_glyph_extents (cr->gstate, 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.
+ **/
+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)];
+
+ if (unlikely (cr->status))
+ return;
+
+ if (utf8 == NULL)
+ return;
+
+ cairo_get_current_point (cr, &x, &y);
+
+ 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;
+ }
+
+ status = _cairo_gstate_text_to_glyphs (cr->gstate,
+ x, y,
+ utf8, utf8_len,
+ &glyphs, &num_glyphs,
+ has_show_text_glyphs ? &clusters : NULL, &num_clusters,
+ &cluster_flags);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (num_glyphs == 0)
+ return;
+
+ status = _cairo_gstate_show_text_glyphs (cr->gstate,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags);
+ if (unlikely (status))
+ goto BAIL;
+
+ last_glyph = &glyphs[num_glyphs - 1];
+ status = _cairo_gstate_glyph_extents (cr->gstate,
+ last_glyph, 1,
+ &extents);
+ if (unlikely (status))
+ goto BAIL;
+
+ x = last_glyph->x + extents.x_advance;
+ y = last_glyph->y + extents.y_advance;
+ cairo_move_to (cr, x, y);
+
+ 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.
+ **/
+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 = _cairo_gstate_show_text_glyphs (cr->gstate,
+ NULL, 0,
+ glyphs, num_glyphs,
+ NULL, 0,
+ FALSE);
+ 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
+ * <firstterm>clusters</firstterm>. 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;
+ }
+
+ /* Make sure clusters cover the entire glyphs and utf8 arrays,
+ * and that cluster boundaries are UTF-8 boundaries. */
+ status = _cairo_validate_text_clusters (utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters, cluster_flags);
+ if (status == CAIRO_STATUS_INVALID_CLUSTERS) {
+ /* Either got invalid UTF-8 text, or cluster mapping is bad.
+ * Differentiate those. */
+
+ cairo_status_t status2;
+
+ status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL);
+ if (status2)
+ status = status2;
+
+ _cairo_set_error (cr, status);
+ return;
+ }
+
+ if (num_glyphs == 0 && utf8_len == 0)
+ return;
+
+ status = _cairo_gstate_show_text_glyphs (cr->gstate,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters, cluster_flags);
+ if (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.
+ **/
+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;
+ int num_glyphs;
+ double x, y;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (utf8 == NULL)
+ return;
+
+ cairo_get_current_point (cr, &x, &y);
+
+ glyphs = stack_glyphs;
+ num_glyphs = ARRAY_LENGTH (stack_glyphs);
+
+ status = _cairo_gstate_text_to_glyphs (cr->gstate,
+ x, y,
+ utf8, strlen (utf8),
+ &glyphs, &num_glyphs,
+ NULL, NULL,
+ NULL);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ if (num_glyphs == 0)
+ return;
+
+ status = _cairo_gstate_glyph_path (cr->gstate,
+ glyphs, num_glyphs,
+ cr->path);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ last_glyph = &glyphs[num_glyphs - 1];
+ status = _cairo_gstate_glyph_extents (cr->gstate,
+ last_glyph, 1,
+ &extents);
+
+ if (unlikely (status))
+ goto BAIL;
+
+ x = last_glyph->x + extents.x_advance;
+ y = last_glyph->y + extents.y_advance;
+ cairo_move_to (cr, x, y);
+
+ 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().
+ **/
+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 (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 = _cairo_gstate_glyph_path (cr->gstate,
+ glyphs, num_glyphs,
+ cr->path);
+ 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.
+ **/
+cairo_operator_t
+cairo_get_operator (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_OPERATOR_DEFAULT;
+
+ return _cairo_gstate_get_operator (cr->gstate);
+}
+
+/**
+ * cairo_get_tolerance:
+ * @cr: a cairo context
+ *
+ * Gets the current tolerance value, as set by cairo_set_tolerance().
+ *
+ * Return value: the current tolerance value.
+ **/
+double
+cairo_get_tolerance (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_TOLERANCE_DEFAULT;
+
+ return _cairo_gstate_get_tolerance (cr->gstate);
+}
+slim_hidden_def (cairo_get_tolerance);
+
+/**
+ * cairo_get_antialias:
+ * @cr: a cairo context
+ *
+ * Gets the current shape antialiasing mode, as set by cairo_set_shape_antialias().
+ *
+ * Return value: the current shape antialiasing mode.
+ **/
+cairo_antialias_t
+cairo_get_antialias (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_ANTIALIAS_DEFAULT;
+
+ return _cairo_gstate_get_antialias (cr->gstate);
+}
+
+/**
+ * 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->path->has_current_point;
+}
+
+/**
+ * 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().
+ **/
+void
+cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret)
+{
+ cairo_fixed_t x_fixed, y_fixed;
+ double x, y;
+
+ if (cr->status == CAIRO_STATUS_SUCCESS &&
+ _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);
+ }
+ else
+ {
+ x = 0.0;
+ y = 0.0;
+ }
+
+ 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.
+ **/
+cairo_fill_rule_t
+cairo_get_fill_rule (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_FILL_RULE_DEFAULT;
+
+ return _cairo_gstate_get_fill_rule (cr->gstate);
+}
+
+/**
+ * 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.
+ **/
+double
+cairo_get_line_width (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_LINE_WIDTH_DEFAULT;
+
+ return _cairo_gstate_get_line_width (cr->gstate);
+}
+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.
+ **/
+cairo_line_cap_t
+cairo_get_line_cap (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_LINE_CAP_DEFAULT;
+
+ return _cairo_gstate_get_line_cap (cr->gstate);
+}
+
+/**
+ * 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.
+ **/
+cairo_line_join_t
+cairo_get_line_join (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_LINE_JOIN_DEFAULT;
+
+ return _cairo_gstate_get_line_join (cr->gstate);
+}
+
+/**
+ * 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.
+ **/
+double
+cairo_get_miter_limit (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return CAIRO_GSTATE_MITER_LIMIT_DEFAULT;
+
+ return _cairo_gstate_get_miter_limit (cr->gstate);
+}
+
+/**
+ * cairo_get_matrix:
+ * @cr: a cairo context
+ * @matrix: return value for the matrix
+ *
+ * Stores the current transformation matrix (CTM) into @matrix.
+ **/
+void
+cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix)
+{
+ if (unlikely (cr->status)) {
+ cairo_matrix_init_identity (matrix);
+ return;
+ }
+
+ _cairo_gstate_get_matrix (cr->gstate, 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() <literal>!=</literal> %CAIRO_STATUS_SUCCESS).
+ * A nil surface is indicated by cairo_surface_status()
+ * <literal>!=</literal> %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().
+ **/
+cairo_surface_t *
+cairo_get_target (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_surface_create_in_error (cr->status);
+
+ return _cairo_gstate_get_original_target (cr->gstate);
+}
+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() <literal>!=</literal> %CAIRO_STATUS_SUCCESS).
+ * A nil surface is indicated by cairo_surface_status()
+ * <literal>!=</literal> %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 _cairo_gstate_get_target (cr->gstate);
+}
+
+/**
+ * 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 (<literal>data==%NULL</literal> and
+ * <literal>num_data==0</literal>), if either of the following
+ * conditions hold:
+ *
+ * <orderedlist>
+ * <listitem>If there is insufficient memory to copy the path. In this
+ * case <literal>path->status</literal> will be set to
+ * %CAIRO_STATUS_NO_MEMORY.</listitem>
+ * <listitem>If @cr is already in an error state. In this case
+ * <literal>path->status</literal> will contain the same status that
+ * would be returned by cairo_status().</listitem>
+ * </orderedlist>
+ *
+ * Return value: the copy of the current path. The caller owns the
+ * returned object and should call cairo_path_destroy() when finished
+ * with it.
+ **/
+cairo_path_t *
+cairo_copy_path (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_path_create_in_error (cr->status);
+
+ return _cairo_path_create (cr->path, cr->gstate);
+}
+
+/**
+ * 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 (<literal>data==%NULL</literal> and
+ * <literal>num_data==0</literal>), if either of the following
+ * conditions hold:
+ *
+ * <orderedlist>
+ * <listitem>If there is insufficient memory to copy the path. In this
+ * case <literal>path->status</literal> will be set to
+ * %CAIRO_STATUS_NO_MEMORY.</listitem>
+ * <listitem>If @cr is already in an error state. In this case
+ * <literal>path->status</literal> will contain the same status that
+ * would be returned by cairo_status().</listitem>
+ * </orderedlist>
+ *
+ * Return value: the copy of the current path. The caller owns the
+ * returned object and should call cairo_path_destroy() when finished
+ * with it.
+ **/
+cairo_path_t *
+cairo_copy_path_flat (cairo_t *cr)
+{
+ if (unlikely (cr->status))
+ return _cairo_path_create_in_error (cr->status);
+
+ return _cairo_path_create_flat (cr->path, cr->gstate);
+}
+
+/**
+ * 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 <literal>path->status</literal> must be
+ * initialized to %CAIRO_STATUS_SUCCESS.
+ **/
+void
+cairo_append_path (cairo_t *cr,
+ const cairo_path_t *path)
+{
+ cairo_status_t status;
+
+ if (unlikely (cr->status))
+ return;
+
+ if (path == NULL) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ if (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 (path->data == NULL) {
+ _cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
+ return;
+ }
+
+ status = _cairo_path_append_to_context (path, cr);
+ 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
+ **/
+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 000000000..52d062352
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairo.h
@@ -0,0 +1,2729 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#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.
+ *
+ * <informalexample><programlisting>
+ * if (cairo_in_stroke (cr, x, y)) {
+ * /<!-- -->* do something *<!-- -->/
+ * }
+ * </programlisting></informalexample>
+ **/
+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().
+ **/
+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().
+ **/
+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_xcb_device_create() creates a device that wraps the connection
+ * to an X Windows System using the XCB library.
+ *
+ * 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:
+ * <programlisting>
+ * x_new = xx * x + xy * y + x0;
+ * y_new = yx * x + yy * y + y0;
+ * </programlisting>
+ **/
+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_<emphasis>type</emphasis>()
+ * functions, some of the pattern types can be implicitly created
+ * using various cairo_set_source_<emphasis>type</emphasis>() 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().
+ **/
+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.
+ **/
+typedef void (*cairo_destroy_func_t) (void *data);
+
+/**
+ * cairo_surface_func_t:
+ * @surface: The surface being referred to.
+ *
+ * #cairo_surface_func_t the type of function which is used for callback
+ * when a surface needs to be apssed as a parameter.
+ */
+typedef void (*cairo_surface_func_t) (cairo_surface_t *surface);
+
+/**
+ * cairo_user_data_key_t:
+ * @unused: not used; ignore.
+ *
+ * #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.
+ **/
+typedef struct _cairo_user_data_key {
+ int unused;
+} cairo_user_data_key_t;
+
+/**
+ * cairo_status_t:
+ * @CAIRO_STATUS_SUCCESS: no error has occurred
+ * @CAIRO_STATUS_NO_MEMORY: out of memory
+ * @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save()
+ * @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()
+ * @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined
+ * @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible)
+ * @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t
+ * @CAIRO_STATUS_NULL_POINTER: %NULL pointer
+ * @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8
+ * @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid
+ * @CAIRO_STATUS_READ_ERROR: error while reading from input stream
+ * @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream
+ * @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished
+ * @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation
+ * @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation
+ * @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t
+ * @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t
+ * @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual*
+ * @CAIRO_STATUS_FILE_NOT_FOUND: file not found
+ * @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting
+ * @CAIRO_STATUS_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_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.
+ **/
+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_NO_DEVICE,
+
+ CAIRO_STATUS_LAST_STATUS
+} cairo_status_t;
+
+/**
+ * cairo_content_t:
+ * @CAIRO_CONTENT_COLOR: The surface will hold color content only.
+ * @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only.
+ * @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content.
+ *
+ * #cairo_content_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.
+ **/
+typedef enum _cairo_content {
+ CAIRO_CONTENT_COLOR = 0x1000,
+ CAIRO_CONTENT_ALPHA = 0x2000,
+ CAIRO_CONTENT_COLOR_ALPHA = 0x3000
+} cairo_content_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
+ **/
+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
+ **/
+typedef cairo_status_t (*cairo_read_func_t) (void *closure,
+ unsigned char *data,
+ unsigned int length);
+
+/* 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)
+ * @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded)
+ * @CAIRO_OPERATOR_OVER: draw source layer on top of destination layer
+ * (bounded)
+ * @CAIRO_OPERATOR_IN: draw source where there was destination content
+ * (unbounded)
+ * @CAIRO_OPERATOR_OUT: draw source where there was no destination
+ * content (unbounded)
+ * @CAIRO_OPERATOR_ATOP: draw source on top of destination content and
+ * only there
+ * @CAIRO_OPERATOR_DEST: ignore the source
+ * @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source
+ * @CAIRO_OPERATOR_DEST_IN: leave destination only where there was
+ * source content (unbounded)
+ * @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no
+ * source content
+ * @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content
+ * and only there (unbounded)
+ * @CAIRO_OPERATOR_XOR: source and destination are shown where there is only
+ * one of them
+ * @CAIRO_OPERATOR_ADD: source and destination layers are accumulated
+ * @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are
+ * disjoint geometries
+ * @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied.
+ * This causes the result to be at least as dark as the darker inputs.
+ * @CAIRO_OPERATOR_SCREEN: source and destination are complemented and
+ * multiplied. This causes the result to be at least as light as the lighter
+ * inputs.
+ * @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the
+ * lightness of the destination color.
+ * @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it
+ * is darker, otherwise keeps the source.
+ * @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it
+ * is lighter, otherwise keeps the source.
+ * @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect
+ * the source color.
+ * @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect
+ * the source color.
+ * @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependant on source
+ * color.
+ * @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependant on source
+ * color.
+ * @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and
+ * destination color.
+ * @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but
+ * with lower contrast.
+ * @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source
+ * and the saturation and luminosity of the target.
+ * @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation
+ * of the source and the hue and luminosity of the target. Painting with
+ * this mode onto a gray area prduces no change.
+ * @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.
+ * @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.
+ *
+ * #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 <firstterm>unbounded</firstterm> 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
+ * <ulink url="http://cairographics.org/operators/">http://cairographics.org/operators/</ulink>.
+ **/
+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
+ * @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask
+ * @CAIRO_ANTIALIAS_GRAY: Perform single-color antialiasing (using
+ * shades of gray for black text on a white background, for example).
+ * @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking
+ * advantage of the order of subpixel elements on devices
+ * such as LCD panels
+ *
+ * Specifies the type of antialiasing to do when rendering text or shapes.
+ **/
+typedef enum _cairo_antialias {
+ CAIRO_ANTIALIAS_DEFAULT,
+ CAIRO_ANTIALIAS_NONE,
+ CAIRO_ANTIALIAS_GRAY,
+ CAIRO_ANTIALIAS_SUBPIXEL
+} 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.
+ * @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.
+ *
+ * #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.
+ **/
+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
+ * @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point
+ * @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point
+ *
+ * Specifies how to render the endpoints of the path when stroking.
+ *
+ * The default line cap style is %CAIRO_LINE_CAP_BUTT.
+ **/
+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()
+ * @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the
+ * joint point
+ * @CAIRO_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half
+ * the line width from the joint point
+ *
+ * Specifies how to render the junction of two lines when stroking.
+ *
+ * The default line join style is %CAIRO_LINE_JOIN_MITER.
+ **/
+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);
+
+/* 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
+ * <firstterm>font backend</firstterm> 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().
+ **/
+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 sheering 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
+ * <firstterm>font backend</firstterm> 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().
+ **/
+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 <firstterm>ligature</firstterm> 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
+ **/
+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
+ * <firstterm>text cluster</firstterm>. 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.
+ *
+ * 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
+ * <literal>cairo_scale(cr, 2.0, 2.0)</literal>, 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.
+ **/
+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 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 <firstterm>line spacing</firstterm>
+ * or <firstterm>external leading</firstterm>. 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 the origin is advanced for any glyph in the font.
+ * @max_y_advance: the maximum distance in the Y direction that
+ * the the origin is advanced for any glyph in the font.
+ * this will be zero for normal fonts used for horizontal
+ * 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 <literal>cairo_scale(cr, 2.0, 2.0)</literal>,
+ * 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.
+ **/
+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
+ * @CAIRO_FONT_SLANT_ITALIC: Italic font style
+ * @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style
+ *
+ * Specifies variants of a font face based on their slant.
+ **/
+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
+ * @CAIRO_FONT_WEIGHT_BOLD: Bold font weight
+ *
+ * Specifies variants of a font face based on their weight.
+ **/
+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
+ * @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally
+ * with red at the left
+ * @CAIRO_SUBPIXEL_ORDER_BGR: Subpixel elements are arranged horizontally
+ * with blue at the left
+ * @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically
+ * with red at the top
+ * @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically
+ * with blue at the top
+ *
+ * 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.
+ **/
+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
+ * @CAIRO_HINT_STYLE_NONE: Do not hint outlines
+ * @CAIRO_HINT_STYLE_SLIGHT: Hint outlines slightly to improve
+ * contrast while retaining good fidelity to the original
+ * shapes.
+ * @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength
+ * giving a compromise between fidelity to the original shapes
+ * and contrast
+ * @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast
+ *
+ * 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.
+ **/
+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
+ * @CAIRO_HINT_METRICS_OFF: Do not hint font metrics
+ * @CAIRO_HINT_METRICS_ON: Hint font metrics
+ *
+ * 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.
+ **/
+typedef enum _cairo_hint_metrics {
+ CAIRO_HINT_METRICS_DEFAULT,
+ CAIRO_HINT_METRICS_OFF,
+ CAIRO_HINT_METRICS_ON
+} cairo_hint_metrics_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_<emphasis>feature_name</emphasis> and
+ * cairo_font_options_get_<emphasis>feature_name</emphasis>, 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.
+ **/
+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);
+
+/* 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_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
+ * @CAIRO_FONT_TYPE_FT: The font is of type FreeType
+ * @CAIRO_FONT_TYPE_WIN32: The font is of type Win32
+ * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6)
+ * @CAIRO_FONT_TYPE_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_<emphasis>type</emphasis>_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_<emphasis>type</emphasis>_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 <literal>x_bearing</literal>,
+ * <literal>width</literal>, and <literal>y_advance</literal> of zero,
+ * <literal>y_bearing</literal> set to <literal>-font_extents.ascent</literal>,
+ * <literal>height</literal> to <literal>font_extents.ascent+font_extents.descent</literal>,
+ * and <literal>x_advance</literal> to <literal>font_extents.max_x_advance</literal>.
+ * The only field user needs to set in majority of cases is
+ * <literal>x_advance</literal>.
+ * If the <literal>width</literal> 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 <firstterm>shaping</firstterm> 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 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. 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
+ * @CAIRO_PATH_LINE_TO: A line-to operation
+ * @CAIRO_PATH_CURVE_TO: A curve-to operation
+ * @CAIRO_PATH_CLOSE_PATH: A close-path operation
+ *
+ * #cairo_path_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.
+ **/
+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:
+ *
+ * <programlisting>
+ * %CAIRO_PATH_MOVE_TO: 1 point
+ * %CAIRO_PATH_LINE_TO: 1 point
+ * %CAIRO_PATH_CURVE_TO: 3 points
+ * %CAIRO_PATH_CLOSE_PATH: 0 points
+ * </programlisting>
+ *
+ * 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:
+ *
+ * <informalexample><programlisting>
+ * int i;
+ * cairo_path_t *path;
+ * cairo_path_data_t *data;
+ * &nbsp;
+ * path = cairo_copy_path (cr);
+ * &nbsp;
+ * for (i=0; i < path->num_data; i += path->data[i].header.length) {
+ * data = &amp;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);
+ * </programlisting></informalexample>
+ *
+ * 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 <literal>data->header.length</literal> to
+ * iterate over the path data, instead of hardcoding the number of
+ * elements for each element type.
+ **/
+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.
+ **/
+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 surface is of type Direct Render Manager
+ * @CAIRO_DEVICE_TYPE_GL: The surface is of type OpenGL
+ * @CAIRO_DEVICE_TYPE_SCRIPT: The surface is of type script
+ * @CAIRO_DEVICE_TYPE_XCB: The surface is of type xcb
+ * @CAIRO_DEVICE_TYPE_XLIB: The surface is of type xlib
+ * @CAIRO_DEVICE_TYPE_XML: The surface is of type XML
+ * cairo_surface_create_for_rectangle()
+ *
+ * #cairo_device_type_t is used to describe the type of a given
+ * device. The devices types are also known as "backends" within cairo.
+ *
+ * The device type can be queried with cairo_device_get_type()
+ *
+ * The various #cairo_device_t functions can be used with surfaces of
+ * 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_<emphasis>type</emphasis>_device<!-- --> such as cairo_xcb_device_debug_set_render_version().
+ *
+ * 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.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_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_for_rectangle (cairo_surface_t *target,
+ double x,
+ double y,
+ double width,
+ double height);
+
+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
+ * @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf
+ * @CAIRO_SURFACE_TYPE_PS: The surface is of type ps
+ * @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib
+ * @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb
+ * @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz
+ * @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz
+ * @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32
+ * @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos
+ * @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb
+ * @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg
+ * @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2
+ * @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface
+ * @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image
+ * @CAIRO_SURFACE_TYPE_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_SKIA: The surface is of type Skia, since 1.10
+ * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
+ * cairo_surface_create_for_rectangle(), since 1.10
+ * @CAIRO_SURFACE_TYPE_D2D: The surface is of type Direct2D
+ *
+ * #cairo_surface_type_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_<emphasis>type</emphasis>_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_<emphasis>type</emphasis>_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_D2D
+} 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);
+
+cairo_public void
+cairo_surface_attach_snapshot (cairo_surface_t *surface,
+ cairo_surface_t *snapshot,
+ cairo_surface_func_t detach_func);
+
+cairo_public void
+cairo_surface_detach_snapshot (cairo_surface_t *snapshot);
+
+#define CAIRO_MIME_TYPE_JPEG "image/jpeg"
+#define CAIRO_MIME_TYPE_PNG "image/png"
+#define CAIRO_MIME_TYPE_JP2 "image/jp2"
+#define CAIRO_MIME_TYPE_URI "text/x-uri"
+
+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 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_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_format_t:
+ * @CAIRO_FORMAT_INVALID: no such format exists or is supported.
+ * @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with
+ * alpha in the upper 8 bits, then red, then green, then blue.
+ * The 32-bit quantities are stored native-endian. Pre-multiplied
+ * alpha is used. (That is, 50% transparent red is 0x80800000,
+ * not 0x80ff0000.)
+ * @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with
+ * the upper 8 bits unused. Red, Green, and Blue are stored
+ * in the remaining 24 bits in that order.
+ * @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding
+ * an alpha value.
+ * @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding
+ * an alpha value. Pixels are packed together into 32-bit
+ * quantities. The ordering of the bits matches the
+ * endianess of the platform. On a big-endian machine, the
+ * first pixel is in the uppermost bit, on a little-endian
+ * machine the first pixel is in the least-significant bit.
+ * @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity
+ * with red in the upper 5 bits, then green in the middle
+ * 6 bits, and blue in the lower 5 bits.
+ *
+ * #cairo_format_t is used to identify the memory format of
+ * image data.
+ *
+ * New entries may be added in future versions.
+ **/
+typedef enum _cairo_format {
+ CAIRO_FORMAT_INVALID = -1,
+ CAIRO_FORMAT_ARGB32 = 0,
+ CAIRO_FORMAT_RGB24 = 1,
+ CAIRO_FORMAT_A8 = 2,
+ CAIRO_FORMAT_A1 = 3,
+ CAIRO_FORMAT_RGB16_565 = 4
+} cairo_format_t;
+
+cairo_public cairo_surface_t *
+cairo_image_surface_create (cairo_format_t format,
+ int width,
+ 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);
+
+/* Null-surface functions */
+
+cairo_public cairo_surface_t *
+cairo_null_surface_create (cairo_content_t content);
+
+/* 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_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.
+ * @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image).
+ * @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient.
+ * @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient.
+ *
+ * #cairo_pattern_type_t 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_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_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
+ * @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating
+ * @CAIRO_EXTEND_REFLECT: the pattern is tiled by reflecting
+ * at the edges (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).
+ *
+ * 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.
+ **/
+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
+ * @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality
+ * similar to %CAIRO_FILTER_BILINEAR
+ * @CAIRO_FILTER_BEST: The highest-quality available, performance may
+ * not be suitable for interactive use.
+ * @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering
+ * @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions
+ * @CAIRO_FILTER_GAUSSIAN: This filter value is currently
+ * unimplemented, and should not be used in current code.
+ *
+ * #cairo_filter_t is used to indicate what filtering should be
+ * applied when reading pixel values from patterns. See
+ * cairo_pattern_set_source() for indicating the desired filter to be
+ * used with a particular pattern.
+ */
+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);
+
+/* 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_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;
+
+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/cairoint.h b/gfx/cairo/cairo/src/cairoint.h
new file mode 100644
index 000000000..be78d6d89
--- /dev/null
+++ b/gfx/cairo/cairo/src/cairoint.h
@@ -0,0 +1,2593 @@
+/* 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 <cworth@cworth.org>
+ */
+
+/*
+ * 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef _MSC_VER
+#define _USE_MATH_DEFINES
+#endif
+#include <math.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "cairo.h"
+#include <pixman.h>
+
+#include "cairo-compiler-private.h"
+
+#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))
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef NDEBUG
+#undef assert
+#define assert(expr) \
+ do { if (!(expr)) fprintf(stderr, "Assertion failed at %s:%d: %s\n", \
+ __FILE__, __LINE__, #expr); } while (0)
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
+
+#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
+ register int y;
+
+ y = (mask >> 1) &033333333333;
+ y = mask - y - ((y >>1) & 033333333333);
+ return (((y + (y >> 3)) & 030707070707) % 077);
+#endif
+}
+
+#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 (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16);
+}
+
+static inline uint32_t cairo_const
+be32_to_cpu(uint32_t v)
+{
+ return cpu_to_be32 (v);
+}
+
+#endif
+
+
+/* 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');
+}
+
+#include "cairo-types-private.h"
+#include "cairo-cache-private.h"
+#include "cairo-reference-count-private.h"
+#include "cairo-spans-private.h"
+
+cairo_private void
+_cairo_box_from_doubles (cairo_box_t *box,
+ 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 cairo_bool_t
+_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle,
+ const cairo_rectangle_int_t *contained_rectangle);
+
+cairo_private void
+_cairo_box_round_to_rectangle (const cairo_box_t *box,
+ cairo_rectangle_int_t *rectangle);
+
+cairo_private void
+_cairo_boxes_get_extents (const cairo_box_t *boxes,
+ int num_boxes,
+ cairo_box_t *extents);
+
+static inline void
+_cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect)
+{
+ rect->x = CAIRO_RECT_INT_MIN;
+ rect->y = CAIRO_RECT_INT_MIN;
+ rect->width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN;
+ rect->height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN;
+}
+
+cairo_private cairo_bool_t
+_cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
+ const cairo_rectangle_int_t *src);
+
+cairo_private cairo_bool_t
+_cairo_box_intersects_line_segment (cairo_box_t *box,
+ cairo_line_t *line) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_box_contains_point (cairo_box_t *box,
+ const cairo_point_t *point) cairo_pure;
+
+/* cairo-array.c structures and functions */
+
+cairo_private void
+_cairo_array_init (cairo_array_t *array, int element_size);
+
+cairo_private void
+_cairo_array_init_snapshot (cairo_array_t *array,
+ const cairo_array_t *other);
+
+cairo_private void
+_cairo_array_fini (cairo_array_t *array);
+
+cairo_private cairo_status_t
+_cairo_array_grow_by (cairo_array_t *array, unsigned int additional);
+
+cairo_private void
+_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements);
+
+cairo_private cairo_status_t
+_cairo_array_append (cairo_array_t *array, const void *element);
+
+cairo_private cairo_status_t
+_cairo_array_append_multiple (cairo_array_t *array,
+ const void *elements,
+ int num_elements);
+
+cairo_private cairo_status_t
+_cairo_array_allocate (cairo_array_t *array,
+ unsigned int num_elements,
+ void **elements);
+
+cairo_private void *
+_cairo_array_index (cairo_array_t *array, unsigned int index);
+
+cairo_private void
+_cairo_array_copy_element (cairo_array_t *array, int index, void *dst);
+
+cairo_private int
+_cairo_array_num_elements (cairo_array_t *array);
+
+cairo_private int
+_cairo_array_size (cairo_array_t *array);
+
+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,
+ 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);
+
+#define _cairo_scaled_glyph_index(g) ((g)->hash_entry.hash)
+#define _cairo_scaled_glyph_set_index(g, i) ((g)->hash_entry.hash = (i))
+
+#include "cairo-scaled-font-private.h"
+
+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_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);
+
+/* the font backend interface */
+
+struct _cairo_unscaled_font_backend {
+ void (*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_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;
+ unsigned long *to_unicode;
+ char **utf8;
+ char **glyph_names;
+ unsigned int num_glyphs;
+ cairo_bool_t is_composite;
+ cairo_bool_t is_scaled;
+} 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);
+ cairo_warn cairo_int_status_t
+ (*show_glyphs) (void *scaled_font,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *surface,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_region_t *clip_region,
+ int *remaining_glyphs);
+
+ 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);
+};
+
+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.
+ */
+ void
+ (*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_DWRITE_FONT
+
+extern const cairo_private struct _cairo_font_face_backend _cairo_dwrite_font_face_backend;
+
+#endif
+
+#if CAIRO_HAS_QUARTZ_FONT
+
+extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend;
+
+#endif
+
+struct _cairo_surface_backend {
+ cairo_surface_type_t type;
+
+ cairo_surface_t *
+ (*create_similar) (void *surface,
+ cairo_content_t content,
+ int width,
+ int height);
+
+ cairo_warn cairo_status_t
+ (*finish) (void *surface);
+
+ cairo_warn cairo_status_t
+ (*acquire_source_image) (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra);
+
+ void
+ (*release_source_image) (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra);
+
+ cairo_warn cairo_status_t
+ (*acquire_dest_image) (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra);
+
+ void
+ (*release_dest_image) (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra);
+
+ /* Create a new surface (@clone_out) with the following
+ * characteristics:
+ *
+ * 1. It is as compatible as possible with @surface (in terms of
+ * efficiency)
+ *
+ * 2. It has the same contents as @src within the given rectangle.
+ *
+ * 3. The offset of the similar surface with respect to the original
+ * surface is returned in the clone_offset vector.
+ * - if you clone the entire surface, this vector is zero.
+ * - if you clone (src_x, src_y)x(w, h) the vector is (src_x, src_y);
+ */
+ cairo_warn cairo_status_t
+ (*clone_similar) (void *surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out);
+
+ /* XXX remove to a separate cairo_surface_compositor_t */
+ /* XXX: dst should be the first argument for consistency */
+ cairo_warn cairo_int_status_t
+ (*composite) (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ void *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region);
+
+ cairo_warn cairo_int_status_t
+ (*fill_rectangles) (void *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects);
+
+ /* XXX: dst should be the first argument for consistency */
+ cairo_warn cairo_int_status_t
+ (*composite_trapezoids) (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *dst,
+ cairo_antialias_t antialias,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int num_traps,
+ cairo_region_t *region);
+
+ cairo_warn cairo_span_renderer_t *
+ (*create_span_renderer) (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *dst,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects,
+ cairo_region_t *clip_region);
+
+ cairo_warn cairo_bool_t
+ (*check_span_renderer) (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *dst,
+ cairo_antialias_t antialias);
+
+ cairo_warn cairo_int_status_t
+ (*copy_page) (void *surface);
+
+ cairo_warn cairo_int_status_t
+ (*show_page) (void *surface);
+
+ /* Get the extents of the current surface. For many surface types
+ * this will be as simple as { x=0, y=0, width=surface->width,
+ * height=surface->height}.
+ *
+ * If this function is not implemented, or if it returns
+ * FALSE the surface is considered to be
+ * boundless and infinite bounds are used for it.
+ */
+ cairo_warn cairo_bool_t
+ (*get_extents) (void *surface,
+ cairo_rectangle_int_t *extents);
+
+ /*
+ * This is an optional entry to let the surface manage its own glyph
+ * resources. If null, render against this surface, using image
+ * surfaces as glyphs.
+ */
+ cairo_warn cairo_int_status_t
+ (*old_show_glyphs) (cairo_scaled_font_t *font,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ void *surface,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_region_t *clip_region);
+
+ void
+ (*get_font_options) (void *surface,
+ cairo_font_options_t *options);
+
+ cairo_warn cairo_status_t
+ (*flush) (void *surface);
+
+ cairo_warn cairo_status_t
+ (*mark_dirty_rectangle) (void *surface,
+ int x,
+ int y,
+ int width,
+ int height);
+
+ void
+ (*scaled_font_fini) (cairo_scaled_font_t *scaled_font);
+
+ void
+ (*scaled_glyph_fini) (cairo_scaled_glyph_t *scaled_glyph,
+ cairo_scaled_font_t *scaled_font);
+
+ /* OK, I'm starting over somewhat by defining the 5 top-level
+ * drawing operators for the surface backend here with consistent
+ * naming and argument-order conventions. */
+ cairo_warn cairo_int_status_t
+ (*paint) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*mask) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*stroke) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*fill) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+ cairo_warn cairo_int_status_t
+ (*show_glyphs) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs);
+
+ cairo_surface_t *
+ (*snapshot) (void *surface);
+
+ cairo_bool_t
+ (*is_similar) (void *surface_a,
+ void *surface_b);
+
+ cairo_warn cairo_int_status_t
+ (*fill_stroke) (void *surface,
+ cairo_operator_t fill_op,
+ const cairo_pattern_t *fill_source,
+ cairo_fill_rule_t fill_rule,
+ double fill_tolerance,
+ cairo_antialias_t fill_antialias,
+ cairo_path_fixed_t *path,
+ cairo_operator_t stroke_op,
+ const cairo_pattern_t *stroke_source,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *stroke_ctm,
+ const cairo_matrix_t *stroke_ctm_inverse,
+ double stroke_tolerance,
+ cairo_antialias_t stroke_antialias,
+ cairo_clip_t *clip);
+
+ cairo_surface_t *
+ (*create_solid_pattern_surface)
+ (void *surface,
+ const cairo_solid_pattern_t *solid_pattern);
+
+ cairo_bool_t
+ (*can_repaint_solid_pattern_surface)
+ (void *surface,
+ const cairo_solid_pattern_t *solid_pattern);
+
+ cairo_bool_t
+ (*has_show_text_glyphs) (void *surface);
+
+ cairo_warn cairo_int_status_t
+ (*show_text_glyphs) (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip);
+};
+
+#include "cairo-surface-private.h"
+
+struct _cairo_image_surface {
+ cairo_surface_t base;
+
+ pixman_format_code_t pixman_format;
+ cairo_format_t format;
+ unsigned char *data;
+
+ int width;
+ int height;
+ int stride;
+ int depth;
+
+ pixman_image_t *pixman_image;
+
+ unsigned owns_data : 1;
+ unsigned transparency : 2;
+};
+
+extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend;
+
+#define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE
+#define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD
+#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD
+
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_clear;
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black;
+extern const cairo_private cairo_solid_pattern_t _cairo_pattern_white;
+
+typedef struct _cairo_surface_attributes {
+ 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;
+} cairo_surface_attributes_t;
+
+typedef struct _cairo_traps {
+ cairo_status_t status;
+
+ const cairo_box_t *limits;
+ int num_limits;
+
+ unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
+ unsigned int has_intersections : 1;
+ unsigned int is_rectilinear : 1;
+ unsigned int is_rectangular : 1;
+
+ int num_traps;
+ int traps_size;
+ cairo_trapezoid_t *traps;
+ cairo_trapezoid_t traps_embedded[16];
+} cairo_traps_t;
+
+#define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL
+#define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL
+
+#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_DWRITE_FONT
+
+#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT
+#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_dwrite_font_face_backend
+
+#elif CAIRO_HAS_WIN32_FONT
+
+#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
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+// Skia uses a tolerance of 0.5 we'll use something more
+// tolerant for now
+#define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.3
+#else
+#define CAIRO_GSTATE_TOLERANCE_DEFAULT 0.1
+#endif
+#define CAIRO_GSTATE_FILL_RULE_DEFAULT CAIRO_FILL_RULE_WINDING
+#define CAIRO_GSTATE_LINE_WIDTH_DEFAULT 2.0
+#define CAIRO_GSTATE_LINE_CAP_DEFAULT CAIRO_LINE_CAP_BUTT
+#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 usr_vector;
+} 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 negative 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 || __STDC_VERSION__ < 199901L
+cairo_private int
+_cairo_lround (double d) cairo_const;
+#else
+#define _cairo_lround lround
+#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 (cairo_color_t *color);
+
+cairo_private void
+_cairo_color_init_rgb (cairo_color_t *color,
+ double red, double green, double blue);
+
+cairo_private void
+_cairo_color_init_rgba (cairo_color_t *color,
+ double red, double green, double blue,
+ 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;
+
+cairo_private void
+_cairo_font_face_init (cairo_font_face_t *font_face,
+ const cairo_font_face_backend_t *backend);
+
+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_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);
+
+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 cairo_status_t
+_cairo_intern_string (const char **str_inout, int len);
+
+cairo_private void
+_cairo_intern_string_reset_static_data (void);
+
+/* cairo-path-fixed.c */
+cairo_private 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 cairo_bool_t
+_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path,
+ const cairo_path_fixed_t *other);
+
+cairo_private void
+_cairo_path_fixed_fini (cairo_path_fixed_t *path);
+
+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_direction_t dir,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_curve_to_func_t *curve_to,
+ cairo_path_fixed_close_path_func_t *close_path,
+ void *closure);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
+ cairo_direction_t dir,
+ cairo_path_fixed_move_to_func_t *move_to,
+ cairo_path_fixed_line_to_func_t *line_to,
+ cairo_path_fixed_close_path_func_t *close_path,
+ void *closure,
+ double tolerance);
+
+cairo_private cairo_bool_t
+_cairo_path_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_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_int_status_t
+_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ cairo_traps_t *traps);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ 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_rectilinear_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_traps_t *traps);
+
+cairo_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_boxes_t *boxes);
+
+cairo_private cairo_status_t
+_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_traps_t *traps);
+
+cairo_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 void
+_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 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_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_surface_t *
+_cairo_surface_create_in_error (cairo_status_t status);
+
+cairo_private cairo_status_t
+_cairo_surface_copy_mime_data (cairo_surface_t *dst,
+ cairo_surface_t *src);
+
+cairo_private cairo_status_t
+_cairo_surface_set_error (cairo_surface_t *surface,
+ cairo_status_t status);
+
+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_similar_scratch (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height);
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_similar_solid (cairo_surface_t *other,
+ cairo_content_t content,
+ int width,
+ int height,
+ const cairo_color_t *color,
+ cairo_bool_t allow_fallback);
+
+cairo_private cairo_surface_t *
+_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other,
+ const cairo_solid_pattern_t *solid_pattern);
+
+cairo_private cairo_int_status_t
+_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other,
+ cairo_surface_t *solid_surface,
+ const cairo_solid_pattern_t *solid_pattern);
+
+cairo_private void
+_cairo_surface_init (cairo_surface_t *surface,
+ const cairo_surface_backend_t *backend,
+ cairo_device_t *device,
+ cairo_content_t content);
+
+cairo_private void
+_cairo_surface_set_font_options (cairo_surface_t *surface,
+ cairo_font_options_t *options);
+
+cairo_private cairo_status_t
+_cairo_surface_composite (cairo_operator_t op,
+ const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ cairo_surface_t *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_status_t
+_cairo_surface_fill_rectangle (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ int x,
+ int y,
+ int width,
+ int height);
+
+cairo_private cairo_status_t
+_cairo_surface_fill_region (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_region_t *region);
+
+cairo_private cairo_status_t
+_cairo_surface_fill_rectangles (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_color_t *color,
+ cairo_rectangle_int_t *rects,
+ int num_rects);
+
+cairo_private cairo_status_t
+_cairo_surface_paint (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_mask (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip);
+
+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,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_stroke (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_fill (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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,
+ cairo_clip_t *clip);
+
+cairo_private cairo_status_t
+_cairo_surface_paint_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_mask_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_stroke_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *style,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_fill_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_glyphs_extents (cairo_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_composite_trapezoids (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_antialias_t antialias,
+ int src_x,
+ int src_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_trapezoid_t *traps,
+ int ntraps,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_span_renderer_t *
+_cairo_surface_create_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_antialias_t antialias,
+ const cairo_composite_rectangles_t *rects,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_bool_t
+_cairo_surface_check_span_renderer (cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ cairo_antialias_t antialias);
+
+cairo_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_status_t
+_cairo_surface_acquire_dest_image (cairo_surface_t *surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra);
+
+cairo_private void
+_cairo_surface_release_dest_image (cairo_surface_t *surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra);
+
+cairo_private cairo_status_t
+_cairo_surface_clone_similar (cairo_surface_t *surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out);
+
+cairo_private cairo_surface_t *
+_cairo_surface_snapshot (cairo_surface_t *surface);
+
+cairo_private cairo_surface_t *
+_cairo_surface_has_snapshot (cairo_surface_t *surface,
+ const cairo_surface_backend_t *backend);
+
+cairo_private cairo_bool_t
+_cairo_surface_is_similar (cairo_surface_t *surface_a,
+ cairo_surface_t *surface_b);
+
+cairo_private cairo_bool_t
+_cairo_surface_get_extents (cairo_surface_t *surface,
+ cairo_rectangle_int_t *extents);
+
+cairo_private cairo_status_t
+_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
+ cairo_operator_t op,
+ const cairo_pattern_t *pattern,
+ cairo_surface_t *surface,
+ int source_x,
+ int source_y,
+ int dest_x,
+ int dest_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_status_t
+_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst,
+ cairo_surface_attributes_t *src_attr,
+ int src_width,
+ int src_height,
+ cairo_surface_attributes_t *mask_attr,
+ int mask_width,
+ int mask_height,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_status_t
+_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst,
+ cairo_surface_attributes_t *src_attr,
+ int src_width,
+ int src_height,
+ int mask_width,
+ int mask_height,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height,
+ cairo_region_t *clip_region);
+
+cairo_private cairo_bool_t
+_cairo_surface_is_opaque (const cairo_surface_t *surface);
+
+cairo_private int
+_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface);
+
+cairo_private void
+_cairo_surface_set_device_scale (cairo_surface_t *surface,
+ double sx,
+ double sy);
+
+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 situtation with X servers with BGR
+ * visuals. This time we invented #cairo_internal_format_t instead,
+ * (see it for more discussion).
+ *
+ * The punchline is that %CAIRO_FORMAT_VALID must not conside any
+ * 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_RGB16_565)
+
+/* 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))
+
+static inline cairo_bool_t
+_cairo_valid_stride_alignment(int stride)
+{
+ return !(stride & (CAIRO_STRIDE_ALIGNMENT-1));
+}
+
+cairo_private int
+_cairo_format_bits_per_pixel (cairo_format_t format) cairo_const;
+
+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_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 void
+_cairo_image_surface_span_render_row (int y,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans,
+ uint8_t *data,
+ uint32_t stride);
+
+cairo_private cairo_image_transparency_t
+_cairo_image_analyze_transparency (cairo_image_surface_t *image);
+
+cairo_private cairo_bool_t
+_cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_surface_is_recording (const cairo_surface_t *surface) cairo_pure;
+
+/* cairo-pen.c */
+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 cairo_status_t
+_cairo_pen_add_points_for_slopes (cairo_pen_t *pen,
+ cairo_point_t *a,
+ cairo_point_t *b,
+ cairo_point_t *c,
+ cairo_point_t *d);
+
+cairo_private int
+_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen,
+ const cairo_slope_t *slope);
+
+cairo_private int
+_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
+ const cairo_slope_t *slope);
+
+/* cairo-polygon.c */
+cairo_private void
+_cairo_polygon_init (cairo_polygon_t *polygon);
+
+cairo_private void
+_cairo_polygon_limit (cairo_polygon_t *polygon,
+ const cairo_box_t *boxes,
+ int num_boxes);
+
+cairo_private void
+_cairo_polygon_fini (cairo_polygon_t *polygon);
+
+cairo_private cairo_status_t
+_cairo_polygon_add_line (cairo_polygon_t *polygon,
+ const cairo_line_t *line,
+ int top, int bottom,
+ int dir);
+
+cairo_private cairo_status_t
+_cairo_polygon_add_external_edge (void *polygon,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2);
+
+cairo_private cairo_status_t
+_cairo_polygon_move_to (cairo_polygon_t *polygon,
+ const cairo_point_t *point);
+
+cairo_private cairo_status_t
+_cairo_polygon_line_to (cairo_polygon_t *polygon,
+ const cairo_point_t *point);
+
+cairo_private cairo_status_t
+_cairo_polygon_close (cairo_polygon_t *polygon);
+
+#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);
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_identity (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_translation (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix,
+ int *itx, int *ity);
+
+cairo_private cairo_bool_t
+_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
+
+cairo_private cairo_bool_t
+_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
+
+cairo_private void
+_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
+ double radius,
+ double *major,
+ double *minor);
+
+cairo_private double
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+ double radius) cairo_pure;
+
+cairo_private void
+_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
+ pixman_transform_t *pixman_transform,
+ double xc,
+ double yc);
+
+/* cairo-traps.c */
+cairo_private void
+_cairo_traps_init (cairo_traps_t *traps);
+
+cairo_private void
+_cairo_traps_limit (cairo_traps_t *traps,
+ const cairo_box_t *boxes,
+ int num_boxes);
+
+cairo_private cairo_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 cairo_status_t
+_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
+ const cairo_point_t *top_left,
+ const cairo_point_t *bottom_right);
+
+cairo_private void
+_cairo_traps_add_trap (cairo_traps_t *traps,
+ cairo_fixed_t top, cairo_fixed_t bottom,
+ cairo_line_t *left, cairo_line_t *right);
+
+cairo_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 int
+_cairo_traps_contain (const cairo_traps_t *traps,
+ double x, double y);
+
+cairo_private void
+_cairo_traps_extents (const cairo_traps_t *traps,
+ cairo_box_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_traps_extract_region (cairo_traps_t *traps,
+ cairo_region_t **region);
+
+cairo_private cairo_status_t
+_cairo_traps_path (const cairo_traps_t *traps,
+ cairo_path_fixed_t *path);
+
+cairo_private void
+_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
+ cairo_trapezoid_t *src_traps,
+ int num_traps,
+ double tx, double ty,
+ double sx, double sy);
+
+/* cairo-pattern.c */
+
+cairo_private cairo_pattern_t *
+_cairo_pattern_create_in_error (cairo_status_t status);
+
+cairo_private cairo_status_t
+_cairo_pattern_create_copy (cairo_pattern_t **pattern,
+ const cairo_pattern_t *other);
+
+cairo_private cairo_status_t
+_cairo_pattern_init_copy (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other);
+
+cairo_private void
+_cairo_pattern_init_static_copy (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other);
+
+cairo_private cairo_status_t
+_cairo_pattern_init_snapshot (cairo_pattern_t *pattern,
+ const cairo_pattern_t *other);
+
+cairo_private void
+_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
+ const cairo_color_t *color);
+
+cairo_private void
+_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
+ cairo_surface_t *surface);
+
+cairo_private void
+_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
+ double x0, double y0, double x1, double y1);
+
+cairo_private void
+_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
+ double cx0, double cy0, double radius0,
+ double cx1, double cy1, double radius1);
+
+cairo_private void
+_cairo_pattern_fini (cairo_pattern_t *pattern);
+
+cairo_private cairo_pattern_t *
+_cairo_pattern_create_solid (const cairo_color_t *color);
+
+cairo_private void
+_cairo_pattern_transform (cairo_pattern_t *pattern,
+ const cairo_matrix_t *ctm_inverse);
+
+cairo_private cairo_bool_t
+_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents,
+ cairo_color_t *color);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_opaque (const cairo_pattern_t *pattern,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_bool_t
+_cairo_pattern_is_clear (const cairo_pattern_t *pattern);
+
+cairo_private_no_warn cairo_filter_t
+_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern,
+ double *pad_out);
+
+enum {
+ CAIRO_PATTERN_ACQUIRE_NONE = 0x0,
+ CAIRO_PATTERN_ACQUIRE_NO_REFLECT = 0x1
+};
+cairo_private cairo_int_status_t
+_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ unsigned int flags,
+ cairo_surface_t **surface_out,
+ cairo_surface_attributes_t *attributes);
+
+cairo_private void
+_cairo_pattern_release_surface (const cairo_pattern_t *pattern,
+ cairo_surface_t *surface,
+ cairo_surface_attributes_t *attributes);
+
+cairo_private cairo_int_status_t
+_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src,
+ const cairo_pattern_t *mask,
+ cairo_surface_t *dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+ int mask_y,
+ unsigned int width,
+ unsigned int height,
+ unsigned int flags,
+ cairo_surface_t **src_out,
+ cairo_surface_t **mask_out,
+ cairo_surface_attributes_t *src_attributes,
+ cairo_surface_attributes_t *mask_attributes);
+
+cairo_private void
+_cairo_pattern_get_extents (const cairo_pattern_t *pattern,
+ cairo_rectangle_int_t *extents);
+
+cairo_private unsigned long
+_cairo_pattern_hash (const cairo_pattern_t *pattern);
+
+cairo_private unsigned long
+_cairo_linear_pattern_hash (unsigned long hash,
+ const cairo_linear_pattern_t *linear);
+
+cairo_private unsigned long
+_cairo_radial_pattern_hash (unsigned long hash,
+ const cairo_radial_pattern_t *radial);
+
+cairo_private cairo_bool_t
+_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
+ const cairo_linear_pattern_t *b);
+
+cairo_private unsigned long
+_cairo_pattern_size (const cairo_pattern_t *pattern);
+
+cairo_private cairo_bool_t
+_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
+ const cairo_radial_pattern_t *b);
+
+cairo_private cairo_bool_t
+_cairo_pattern_equal (const cairo_pattern_t *a,
+ const cairo_pattern_t *b);
+
+cairo_private void
+_cairo_pattern_reset_static_data (void);
+
+#if CAIRO_HAS_DRM_SURFACE
+
+cairo_private void
+_cairo_drm_device_reset_static_data (void);
+
+#endif
+
+cairo_private void
+_cairo_clip_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);
+
+#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS || CAIRO_HAS_DW_FONT
+# 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-observer.c */
+
+cairo_private void
+_cairo_observers_notify (cairo_list_t *observers, void *arg);
+
+/* 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_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_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_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 (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_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_destroy);
+slim_hidden_proto (cairo_surface_finish);
+slim_hidden_proto (cairo_surface_flush);
+slim_hidden_proto (cairo_surface_get_content);
+slim_hidden_proto (cairo_surface_get_device_offset);
+slim_hidden_proto (cairo_surface_get_font_options);
+slim_hidden_proto (cairo_surface_get_mime_data);
+slim_hidden_proto (cairo_surface_get_type);
+slim_hidden_proto (cairo_surface_has_show_text_glyphs);
+slim_hidden_proto (cairo_surface_set_subpixel_antialiasing);
+slim_hidden_proto (cairo_surface_get_subpixel_antialiasing);
+slim_hidden_proto (cairo_surface_mark_dirty);
+slim_hidden_proto (cairo_surface_mark_dirty_rectangle);
+slim_hidden_proto_no_warn (cairo_surface_reference);
+slim_hidden_proto (cairo_surface_set_device_offset);
+slim_hidden_proto (cairo_surface_set_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_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_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_private_no_warn cairo_filter_t
+_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern,
+ double *pad_out);
+
+CAIRO_END_DECLS
+
+#include "cairo-mutex-private.h"
+#include "cairo-fixed-private.h"
+#include "cairo-wideint-private.h"
+#include "cairo-malloc-private.h"
+#include "cairo-hash-private.h"
+
+#if HAVE_VALGRIND
+#include <memcheck.h>
+
+#define VG(x) x
+
+cairo_private void
+_cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface);
+
+#else
+
+#define VG(x)
+#define _cairo_debug_check_image_surface_is_defined(X)
+
+#endif
+
+cairo_private void
+_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path);
+
+cairo_private void
+_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip);
+
+#endif
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 000000000..120412776
--- /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-link.c b/gfx/cairo/cairo/src/check-link.c
new file mode 100644
index 000000000..66ca1b241
--- /dev/null
+++ b/gfx/cairo/cairo/src/check-link.c
@@ -0,0 +1,24 @@
+#define CAIRO_VERSION_H 1
+
+#include <cairo.h>
+
+/* get the "real" version info instead of dummy cairo-version.h */
+#undef CAIRO_VERSION_H
+#include "../cairo-version.h"
+
+#include <stdio.h>
+
+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/filterpublic.awk b/gfx/cairo/cairo/src/filterpublic.awk
new file mode 100644
index 000000000..98846102b
--- /dev/null
+++ b/gfx/cairo/cairo/src/filterpublic.awk
@@ -0,0 +1,22 @@
+#!/bin/awk -f
+# Reads cairo header files on stdin, and outputs a file with defines for
+# renaming all public functions to Mozilla-specific names.
+# Usage:
+# cat *.h | awk -f ./filterpublic.awk | sort > cairo-rename.h
+#
+# pixman:
+# grep '(' ../../libpixman/src/pixman.h | grep '^[a-z]' | sed 's, *(.*$,,' | sed 's,^.* ,,'
+
+BEGIN { state = "public"; }
+
+/^cairo_public/ { state = "function"; next; }
+/[a-zA-Z_]+/ {
+ if (state == "function") {
+ print "#define " $1 " _moz_" $1;
+ state = "public";
+ }
+ }
+
+# catch some one-off things
+END {
+}
diff --git a/gfx/cairo/cairo/src/moz.build b/gfx/cairo/cairo/src/moz.build
new file mode 100644
index 000000000..14b602ac2
--- /dev/null
+++ b/gfx/cairo/cairo/src/moz.build
@@ -0,0 +1,251 @@
+# -*- 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/.
+
+CONFIGURE_SUBST_FILES += ['cairo-features.h']
+
+EXPORTS.cairo += [
+ '!cairo-features.h',
+ 'cairo-deprecated.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-operators.c',
+ 'cairo-pdf-surface.c',
+ 'cairo-truetype-subset.c',
+ # cairo-type1-subset.c should be here, but it's only supported on freetype platforms
+ 'cairo-type1-fallback.c',
+ 'cairo-type3-glyph-surface.c',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ EXPORTS.cairo += [
+ 'cairo-win32.h',
+ ]
+ if CONFIG['MOZ_ENABLE_DWRITE_FONT']:
+ SOURCES += [
+ 'cairo-dwrite-font.cpp',
+ ]
+ if CONFIG['MOZ_ENABLE_D2D_SURFACE']:
+ SOURCES += [
+ 'cairo-d2d-surface.cpp',
+ ]
+ SOURCES += [
+ 'cairo-win32-font.c',
+ 'cairo-win32-surface.c',
+ ]
+ DEFINES['DISABLE_SOME_FLOATING_POINT'] = True
+ DEFINES['CAIRO_WIN32_STATIC_BUILD'] = True
+ if CONFIG['NS_PRINTING']:
+ SOURCES += [
+ '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',
+ ]
+elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ EXPORTS.cairo += [
+ 'cairo-ps.h',
+ ]
+ SOURCES += [
+ 'cairo-ps-surface.c',
+ ]
+
+if CONFIG['MOZ_X11']:
+ EXPORTS.cairo += [
+ 'cairo-xlib-xrender.h',
+ 'cairo-xlib.h',
+ ]
+ SOURCES += [
+ 'cairo-xlib-display.c',
+ 'cairo-xlib-screen.c',
+ 'cairo-xlib-surface.c',
+ 'cairo-xlib-visual.c',
+ ]
+
+if CONFIG['MOZ_ENABLE_CAIRO_FT']:
+ EXPORTS.cairo += [
+ 'cairo-ft.h',
+ ]
+ SOURCES += [
+ 'cairo-ft-font.c',
+ 'cairo-type1-subset.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-surface-wrapper.c', # redefinition of '_copy_transformed_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.c',
+ 'cairo-color.c',
+ 'cairo-composite-rectangles.c',
+ 'cairo-debug.c',
+ 'cairo-device.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-info.c',
+ 'cairo-image-surface.c',
+ 'cairo-lzw.c',
+ 'cairo-matrix.c',
+ 'cairo-misc.c',
+ 'cairo-mutex.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.c',
+ 'cairo-path.c',
+ 'cairo-pattern.c',
+ 'cairo-pen.c',
+ 'cairo-polygon.c',
+ 'cairo-recording-surface.c',
+ 'cairo-rectangle.c',
+ 'cairo-rectangular-scan-converter.c',
+ 'cairo-region.c',
+ 'cairo-scaled-font-subsets.c',
+ 'cairo-scaled-font.c',
+ 'cairo-slope.c',
+ 'cairo-spans.c',
+ 'cairo-spline.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-tee-surface.c',
+ 'cairo-tor-scan-converter.c',
+ 'cairo-toy-font-face.c',
+ 'cairo-traps.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.
+ALLOW_COMPILER_WARNINGS = True
+
+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
+
+for var in ('MOZ_TREE_CAIRO', 'MOZ_TREE_PIXMAN'):
+ if CONFIG[var]:
+ DEFINES[var] = True
+
+if CONFIG['GNU_CC']:
+ DEFINES['HAVE_CXX11_ATOMIC_PRIMITIVES'] = True
+ # 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
+
+if CONFIG['MOZ_TREE_FREETYPE']:
+ DEFINES['HAVE_FT_LIBRARY_SETLCDFILTER'] = True
+ DEFINES['FT_LCD_FILTER_H'] = '%s/modules/freetype2/include/freetype/ftlcdfil.h' % TOPSRCDIR
+
+# Suppress warnings in third-party code.
+if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+ CFLAGS += [
+ '-Wno-enum-compare',
+ '-Wno-int-to-pointer-cast',
+ '-Wno-sign-compare',
+ '-Wno-type-limits',
+ '-Wno-missing-field-initializers',
+ '-Wno-conversion',
+ '-Wno-unused-but-set-variable',
+ ]
+if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
+ CFLAGS += [
+ '-Wno-incompatible-pointer-types',
+ '-Wno-tautological-compare',
+ '-Wno-tautological-constant-out-of-range-compare',
+ '-Wno-error=uninitialized',
+ ]
+if CONFIG['CLANG_CL']:
+ CFLAGS += [
+ '-Wno-deprecated-register',
+ '-Wno-macro-redefined',
+ '-Wno-unused-variable',
+ ]
+
+# See bug 386897.
+if CONFIG['GNU_CC'] and CONFIG['OS_TARGET'] == 'Android' and CONFIG['MOZ_OPTIMIZE']:
+ CFLAGS += ['-O2']
+ CXXFLAGS += ['-O2']
+
+if CONFIG['MOZ_X11']:
+ CFLAGS += CONFIG['XCFLAGS']
+
+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 000000000..431cd92e9
--- /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-fallback-surface.c b/gfx/cairo/cairo/src/test-fallback-surface.c
new file mode 100644
index 000000000..66399d4ab
--- /dev/null
+++ b/gfx/cairo/cairo/src/test-fallback-surface.c
@@ -0,0 +1,237 @@
+/* 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 <cworth@cworth.org>
+ */
+
+/* This isn't a "real" surface, but just something to be used by the
+ * test suite to test a mythical backend that uses nothing but
+ * fallbacks.
+ *
+ * The defining feature of this backend is that it has as many %NULL
+ * backend function entries as possible. The ones that aren't %NULL are
+ * simply those that must be implemented to have working fallbacks.
+ * (Except for create_similar---fallbacks would work fine without
+ * that---I implemented it here in order to create as many surfaces as
+ * possible of type test_fallback_surface_t during the test suite
+ * run).
+ *
+ * It's possible that this code might serve as a good starting point
+ * for someone working on bringing up a new backend, starting with the
+ * minimal all-fallbacks approach and working up gradually from
+ * there.
+ */
+
+#include "cairoint.h"
+
+#include "test-fallback-surface.h"
+#include "cairo-error-private.h"
+
+typedef struct _test_fallback_surface {
+ cairo_surface_t base;
+
+ /* This is a cairo_image_surface to hold the actual contents. */
+ cairo_surface_t *backing;
+} test_fallback_surface_t;
+
+static const cairo_surface_backend_t test_fallback_surface_backend;
+
+slim_hidden_proto (_cairo_test_fallback_surface_create);
+
+cairo_surface_t *
+_cairo_test_fallback_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ test_fallback_surface_t *surface;
+ cairo_surface_t *backing;
+
+ backing = _cairo_image_surface_create_with_content (content, width, height);
+ if (cairo_surface_status (backing))
+ return backing;
+
+ surface = malloc (sizeof (test_fallback_surface_t));
+ if (unlikely (surface == NULL)) {
+ cairo_surface_destroy (backing);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ _cairo_surface_init (&surface->base,
+ &test_fallback_surface_backend,
+ NULL, /* device */
+ content);
+
+ surface->backing = backing;
+
+ return &surface->base;
+}
+slim_hidden_def (_cairo_test_fallback_surface_create);
+
+static cairo_surface_t *
+_test_fallback_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+{
+ assert (CAIRO_CONTENT_VALID (content));
+
+ return _cairo_test_fallback_surface_create (content,
+ width, height);
+}
+
+static cairo_status_t
+_test_fallback_surface_finish (void *abstract_surface)
+{
+ test_fallback_surface_t *surface = abstract_surface;
+
+ cairo_surface_destroy (surface->backing);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_test_fallback_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ test_fallback_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_acquire_source_image (surface->backing,
+ image_out, image_extra);
+}
+
+static void
+_test_fallback_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ test_fallback_surface_t *surface = abstract_surface;
+
+ _cairo_surface_release_source_image (surface->backing,
+ image, image_extra);
+}
+
+static cairo_status_t
+_test_fallback_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect_out,
+ void **image_extra)
+{
+ test_fallback_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_acquire_dest_image (surface->backing,
+ interest_rect,
+ image_out,
+ image_rect_out,
+ image_extra);
+}
+
+static void
+_test_fallback_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+{
+ test_fallback_surface_t *surface = abstract_surface;
+
+ _cairo_surface_release_dest_image (surface->backing,
+ interest_rect,
+ image,
+ image_rect,
+ image_extra);
+}
+
+static cairo_status_t
+_test_fallback_surface_clone_similar (void *abstract_surface,
+ cairo_surface_t *src,
+ int src_x,
+ int src_y,
+ int width,
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out)
+{
+ test_fallback_surface_t *surface = abstract_surface;
+
+ if (src->backend == surface->base.backend) {
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ *clone_out = cairo_surface_reference (src);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+static cairo_bool_t
+_test_fallback_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ test_fallback_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_get_extents (surface->backing, rectangle);
+}
+
+static const cairo_surface_backend_t test_fallback_surface_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
+ _test_fallback_surface_create_similar,
+ _test_fallback_surface_finish,
+ _test_fallback_surface_acquire_source_image,
+ _test_fallback_surface_release_source_image,
+ _test_fallback_surface_acquire_dest_image,
+ _test_fallback_surface_release_dest_image,
+ _test_fallback_surface_clone_similar,
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _test_fallback_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+ NULL, /* paint */
+ NULL, /* mask */
+ NULL, /* stroke */
+ NULL, /* fill */
+ NULL, /* show_glyphs */
+ NULL /* snapshot */
+};
diff --git a/gfx/cairo/cairo/src/test-fallback-surface.h b/gfx/cairo/cairo/src/test-fallback-surface.h
new file mode 100644
index 000000000..e70715113
--- /dev/null
+++ b/gfx/cairo/cairo/src/test-fallback-surface.h
@@ -0,0 +1,50 @@
+/* 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 <cworth@cworth.org>
+ */
+
+#ifndef TEST_FALLBACK_SURFACE_H
+#define TEST_FALLBACK_SURFACE_H
+
+#include "cairo.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_surface_t *
+_cairo_test_fallback_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_FALLBACK_SURFACE_H */
diff --git a/gfx/cairo/cairo/src/test-meta-surface.c b/gfx/cairo/cairo/src/test-meta-surface.c
new file mode 100644
index 000000000..d5e14d7d1
--- /dev/null
+++ b/gfx/cairo/cairo/src/test-meta-surface.c
@@ -0,0 +1,342 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <cworth@cworth.org>
+ */
+
+/* This isn't a "real" surface, but just something to be used by the
+ * test suite to help exercise the meta-surface paths in cairo.
+ *
+ * The defining feature of this backend is that it uses a meta surface
+ * to record all operations, and then replays everything to an image
+ * surface.
+ *
+ * It's possible that this code might serve as a good starting point
+ * for someone working on bringing up a new meta-surface-based
+ * backend.
+ */
+
+#include "cairoint.h"
+
+#include "test-meta-surface.h"
+
+#include "cairo-meta-surface-private.h"
+
+typedef struct _test_meta_surface {
+ cairo_surface_t base;
+
+ /* This is a cairo_meta_surface to record all operations. */
+ cairo_surface_t *meta;
+
+ /* And this is a cairo_image_surface to hold the final result. */
+ cairo_surface_t *image;
+
+ cairo_bool_t image_reflects_meta;
+} test_meta_surface_t;
+
+static const cairo_surface_backend_t test_meta_surface_backend;
+
+static cairo_int_status_t
+_test_meta_surface_show_page (void *abstract_surface);
+
+cairo_surface_t *
+_cairo_test_meta_surface_create (cairo_content_t content,
+ int width,
+ int height)
+{
+ test_meta_surface_t *surface;
+ cairo_status_t status;
+
+ surface = malloc (sizeof (test_meta_surface_t));
+ if (unlikely (surface == NULL)) {
+ status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto FAIL;
+ }
+
+ _cairo_surface_init (&surface->base, &test_meta_surface_backend,
+ content);
+
+ surface->meta = _cairo_meta_surface_create (content, width, height);
+ status = cairo_surface_status (surface->meta);
+ if (status)
+ goto FAIL_CLEANUP_SURFACE;
+
+ surface->image = _cairo_image_surface_create_with_content (content,
+ width, height);
+ status = cairo_surface_status (surface->image);
+ if (status)
+ goto FAIL_CLEANUP_META;
+
+ surface->image_reflects_meta = FALSE;
+
+ return &surface->base;
+
+ FAIL_CLEANUP_META:
+ cairo_surface_destroy (surface->meta);
+ FAIL_CLEANUP_SURFACE:
+ free (surface);
+ FAIL:
+ return _cairo_surface_create_in_error (status);
+}
+
+static cairo_status_t
+_test_meta_surface_finish (void *abstract_surface)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ cairo_surface_destroy (surface->meta);
+ cairo_surface_destroy (surface->image);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_test_meta_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+{
+ test_meta_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (! surface->image_reflects_meta) {
+ status = _test_meta_surface_show_page (abstract_surface);
+ if (status)
+ return status;
+ }
+
+ return _cairo_surface_acquire_source_image (surface->image,
+ image_out, image_extra);
+}
+
+static void
+_test_meta_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ _cairo_surface_release_source_image (surface->image,
+ image, image_extra);
+}
+
+static cairo_int_status_t
+_test_meta_surface_show_page (void *abstract_surface)
+{
+ test_meta_surface_t *surface = abstract_surface;
+ cairo_status_t status;
+
+ if (surface->image_reflects_meta)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_meta_surface_replay (surface->meta, surface->image);
+ if (status)
+ return status;
+
+ surface->image_reflects_meta = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_test_meta_surface_intersect_clip_path (void *abstract_surface,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ return _cairo_surface_intersect_clip_path (surface->meta,
+ path, fill_rule,
+ tolerance, antialias);
+}
+
+static cairo_int_status_t
+_test_meta_surface_get_extents (void *abstract_surface,
+ cairo_rectangle_int_t *rectangle)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ surface->image_reflects_meta = FALSE;
+
+ return _cairo_surface_get_extents (surface->image, rectangle);
+}
+
+static cairo_int_status_t
+_test_meta_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_rectangle_int_t *extents)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ surface->image_reflects_meta = FALSE;
+
+ return _cairo_surface_paint (surface->meta, op, source, extents);
+}
+
+static cairo_int_status_t
+_test_meta_surface_mask (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_rectangle_int_t *extents)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ surface->image_reflects_meta = FALSE;
+
+ return _cairo_surface_mask (surface->meta, op, source, mask, extents);
+}
+
+static cairo_int_status_t
+_test_meta_surface_stroke (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_stroke_style_t *style,
+ cairo_matrix_t *ctm,
+ cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_rectangle_int_t *extents)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ surface->image_reflects_meta = FALSE;
+
+ return _cairo_surface_stroke (surface->meta, op, source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias, extents);
+}
+
+static cairo_int_status_t
+_test_meta_surface_fill (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_rectangle_int_t *extents)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ surface->image_reflects_meta = FALSE;
+
+ return _cairo_surface_fill (surface->meta, op, source,
+ path, fill_rule,
+ tolerance, antialias, extents);
+}
+
+static cairo_bool_t
+_test_meta_surface_has_show_text_glyphs (void *abstract_surface)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ return cairo_surface_has_show_text_glyphs (surface->meta);
+}
+
+static cairo_int_status_t
+_test_meta_surface_show_text_glyphs (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ const char *utf8,
+ int utf8_len,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ const cairo_text_cluster_t *clusters,
+ int num_clusters,
+ cairo_text_cluster_flags_t cluster_flags,
+ cairo_scaled_font_t *scaled_font,
+ cairo_rectangle_int_t *extents)
+{
+ test_meta_surface_t *surface = abstract_surface;
+
+ surface->image_reflects_meta = FALSE;
+
+ return _cairo_surface_show_text_glyphs (surface->meta, op, source,
+ utf8, utf8_len,
+ glyphs, num_glyphs,
+ clusters, num_clusters, cluster_flags,
+ scaled_font, extents);
+}
+
+
+static cairo_surface_t *
+_test_meta_surface_snapshot (void *abstract_other)
+{
+ test_meta_surface_t *other = abstract_other;
+
+ return _cairo_surface_snapshot (other->meta);
+}
+
+static const cairo_surface_backend_t test_meta_surface_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_TEST_META,
+ NULL, /* create_similar */
+ _test_meta_surface_finish,
+ _test_meta_surface_acquire_source_image,
+ _test_meta_surface_release_source_image,
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ _test_meta_surface_show_page,
+ NULL, /* set_clip_region */
+ _test_meta_surface_intersect_clip_path,
+ _test_meta_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+ _test_meta_surface_paint,
+ _test_meta_surface_mask,
+ _test_meta_surface_stroke,
+ _test_meta_surface_fill,
+ NULL, /* show_glyphs */
+ _test_meta_surface_snapshot,
+ NULL, /* is_similar */
+ NULL, /* reset */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ _test_meta_surface_has_show_text_glyphs,
+ _test_meta_surface_show_text_glyphs
+};
diff --git a/gfx/cairo/cairo/src/test-meta-surface.h b/gfx/cairo/cairo/src/test-meta-surface.h
new file mode 100644
index 000000000..c036bb9c8
--- /dev/null
+++ b/gfx/cairo/cairo/src/test-meta-surface.h
@@ -0,0 +1,50 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2005 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Red Hat, Inc.
+ *
+ * Contributor(s):
+ * Carl Worth <cworth@cworth.org>
+ */
+
+#ifndef TEST_META_SURFACE_H
+#define TEST_META_SURFACE_H
+
+#include "cairo.h"
+
+CAIRO_BEGIN_DECLS
+
+cairo_surface_t *
+_cairo_test_meta_surface_create (cairo_content_t content,
+ int width,
+ int height);
+
+CAIRO_END_DECLS
+
+#endif /* TEST_META_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 000000000..e06cbed71
--- /dev/null
+++ b/gfx/cairo/cairo/src/test-paginated-surface.c
@@ -0,0 +1,291 @@
+/* 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 <cworth@cworth.org>
+ */
+
+/* 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-error-private.h"
+#include "cairo-paginated-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 = 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);
+
+ 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,
+ 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,
+ 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,
+ 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)
+{
+ 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,
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ 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,
+ 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 void
+_test_paginated_surface_set_paginated_mode (void *abstract_surface,
+ cairo_paginated_mode_t mode)
+{
+ test_paginated_surface_t *surface = abstract_surface;
+
+ surface->paginated_mode = mode;
+}
+
+static const cairo_surface_backend_t test_paginated_surface_backend = {
+ CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
+
+ /* Since we are a paginated user, we get to regard most of the
+ * surface backend interface as historical cruft and ignore it. */
+
+ NULL, /* create_similar */
+ _test_paginated_surface_finish,
+ NULL, /* acquire_source_image */
+ NULL, /* release_source_image */
+ NULL, /* acquire_dest_image */
+ NULL, /* release_dest_image */
+ NULL, /* clone_similar */
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _test_paginated_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+ NULL, /* mark_dirty_rectangle */
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ /* Here is the more "modern" section of the surface backend
+ * interface which is mostly just drawing functions */
+
+ _test_paginated_surface_paint,
+ _test_paginated_surface_mask,
+ _test_paginated_surface_stroke,
+ _test_paginated_surface_fill,
+ NULL, /* replaced by show_text_glyphs */
+
+ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+
+ _test_paginated_surface_has_show_text_glyphs,
+ _test_paginated_surface_show_text_glyphs
+};
+
+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 000000000..2bd98aa5e
--- /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 <cworth@cworth.org>
+ */
+
+#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_qt_a8_fallback.diff b/gfx/cairo/cairo_qt_a8_fallback.diff
new file mode 100644
index 000000000..a8388dda7
--- /dev/null
+++ b/gfx/cairo/cairo_qt_a8_fallback.diff
@@ -0,0 +1,68 @@
+CAIRO_FORMAT_A8 not allowed for cairo-qt image backend
+diff --git a/gfx/cairo/cairo/src/cairo-qt-surface.cpp b/gfx/cairo/cairo/src/cairo-qt-surface.cpp
+--- a/gfx/cairo/cairo/src/cairo-qt-surface.cpp
++++ b/gfx/cairo/cairo/src/cairo-qt-surface.cpp
+@@ -459,17 +459,17 @@ _cairo_qt_surface_finish (void *abstract
+ {
+ 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
++ else if (qs->p)
+ qs->p->restore ();
+
+ if (qs->image_equiv)
+ cairo_surface_destroy (qs->image_equiv);
+
+ _cairo_surface_clipper_reset (&qs->clipper);
+
+ if (qs->image)
+@@ -736,17 +736,17 @@ _cairo_qt_surface_set_clip_region (cairo
+ }
+
+ static cairo_int_status_t
+ _cairo_qt_surface_set_clip (cairo_qt_surface_t *qs,
+ cairo_clip_t *clip)
+ {
+ cairo_int_status_t status;
+
+- D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)"));
++ D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", qs, clip ? "(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 {
+@@ -1605,16 +1605,25 @@ cairo_qt_surface_create_with_qimage (cai
+
+ _cairo_surface_init (&qs->base,
+ &cairo_qt_surface_backend,
+ _cairo_content_from_format (format));
+
+ _cairo_surface_clipper_init (&qs->clipper,
+ _cairo_qt_surface_clipper_intersect_clip_path);
+
++ if (CAIRO_FORMAT_A8 == format) {
++ qs->image = NULL;
++ qs->image_equiv = cairo_image_surface_create(format,
++ width, height);
++ qs->p = NULL;
++ qs->supports_porter_duff = false;
++ qs->window = QRect(0, 0, width, height);
++ return &qs->base;
++ }
+
+ QImage *image = new QImage (width, height,
+ _qimage_format_from_cairo_format (format));
+
+ qs->image = image;
+
+ if (!image->isNull()) {
+ qs->p = new QPainter(image);
diff --git a/gfx/cairo/cairo_qt_glyphs.patch b/gfx/cairo/cairo_qt_glyphs.patch
new file mode 100644
index 000000000..b12356287
--- /dev/null
+++ b/gfx/cairo/cairo_qt_glyphs.patch
@@ -0,0 +1,256 @@
+Bug 29092 - Fix glyphs rendering for cairo-qpainter-surface
+diff --git a/src/cairo-qt-surface.cpp b/src/cairo-qt-surface.cpp
+index 2ac06ef..5b61b42 100644
+--- a/src/cairo-qt-surface.cpp
++++ b/src/cairo-qt-surface.cpp
+@@ -45,6 +45,7 @@
+ #include "cairo-surface-clipper-private.h"
+ #include "cairo-types-private.h"
+
++#include "cairo-ft.h"
+ #include "cairo-qt.h"
+
+ #include <memory>
+@@ -58,14 +59,10 @@
+ #include <QtGui/QPen>
+ #include <QtGui/QWidget>
+ #include <QtGui/QX11Info>
++#include <QtCore/QVarLengthArray>
+
+-#if CAIRO_HAS_XLIB_XRENDER_SURFACE
+-#include "cairo-xlib.h"
+-#include "cairo-xlib-xrender.h"
+-// I hate X
+-#undef Status
+-#undef CursorShape
+-#undef Bool
++#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)
++extern void qt_draw_glyphs(QPainter *, const quint32 *glyphs, const QPointF *positions, int count);
+ #endif
+
+ #include <sys/time.h>
+@@ -118,15 +115,6 @@ struct cairo_qt_surface_t {
+
+ cairo_bool_t supports_porter_duff;
+
+-#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+- /* temporary, so that we can share the xlib surface's glyphs code */
+- bool xlib_has_clipping;
+- cairo_surface_t *xlib_equiv;
+- QRect xlib_clip_bounds;
+- int xlib_clip_serial;
+- QPoint redir_offset;
+-#endif
+-
+ QPainter *p;
+
+ /* The pixmap/image constructors will store their objects here */
+@@ -145,11 +133,6 @@ struct cairo_qt_surface_t {
+ */
+ static cairo_bool_t _qpixmaps_have_no_alpha = FALSE;
+
+-#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+-slim_hidden_proto (cairo_xlib_surface_create);
+-slim_hidden_proto (cairo_xlib_surface_create_with_xrender_format);
+-#endif
+-
+ /**
+ ** Helper methods
+ **/
+@@ -498,11 +481,6 @@ _cairo_qt_surface_finish (void *abstract_surface)
+
+ _cairo_surface_clipper_reset (&qs->clipper);
+
+-#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+- if (qs->xlib_equiv)
+- cairo_surface_destroy (qs->xlib_equiv);
+-#endif
+-
+ if (qs->image)
+ delete qs->image;
+
+@@ -1392,33 +1370,40 @@ _cairo_qt_surface_show_glyphs (void *abstract_surface,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+ {
++#if (QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)
+ cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+
+-#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+- /* If we have an equivalent X surface, let the xlib surface handle this
+- * until we figure out how to do this natively with Qt.
+- */
+- if (qs->xlib_equiv) {
+- D(fprintf(stderr, "q[%p] show_glyphs (x11 equiv) op:%s nglyphs: %d\n", abstract_surface, _opstr(op), num_glyphs));
+-
+- for (int i = 0; i < num_glyphs; i++) {
+- glyphs[i].x -= qs->redir_offset.x();
+- glyphs[i].y -= qs->redir_offset.y();
+- }
+-
+- return (cairo_int_status_t)
+- _cairo_surface_show_text_glyphs (qs->xlib_equiv,
+- op, source,
+- NULL, 0,
+- glyphs, num_glyphs,
+- NULL, 0,
+- (cairo_text_cluster_flags_t) 0,
+- scaled_font,
+- clip);
++ // 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<QPointF> positions(num_glyphs);
++ QVarLengthArray<unsigned int> 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;
+ }
+-#endif
+-
++ 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_INT_STATUS_UNSUPPORTED;
++#endif
+ }
+
+ static cairo_int_status_t
+@@ -1555,24 +1540,6 @@ _cairo_qt_surface_composite (cairo_operator_t op,
+ }
+
+ static cairo_status_t
+-_cairo_qt_surface_flush (void *abstract_surface)
+-{
+- cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
+-
+- if (qs->p == NULL)
+- return CAIRO_STATUS_SUCCESS;
+-
+- if (qs->image || qs->pixmap) {
+- qs->p->end ();
+- qs->p->begin (qs->p->device ());
+- } else {
+- qs->p->restore ();
+- }
+-
+- return CAIRO_STATUS_SUCCESS;
+-}
+-
+-static cairo_status_t
+ _cairo_qt_surface_mark_dirty (void *abstract_surface,
+ int x, int y,
+ int width, int height)
+@@ -1609,7 +1576,7 @@ static const cairo_surface_backend_t cairo_qt_surface_backend = {
+ _cairo_qt_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+- _cairo_qt_surface_flush,
++ NULL, /* flush */
+ _cairo_qt_surface_mark_dirty,
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+@@ -1629,64 +1596,6 @@ static const cairo_surface_backend_t cairo_qt_surface_backend = {
+ NULL, /* show_text_glyphs */
+ };
+
+-#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+-static cairo_surface_t *
+-_cairo_qt_create_xlib_surface (cairo_qt_surface_t *qs)
+-{
+- if (!qs->p)
+- return NULL;
+-
+- QPaintDevice *pd = qs->p->device();
+- if (!pd)
+- return NULL;
+-
+- QPoint offs;
+- QPaintDevice *rpd = QPainter::redirected(pd, &offs);
+- if (rpd) {
+- pd = rpd;
+- qs->redir_offset = offs;
+- }
+-
+- if (pd->devType() == QInternal::Widget) {
+- QWidget *w = (QWidget*) pd;
+- QX11Info xinfo = w->x11Info();
+-
+- return cairo_xlib_surface_create (xinfo.display(),
+- (Drawable) w->handle (),
+- (Visual *) xinfo.visual (),
+- w->width (), w->height ());
+- } else if (pd->devType() == QInternal::Pixmap) {
+- QPixmap *pixmap = (QPixmap*) pd;
+- QX11Info xinfo = pixmap->x11Info ();
+- XRenderPictFormat *xrender_format;
+- int pict_format;
+-
+- switch (pixmap->depth ()) {
+- case 1:
+- pict_format = PictStandardA1; break;
+- case 8:
+- pict_format = PictStandardA8; break;
+- case 24:
+- pict_format = PictStandardRGB24; break;
+- default:
+- ASSERT_NOT_REACHED;
+- case 32:
+- pict_format = PictStandardARGB32; break;
+- }
+- xrender_format = XRenderFindStandardFormat (xinfo.display (),
+- pict_format);
+-
+- return cairo_xlib_surface_create_with_xrender_format (xinfo.display(),
+- (Drawable) pixmap->handle (),
+- ScreenOfDisplay (xinfo.display (),
+- xinfo.screen ()),
+- xrender_format,
+- pixmap->width (), pixmap->height ());
+- } else
+- return NULL;
+-}
+-#endif
+-
+ cairo_surface_t *
+ cairo_qt_surface_create (QPainter *painter)
+ {
+@@ -1717,10 +1626,6 @@ cairo_qt_surface_create (QPainter *painter)
+
+ qs->window = painter->window();
+
+-#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+- qs->xlib_equiv = _cairo_qt_create_xlib_surface (qs);
+-#endif
+-
+ 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));
+@@ -1819,10 +1724,6 @@ cairo_qt_surface_create_with_qpixmap (cairo_content_t content,
+
+ qs->window = QRect(0, 0, width, height);
+
+-#if defined(Q_WS_X11) && CAIRO_HAS_XLIB_XRENDER_SURFACE
+- qs->xlib_equiv = _cairo_qt_create_xlib_surface (qs);
+-#endif
+-
+ 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));
diff --git a/gfx/cairo/clip-invariant.patch b/gfx/cairo/clip-invariant.patch
new file mode 100644
index 000000000..08ba4d4de
--- /dev/null
+++ b/gfx/cairo/clip-invariant.patch
@@ -0,0 +1,1255 @@
+diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
+index 2acc8b5..019249e 100644
+--- a/src/cairo-gl-surface.c
++++ b/src/cairo-gl-surface.c
+@@ -2012,13 +2012,14 @@ typedef struct _cairo_gl_surface_span_renderer {
+
+ cairo_gl_composite_setup_t setup;
+
++ int xmin, xmax;
++
+ cairo_operator_t op;
+ cairo_antialias_t antialias;
+
+ cairo_gl_surface_t *dst;
+ cairo_region_t *clip;
+
+- cairo_composite_rectangles_t composite_rectangles;
+ GLuint vbo;
+ void *vbo_base;
+ unsigned int vbo_size;
+@@ -2049,11 +2050,11 @@ _cairo_gl_span_renderer_flush (cairo_gl_surface_span_renderer_t *renderer)
+ cairo_region_get_rectangle (renderer->clip, i, &rect);
+
+ glScissor (rect.x, rect.y, rect.width, rect.height);
+- glDrawArrays (GL_LINES, 0, count);
++ glDrawArrays (GL_QUADS, 0, count);
+ }
+ glDisable (GL_SCISSOR_TEST);
+ } else {
+- glDrawArrays (GL_LINES, 0, count);
++ glDrawArrays (GL_QUADS, 0, count);
+ }
+ }
+
+@@ -2134,72 +2135,87 @@ _cairo_gl_emit_span_vertex (cairo_gl_surface_span_renderer_t *renderer,
+
+ static void
+ _cairo_gl_emit_span (cairo_gl_surface_span_renderer_t *renderer,
+- int x1, int x2, int y, uint8_t alpha)
++ int x, int y1, int y2,
++ uint8_t alpha)
+ {
+ float *vertices = _cairo_gl_span_renderer_get_vbo (renderer, 2);
+
+- _cairo_gl_emit_span_vertex (renderer, x1, y, alpha, vertices);
+- _cairo_gl_emit_span_vertex (renderer, x2, y, alpha,
++ _cairo_gl_emit_span_vertex (renderer, x, y1, alpha, vertices);
++ _cairo_gl_emit_span_vertex (renderer, x, y2, alpha,
+ vertices + renderer->vertex_size / 4);
+ }
+
+-/* Emits the contents of the span renderer rows as GL_LINES with the span's
+- * alpha.
+- *
+- * Unlike the image surface, which is compositing into a temporary, we emit
+- * coverage even for alpha == 0, in case we're using an unbounded operator.
+- * But it means we avoid having to do the fixup.
+- */
++static void
++_cairo_gl_emit_rectangle (cairo_gl_surface_span_renderer_t *renderer,
++ int x1, int y1,
++ int x2, int y2,
++ int coverage)
++{
++ _cairo_gl_emit_span (renderer, x1, y1, y2, coverage);
++ _cairo_gl_emit_span (renderer, x2, y2, y1, coverage);
++}
++
+ static cairo_status_t
+-_cairo_gl_surface_span_renderer_render_row (
+- void *abstract_renderer,
+- int y,
+- const cairo_half_open_span_t *spans,
+- unsigned num_spans)
++_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;
+- int xmin = renderer->composite_rectangles.mask.x;
+- int xmax = xmin + renderer->composite_rectangles.width;
+- int prev_x = xmin;
+- int prev_alpha = 0;
+- unsigned i;
+- int x_translate;
+-
+- /* Make sure we're within y-range. */
+- if (y < renderer->composite_rectangles.mask.y ||
+- y >= renderer->composite_rectangles.mask.y +
+- renderer->composite_rectangles.height)
++
++ if (num_spans == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+- x_translate = renderer->composite_rectangles.dst.x -
+- renderer->composite_rectangles.mask.x;
+- y += renderer->composite_rectangles.dst.y -
+- renderer->composite_rectangles.mask.y;
++ do {
++ if (spans[0].coverage) {
++ _cairo_gl_emit_rectangle (renderer,
++ spans[0].x, y,
++ spans[1].x, y + height,
++ spans[0].coverage);
++ }
+
+- /* Find the first span within x-range. */
+- for (i=0; i < num_spans && spans[i].x < xmin; i++) {}
+- if (i>0)
+- prev_alpha = spans[i-1].coverage;
++ spans++;
++ } while (--num_spans > 1);
+
+- /* Set the intermediate spans. */
+- for (; i < num_spans; i++) {
+- int x = spans[i].x;
++ return CAIRO_STATUS_SUCCESS;
++}
+
+- if (x >= xmax)
+- break;
++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;
+
+- _cairo_gl_emit_span (renderer,
+- prev_x + x_translate, x + x_translate, y,
+- prev_alpha);
++ if (num_spans == 0) {
++ _cairo_gl_emit_rectangle (renderer,
++ renderer->xmin, y,
++ renderer->xmax, y + height,
++ 0);
++ return CAIRO_STATUS_SUCCESS;
++ }
+
+- prev_x = x;
+- prev_alpha = spans[i].coverage;
++ if (spans[0].x != renderer->xmin) {
++ _cairo_gl_emit_rectangle (renderer,
++ renderer->xmin, y,
++ spans[0].x, y + height,
++ 0);
+ }
+
+- if (prev_x < xmax) {
+- _cairo_gl_emit_span (renderer,
+- prev_x + x_translate, xmax + x_translate, y,
+- prev_alpha);
++ do {
++ _cairo_gl_emit_rectangle (renderer,
++ 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_emit_rectangle (renderer,
++ spans[0].x, y,
++ renderer->xmax, y + height,
++ 0);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+@@ -2274,8 +2290,6 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t op,
+ cairo_gl_surface_t *dst = abstract_dst;
+ cairo_gl_surface_span_renderer_t *renderer;
+ cairo_status_t status;
+- int width = rects->width;
+- int height = rects->height;
+ cairo_surface_attributes_t *src_attributes;
+ GLenum err;
+
+diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
+index 48d8013..d52979d 100644
+--- a/src/cairo-image-surface.c
++++ b/src/cairo-image-surface.c
+@@ -1390,11 +1390,13 @@ typedef struct _cairo_image_surface_span_renderer {
+ const cairo_pattern_t *pattern;
+ cairo_antialias_t antialias;
+
++ uint8_t *mask_data;
++ uint32_t mask_stride;
++
+ cairo_image_surface_t *src;
+ cairo_surface_attributes_t src_attributes;
+ cairo_image_surface_t *mask;
+ cairo_image_surface_t *dst;
+-
+ cairo_composite_rectangles_t composite_rectangles;
+ } cairo_image_surface_span_renderer_t;
+
+@@ -1403,66 +1405,46 @@ _cairo_image_surface_span_render_row (
+ int y,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans,
+- cairo_image_surface_t *mask,
+- const cairo_composite_rectangles_t *rects)
++ uint8_t *data,
++ uint32_t stride)
+ {
+- int xmin = rects->mask.x;
+- int xmax = xmin + rects->width;
+ uint8_t *row;
+- int prev_x = xmin;
+- int prev_alpha = 0;
+ unsigned i;
+
+- /* Make sure we're within y-range. */
+- y -= rects->mask.y;
+- if (y < 0 || y >= rects->height)
++ if (num_spans == 0)
+ return;
+
+- row = (uint8_t*)(mask->data) + y*(size_t)mask->stride - xmin;
+-
+- /* Find the first span within x-range. */
+- for (i=0; i < num_spans && spans[i].x < xmin; i++) {}
+- if (i>0)
+- prev_alpha = spans[i-1].coverage;
+-
+- /* Set the intermediate spans. */
+- for (; i < num_spans; i++) {
+- int x = spans[i].x;
+-
+- if (x >= xmax)
+- break;
+-
+- if (prev_alpha != 0) {
+- /* We implement setting rendering the most common single
+- * pixel wide span case to avoid the overhead of a memset
+- * call. Open coding setting longer spans didn't show a
+- * noticeable improvement over memset. */
+- if (x == prev_x + 1) {
+- row[prev_x] = prev_alpha;
+- }
+- else {
+- memset(row + prev_x, prev_alpha, x - prev_x);
+- }
++ row = data + y * stride;
++ for (i = 0; i < num_spans - 1; i++) {
++ if (! spans[i].coverage)
++ continue;
++
++ /* We implement setting the most common single pixel wide
++ * span case to avoid the overhead of a memset call.
++ * Open coding setting longer spans didn't show a
++ * noticeable improvement over memset.
++ */
++ if (spans[i+1].x == spans[i].x + 1) {
++ row[spans[i].x] = spans[i].coverage;
++ } else {
++ memset (row + spans[i].x,
++ spans[i].coverage,
++ spans[i+1].x - spans[i].x);
+ }
+-
+- prev_x = x;
+- prev_alpha = spans[i].coverage;
+- }
+-
+- if (prev_alpha != 0 && prev_x < xmax) {
+- memset(row + prev_x, prev_alpha, xmax - prev_x);
+ }
+ }
+
+ static cairo_status_t
+-_cairo_image_surface_span_renderer_render_row (
++_cairo_image_surface_span_renderer_render_rows (
+ void *abstract_renderer,
+ int y,
++ int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+ {
+ cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
+- _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles);
++ while (height--)
++ _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+@@ -1517,11 +1499,11 @@ _cairo_image_surface_span_renderer_finish (void *abstract_renderer)
+ &dst->base,
+ src_attributes,
+ src->width, src->height,
+- rects->width, rects->height,
++ width, height,
+ rects->src.x, rects->src.y,
+ 0, 0, /* mask.x, mask.y */
+ rects->dst.x, rects->dst.y,
+- rects->width, rects->height,
++ width, height,
+ dst->clip_region);
+ }
+ }
+@@ -1567,7 +1549,7 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op,
+
+ renderer->base.destroy = _cairo_image_surface_span_renderer_destroy;
+ renderer->base.finish = _cairo_image_surface_span_renderer_finish;
+- renderer->base.render_row = _cairo_image_surface_span_renderer_render_row;
++ renderer->base.render_rows = _cairo_image_surface_span_renderer_render_rows;
+ renderer->op = op;
+ renderer->pattern = pattern;
+ renderer->antialias = antialias;
+@@ -1604,6 +1586,9 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t op,
+ _cairo_image_surface_span_renderer_destroy (renderer);
+ return _cairo_span_renderer_create_in_error (status);
+ }
++
++ renderer->mask_data = renderer->mask->data - rects->mask.x - rects->mask.y * renderer->mask->stride;
++ renderer->mask_stride = renderer->mask->stride;
+ return &renderer->base;
+ }
+
+diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
+index e29a567..af3b38c 100644
+--- a/src/cairo-spans-private.h
++++ b/src/cairo-spans-private.h
+@@ -47,26 +47,24 @@ typedef struct _cairo_half_open_span {
+ * 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 source by whatever compositing
+- * method is required. The function should ignore spans outside
+- * the bounding box set by the init() function. */
+- cairo_status_t (*render_row)(
+- void *abstract_renderer,
+- int y,
+- const cairo_half_open_span_t *coverages,
+- unsigned num_coverages);
++ /* Render the spans on row y of the destination by whatever compositing
++ * method is required. */
++ cairo_warn 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);
+-
+- /* Private status variable. */
+- cairo_status_t status;
++ cairo_status_t (*finish) (void *abstract_renderer);
+ };
+
+ /* Scan converter interface. */
+diff --git a/src/cairo-spans.c b/src/cairo-spans.c
+index af3b85f..69894c1 100644
+--- a/src/cairo-spans.c
++++ b/src/cairo-spans.c
+@@ -275,13 +275,15 @@ _cairo_scan_converter_create_in_error (cairo_status_t status)
+ }
+
+ static cairo_status_t
+-_cairo_nil_span_renderer_render_row (
++_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);
+@@ -310,7 +312,7 @@ _cairo_span_renderer_set_error (
+ ASSERT_NOT_REACHED;
+ }
+ if (renderer->status == CAIRO_STATUS_SUCCESS) {
+- renderer->render_row = _cairo_nil_span_renderer_render_row;
++ renderer->render_rows = _cairo_nil_span_renderer_render_rows;
+ renderer->finish = _cairo_nil_span_renderer_finish;
+ renderer->status = error;
+ }
+diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
+index 29262c2..2b9fb1b 100644
+--- a/src/cairo-tor-scan-converter.c
++++ b/src/cairo-tor-scan-converter.c
+@@ -128,27 +128,29 @@ blit_with_span_renderer(
+ cairo_span_renderer_t *span_renderer,
+ struct pool *span_pool,
+ int y,
++ int height,
+ int xmin,
+ int xmax);
+
+ static glitter_status_t
+-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y);
++blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height);
+
+ #define GLITTER_BLIT_COVERAGES_ARGS \
+ cairo_span_renderer_t *span_renderer, \
+ struct pool *span_pool
+
+-#define GLITTER_BLIT_COVERAGES(cells, y, xmin, xmax) do { \
++#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \
+ cairo_status_t status = blit_with_span_renderer (cells, \
+ span_renderer, \
+ span_pool, \
+- y, xmin, xmax); \
++ y, height, \
++ xmin, xmax); \
+ if (unlikely (status)) \
+ return status; \
+ } while (0)
+
+-#define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax) do { \
+- cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y); \
++#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \
++ cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \
+ if (unlikely (status)) \
+ return status; \
+ } while (0)
+@@ -309,8 +311,8 @@ typedef int grid_area_t;
+ #define UNROLL3(x) x x x
+
+ struct quorem {
+- int quo;
+- int rem;
++ int32_t quo;
++ int32_t rem;
+ };
+
+ /* Header for a chunk of memory in a memory pool. */
+@@ -382,6 +384,7 @@ struct edge {
+ /* Original sign of the edge: +1 for downwards, -1 for upwards
+ * edges. */
+ int dir;
++ int vertical;
+ };
+
+ /* Number of subsample rows per y-bucket. Must be GRID_Y. */
+@@ -389,18 +392,28 @@ struct edge {
+
+ #define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
+
++struct bucket {
++ /* Unsorted list of edges starting within this bucket. */
++ struct edge *edges;
++
++ /* Set to non-zero if there are edges starting strictly within the
++ * bucket. */
++ unsigned have_inside_edges;
++};
++
+ /* 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. */
++ /* The clip extents. */
++ grid_scaled_x_t xmin, xmax;
+ 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 bucket *y_buckets;
++ struct bucket y_buckets_embedded[64];
+
+ struct {
+ struct pool base[1];
+@@ -702,7 +715,6 @@ static void
+ cell_list_fini(struct cell_list *cells)
+ {
+ pool_fini (cells->cell_pool.base);
+- cell_list_init (cells);
+ }
+
+ /* Empty the cell list. This is called at the start of every pixel
+@@ -715,6 +727,26 @@ cell_list_reset (struct cell_list *cells)
+ pool_reset (cells->cell_pool.base);
+ }
+
++static struct cell *
++cell_list_alloc (struct cell_list *cells,
++ struct cell **cursor,
++ struct cell *tail,
++ int x)
++{
++ struct cell *cell;
++
++ cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
++ if (unlikely (NULL == cell))
++ return NULL;
++
++ *cursor = cell;
++ cell->next = tail;
++ cell->x = x;
++ cell->uncovered_area = 0;
++ cell->covered_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
+@@ -737,22 +769,10 @@ cell_list_find (struct cell_list *cells, int x)
+ }
+ cells->cursor = cursor;
+
+- if (tail->x == x) {
++ if (tail->x == x)
+ return tail;
+- } else {
+- struct cell *cell;
+-
+- cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+- if (unlikely (NULL == cell))
+- return NULL;
+
+- *cursor = cell;
+- cell->next = tail;
+- cell->x = x;
+- cell->uncovered_area = 0;
+- cell->covered_height = 0;
+- return cell;
+- }
++ return cell_list_alloc (cells, cursor, tail, x);
+ }
+
+ /* Find two cells at x1 and x2. This is exactly equivalent
+@@ -832,9 +852,8 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2)
+ /* Add an unbounded subpixel span covering subpixels >= x to the
+ * coverage cells. */
+ static glitter_status_t
+-cell_list_add_unbounded_subspan(
+- struct cell_list *cells,
+- grid_scaled_x_t x)
++cell_list_add_unbounded_subspan (struct cell_list *cells,
++ grid_scaled_x_t x)
+ {
+ struct cell *cell;
+ int ix, fx;
+@@ -907,20 +926,24 @@ cell_list_render_edge(
+ struct edge *edge,
+ int sign)
+ {
+- struct quorem x1 = edge->x;
+- struct quorem x2 = x1;
+ grid_scaled_y_t y1, y2, dy;
+ grid_scaled_x_t dx;
+ int ix1, ix2;
+ grid_scaled_x_t fx1, fx2;
+
+- x2.quo += edge->dxdy_full.quo;
+- x2.rem += edge->dxdy_full.rem;
+- if (x2.rem >= 0) {
+- ++x2.quo;
+- x2.rem -= edge->dy;
++ 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;
+ }
+- edge->x = x2;
+
+ GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
+ GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
+@@ -1026,6 +1049,7 @@ static void
+ polygon_init (struct polygon *polygon)
+ {
+ polygon->ymin = polygon->ymax = 0;
++ polygon->xmin = polygon->xmax = 0;
+ polygon->y_buckets = polygon->y_buckets_embedded;
+ pool_init (polygon->edge_pool.base,
+ 8192 - sizeof (struct _pool_chunk),
+@@ -1045,10 +1069,11 @@ polygon_fini (struct polygon *polygon)
+ * 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)
++polygon_reset (struct polygon *polygon,
++ grid_scaled_x_t xmin,
++ grid_scaled_x_t xmax,
++ grid_scaled_y_t ymin,
++ grid_scaled_y_t ymax)
+ {
+ unsigned h = ymax - ymin;
+ unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
+@@ -1065,14 +1090,16 @@ polygon_reset(
+ 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 *));
++ sizeof (struct bucket));
+ if (unlikely (NULL == polygon->y_buckets))
+ goto bail_no_mem;
+ }
+- memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
++ memset (polygon->y_buckets, 0, num_buckets * sizeof (struct bucket));
+
+ polygon->ymin = ymin;
+ polygon->ymax = ymax;
++ polygon->xmin = xmin;
++ polygon->xmax = xmax;
+ return GLITTER_STATUS_SUCCESS;
+
+ bail_no_mem:
+@@ -1086,10 +1113,13 @@ _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];
++ unsigned j = e->ytop - polygon->ymin;
++ unsigned ix = j / EDGE_Y_BUCKET_HEIGHT;
++ unsigned offset = j % EDGE_Y_BUCKET_HEIGHT;
++ struct edge **ptail = &polygon->y_buckets[ix].edges;
+ e->next = *ptail;
+ *ptail = e;
++ polygon->y_buckets[ix].have_inside_edges |= offset;
+ }
+
+ inline static glitter_status_t
+@@ -1115,30 +1145,53 @@ polygon_add_edge (struct polygon *polygon,
+ dx = edge->line.p2.x - edge->line.p1.x;
+ dy = edge->line.p2.y - edge->line.p1.y;
+ e->dy = dy;
+- e->dxdy = floored_divrem (dx, dy);
+-
+- if (ymin <= edge->top)
+- ytop = edge->top;
+- else
+- ytop = ymin;
+- 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;
+- }
+-
+ e->dir = edge->dir;
++
++ ytop = edge->top >= ymin ? edge->top : ymin;
++ ybot = edge->bottom <= ymax ? edge->bottom : ymax;
+ e->ytop = ytop;
+- ybot = edge->bottom < ymax ? edge->bottom : ymax;
+ e->height_left = ybot - ytop;
+
+- if (e->height_left >= GRID_Y) {
+- e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+- } else {
++ if (dx == 0) {
++ e->vertical = TRUE;
++ e->x.quo = edge->line.p1.x;
++ e->x.rem = 0;
++ e->dxdy.quo = 0;
++ e->dxdy.rem = 0;
+ e->dxdy_full.quo = 0;
+ e->dxdy_full.rem = 0;
++
++ /* Drop edges to the right of the clip extents. */
++ if (e->x.quo >= polygon->xmax)
++ return GLITTER_STATUS_SUCCESS;
++
++ /* Offset vertical edges at the left side of the clip extents
++ * to just shy of the left side. We depend on this when
++ * checking for possible intersections within the clip
++ * rectangle. */
++ if (e->x.quo <= polygon->xmin) {
++ e->x.quo = polygon->xmin - 1;
++ }
++ } else {
++ e->vertical = FALSE;
++ e->dxdy = floored_divrem (dx, dy);
++ if (ytop == edge->line.p1.y) {
++ e->x.quo = edge->line.p1.x;
++ e->x.rem = 0;
++ } else {
++ e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
++ e->x.quo += edge->line.p1.x;
++ }
++
++ if (e->x.quo >= polygon->xmax && e->dxdy.quo >= 0)
++ return GLITTER_STATUS_SUCCESS;
++
++ if (e->height_left >= GRID_Y) {
++ e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
++ } else {
++ e->dxdy_full.quo = 0;
++ e->dxdy_full.rem = 0;
++ }
+ }
+
+ _polygon_insert_edge_into_its_y_bucket (polygon, e);
+@@ -1161,31 +1214,30 @@ active_list_init(struct active_list *active)
+ active_list_reset(active);
+ }
+
+-static void
+-active_list_fini(
+- struct active_list *active)
+-{
+- active_list_reset(active);
+-}
+-
+ /* Merge the edges in an unsorted list of edges into a sorted
+ * list. The sort order is edges ascending by edge->x.quo. Returns
+ * the new head of the sorted list. */
+ static struct edge *
+ merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head)
+ {
+- struct edge *head = unsorted_head;
+ struct edge **cursor = &sorted_head;
+ int x;
+
+- while (NULL != head) {
++ if (sorted_head == NULL) {
++ sorted_head = unsorted_head;
++ unsorted_head = unsorted_head->next;
++ sorted_head->next = NULL;
++ if (unsorted_head == NULL)
++ return sorted_head;
++ }
++
++ do {
++ struct edge *next = unsorted_head->next;
+ struct edge *prev = *cursor;
+- struct edge *next = head->next;
+- x = head->x.quo;
+
+- if (NULL == prev || x < prev->x.quo) {
++ x = unsorted_head->x.quo;
++ if (x < prev->x.quo)
+ cursor = &sorted_head;
+- }
+
+ while (1) {
+ UNROLL3({
+@@ -1196,26 +1248,29 @@ merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head)
+ });
+ }
+
+- head->next = *cursor;
+- *cursor = head;
++ unsorted_head->next = *cursor;
++ *cursor = unsorted_head;
++ unsorted_head = next;
++ } while (unsorted_head != NULL);
+
+- head = next;
+- }
+ return sorted_head;
+ }
+
+ /* Test if the edges on the active list can be safely advanced by a
+ * full row without intersections or any edges ending. */
+ inline static int
+-active_list_can_step_full_row(
+- struct active_list *active)
++active_list_can_step_full_row (struct active_list *active,
++ grid_scaled_x_t xmin)
+ {
++ const struct edge *e;
++ grid_scaled_x_t 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) {
+- struct edge *e = active->head;
+ int min_height = INT_MAX;
+
++ e = active->head;
+ while (NULL != e) {
+ if (e->height_left < min_height)
+ min_height = e->height_left;
+@@ -1225,27 +1280,38 @@ active_list_can_step_full_row(
+ active->min_height = min_height;
+ }
+
+- /* Check for intersections only if no edges end during the next
+- * row. */
+- if (active->min_height >= GRID_Y) {
+- grid_scaled_x_t prev_x = INT_MIN;
+- struct edge *e = active->head;
+- while (NULL != e) {
+- struct quorem x = e->x;
++ 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)
++ /* There's may be an intersection if the edge sort order might
++ * change. */
++ if (x.quo <= prev_x) {
++ /* Ignore intersections to the left of the clip extents.
++ * This assumes that all vertical edges on or at the left
++ * side of the clip rectangle have been shifted slightly
++ * to the left in polygon_add_edge(). */
++ if (prev_x >= xmin || x.quo >= xmin || e->x.quo >= xmin)
+ return 0;
++ }
++ else {
+ prev_x = x.quo;
+- e = e->next;
+ }
+- return 1;
++ e = e->next;
+ }
+- return 0;
++
++ return 1;
+ }
+
+ /* Merges edges on the given subpixel row from the polygon to the
+@@ -1261,7 +1327,7 @@ active_list_merge_edges_from_polygon(
+ unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin);
+ int min_height = active->min_height;
+ struct edge *subrow_edges = NULL;
+- struct edge **ptail = &polygon->y_buckets[ix];
++ struct edge **ptail = &polygon->y_buckets[ix].edges;
+
+ while (1) {
+ struct edge *tail = *ptail;
+@@ -1277,8 +1343,10 @@ active_list_merge_edges_from_polygon(
+ ptail = &tail->next;
+ }
+ }
+- active->head = merge_unsorted_edges(active->head, subrow_edges);
+- active->min_height = min_height;
++ if (subrow_edges) {
++ active->head = merge_unsorted_edges(active->head, subrow_edges);
++ active->min_height = min_height;
++ }
+ }
+
+ /* Advance the edges on the active list by one subsample row by
+@@ -1439,11 +1507,13 @@ apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
+ }
+ }
+
+- 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;
++ 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;
++ }
+ }
+ }
+
+@@ -1472,6 +1542,7 @@ apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+ 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)
+@@ -1490,17 +1561,22 @@ apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
+ else
+ *cursor = right_edge->next;
+
++ winding += right_edge->dir;
++ if ((winding & 1) == 0) {
+ if (right_edge->next == NULL ||
+ right_edge->next->x.quo != right_edge->x.quo)
+ {
+ break;
+ }
++ }
+
+- 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;
++ 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;
++ }
+ }
+ }
+
+@@ -1537,8 +1613,14 @@ blit_span(
+ }
+ }
+
+-#define GLITTER_BLIT_COVERAGES(coverages, y, xmin, xmax) \
+- blit_cells(coverages, raster_pixels + (y)*raster_stride, xmin, xmax)
++#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \
++ do { \
++ int __y = y; \
++ int __h = height; \
++ do { \
++ blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \
++ } while (--__h); \
++ } while (0)
+
+ static void
+ blit_cells(
+@@ -1597,7 +1679,6 @@ static void
+ _glitter_scan_converter_fini(glitter_scan_converter_t *converter)
+ {
+ polygon_fini(converter->polygon);
+- active_list_fini(converter->active);
+ cell_list_fini(converter->coverages);
+ converter->xmin=0;
+ converter->ymin=0;
+@@ -1641,7 +1722,7 @@ glitter_scan_converter_reset(
+
+ active_list_reset(converter->active);
+ cell_list_reset(converter->coverages);
+- status = polygon_reset(converter->polygon, ymin, ymax);
++ status = polygon_reset(converter->polygon, xmin, xmax, ymin, ymax);
+ if (status)
+ return status;
+
+@@ -1711,19 +1792,48 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
+ #endif
+
+ #ifndef GLITTER_BLIT_COVERAGES_EMPTY
+-# define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax)
++# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax)
+ #endif
+
++static cairo_bool_t
++active_list_is_vertical (struct active_list *active)
++{
++ struct edge *e;
++
++ for (e = active->head; e != NULL; e = e->next) {
++ if (! e->vertical)
++ return FALSE;
++ }
++
++ return TRUE;
++}
++
++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;
++ }
++}
++
+ I glitter_status_t
+ glitter_scan_converter_render(
+ glitter_scan_converter_t *converter,
+ int nonzero_fill,
+ GLITTER_BLIT_COVERAGES_ARGS)
+ {
+- int i;
++ int i, j;
+ int ymax_i = converter->ymax / GRID_Y;
+ int ymin_i = converter->ymin / GRID_Y;
+ int xmin_i, xmax_i;
++ grid_scaled_x_t xmin = converter->xmin;
+ int h = ymax_i - ymin_i;
+ struct polygon *polygon = converter->polygon;
+ struct cell_list *coverages = converter->coverages;
+@@ -1738,22 +1848,28 @@ glitter_scan_converter_render(
+ GLITTER_BLIT_COVERAGES_BEGIN;
+
+ /* Render each pixel row. */
+- for (i=0; i<h; i++) {
++ for (i = 0; i < h; i = j) {
+ int do_full_step = 0;
+ glitter_status_t status = 0;
+
++ j = i + 1;
++
+ /* Determine if we can ignore this row or use the full pixel
+ * stepper. */
+- if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
++ if (polygon->y_buckets[i].edges == NULL) {
+ if (! active->head) {
+- GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, xmin_i, xmax_i);
++ for (; j < h && ! polygon->y_buckets[j].edges; j++)
++ ;
++ GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i);
+ continue;
+ }
+-
+- do_full_step = active_list_can_step_full_row (active);
++ do_full_step = active_list_can_step_full_row (active, xmin);
++ }
++ else if (! polygon->y_buckets[i].have_inside_edges) {
++ grid_scaled_y_t y = (i+ymin_i)*GRID_Y;
++ active_list_merge_edges_from_polygon (active, y, polygon);
++ do_full_step = active_list_can_step_full_row (active, xmin);
+ }
+-
+- cell_list_reset (coverages);
+
+ if (do_full_step) {
+ /* Step by a full pixel row's worth. */
+@@ -1764,8 +1880,20 @@ glitter_scan_converter_render(
+ status = apply_evenodd_fill_rule_and_step_edges (active,
+ coverages);
+ }
++
++ if (active_list_is_vertical (active)) {
++ while (j < h &&
++ polygon->y_buckets[j].edges == NULL &&
++ active->min_height >= 2*GRID_Y)
++ {
++ active->min_height -= GRID_Y;
++ j++;
++ }
++ if (j != i + 1)
++ step_edges (active, j - (i + 1));
++ }
+ } else {
+- /* Subsample this row. */
++ /* Supersample this row. */
+ grid_scaled_y_t suby;
+ for (suby = 0; suby < GRID_Y; suby++) {
+ grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
+@@ -1787,13 +1915,13 @@ glitter_scan_converter_render(
+ if (unlikely (status))
+ return status;
+
+- GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, xmin_i, xmax_i);
++ GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i);
++ cell_list_reset (coverages);
+
+- if (! active->head) {
++ if (! active->head)
+ active->min_height = INT_MAX;
+- } else {
++ else
+ active->min_height -= GRID_Y;
+- }
+ }
+
+ /* Clean up the coverage blitter. */
+@@ -1807,21 +1935,20 @@ glitter_scan_converter_render(
+ * scan converter subclass. */
+
+ static glitter_status_t
+-blit_with_span_renderer(
+- struct cell_list *cells,
+- cairo_span_renderer_t *renderer,
+- struct pool *span_pool,
+- int y,
+- int xmin,
+- int xmax)
++blit_with_span_renderer (struct cell_list *cells,
++ cairo_span_renderer_t *renderer,
++ struct pool *span_pool,
++ int y, int height,
++ int xmin, int xmax)
+ {
+ struct cell *cell = cells->head;
+ int prev_x = xmin;
+ int cover = 0;
+ cairo_half_open_span_t *spans;
+ unsigned num_spans;
++
+ if (cell == NULL)
+- return CAIRO_STATUS_SUCCESS;
++ return blit_empty_with_span_renderer (renderer, y, height);
+
+ /* Skip cells to the left of the clip region. */
+ while (cell != NULL && cell->x < xmin) {
+@@ -1833,12 +1960,12 @@ blit_with_span_renderer(
+ /* Count number of cells remaining. */
+ {
+ struct cell *next = cell;
+- num_spans = 0;
+- while (next) {
++ num_spans = 1;
++ while (next != NULL) {
+ next = next->next;
+ ++num_spans;
+ }
+- num_spans = 2*num_spans + 1;
++ num_spans = 2*num_spans;
+ }
+
+ /* Allocate enough spans for the row. */
+@@ -1853,6 +1980,7 @@ blit_with_span_renderer(
+ for (; cell != NULL; cell = cell->next) {
+ int x = cell->x;
+ int area;
++
+ if (x >= xmax)
+ break;
+
+@@ -1872,20 +2000,26 @@ blit_with_span_renderer(
+ prev_x = x+1;
+ }
+
+- if (prev_x < xmax) {
++ if (prev_x <= xmax) {
+ spans[num_spans].x = prev_x;
+ spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
+ ++num_spans;
+ }
+
++ if (prev_x < xmax && cover) {
++ spans[num_spans].x = xmax;
++ spans[num_spans].coverage = 0;
++ ++num_spans;
++ }
++
+ /* Dump them into the renderer. */
+- return renderer->render_row (renderer, y, spans, num_spans);
++ return renderer->render_rows (renderer, y, height, spans, num_spans);
+ }
+
+ static glitter_status_t
+-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y)
++blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height)
+ {
+- return renderer->render_row (renderer, y, NULL, 0);
++ return renderer->render_rows (renderer, y, height, NULL, 0);
+ }
+
+ struct _cairo_tor_scan_converter {
+diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
+index 82d1cf5..d4575a3 100644
+--- a/src/cairo-win32-surface.c
++++ b/src/cairo-win32-surface.c
+@@ -1954,6 +1954,9 @@ typedef struct _cairo_win32_surface_span_renderer {
+ const cairo_pattern_t *pattern;
+ cairo_antialias_t antialias;
+
++ uint8_t *mask_data;
++ uint32_t mask_stride;
++
+ cairo_image_surface_t *mask;
+ cairo_win32_surface_t *dst;
+ cairo_region_t *clip_region;
+@@ -1962,14 +1965,16 @@ typedef struct _cairo_win32_surface_span_renderer {
+ } cairo_win32_surface_span_renderer_t;
+
+ static cairo_status_t
+-_cairo_win32_surface_span_renderer_render_row (
++_cairo_win32_surface_span_renderer_render_rows (
+ void *abstract_renderer,
+ int y,
++ int height,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans)
+ {
+ cairo_win32_surface_span_renderer_t *renderer = abstract_renderer;
+- _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles);
++ while (height--)
++ _cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+@@ -2066,8 +2071,7 @@ _cairo_win32_surface_create_span_renderer (cairo_operator_t op,
+
+ renderer->base.destroy = _cairo_win32_surface_span_renderer_destroy;
+ renderer->base.finish = _cairo_win32_surface_span_renderer_finish;
+- renderer->base.render_row =
+- _cairo_win32_surface_span_renderer_render_row;
++ renderer->base.render_rows = _cairo_win32_surface_span_renderer_render_rows;
+ renderer->op = op;
+ renderer->pattern = pattern;
+ renderer->antialias = antialias;
+@@ -2088,6 +2092,9 @@ _cairo_win32_surface_create_span_renderer (cairo_operator_t op,
+ _cairo_win32_surface_span_renderer_destroy (renderer);
+ return _cairo_span_renderer_create_in_error (status);
+ }
++
++ renderer->mask_data = renderer->mask->data - rects->mask.x - rects->mask.y * renderer->mask->stride;
++ renderer->mask_stride = renderer->mask->stride;
+ return &renderer->base;
+ }
+
+diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
+index a7a40b8..566d9fb 100644
+--- a/src/cairo-xlib-display.c
++++ b/src/cairo-xlib-display.c
+@@ -407,6 +407,10 @@ _cairo_xlib_display_get (Display *dpy,
+ display->buggy_pad_reflect = TRUE;
+ }
+
++ /* gradients don't seem to work */
++ display->buggy_gradients = TRUE;
++
++
+ /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */
+ /* If buggy_repeat_force == -1, then initialize.
+ * - set to -2, meaning "nothing was specified", and we trust the above detection.
+diff --git a/src/cairoint.h b/src/cairoint.h
+index 58850ab..1cdf6ff 100644
+--- a/src/cairoint.h
++++ b/src/cairoint.h
+@@ -2257,8 +2257,8 @@ cairo_private void
+ _cairo_image_surface_span_render_row (int y,
+ const cairo_half_open_span_t *spans,
+ unsigned num_spans,
+- cairo_image_surface_t *mask,
+- const cairo_composite_rectangles_t *rects);
++ uint8_t *data,
++ uint32_t stride);
+
+ cairo_private cairo_image_transparency_t
+ _cairo_image_analyze_transparency (cairo_image_surface_t *image);
diff --git a/gfx/cairo/clip-rects-surface-extents.patch b/gfx/cairo/clip-rects-surface-extents.patch
new file mode 100644
index 000000000..1a9972aa7
--- /dev/null
+++ b/gfx/cairo/clip-rects-surface-extents.patch
@@ -0,0 +1,163 @@
+From 108b1c7825116ed3f93aa57384bbd3290cdc9181 Mon Sep 17 00:00:00 2001
+From: Karl Tomlinson <karlt+@karlt.net>
+Date: Sat, 17 Jul 2010 01:08:53 +0000
+Subject: clip: consider gstate target extents in _cairo_gstate_copy_clip_rectangle_list
+
+Fixes https://bugs.freedesktop.org/show_bug.cgi?id=29125
+
+To be consistent with _cairo_gstate_clip_extents, the context's clip
+should be intersected with the target surface extents (instead of only
+using them when there is no clip).
+
+Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
+---
+diff --git a/src/cairo-clip.c b/src/cairo-clip.c
+index 77d8214..d5a2fab 100644
+--- a/src/cairo-clip.c
++++ b/src/cairo-clip.c
+@@ -1495,7 +1495,7 @@ _cairo_rectangle_list_create_in_error (cairo_status_t status)
+ 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));
++#define ERROR_LIST(S) _cairo_rectangle_list_create_in_error (_cairo_error (S))
+
+ cairo_rectangle_list_t *list;
+ cairo_rectangle_t *rectangles = NULL;
+@@ -1507,57 +1507,37 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
+ if (clip->all_clipped)
+ goto DONE;
+
+- if (clip->path != NULL) {
+- status = _cairo_clip_get_region (clip, &region);
+- if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+- goto DONE;
+- } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+- return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+- } else if (unlikely (status)) {
+- return ERROR_LIST (status);
+- }
+- }
+-
+- if (region != NULL) {
+- 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);
+- }
++ if (!clip->path)
++ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+
+- for (i = 0; i < n_rects; ++i) {
+- cairo_rectangle_int_t clip_rect;
+-
+- cairo_region_get_rectangle (region, i, &clip_rect);
++ status = _cairo_clip_get_region (clip, &region);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ goto DONE;
++ } else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
++ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
++ } else if (unlikely (status)) {
++ return ERROR_LIST (status);
++ }
+
+- if (! _cairo_clip_int_rect_to_user (gstate,
+- &clip_rect,
+- &rectangles[i]))
+- {
+- free (rectangles);
+- return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
+- }
+- }
++ 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);
+ }
+- } else {
+- cairo_rectangle_int_t extents;
+
+- if (! _cairo_surface_get_extents (_cairo_gstate_get_target (gstate),
+- &extents))
+- {
+- /* unbounded surface -> unclipped */
+- goto DONE;
+- }
++ for (i = 0; i < n_rects; ++i) {
++ cairo_rectangle_int_t clip_rect;
+
+- n_rects = 1;
+- rectangles = malloc(sizeof (cairo_rectangle_t));
+- if (unlikely (rectangles == NULL))
+- return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
++ cairo_region_get_rectangle (region, i, &clip_rect);
+
+- if (! _cairo_clip_int_rect_to_user (gstate, &extents, rectangles)) {
+- free (rectangles);
+- return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
++ if (! _cairo_clip_int_rect_to_user (gstate,
++ &clip_rect,
++ &rectangles[i]))
++ {
++ free (rectangles);
++ return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
++ }
+ }
+ }
+
+diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
+index baf6145..7caf624 100644
+--- a/src/cairo-gstate.c
++++ b/src/cairo-gstate.c
+@@ -1555,7 +1555,19 @@ _cairo_gstate_clip_extents (cairo_gstate_t *gstate,
+ cairo_rectangle_list_t*
+ _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate)
+ {
+- return _cairo_clip_copy_rectangle_list (&gstate->clip, gstate);
++ cairo_clip_t clip;
++ cairo_rectangle_int_t extents;
++ cairo_rectangle_list_t *list;
++
++ _cairo_clip_init_copy (&clip, &gstate->clip);
++
++ if (_cairo_surface_get_extents (gstate->target, &extents))
++ _cairo_clip_rectangle (&clip, &extents);
++
++ list = _cairo_clip_copy_rectangle_list (&clip, gstate);
++ _cairo_clip_fini (&clip);
++
++ return list;
+ }
+
+ static void
+diff --git a/test/get-clip.c b/test/get-clip.c
+index f0477a1..f97db3f 100644
+--- a/test/get-clip.c
++++ b/test/get-clip.c
+@@ -120,6 +120,22 @@ preamble (cairo_test_context_t *ctx)
+ }
+ cairo_rectangle_list_destroy (rectangle_list);
+
++ /* We should get the same results after applying a clip that contains the
++ existing clip. */
++ phase = "Clip beyond surface extents";
++ cairo_save (cr);
++ cairo_rectangle (cr, -10, -10, SIZE + 20 , SIZE + 20);
++ cairo_clip (cr);
++ rectangle_list = cairo_copy_clip_rectangle_list (cr);
++ if (! check_count (ctx, phase, rectangle_list, 1) ||
++ ! check_clip_extents (ctx, phase, cr, 0, 0, SIZE, SIZE) ||
++ ! check_rectangles_contain (ctx, phase, rectangle_list, 0, 0, SIZE, SIZE))
++ {
++ goto FAIL;
++ }
++ cairo_rectangle_list_destroy (rectangle_list);
++ cairo_restore (cr);
++
+ /* Test simple clip rect. */
+ phase = "Simple clip rect";
+ cairo_save (cr);
+--
+cgit v0.8.3-6-g21f6
diff --git a/gfx/cairo/copyarea-with-alpha.patch b/gfx/cairo/copyarea-with-alpha.patch
new file mode 100644
index 000000000..c6765105e
--- /dev/null
+++ b/gfx/cairo/copyarea-with-alpha.patch
@@ -0,0 +1,110 @@
+diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+--- a/gfx/cairo/cairo/src/cairo-xlib-surface.c
++++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+@@ -1722,30 +1722,31 @@ _surface_has_alpha (cairo_xlib_surface_t
+ else
+ return FALSE;
+ } else {
+ /* In the no-render case, we never have alpha */
+ return FALSE;
+ }
+ }
+
+-/* Returns true if the given operator and source-alpha combination
+- * requires alpha compositing to complete.
++/* Returns true if the given operator and alpha combination requires alpha
++ * compositing to complete on source and destination surfaces with the same
++ * format. i.e. if a simple bitwise copy is not appropriate.
+ */
+ static cairo_bool_t
+ _operator_needs_alpha_composite (cairo_operator_t op,
+- cairo_bool_t destination_has_alpha,
+- cairo_bool_t source_has_alpha)
++ cairo_bool_t surfaces_have_alpha)
+ {
+- if (op == CAIRO_OPERATOR_SOURCE ||
+- (! source_has_alpha &&
+- (op == CAIRO_OPERATOR_OVER ||
+- op == CAIRO_OPERATOR_ATOP ||
+- op == CAIRO_OPERATOR_IN)))
+- return destination_has_alpha;
++ if (op == CAIRO_OPERATOR_SOURCE)
++ return FALSE;
++
++ if (op == CAIRO_OPERATOR_OVER ||
++ op == CAIRO_OPERATOR_IN ||
++ op == CAIRO_OPERATOR_ATOP)
++ return surfaces_have_alpha;
+
+ return TRUE;
+ }
+
+ /* There is a bug in most older X servers with compositing using a
+ * untransformed repeating source pattern when the source is in off-screen
+ * video memory, and another with repeated transformed images using a
+ * general transform matrix. When these bugs could be triggered, we need a
+@@ -1843,24 +1844,24 @@ _categorize_composite_operation (cairo_x
+ */
+ static composite_operation_t
+ _recategorize_composite_operation (cairo_xlib_surface_t *dst,
+ cairo_operator_t op,
+ cairo_xlib_surface_t *src,
+ cairo_surface_attributes_t *src_attr,
+ cairo_bool_t have_mask)
+ {
+- /* Can we use the core protocol? */
++ /* Can we use the core protocol? (If _surfaces_compatible, then src and
++ * dst have the same format and _surface_has_alpha is the same for each.)
++ */
+ if (! have_mask &&
+ src->owns_pixmap &&
+- src->depth == dst->depth &&
++ _surfaces_compatible (src, dst) &&
+ _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) &&
+- ! _operator_needs_alpha_composite (op,
+- _surface_has_alpha (dst),
+- _surface_has_alpha (src)))
++ ! _operator_needs_alpha_composite (op, _surface_has_alpha (dst)))
+ {
+ if (src_attr->extend == CAIRO_EXTEND_NONE)
+ return DO_XCOPYAREA;
+
+ if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT)
+ return DO_XTILE;
+ }
+
+@@ -2211,34 +2212,28 @@ _cairo_xlib_surface_composite (cairo_ope
+ cairo_surface_attributes_t src_attr, mask_attr;
+ cairo_xlib_surface_t *dst = abstract_dst;
+ cairo_xlib_surface_t *src;
+ cairo_xlib_surface_t *mask;
+ cairo_int_status_t status;
+ composite_operation_t operation;
+ int itx, ity;
+ cairo_bool_t is_integer_translation;
+- cairo_bool_t needs_alpha_composite;
+ GC gc;
+
+ if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))
+ return UNSUPPORTED ("no support for masks");
+
+ operation = _categorize_composite_operation (dst, op, src_pattern,
+ mask_pattern != NULL);
+ if (operation == DO_UNSUPPORTED)
+ return UNSUPPORTED ("unsupported operation");
+
+ X_DEBUG ((dst->dpy, "composite (dst=%x)", (unsigned int) dst->drawable));
+
+- needs_alpha_composite =
+- _operator_needs_alpha_composite (op,
+- _surface_has_alpha (dst),
+- ! _cairo_pattern_is_opaque (src_pattern));
+-
+ _cairo_xlib_display_notify (dst->display);
+
+ status =
+ _cairo_xlib_surface_acquire_pattern_surfaces (dst,
+ src_pattern, mask_pattern,
+ src_x, src_y,
+ mask_x, mask_y,
+ width, height,
diff --git a/gfx/cairo/d2d-gradient-ensure-stops.patch b/gfx/cairo/d2d-gradient-ensure-stops.patch
new file mode 100644
index 000000000..2f7bb9334
--- /dev/null
+++ b/gfx/cairo/d2d-gradient-ensure-stops.patch
@@ -0,0 +1,32 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1348618772 -43200
+# Node ID 55ccbc8d52e69b020f2ba493e92ad2e214388df0
+# Parent e0d69219dd2b3b2826d186dc99c673b879409ea6
+Bug 792903. Prevent num_stops from being set to zero. r=bas
+
+diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
++++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+@@ -1641,17 +1641,20 @@ static RefPtr<ID2D1Brush>
+ min_dist = MIN(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)),
+ _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1)));
+ min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1)));
+ min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1)));
+
+ min_dist = MAX(-min_dist, 0);
+
+ // Repeats after gradient start.
+- int after_repeat = (int)ceil(max_dist / gradient_length);
++ // It's possible for max_dist and min_dist to both be zero, in which case
++ // we'll set num_stops to 0 and crash D2D. Let's just ensure after_repeat
++ // is at least 1.
++ int after_repeat = MAX((int)ceil(max_dist / gradient_length), 1);
+ int before_repeat = (int)ceil(min_dist / gradient_length);
+ num_stops *= (after_repeat + before_repeat);
+
+ p2.x = p1.x + u.x * after_repeat * gradient_length;
+ p2.y = p1.y + u.y * after_repeat * gradient_length;
+ p1.x = p1.x - u.x * before_repeat * gradient_length;
+ p1.y = p1.y - u.y * before_repeat * gradient_length;
+
diff --git a/gfx/cairo/d2d-repeating-gradients.patch b/gfx/cairo/d2d-repeating-gradients.patch
new file mode 100644
index 000000000..993695bf0
--- /dev/null
+++ b/gfx/cairo/d2d-repeating-gradients.patch
@@ -0,0 +1,271 @@
+From: Robert O'Callahan <robert@ocallahan.org>
+Bug 768775. Improve the precision of the calculation of the number of stops that need to be added to handle 'repeat' and 'reflect', when we're filling a path. r=bas
+
+diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
++++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+@@ -1411,17 +1411,17 @@ static RefPtr<ID2D1Brush>
+
+ gradient_center.x = _cairo_fixed_to_float(source_pattern->c1.x);
+ gradient_center.y = _cairo_fixed_to_float(source_pattern->c1.y);
+
+ // Transform surface corners into pattern coordinates.
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_left.x, &top_left.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_right.x, &top_right.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_left.x, &bottom_left.y);
+- cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &top_left.y);
++ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &bottom_right.y);
+
+ // Find the corner furthest away from the gradient center in pattern space.
+ double largest = MAX(_cairo_d2d_point_dist(top_left, gradient_center), _cairo_d2d_point_dist(top_right, gradient_center));
+ largest = MAX(largest, _cairo_d2d_point_dist(bottom_left, gradient_center));
+ largest = MAX(largest, _cairo_d2d_point_dist(bottom_right, gradient_center));
+
+ unsigned int minSize = (unsigned int)ceil(largest);
+
+@@ -1531,16 +1531,17 @@ static RefPtr<ID2D1Brush>
+ stopCollection,
+ &brush);
+ delete [] stops;
+ return brush;
+ }
+
+ static RefPtr<ID2D1Brush>
+ _cairo_d2d_create_linear_gradient_brush(cairo_d2d_surface_t *d2dsurf,
++ cairo_path_fixed_t *fill_path,
+ cairo_linear_pattern_t *source_pattern)
+ {
+ if (source_pattern->p1.x == source_pattern->p2.x &&
+ source_pattern->p1.y == source_pattern->p2.y) {
+ // Cairo behavior in this situation is to draw a solid color the size of the last stop.
+ RefPtr<ID2D1SolidColorBrush> brush;
+ d2dsurf->rt->CreateSolidColorBrush(
+ _cairo_d2d_color_from_cairo_color_stop(source_pattern->base.stops[source_pattern->base.n_stops - 1].color),
+@@ -1564,35 +1565,46 @@ static RefPtr<ID2D1Brush>
+ p1.x = _cairo_fixed_to_float(source_pattern->p1.x);
+ p1.y = _cairo_fixed_to_float(source_pattern->p1.y);
+ p2.x = _cairo_fixed_to_float(source_pattern->p2.x);
+ p2.y = _cairo_fixed_to_float(source_pattern->p2.y);
+
+ D2D1_GRADIENT_STOP *stops;
+ int num_stops = source_pattern->base.n_stops;
+ if (source_pattern->base.base.extend == CAIRO_EXTEND_REPEAT || source_pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+-
+- RefPtr<IDXGISurface> surf;
+- d2dsurf->surface->QueryInterface(&surf);
+- DXGI_SURFACE_DESC desc;
+- surf->GetDesc(&desc);
+-
+ // Get this when the points are not transformed yet.
+ double gradient_length = _cairo_d2d_point_dist(p1, p2);
+-
+- // Calculate the repeat count needed;
+- cairo_point_double_t top_left, top_right, bottom_left, bottom_right;
+- top_left.x = bottom_left.x = top_left.y = top_right.y = 0;
+- top_right.x = bottom_right.x = desc.Width;
+- bottom_right.y = bottom_left.y = desc.Height;
++ cairo_point_double_t top_left, top_right, bottom_left, bottom_right;
++
++ if (fill_path) {
++ // Calculate the repeat count needed;
++ cairo_box_t fill_extents;
++ _cairo_path_fixed_extents (fill_path, &fill_extents);
++
++ top_left.x = bottom_left.x = _cairo_fixed_to_double (fill_extents.p1.x);
++ top_left.y = top_right.y = _cairo_fixed_to_double (fill_extents.p1.y);
++ top_right.x = bottom_right.x = _cairo_fixed_to_double (fill_extents.p2.x);
++ bottom_right.y = bottom_left.y = _cairo_fixed_to_double (fill_extents.p2.y);
++ } else {
++ RefPtr<IDXGISurface> surf;
++ d2dsurf->surface->QueryInterface(&surf);
++ DXGI_SURFACE_DESC desc;
++ surf->GetDesc(&desc);
++
++ top_left.x = bottom_left.x = 0;
++ top_left.y = top_right.y = 0;
++ top_right.x = bottom_right.x = desc.Width;
++ bottom_right.y = bottom_left.y = desc.Height;
++ }
++
+ // Transform the corners of our surface to pattern space.
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_left.x, &top_left.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &top_right.x, &top_right.y);
+ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_left.x, &bottom_left.y);
+- cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &top_left.y);
++ cairo_matrix_transform_point(&source_pattern->base.base.matrix, &bottom_right.x, &bottom_right.y);
+
+ cairo_point_double_t u;
+ // Unit vector of the gradient direction.
+ u = _cairo_d2d_subtract_point(p2, p1);
+ _cairo_d2d_normalize_point(&u);
+
+ // (corner - p1) . u = |corner - p1| cos(a) where a is the angle between the two vectors.
+ // Coincidentally |corner - p1| cos(a) is actually also the distance our gradient needs to cover since
+@@ -1701,17 +1713,18 @@ static RefPtr<ID2D1Brush>
+ * \param d2dsurf Surface to create a brush for
+ * \param pattern The pattern to create a brush for
+ * \param unique We cache the bitmap/color brush for speed. If this
+ * needs a brush that is unique (i.e. when more than one is needed),
+ * this will make the function return a seperate brush.
+ * \return A brush object
+ */
+ static RefPtr<ID2D1Brush>
+-_cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
++_cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
++ cairo_path_fixed_t *fill_path,
+ const cairo_pattern_t *pattern,
+ bool unique = false)
+ {
+ HRESULT hr;
+
+ if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *sourcePattern =
+ (cairo_solid_pattern_t*)pattern;
+@@ -1729,17 +1742,17 @@ static RefPtr<ID2D1Brush>
+ d2dsurf->solidColorBrush->SetColor(color);
+ }
+ return d2dsurf->solidColorBrush;
+ }
+
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *source_pattern =
+ (cairo_linear_pattern_t*)pattern;
+- return _cairo_d2d_create_linear_gradient_brush(d2dsurf, source_pattern);
++ return _cairo_d2d_create_linear_gradient_brush(d2dsurf, fill_path, source_pattern);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ cairo_radial_pattern_t *source_pattern =
+ (cairo_radial_pattern_t*)pattern;
+ return _cairo_d2d_create_radial_gradient_brush(d2dsurf, source_pattern);
+ } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ cairo_matrix_t mat = pattern->matrix;
+ cairo_matrix_invert(&mat);
+
+@@ -3228,17 +3241,17 @@ static cairo_int_status_t
+
+ if (unlikely(status))
+ return status;
+ }
+ #endif
+
+ target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
+
+- RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf,
++ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL,
+ source);
+
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ D2D1_SIZE_F size = target_rt->GetSize();
+ target_rt->FillRectangle(D2D1::RectF((FLOAT)0,
+@@ -3349,17 +3362,17 @@ static cairo_int_status_t
+ source->filter,
+ solidAlphaValue);
+ if (rv != CAIRO_INT_STATUS_UNSUPPORTED) {
+ return rv;
+ }
+ }
+ }
+
+- RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source);
++ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, source);
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ RefPtr<ID2D1RenderTarget> target_rt = d2dsurf->rt;
+ #ifndef ALWAYS_MANUAL_COMPOSITE
+ if (op != CAIRO_OPERATOR_OVER) {
+ #endif
+@@ -3389,17 +3402,17 @@ static cairo_int_status_t
+ brush->SetOpacity(1.0);
+
+ if (target_rt.get() != d2dsurf->rt.get()) {
+ return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip);
+ }
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+- RefPtr<ID2D1Brush> opacityBrush = _cairo_d2d_create_brush_for_pattern(d2dsurf, mask, true);
++ RefPtr<ID2D1Brush> opacityBrush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL, mask, true);
+ if (!opacityBrush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (!d2dsurf->maskLayer) {
+ d2dsurf->rt->CreateLayer(&d2dsurf->maskLayer);
+ }
+ target_rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(),
+@@ -3475,17 +3488,17 @@ static cairo_int_status_t
+ D2D1_FIGURE_BEGIN_FILLED);
+
+ bool transformed = true;
+
+ if (_cairo_matrix_is_identity(ctm)) {
+ transformed = false;
+ }
+
+- RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf,
++ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, NULL,
+ source);
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ D2D1::Matrix3x2F mat;
+ if (transformed) {
+ // If we are transformed we will draw the geometry multiplied by the
+@@ -3602,31 +3615,31 @@ static cairo_int_status_t
+ }
+
+ if (is_box) {
+ float x1 = _cairo_fixed_to_float(box.p1.x);
+ float y1 = _cairo_fixed_to_float(box.p1.y);
+ float x2 = _cairo_fixed_to_float(box.p2.x);
+ float y2 = _cairo_fixed_to_float(box.p2.y);
+ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf,
+- source);
++ path, source);
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ target_rt->FillRectangle(D2D1::RectF(x1,
+ y1,
+ x2,
+ y2),
+ brush);
+ } else {
+ RefPtr<ID2D1Geometry> d2dpath = _cairo_d2d_create_path_geometry_for_path(path, fill_rule, D2D1_FIGURE_BEGIN_FILLED);
+
+ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(d2dsurf,
+- source);
++ path, source);
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ target_rt->FillGeometry(d2dpath, brush);
+ }
+
+ if (target_rt.get() != d2dsurf->rt.get()) {
+ double x1, y1, x2, y2;
+@@ -4138,17 +4151,17 @@ static cairo_int_status_t
+ DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1,
+ &bounds);
+ fontArea.x = bounds.left;
+ fontArea.y = bounds.top;
+ fontArea.width = bounds.right - bounds.left;
+ fontArea.height = bounds.bottom - bounds.top;
+ }
+
+- RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(dst,
++ RefPtr<ID2D1Brush> brush = _cairo_d2d_create_brush_for_pattern(dst, NULL,
+ source);
+
+ if (!brush) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (transform) {
+ D2D1::Matrix3x2F mat_inverse = _cairo_d2d_matrix_from_matrix(&dwritesf->mat_inverse);
diff --git a/gfx/cairo/d2d.patch b/gfx/cairo/d2d.patch
new file mode 100644
index 000000000..b8dd155e7
--- /dev/null
+++ b/gfx/cairo/d2d.patch
@@ -0,0 +1,465 @@
+commit 4a412c0b144ed1fdde668e0e91241bac8bedd579
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Sun Jan 24 14:04:33 2010 -0500
+
+ d2d
+
+diff --git a/src/cairo-fixed-private.h b/src/cairo-fixed-private.h
+index c299def..a37ca6a 100644
+--- a/src/cairo-fixed-private.h
++++ b/src/cairo-fixed-private.h
+@@ -50,6 +50,7 @@
+
+ #define CAIRO_FIXED_ONE ((cairo_fixed_t)(1 << CAIRO_FIXED_FRAC_BITS))
+ #define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS))
++#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS))
+ #define CAIRO_FIXED_EPSILON ((cairo_fixed_t)(1))
+
+ #define CAIRO_FIXED_FRAC_MASK (((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS))
+@@ -141,6 +142,12 @@ _cairo_fixed_to_double (cairo_fixed_t f)
+ return ((double) f) / CAIRO_FIXED_ONE_DOUBLE;
+ }
+
++static inline float
++_cairo_fixed_to_float (cairo_fixed_t f)
++{
++ return ((float) f) / CAIRO_FIXED_ONE_FLOAT;
++}
++
+ static inline int
+ _cairo_fixed_is_integer (cairo_fixed_t f)
+ {
+diff --git a/src/cairo-win32-private.h b/src/cairo-win32-private.h
+index b9926bb..ba57595 100644
+--- a/src/cairo-win32-private.h
++++ b/src/cairo-win32-private.h
+@@ -231,4 +231,19 @@ inline BOOL ModifyWorldTransform(HDC hdc, CONST XFORM * lpxf, DWORD mode) { retu
+
+ #endif
+
++#ifdef CAIRO_HAS_DWRITE_FONT
++CAIRO_BEGIN_DECLS
++
++cairo_public 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_rectangle_int_t *extents);
++
++
++CAIRO_END_DECLS
++#endif /* CAIRO_HAS_DWRITE_FONT */
+ #endif /* CAIRO_WIN32_PRIVATE_H */
+diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
+index 0dc5e76..bee00b1 100644
+--- a/src/cairo-win32-surface.c
++++ b/src/cairo-win32-surface.c
+@@ -1547,152 +1547,158 @@ _cairo_win32_surface_show_glyphs (void *surface,
+ int *remaining_glyphs)
+ {
+ #if defined(CAIRO_HAS_WIN32_FONT) && !defined(WINCE)
+- cairo_win32_surface_t *dst = surface;
+-
+- WORD glyph_buf_stack[STACK_GLYPH_SIZE];
+- WORD *glyph_buf = glyph_buf_stack;
+- int dxy_buf_stack[2 * STACK_GLYPH_SIZE];
+- int *dxy_buf = dxy_buf_stack;
+-
+- BOOL win_result = 0;
+- int i, j;
++ if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) {
++#ifdef CAIRO_HAS_DWRITE_FONT
++ return cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip);
++#endif
++ } else {
++ cairo_win32_surface_t *dst = surface;
++
++ WORD glyph_buf_stack[STACK_GLYPH_SIZE];
++ WORD *glyph_buf = glyph_buf_stack;
++ int dxy_buf_stack[2 * STACK_GLYPH_SIZE];
++ int *dxy_buf = dxy_buf_stack;
+
+- cairo_solid_pattern_t *solid_pattern;
+- COLORREF color;
++ BOOL win_result = 0;
++ int i, j;
+
+- cairo_matrix_t device_to_logical;
++ cairo_solid_pattern_t *solid_pattern;
++ COLORREF color;
+
+- int start_x, start_y;
+- double user_x, user_y;
+- int logical_x, logical_y;
+- unsigned int glyph_index_option;
++ cairo_matrix_t device_to_logical;
+
+- /* We can only handle win32 fonts */
+- if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32)
+- return CAIRO_INT_STATUS_UNSUPPORTED;
++ int start_x, start_y;
++ double user_x, user_y;
++ int logical_x, logical_y;
++ unsigned int glyph_index_option;
+
+- /* We can only handle opaque solid color sources */
+- if (!_cairo_pattern_is_opaque_solid(source))
+- return CAIRO_INT_STATUS_UNSUPPORTED;
++ /* We can only handle win32 fonts */
++ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32)
++ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+- /* We can only handle operator SOURCE or OVER with the destination
+- * having no alpha */
+- if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) ||
+- (dst->format != CAIRO_FORMAT_RGB24))
+- return CAIRO_INT_STATUS_UNSUPPORTED;
++ /* We can only handle opaque solid color sources */
++ if (!_cairo_pattern_is_opaque_solid(source))
++ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+- /* If we have a fallback mask clip set on the dst, we have
+- * to go through the fallback path, but only if we're not
+- * doing this for printing */
+- if (clip != NULL) {
+- if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) {
+- cairo_region_t *clip_region;
+- cairo_status_t status;
++ /* We can only handle operator SOURCE or OVER with the destination
++ * having no alpha */
++ if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) ||
++ (dst->format != CAIRO_FORMAT_RGB24))
++ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+- status = _cairo_clip_get_region (clip, &clip_region);
+- assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+- if (status)
+- return status;
++ /* If we have a fallback mask clip set on the dst, we have
++ * to go through the fallback path, but only if we're not
++ * doing this for printing */
++ if (clip != NULL) {
++ if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) {
++ cairo_region_t *clip_region;
++ cairo_status_t status;
++
++ status = _cairo_clip_get_region (clip, &clip_region);
++ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
++ if (status)
++ return status;
+
+- _cairo_win32_surface_set_clip_region (surface, clip_region);
++ _cairo_win32_surface_set_clip_region (surface, clip_region);
++ }
+ }
+- }
+
+- 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);
++ 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);
++ cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical);
+
+- SaveDC(dst->dc);
++ 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);
++ 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);
+- }
++ 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. */
++ /* 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;
++ user_x = glyphs[0].x;
++ user_y = glyphs[0].y;
+
+- cairo_matrix_transform_point(&device_to_logical,
+- &user_x, &user_y);
++ cairo_matrix_transform_point(&device_to_logical,
++ &user_x, &user_y);
+
+- logical_x = _cairo_lround (user_x);
+- logical_y = _cairo_lround (user_y);
++ logical_x = _cairo_lround (user_x);
++ logical_y = _cairo_lround (user_y);
+
+- start_x = logical_x;
+- start_y = logical_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;
++ 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);
++ 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);
++ next_logical_x = _cairo_lround (next_user_x);
++ next_logical_y = _cairo_lround (next_user_y);
+
+- dxy_buf[j] = _cairo_lround (next_logical_x - logical_x);
+- dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y);
+- /* note that GDI coordinate system is inverted */
++ dxy_buf[j] = _cairo_lround (next_logical_x - logical_x);
++ dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y);
++ /* note that GDI coordinate system is inverted */
+
+- logical_x = next_logical_x;
+- logical_y = next_logical_y;
+- }
+- }
++ logical_x = next_logical_x;
++ logical_y = next_logical_y;
++ }
++ }
+
+- /* Using glyph indices for a Type 1 font does not work on a
+- * printer DC. The win32 printing surface will convert the the
+- * glyph indices of Type 1 fonts to the unicode values.
+- */
+- if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) &&
+- _cairo_win32_scaled_font_is_type1 (scaled_font))
+- {
+- glyph_index_option = 0;
+- }
+- else
+- {
+- glyph_index_option = ETO_GLYPH_INDEX;
+- }
++ /* Using glyph indices for a Type 1 font does not work on a
++ * printer DC. The win32 printing surface will convert the the
++ * glyph indices of Type 1 fonts to the unicode values.
++ */
++ if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) &&
++ _cairo_win32_scaled_font_is_type1 (scaled_font))
++ {
++ glyph_index_option = 0;
++ }
++ else
++ {
++ glyph_index_option = ETO_GLYPH_INDEX;
++ }
+
+- 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)");
+- }
++ 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);
++ RestoreDC(dst->dc, -1);
+
+- if (glyph_buf != glyph_buf_stack) {
+- free(glyph_buf);
+- free(dxy_buf);
++ if (glyph_buf != glyph_buf_stack) {
++ free(glyph_buf);
++ free(dxy_buf);
++ }
++ return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+- return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED;
+ #else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ #endif
+diff --git a/src/cairo-win32.h b/src/cairo-win32.h
+index 6b86d4e..fcf20b8 100644
+--- a/src/cairo-win32.h
++++ b/src/cairo-win32.h
+@@ -109,6 +109,63 @@ cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font,
+
+ #endif /* CAIRO_HAS_WIN32_FONT */
+
++#if CAIRO_HAS_DWRITE_FONT
++
++/*
++ * Win32 DirectWrite font support
++ */
++cairo_public cairo_font_face_t *
++cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrite_font_face);
++
++#endif /* CAIRO_HAS_DWRITE_FONT */
++
++#if CAIRO_HAS_D2D_SURFACE
++
++/**
++ * Create a D2D surface for an HWND
++ *
++ * \param wnd Handle for the window
++ * \return New cairo surface
++ */
++cairo_public cairo_surface_t *
++cairo_d2d_surface_create_for_hwnd(HWND wnd);
++
++/**
++ * Create a D2D surface of a certain size.
++ *
++ * \param format Cairo format of the surface
++ * \param width Width of the surface
++ * \param height Height of the surface
++ * \return New cairo surface
++ */
++cairo_public cairo_surface_t *
++cairo_d2d_surface_create(cairo_format_t format,
++ int width,
++ int height);
++
++/**
++ * Present the backbuffer for a surface create for an HWND. This needs
++ * to be called when the owner of the original window surface wants to
++ * actually present the executed drawing operations to the screen.
++ *
++ * \param surface D2D surface.
++ */
++void cairo_d2d_present_backbuffer(cairo_surface_t *surface);
++
++/**
++ * Scroll the surface, this only moves the surface graphics, it does not
++ * actually scroll child windows or anything like that. Nor does it invalidate
++ * that area of the window.
++ *
++ * \param surface The d2d surface this operation should apply to.
++ * \param x The x delta for the movement
++ * \param y The y delta for the movement
++ * \param clip The clip rectangle, the is the 'part' of the surface that needs
++ * scrolling.
++ */
++void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip);
++#endif
++
+ CAIRO_END_DECLS
+
+ #else /* CAIRO_HAS_WIN32_SURFACE */
+diff --git a/src/cairo.h b/src/cairo.h
+index 3a8b8a6..21827aa 100644
+--- a/src/cairo.h
++++ b/src/cairo.h
+@@ -1370,7 +1370,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
+@@ -2009,7 +2010,7 @@ typedef enum _cairo_surface_type {
+ CAIRO_SURFACE_TYPE_TEE,
+ CAIRO_SURFACE_TYPE_XML,
+ CAIRO_SURFACE_TYPE_SKIA,
+- CAIRO_SURFACE_TYPE_DDRAW
++ CAIRO_SURFACE_TYPE_D2D
+ } cairo_surface_type_t;
+
+ cairo_public cairo_surface_type_t
+diff --git a/src/cairoint.h b/src/cairoint.h
+index b942b4b..58850ab 100644
+--- a/src/cairoint.h
++++ b/src/cairoint.h
+@@ -587,6 +587,12 @@ extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_fac
+
+ #endif
+
++#if CAIRO_HAS_DWRITE_FONT
++
++extern const cairo_private struct _cairo_font_face_backend _cairo_dwrite_font_face_backend;
++
++#endif
++
+ #if CAIRO_HAS_QUARTZ_FONT
+
+ extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend;
+@@ -932,7 +938,12 @@ typedef struct _cairo_traps {
+ #define CAIRO_FT_FONT_FAMILY_DEFAULT ""
+ #define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:"
+
+-#if CAIRO_HAS_WIN32_FONT
++#if CAIRO_HAS_DWRITE_FONT
++
++#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT
++#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_dwrite_font_face_backend
++
++#elif CAIRO_HAS_WIN32_FONT
+
+ #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT
+ #define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend
+@@ -2617,7 +2628,7 @@ cairo_private int
+ _cairo_ucs4_to_utf8 (uint32_t unicode,
+ char *utf8);
+
+-#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS
++#if CAIRO_HAS_WIN32_FONT || CAIRO_HAS_QUARTZ_FONT || CAIRO_HAS_PDF_OPERATORS || CAIRO_HAS_DW_FONT
+ # define CAIRO_HAS_UTF8_TO_UTF16 1
+ #endif
+ #if CAIRO_HAS_UTF8_TO_UTF16
diff --git a/gfx/cairo/dasharray-zero-gap.patch b/gfx/cairo/dasharray-zero-gap.patch
new file mode 100644
index 000000000..e26580ae1
--- /dev/null
+++ b/gfx/cairo/dasharray-zero-gap.patch
@@ -0,0 +1,60 @@
+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
+@@ -2573,29 +2573,43 @@ static cairo_int_status_t
+
+ if (style->dash && style->num_dashes) {
+ #define STATIC_DASH 32
+ cairo_quartz_float_t sdash[STATIC_DASH];
+ 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 > STATIC_DASH)
+- fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
+- if (fdash == NULL)
+- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+-
+- for (k = 0; k < max_dashes; k++)
+- fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
+-
+- CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
+- if (fdash != sdash)
+- free (fdash);
++ bool set_line_dash = false;
++ if (style->num_dashes % 2 == 0) {
++ for (k = 1; k < max_dashes; k++) {
++ if (style->dash[k]) {
++ set_line_dash = true;
++ break;
++ }
++ }
++ } else
++ set_line_dash = true;
++
++ if (set_line_dash) {
++ if (style->num_dashes%2)
++ max_dashes *= 2;
++ if (max_dashes > STATIC_DASH)
++ fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
++ if (fdash == NULL)
++ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
++
++ for (k = 0; k < max_dashes; k++)
++ fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
++
++ CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
++ if (fdash != sdash)
++ free (fdash);
++ } else
++ CGContextSetLineDash (state.context, 0, NULL, 0);
+ } else
+ CGContextSetLineDash (state.context, 0, NULL, 0);
+
+
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
+
+ _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+ CGContextConcatCTM (state.context, strokeTransform);
diff --git a/gfx/cairo/disable-previous-scaled-font-cache.patch b/gfx/cairo/disable-previous-scaled-font-cache.patch
new file mode 100644
index 000000000..afeac5ec8
--- /dev/null
+++ b/gfx/cairo/disable-previous-scaled-font-cache.patch
@@ -0,0 +1,16 @@
+diff --git a/gfx/cairo/cairo/src/cairo.c b/gfx/cairo/cairo/src/cairo.c
+--- a/gfx/cairo/cairo/src/cairo.c
++++ b/gfx/cairo/cairo/src/cairo.c
+@@ -3201,8 +3201,12 @@ cairo_set_scaled_font (cairo_t
+
+ _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options);
+
++ /* XXX: Mozilla code assumes that the ctm of a scaled font doesn't need to
++ * match the context ctm. This assumption breaks the previous_scaled_font
++ * cache. So we avoid using the cache for now.
+ if (was_previous)
+ cr->gstate->scaled_font = cairo_scaled_font_reference ((cairo_scaled_font_t *) scaled_font);
++ */
+
+ return;
+
diff --git a/gfx/cairo/disable-printing.patch b/gfx/cairo/disable-printing.patch
new file mode 100644
index 000000000..c1006d819
--- /dev/null
+++ b/gfx/cairo/disable-printing.patch
@@ -0,0 +1,27 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -1892,21 +1892,22 @@ cairo_win32_surface_get_dc (cairo_surfac
+ return winsurf->dc;
+ }
+
+ if (_cairo_surface_is_paginated (surface)) {
+ cairo_surface_t *target;
+
+ target = _cairo_paginated_surface_get_target (surface);
+
++#ifndef CAIRO_OMIT_WIN32_PRINTING
+ if (_cairo_surface_is_win32_printing (target)) {
+ winsurf = (cairo_win32_surface_t *) target;
+-
+ return winsurf->dc;
+ }
++#endif
+ }
+
+ return NULL;
+ }
+
+ /**
+ * cairo_win32_surface_get_image
+ * @surface: a #cairo_surface_t
diff --git a/gfx/cairo/disable-server-gradients.patch b/gfx/cairo/disable-server-gradients.patch
new file mode 100644
index 000000000..2d8fb5b6d
--- /dev/null
+++ b/gfx/cairo/disable-server-gradients.patch
@@ -0,0 +1,21 @@
+commit 31579379422b75c3fe01b75d363e03f9b1e85604
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Fri Mar 12 16:07:59 2010 -0500
+
+ xlib bugs
+
+diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
+index a7a40b8..566d9fb 100644
+--- a/src/cairo-xlib-display.c
++++ b/src/cairo-xlib-display.c
+@@ -407,6 +407,10 @@ _cairo_xlib_display_get (Display *dpy,
+ display->buggy_pad_reflect = TRUE;
+ }
+
++ /* gradients don't seem to work */
++ display->buggy_gradients = TRUE;
++
++
+ /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */
+ /* If buggy_repeat_force == -1, then initialize.
+ * - set to -2, meaning "nothing was specified", and we trust the above detection.
diff --git a/gfx/cairo/disable-subpixel-antialiasing.patch b/gfx/cairo/disable-subpixel-antialiasing.patch
new file mode 100644
index 000000000..7a0eb3a06
--- /dev/null
+++ b/gfx/cairo/disable-subpixel-antialiasing.patch
@@ -0,0 +1,519 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1294019288 -46800
+# Node ID 8857392e37aea7475ed6d8ee4b45023e1233bcec
+# Parent c53f60831c43cca397dfed8adf8d350aeec7d3ca
+Bug 363861. Part 2: Introduce cairo_surface_get/set_subpixel_antialiasing. r=jrmuizel,sr=vlad,a=blocking
+
+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
+@@ -2473,16 +2473,17 @@ _cairo_quartz_surface_show_glyphs (void
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+ cairo_quartz_drawing_state_t state;
+ float xprev, yprev;
+ int i;
+ CGFontRef cgfref = NULL;
+
+ cairo_bool_t isClipping = FALSE;
+ cairo_bool_t didForceFontSmoothing = FALSE;
++ cairo_antialias_t effective_antialiasing;
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (num_glyphs <= 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
+@@ -2514,16 +2515,22 @@ _cairo_quartz_surface_show_glyphs (void
+ goto BAIL;
+ }
+
+ /* this doesn't addref */
+ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
+ CGContextSetFont (state.context, cgfref);
+ CGContextSetFontSize (state.context, 1.0);
+
++ effective_antialiasing = scaled_font->options.antialias;
++ if (effective_antialiasing == CAIRO_ANTIALIAS_SUBPIXEL &&
++ !surface->base.permit_subpixel_antialiasing) {
++ effective_antialiasing = CAIRO_ANTIALIAS_GRAY;
++ }
++
+ switch (scaled_font->options.antialias) {
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+ CGContextSetShouldAntialias (state.context, TRUE);
+ CGContextSetShouldSmoothFonts (state.context, TRUE);
+ if (CGContextSetAllowsFontSmoothingPtr &&
+ !CGContextGetAllowsFontSmoothingPtr (state.context))
+ {
+ didForceFontSmoothing = 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
+@@ -58,16 +58,17 @@ struct _cairo_surface {
+
+ cairo_reference_count_t ref_count;
+ cairo_status_t status;
+ unsigned int unique_id;
+
+ unsigned finished : 1;
+ unsigned is_clear : 1;
+ unsigned has_font_options : 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;
+
+ /* The actual resolution of the device, in dots per inch. */
+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
+@@ -49,17 +49,18 @@ const cairo_surface_t name = { \
+ NULL, /* backend */ \
+ CAIRO_SURFACE_TYPE_IMAGE, /* type */ \
+ CAIRO_CONTENT_COLOR, /* content */ \
+ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \
+ status, /* status */ \
+ 0, /* unique id */ \
+ FALSE, /* finished */ \
+ TRUE, /* is_clear */ \
+- FALSE, /* has_font_options */ \
++ FALSE, /* has_font_options */ \
++ 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 */ \
+ 0.0, /* x_resolution */ \
+ 0.0, /* y_resolution */ \
+ 0.0, /* x_fallback_resolution */ \
+ 0.0, /* y_fallback_resolution */ \
+@@ -342,46 +343,48 @@ _cairo_surface_init (cairo_surface_t *
+ surface->content = content;
+ surface->type = backend->type;
+
+ 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->is_clear = FALSE;
++ surface->has_font_options = FALSE;
++ surface->permit_subpixel_antialiasing = TRUE;
+
+ _cairo_user_data_array_init (&surface->user_data);
+ _cairo_user_data_array_init (&surface->mime_data);
+
+ cairo_matrix_init_identity (&surface->device_transform);
+ cairo_matrix_init_identity (&surface->device_transform_inverse);
+
+ 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_array_init (&surface->snapshots, sizeof (cairo_surface_t *));
+ 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_t *
+ _cairo_surface_create_similar_scratch (cairo_surface_t *other,
+ cairo_content_t content,
+@@ -2482,16 +2485,67 @@ cairo_surface_has_show_text_glyphs (cair
+
+ 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);
+
++/**
++ * cairo_surface_set_subpixel_antialiasing:
++ * @surface: a #cairo_surface_t
++ *
++ * Sets whether the surface permits subpixel antialiasing. By default,
++ * surfaces permit subpixel antialiasing.
++ *
++ * Enabling subpixel antialiasing for CONTENT_COLOR_ALPHA surfaces generally
++ * requires that the pixels in the areas under a subpixel antialiasing
++ * operation already be opaque.
++ *
++ * Since: 1.12
++ **/
++void
++cairo_surface_set_subpixel_antialiasing (cairo_surface_t *surface,
++ cairo_subpixel_antialiasing_t enabled)
++{
++ if (surface->status)
++ return;
++
++ if (surface->finished) {
++ _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
++ return;
++ }
++
++ surface->permit_subpixel_antialiasing =
++ enabled == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
++}
++slim_hidden_def (cairo_surface_set_subpixel_antialiasing);
++
++/**
++ * cairo_surface_get_subpixel_antialiasing:
++ * @surface: a #cairo_surface_t
++ *
++ * Gets whether the surface supports subpixel antialiasing. By default,
++ * CAIRO_CONTENT_COLOR surfaces support subpixel antialiasing but other
++ * surfaces do not.
++ *
++ * Since: 1.12
++ **/
++cairo_subpixel_antialiasing_t
++cairo_surface_get_subpixel_antialiasing (cairo_surface_t *surface)
++{
++ if (surface->status)
++ return CAIRO_SUBPIXEL_ANTIALIASING_DISABLED;
++
++ return surface->permit_subpixel_antialiasing ?
++ CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED;
++}
++slim_hidden_def (cairo_surface_get_subpixel_antialiasing);
++
+ /* Note: the backends may modify the contents of the glyph array as long as
+ * they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to
+ * 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
+diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c
+--- a/gfx/cairo/cairo/src/cairo-win32-font.c
++++ b/gfx/cairo/cairo/src/cairo-win32-font.c
+@@ -1380,16 +1380,17 @@ _cairo_win32_scaled_font_show_glyphs (vo
+ cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface;
+ cairo_status_t status;
+
+ if (width == 0 || height == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (_cairo_surface_is_win32 (generic_surface) &&
+ surface->format == CAIRO_FORMAT_RGB24 &&
++ (generic_surface->permit_subpixel_antialiasing || scaled_font->quality != CLEARTYPE_QUALITY) &&
+ op == CAIRO_OPERATOR_OVER &&
+ _cairo_pattern_is_opaque_solid (pattern)) {
+
+ cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern;
+
+ /* When compositing OVER on a GDI-understood surface, with a
+ * solid opaque color, we can just call ExtTextOut directly.
+ */
+@@ -1411,16 +1412,18 @@ _cairo_win32_scaled_font_show_glyphs (vo
+ * surface by drawing the the glyphs onto a DIB, black-on-white then
+ * inverting. GDI outputs gamma-corrected images so inverted black-on-white
+ * is very different from white-on-black. We favor the more common
+ * case where the final output is dark-on-light.
+ */
+ cairo_win32_surface_t *tmp_surface;
+ cairo_surface_t *mask_surface;
+ cairo_surface_pattern_t mask;
++ cairo_bool_t use_subpixel_antialiasing =
++ scaled_font->quality == CLEARTYPE_QUALITY && generic_surface->permit_subpixel_antialiasing;
+ RECT r;
+
+ tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height);
+ if (tmp_surface->base.status)
+ return tmp_surface->base.status;
+
+ r.left = 0;
+ r.top = 0;
+@@ -1432,17 +1435,17 @@ _cairo_win32_scaled_font_show_glyphs (vo
+ scaled_font, RGB (0, 0, 0),
+ dest_x, dest_y,
+ glyphs, num_glyphs);
+ if (status) {
+ cairo_surface_destroy (&tmp_surface->base);
+ return status;
+ }
+
+- if (scaled_font->quality == CLEARTYPE_QUALITY) {
++ if (use_subpixel_antialiasing) {
+ /* For ClearType, we need a 4-channel mask. If we are compositing on
+ * a surface with alpha, we need to compute the alpha channel of
+ * the mask (we just copy the green channel). But for a destination
+ * surface without alpha the alpha channel of the mask is ignored
+ */
+
+ if (surface->format != CAIRO_FORMAT_RGB24)
+ _compute_argb32_mask_alpha (tmp_surface);
+@@ -1460,17 +1463,17 @@ _cairo_win32_scaled_font_show_glyphs (vo
+
+ /* For op == OVER, no-cleartype, a possible optimization here is to
+ * draw onto an intermediate ARGB32 surface and alpha-blend that with the
+ * destination
+ */
+ _cairo_pattern_init_for_surface (&mask, mask_surface);
+ cairo_surface_destroy (mask_surface);
+
+- if (scaled_font->quality == CLEARTYPE_QUALITY)
++ if (use_subpixel_antialiasing)
+ mask.base.has_component_alpha = TRUE;
+
+ status = _cairo_surface_composite (op, pattern,
+ &mask.base,
+ &surface->base,
+ source_x, source_y,
+ 0, 0,
+ dest_x, dest_y,
+diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+--- a/gfx/cairo/cairo/src/cairo-xlib-surface.c
++++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+@@ -3570,16 +3570,17 @@ typedef struct _cairo_xlib_font_glyphset
+ GlyphSet glyphset;
+ cairo_format_t format;
+ XRenderPictFormat *xrender_format;
+ cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs;
+ } cairo_xlib_font_glyphset_info_t;
+
+ typedef struct _cairo_xlib_surface_font_private {
+ cairo_scaled_font_t *scaled_font;
++ cairo_scaled_font_t *grayscale_font;
+ cairo_xlib_hook_t close_display_hook;
+ cairo_xlib_display_t *display;
+ cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS];
+ } cairo_xlib_surface_font_private_t;
+
+ /* callback from CloseDisplay */
+ static void
+ _cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display,
+@@ -3599,16 +3600,20 @@ _cairo_xlib_surface_remove_scaled_font (
+
+ _cairo_scaled_font_reset_cache (scaled_font);
+ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
+
+ if (font_private != NULL) {
+ Display *dpy;
+ int i;
+
++ if (font_private->grayscale_font) {
++ cairo_scaled_font_destroy (font_private->grayscale_font);
++ }
++
+ dpy = _cairo_xlib_display_get_dpy (display);
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xlib_font_glyphset_info_t *glyphset_info;
+
+ glyphset_info = &font_private->glyphset_info[i];
+ if (glyphset_info->glyphset)
+ XRenderFreeGlyphSet (dpy, glyphset_info->glyphset);
+
+@@ -3629,16 +3634,17 @@ _cairo_xlib_surface_font_init (Display
+ cairo_status_t status;
+ int i;
+
+ font_private = malloc (sizeof (cairo_xlib_surface_font_private_t));
+ if (unlikely (font_private == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ font_private->scaled_font = scaled_font;
++ font_private->grayscale_font = NULL;
+ status = _cairo_xlib_display_get (dpy, &font_private->display);
+ if (unlikely (status)) {
+ free (font_private);
+ return status;
+ }
+
+ /* initialize and hook into the CloseDisplay callback */
+ font_private->close_display_hook.func =
+@@ -3671,16 +3677,20 @@ _cairo_xlib_surface_scaled_font_fini (ca
+ {
+ cairo_xlib_surface_font_private_t *font_private;
+
+ font_private = scaled_font->surface_private;
+ if (font_private != NULL) {
+ cairo_xlib_display_t *display;
+ int i;
+
++ if (font_private->grayscale_font) {
++ cairo_scaled_font_destroy (font_private->grayscale_font);
++ }
++
+ display = font_private->display;
+ _cairo_xlib_remove_close_display_hook (display,
+ &font_private->close_display_hook);
+
+ for (i = 0; i < NUM_GLYPHSETS; i++) {
+ cairo_xlib_font_glyphset_info_t *glyphset_info;
+
+ glyphset_info = &font_private->glyphset_info[i];
+@@ -4417,16 +4427,62 @@ _cairo_xlib_surface_owns_font (cairo_xli
+ (font_private != NULL && font_private->display != dst->display))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
++/* Gets a grayscale version of scaled_font. The grayscale version is cached
++ * in our surface_private data.
++ */
++static cairo_scaled_font_t *
++_cairo_xlib_get_grayscale_font (cairo_xlib_surface_t *dst,
++ cairo_scaled_font_t *scaled_font)
++{
++ cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private;
++ cairo_bool_t needs_font;
++
++ if (font_private == NULL) {
++ cairo_status_t status = _cairo_xlib_surface_font_init (dst->dpy, scaled_font);
++ if (unlikely (status))
++ return _cairo_scaled_font_create_in_error (status);
++ font_private = scaled_font->surface_private;
++ }
++
++ CAIRO_MUTEX_LOCK (scaled_font->mutex);
++ needs_font = !font_private->grayscale_font;
++ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
++
++ if (needs_font) {
++ cairo_font_options_t options;
++ cairo_scaled_font_t *new_font;
++
++ options = scaled_font->options;
++ options.antialias = CAIRO_ANTIALIAS_GRAY;
++ new_font = cairo_scaled_font_create (scaled_font->font_face,
++ &scaled_font->font_matrix,
++ &scaled_font->ctm, &options);
++
++ CAIRO_MUTEX_LOCK (scaled_font->mutex);
++ if (!font_private->grayscale_font) {
++ font_private->grayscale_font = new_font;
++ new_font = NULL;
++ }
++ CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
++
++ if (new_font) {
++ cairo_scaled_font_destroy (new_font);
++ }
++ }
++
++ return font_private->grayscale_font;
++}
++
+ static cairo_int_status_t
+ _cairo_xlib_surface_show_glyphs (void *abstract_dst,
+ cairo_operator_t op,
+ const cairo_pattern_t *src_pattern,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+@@ -4475,16 +4531,21 @@ _cairo_xlib_surface_show_glyphs (void
+
+ operation = _categorize_composite_operation (dst, op, src_pattern, TRUE);
+ if (operation == DO_UNSUPPORTED)
+ return UNSUPPORTED ("unsupported op");
+
+ if (! _cairo_xlib_surface_owns_font (dst, scaled_font))
+ return UNSUPPORTED ("unowned font");
+
++ if (!dst->base.permit_subpixel_antialiasing &&
++ scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
++ scaled_font = _cairo_xlib_get_grayscale_font (dst, scaled_font);
++ }
++
+ X_DEBUG ((dst->dpy, "show_glyphs (dst=%x)", (unsigned int) dst->drawable));
+
+ if (clip_region != NULL &&
+ cairo_region_num_rectangles (clip_region) == 1)
+ {
+ cairo_rectangle_int_t glyph_extents;
+ const cairo_rectangle_int_t *clip_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
+@@ -2101,16 +2101,35 @@ 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_format_t:
+ * @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,
+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
+@@ -2750,16 +2750,18 @@ slim_hidden_proto (cairo_surface_destroy
+ slim_hidden_proto (cairo_surface_finish);
+ slim_hidden_proto (cairo_surface_flush);
+ slim_hidden_proto (cairo_surface_get_content);
+ slim_hidden_proto (cairo_surface_get_device_offset);
+ slim_hidden_proto (cairo_surface_get_font_options);
+ slim_hidden_proto (cairo_surface_get_mime_data);
+ slim_hidden_proto (cairo_surface_get_type);
+ slim_hidden_proto (cairo_surface_has_show_text_glyphs);
++slim_hidden_proto (cairo_surface_set_subpixel_antialiasing);
++slim_hidden_proto (cairo_surface_get_subpixel_antialiasing);
+ slim_hidden_proto (cairo_surface_mark_dirty_rectangle);
+ slim_hidden_proto_no_warn (cairo_surface_reference);
+ slim_hidden_proto (cairo_surface_set_device_offset);
+ slim_hidden_proto (cairo_surface_set_fallback_resolution);
+ slim_hidden_proto (cairo_surface_set_mime_data);
+ slim_hidden_proto (cairo_surface_show_page);
+ slim_hidden_proto (cairo_surface_status);
+ slim_hidden_proto (cairo_text_cluster_allocate);
diff --git a/gfx/cairo/dwrite-font-match-robustness.patch b/gfx/cairo/dwrite-font-match-robustness.patch
new file mode 100644
index 000000000..eadcce3bd
--- /dev/null
+++ b/gfx/cairo/dwrite-font-match-robustness.patch
@@ -0,0 +1,26 @@
+From: Robert O'Callahan <robert@ocallahan.org>
+Bug 717178. Part 1: Don't crash when passing a nil scaled-font to _name_tables_match. r=jfkthame
+
+diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+--- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
++++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+@@ -1489,17 +1489,18 @@ static cairo_bool_t
+ 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->load_truetype_table ||
++ 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)
diff --git a/gfx/cairo/dwrite-font-printing.patch b/gfx/cairo/dwrite-font-printing.patch
new file mode 100644
index 000000000..837e19f6c
--- /dev/null
+++ b/gfx/cairo/dwrite-font-printing.patch
@@ -0,0 +1,157 @@
+diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+--- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
++++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+@@ -37,16 +37,17 @@
+ #include "cairoint.h"
+
+ #include "cairo-win32-private.h"
+ #include "cairo-surface-private.h"
+ #include "cairo-clip-private.h"
+
+ #include "cairo-d2d-private.h"
+ #include "cairo-dwrite-private.h"
++#include "cairo-truetype-subset-private.h"
+ #include <float.h>
+
+ typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
+ D2D1_FACTORY_TYPE factoryType,
+ REFIID iid,
+ CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
+ void **factory
+ );
+@@ -1036,17 +1037,17 @@ cairo_int_status_t
+ {
+ cairo_dwrite_scaled_font_t *dwritesf = static_cast<cairo_dwrite_scaled_font_t*>(scaled_font);
+ cairo_dwrite_font_face_t *face = reinterpret_cast<cairo_dwrite_font_face_t*>(dwritesf->base.font_face);
+
+ const void *data;
+ UINT32 size;
+ void *tableContext;
+ BOOL exists;
+- face->dwriteface->TryGetFontTable(tag,
++ face->dwriteface->TryGetFontTable(be32_to_cpu (tag),
+ &data,
+ &size,
+ &tableContext,
+ &exists);
+
+ if (!exists) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+@@ -1476,16 +1477,59 @@ DWriteFactory::CreateRenderingParams()
+ 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->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) {
+@@ -1528,19 +1572,18 @@ cairo_int_status_t
+ &ctm,
+ &options);
+ cairo_font_face_destroy (win32_face);
+
+ if (!font) {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+- if (_cairo_win32_scaled_font_is_type1 (font) || _cairo_win32_scaled_font_is_bitmap (font)) {
+- // If we somehow got a Type1 or bitmap font, it can't be the same physical font
+- // as directwrite was using, so glyph IDs will not match; best we can do is to
+- // throw it away and fall back on rendering paths or blitting bitmaps instead.
++ 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/cairo-truetype-subset-private.h b/gfx/cairo/cairo/src/cairo-truetype-subset-private.h
+--- a/gfx/cairo/cairo/src/cairo-truetype-subset-private.h
++++ b/gfx/cairo/cairo/src/cairo-truetype-subset-private.h
+@@ -34,16 +34,18 @@
+ * Adrian Johnson <ajohnson@redneon.com>
+ */
+
+ #ifndef CAIRO_TRUETYPE_SUBSET_PRIVATE_H
+ #define CAIRO_TRUETYPE_SUBSET_PRIVATE_H
+
+ #include "cairoint.h"
+
++CAIRO_BEGIN_DECLS
++
+ #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.
+ *
+@@ -191,9 +193,11 @@ typedef struct _tt_composite_glyph {
+ 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 */
+
++CAIRO_END_DECLS
++
+ #endif /* CAIRO_TRUETYPE_SUBSET_PRIVATE_H */
diff --git a/gfx/cairo/dwrite-glyph-extents.patch b/gfx/cairo/dwrite-glyph-extents.patch
new file mode 100644
index 000000000..d3625e198
--- /dev/null
+++ b/gfx/cairo/dwrite-glyph-extents.patch
@@ -0,0 +1,44 @@
+diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+--- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
++++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+@@ -582,22 +582,37 @@ _cairo_dwrite_scaled_font_init_glyph_met
+ 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.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;
++ 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;
++ extents.height += scaled_font->mat_inverse.yy * 2;
++ extents.y_bearing -= scaled_font->mat_inverse.yy;
++ }
++
+ _cairo_scaled_glyph_set_metrics (scaled_glyph,
+ &scaled_font->base,
+ &extents);
+ return CAIRO_INT_STATUS_SUCCESS;
+ }
+
+ /**
+ * Stack-based helper implementing IDWriteGeometrySink.
diff --git a/gfx/cairo/empty-clip-extents.patch b/gfx/cairo/empty-clip-extents.patch
new file mode 100644
index 000000000..306a61ad6
--- /dev/null
+++ b/gfx/cairo/empty-clip-extents.patch
@@ -0,0 +1,59 @@
+From b79ea8a6cab8bd28aebecf6e1e8229d5ac017264 Mon Sep 17 00:00:00 2001
+From: Karl Tomlinson <karlt+@karlt.net>
+Date: Fri, 16 Jul 2010 23:46:25 +0000
+Subject: clip: consider all_clipped in _cairo_clip_get_extents
+
+If the gstate clip in _cairo_gstate_int_clip_extents() has all_clipped
+set (and path NULL), then it returns the gstate target extents instead of
+an empty rectangle. If the target is infinite, then it says the clip is
+unbounded.
+
+Fixes https://bugs.freedesktop.org/show_bug.cgi?id=29124
+Tested-by test/get-clip
+
+Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
+---
+diff --git a/src/cairo-clip.c b/src/cairo-clip.c
+index f6173c6..77d8214 100644
+--- a/src/cairo-clip.c
++++ b/src/cairo-clip.c
+@@ -1264,9 +1264,14 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip,
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 };
++
+ const cairo_rectangle_int_t *
+ _cairo_clip_get_extents (const cairo_clip_t *clip)
+ {
++ if (clip->all_clipped)
++ return &_cairo_empty_rectangle_int;
++
+ if (clip->path == NULL)
+ return NULL;
+
+diff --git a/test/get-clip.c b/test/get-clip.c
+index 9d6e796..f0477a1 100644
+--- a/test/get-clip.c
++++ b/test/get-clip.c
+@@ -83,6 +83,8 @@ check_clip_extents (const cairo_test_context_t *ctx,
+ cairo_clip_extents (cr, &ext_x1, &ext_y1, &ext_x2, &ext_y2);
+ if (ext_x1 == x && ext_y1 == y && ext_x2 == x + width && ext_y2 == y + height)
+ return 1;
++ if (width == 0.0 && height == 0.0 && ext_x1 == ext_x2 && ext_y1 == ext_y2)
++ return 1;
+ cairo_test_log (ctx, "Error: %s; clip extents %f,%f,%f,%f should be %f,%f,%f,%f\n",
+ message, ext_x1, ext_y1, ext_x2 - ext_x1, ext_y2 - ext_y1,
+ x, y, width, height);
+@@ -138,7 +140,8 @@ preamble (cairo_test_context_t *ctx)
+ cairo_save (cr);
+ cairo_clip (cr);
+ rectangle_list = cairo_copy_clip_rectangle_list (cr);
+- if (! check_count (ctx, phase, rectangle_list, 0))
++ if (! check_count (ctx, phase, rectangle_list, 0) ||
++ ! check_clip_extents (ctx, phase, cr, 0, 0, 0, 0))
+ {
+ goto FAIL;
+ }
+--
+cgit v0.8.3-6-g21f6
diff --git a/gfx/cairo/empty-clip-rectangles.patch b/gfx/cairo/empty-clip-rectangles.patch
new file mode 100644
index 000000000..c9aa558a2
--- /dev/null
+++ b/gfx/cairo/empty-clip-rectangles.patch
@@ -0,0 +1,28 @@
+From f2fa15680ec3ac95cb68d4957557f06561a7dc55 Mon Sep 17 00:00:00 2001
+From: Karl Tomlinson <karlt+@karlt.net>
+Date: Fri, 16 Jul 2010 22:39:50 +0000
+Subject: clip: return empty clip from _cairo_clip_copy_rectangle_list when all_clipped
+
+Fixes https://bugs.freedesktop.org/show_bug.cgi?id=29122
+Tested by test/get-clip
+
+Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
+---
+diff --git a/src/cairo-clip.c b/src/cairo-clip.c
+index 12dc04d..f6173c6 100644
+--- a/src/cairo-clip.c
++++ b/src/cairo-clip.c
+@@ -1499,7 +1499,10 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate)
+ int n_rects = 0;
+ int i;
+
+- if (clip != NULL && clip->path != NULL) {
++ if (clip->all_clipped)
++ goto DONE;
++
++ if (clip->path != NULL) {
+ status = _cairo_clip_get_region (clip, &region);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ goto DONE;
+--
+cgit v0.8.3-6-g21f6
diff --git a/gfx/cairo/ensure-text-flushed.patch b/gfx/cairo/ensure-text-flushed.patch
new file mode 100644
index 000000000..5ec578fe5
--- /dev/null
+++ b/gfx/cairo/ensure-text-flushed.patch
@@ -0,0 +1,16 @@
+diff --git a/gfx/cairo/cairo/src/cairo-pdf-operators.c b/gfx/cairo/cairo/src/cairo-pdf-operators.c
+--- a/gfx/cairo/cairo/src/cairo-pdf-operators.c
++++ b/gfx/cairo/cairo/src/cairo-pdf-operators.c
+@@ -480,6 +480,12 @@ _cairo_pdf_operators_clip (cairo_pdf_ope
+ 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 ");
diff --git a/gfx/cairo/expose-snapshot.patch b/gfx/cairo/expose-snapshot.patch
new file mode 100644
index 000000000..879b1fc90
--- /dev/null
+++ b/gfx/cairo/expose-snapshot.patch
@@ -0,0 +1,528 @@
+diff --git a/gfx/cairo/cairo/src/cairo-analysis-surface-private.h b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h
+--- a/gfx/cairo/cairo/src/cairo-analysis-surface-private.h
++++ b/gfx/cairo/cairo/src/cairo-analysis-surface-private.h
+@@ -65,14 +65,11 @@ _cairo_analysis_surface_has_unsupported
+ 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);
+-
+ CAIRO_END_DECLS
+
+ #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
+--- a/gfx/cairo/cairo/src/cairo-analysis-surface.c
++++ b/gfx/cairo/cairo/src/cairo-analysis-surface.c
+@@ -902,17 +902,17 @@ static const cairo_surface_backend_t cai
+ NULL, /* fill_stroke */
+ NULL, /* create_solid_pattern_surface */
+ NULL, /* can_repaint_solid_pattern_surface */
+ NULL, /* has_show_text_glyphs */
+ NULL /* show_text_glyphs */
+ };
+
+ cairo_surface_t *
+-_cairo_null_surface_create (cairo_content_t content)
++cairo_null_surface_create (cairo_content_t content)
+ {
+ cairo_surface_t *surface;
+
+ surface = malloc (sizeof (cairo_surface_t));
+ if (unlikely (surface == NULL)) {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
++++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+@@ -1951,24 +1951,24 @@ _cairo_d2d_create_brush_for_pattern(cair
+ rect = D2D1::RectU(1, 1, srcSurf->width + 1, srcSurf->height + 1);
+ } else {
+ rect = D2D1::RectU(0, 0, srcSurf->width, srcSurf->height);
+ }
+ sourceBitmap->CopyFromMemory(&rect,
+ srcSurf->data,
+ srcSurf->stride);
+ cairo_surface_t *nullSurf =
+- _cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
++ cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
+ cachebitmap->refs++;
+ cachebitmap->dirty = false;
+ cairo_surface_set_user_data(nullSurf,
+ &bitmap_key_snapshot,
+ cachebitmap,
+ NULL);
+- _cairo_surface_attach_snapshot(surfacePattern->surface,
++ cairo_surface_attach_snapshot(surfacePattern->surface,
+ nullSurf,
+ _d2d_snapshot_detached);
+ }
+ } else {
+ if (pattern->extend != CAIRO_EXTEND_NONE) {
+ d2dsurf->rt->CreateBitmap(D2D1::SizeU(width, height),
+ data + yoffset * stride + xoffset * Bpp,
+ stride,
+@@ -2015,22 +2015,22 @@ _cairo_d2d_create_brush_for_pattern(cair
+ * and one more in the user data structure.
+ */
+ cachebitmap->refs = 2;
+ cairo_surface_set_user_data(surfacePattern->surface,
+ key,
+ cachebitmap,
+ _d2d_release_bitmap);
+ cairo_surface_t *nullSurf =
+- _cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
++ cairo_null_surface_create(CAIRO_CONTENT_COLOR_ALPHA);
+ cairo_surface_set_user_data(nullSurf,
+ &bitmap_key_snapshot,
+ cachebitmap,
+ NULL);
+- _cairo_surface_attach_snapshot(surfacePattern->surface,
++ cairo_surface_attach_snapshot(surfacePattern->surface,
+ nullSurf,
+ _d2d_snapshot_detached);
+ cache_usage += _d2d_compute_bitmap_mem_size(sourceBitmap);
+ }
+ if (pix_image) {
+ pixman_image_unref(pix_image);
+ }
+ }
+diff --git a/gfx/cairo/cairo/src/cairo-recording-surface.c b/gfx/cairo/cairo/src/cairo-recording-surface.c
+--- a/gfx/cairo/cairo/src/cairo-recording-surface.c
++++ b/gfx/cairo/cairo/src/cairo-recording-surface.c
+@@ -276,17 +276,17 @@ _cairo_recording_surface_acquire_source_
+ -surface->extents.y);
+
+ status = _cairo_recording_surface_replay (&surface->base, image);
+ if (unlikely (status)) {
+ cairo_surface_destroy (image);
+ return status;
+ }
+
+- _cairo_surface_attach_snapshot (&surface->base, image, NULL);
++ cairo_surface_attach_snapshot (&surface->base, image, NULL);
+
+ *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,
+@@ -1046,17 +1046,17 @@ 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->content);
++ null_surface = cairo_null_surface_create (surface->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)
+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
+@@ -40,18 +40,16 @@
+
+ #include "cairo.h"
+
+ #include "cairo-types-private.h"
+ #include "cairo-list-private.h"
+ #include "cairo-reference-count-private.h"
+ #include "cairo-clip-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;
+diff --git a/gfx/cairo/cairo/src/cairo-surface-snapshot.c b/gfx/cairo/cairo/src/cairo-surface-snapshot.c
+--- a/gfx/cairo/cairo/src/cairo-surface-snapshot.c
++++ b/gfx/cairo/cairo/src/cairo-surface-snapshot.c
+@@ -209,17 +209,17 @@ _cairo_surface_snapshot (cairo_surface_t
+ if (unlikely (status)) {
+ cairo_surface_destroy (snap);
+ return _cairo_surface_create_in_error (status);
+ }
+
+ snap->device_transform = surface->device_transform;
+ snap->device_transform_inverse = surface->device_transform_inverse;
+
+- _cairo_surface_attach_snapshot (surface, snap, NULL);
++ cairo_surface_attach_snapshot (surface, snap, NULL);
+
+ return snap;
+ }
+ }
+
+ snapshot = (cairo_surface_snapshot_t *)
+ _cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend);
+ if (snapshot != NULL)
+@@ -242,14 +242,14 @@ _cairo_surface_snapshot (cairo_surface_t
+ 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,
++ 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.c b/gfx/cairo/cairo/src/cairo-surface-subsurface.c
+--- a/gfx/cairo/cairo/src/cairo-surface-subsurface.c
++++ b/gfx/cairo/cairo/src/cairo-surface-subsurface.c
+@@ -326,17 +326,17 @@ _cairo_surface_subsurface_acquire_source
+ _cairo_image_surface_create_with_content (meta->content,
+ surface->extents.width,
+ surface->extents.height);
+ if (unlikely (image->base.status))
+ return image->base.status;
+
+ cairo_surface_paint_to_target (&image->base, surface);
+
+- _cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
++ cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
+
+ *image_out = image;
+ *extra_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+ extra = malloc (sizeof (struct extra));
+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
+@@ -305,51 +305,51 @@ _cairo_surface_detach_mime_data (cairo_s
+ 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)
++cairo_surface_detach_snapshots (cairo_surface_t *surface)
+ {
+ while (_cairo_surface_has_snapshots (surface)) {
+- _cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots,
++ cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots,
+ cairo_surface_t,
+ snapshot));
+ }
+ }
+
+ void
+-_cairo_surface_detach_snapshot (cairo_surface_t *snapshot)
++cairo_surface_detach_snapshot (cairo_surface_t *snapshot)
+ {
+ assert (snapshot->snapshot_of != NULL);
+
+ 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_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);
++ 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);
+ }
+@@ -382,17 +382,17 @@ _cairo_surface_is_writable (cairo_surfac
+
+ static void
+ _cairo_surface_begin_modification (cairo_surface_t *surface)
+ {
+ assert (surface->status == CAIRO_STATUS_SUCCESS);
+ assert (! surface->finished);
+ assert (surface->snapshot_of == NULL);
+
+- _cairo_surface_detach_snapshots (surface);
++ cairo_surface_detach_snapshots (surface);
+ _cairo_surface_detach_mime_data (surface);
+ }
+
+ void
+ _cairo_surface_init (cairo_surface_t *surface,
+ const cairo_surface_backend_t *backend,
+ cairo_device_t *device,
+ cairo_content_t content)
+@@ -711,19 +711,19 @@ cairo_surface_finish (cairo_surface_t *s
+
+ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
+ return;
+
+ if (surface->finished)
+ return;
+
+ /* update the snapshots *before* we declare the surface as finished */
+- _cairo_surface_detach_snapshots (surface);
++ cairo_surface_detach_snapshots (surface);
+ if (surface->snapshot_of != NULL)
+- _cairo_surface_detach_snapshot (surface);
++ cairo_surface_detach_snapshot (surface);
+
+ cairo_surface_flush (surface);
+ surface->finished = TRUE;
+
+ /* call finish even if in error mode */
+ if (surface->backend->finish) {
+ status = surface->backend->finish (surface);
+ if (unlikely (status))
+@@ -1106,17 +1106,17 @@ cairo_surface_flush (cairo_surface_t *su
+
+ if (surface->status)
+ return;
+
+ if (surface->finished)
+ return;
+
+ /* update the current snapshots *before* the user updates the surface */
+- _cairo_surface_detach_snapshots (surface);
++ cairo_surface_detach_snapshots (surface);
+
+ if (surface->backend->flush) {
+ status = surface->backend->flush (surface);
+ if (unlikely (status))
+ status = _cairo_surface_set_error (surface, status);
+ }
+ }
+ slim_hidden_def (cairo_surface_flush);
+@@ -1628,17 +1628,17 @@ _cairo_recording_surface_clone_similar (
+ return similar->status;
+
+ status = _cairo_recording_surface_replay (src, similar);
+ if (unlikely (status)) {
+ cairo_surface_destroy (similar);
+ return status;
+ }
+
+- _cairo_surface_attach_snapshot (src, similar, NULL);
++ cairo_surface_attach_snapshot (src, similar, NULL);
+
+ src_x = src_y = 0;
+ }
+
+ *clone_out = similar;
+ *clone_offset_x = src_x;
+ *clone_offset_y = src_y;
+ return CAIRO_STATUS_SUCCESS;
+diff --git a/gfx/cairo/cairo/src/cairo-vg-surface.c b/gfx/cairo/cairo/src/cairo-vg-surface.c
+--- a/gfx/cairo/cairo/src/cairo-vg-surface.c
++++ b/gfx/cairo/cairo/src/cairo-vg-surface.c
+@@ -977,17 +977,17 @@ _vg_setup_surface_source (cairo_vg_conte
+ 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,
++ 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);
+
+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
+@@ -560,17 +560,17 @@ _cairo_xcb_surface_acquire_source_image
+ image = (cairo_image_surface_t *) cairo_surface_reference (&image->base);
+ goto DONE;
+ }
+
+ status = _get_image (surface, FALSE, &image);
+ if (unlikely (status))
+ return status;
+
+- _cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
++ cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
+
+ DONE:
+ *image_out = image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static void
+@@ -713,17 +713,17 @@ _cairo_xcb_surface_flush (void *abstract
+ status = cairo_surface_status (surface->fallback);
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+ status = _put_image (surface,
+ (cairo_image_surface_t *) surface->fallback);
+ }
+
+ if (status == CAIRO_STATUS_SUCCESS) {
+- _cairo_surface_attach_snapshot (&surface->base,
++ cairo_surface_attach_snapshot (&surface->base,
+ surface->fallback,
+ cairo_surface_finish);
+ }
+ }
+
+ cairo_surface_destroy (surface->fallback);
+ surface->fallback = NULL;
+
+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
+@@ -214,16 +214,25 @@ typedef struct _cairo_pattern cairo_patt
+ *
+ * #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.
+ **/
+ typedef void (*cairo_destroy_func_t) (void *data);
+
+ /**
++ * cairo_surface_func_t:
++ * @surface: The surface being referred to.
++ *
++ * #cairo_surface_func_t the type of function which is used for callback
++ * when a surface needs to be apssed as a parameter.
++ */
++typedef void (*cairo_surface_func_t) (cairo_surface_t *surface);
++
++/**
+ * cairo_user_data_key_t:
+ * @unused: not used; ignore.
+ *
+ * #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.
+@@ -2150,16 +2159,24 @@ cairo_surface_get_user_data (cairo_surfa
+ 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);
+
++cairo_public void
++cairo_surface_attach_snapshot (cairo_surface_t *surface,
++ cairo_surface_t *snapshot,
++ cairo_surface_func_t detach_func);
++
++cairo_public void
++cairo_surface_detach_snapshot (cairo_surface_t *snapshot);
++
+ #define CAIRO_MIME_TYPE_JPEG "image/jpeg"
+ #define CAIRO_MIME_TYPE_PNG "image/png"
+ #define CAIRO_MIME_TYPE_JP2 "image/jp2"
+ #define CAIRO_MIME_TYPE_URI "text/x-uri"
+
+ cairo_public void
+ cairo_surface_get_mime_data (cairo_surface_t *surface,
+ const char *mime_type,
+@@ -2328,16 +2345,21 @@ cairo_recording_surface_create (cairo_co
+
+ cairo_public void
+ cairo_recording_surface_ink_extents (cairo_surface_t *surface,
+ double *x0,
+ double *y0,
+ double *width,
+ double *height);
+
++/* Null-surface functions */
++
++cairo_public cairo_surface_t *
++cairo_null_surface_create (cairo_content_t content);
++
+ /* 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);
+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
+@@ -1770,27 +1770,19 @@ _cairo_surface_clone_similar (cairo_surf
+ int height,
+ int *clone_offset_x,
+ int *clone_offset_y,
+ cairo_surface_t **clone_out);
+
+ cairo_private cairo_surface_t *
+ _cairo_surface_snapshot (cairo_surface_t *surface);
+
+-cairo_private void
+-_cairo_surface_attach_snapshot (cairo_surface_t *surface,
+- cairo_surface_t *snapshot,
+- cairo_surface_func_t detach_func);
+-
+ cairo_private cairo_surface_t *
+ _cairo_surface_has_snapshot (cairo_surface_t *surface,
+- const cairo_surface_backend_t *backend);
+-
+-cairo_private void
+-_cairo_surface_detach_snapshot (cairo_surface_t *snapshot);
++ const cairo_surface_backend_t *backend);
+
+ cairo_private cairo_bool_t
+ _cairo_surface_is_similar (cairo_surface_t *surface_a,
+ cairo_surface_t *surface_b);
+
+ cairo_private cairo_bool_t
+ _cairo_surface_get_extents (cairo_surface_t *surface,
+ cairo_rectangle_int_t *extents);
diff --git a/gfx/cairo/fix-build-with-Werror=return-type.patch b/gfx/cairo/fix-build-with-Werror=return-type.patch
new file mode 100644
index 000000000..7cd77a597
--- /dev/null
+++ b/gfx/cairo/fix-build-with-Werror=return-type.patch
@@ -0,0 +1,21 @@
+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
+@@ -436,16 +436,17 @@ _cairo_quartz_cairo_operator_to_quartz_c
+ 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 (0);
++ return kPrivateCGCompositeClear;
+ }
+ }
+
+ 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));
+
diff --git a/gfx/cairo/fix-cairo-surface-wrapper-flush-build-warning.patch b/gfx/cairo/fix-cairo-surface-wrapper-flush-build-warning.patch
new file mode 100644
index 000000000..0e7c0b973
--- /dev/null
+++ b/gfx/cairo/fix-cairo-surface-wrapper-flush-build-warning.patch
@@ -0,0 +1,19 @@
+# HG changeset patch
+# Parent 5479a346b95b82162c72419a95cbb4022cbbfe4d
+# User Ed Morley <bmo@edmorley.co.uk>
+Bug 631155 - undefined return value in function '_cairo_surface_wrapper_flush'; r=jrmuizel
+
+diff --git a/gfx/cairo/cairo/src/cairo-surface-wrapper.c b/gfx/cairo/cairo/src/cairo-surface-wrapper.c
+--- a/gfx/cairo/cairo/src/cairo-surface-wrapper.c
++++ b/gfx/cairo/cairo/src/cairo-surface-wrapper.c
+@@ -712,9 +712,10 @@ _cairo_surface_wrapper_fini (cairo_surfa
+ }
+
+ cairo_status_t
+ _cairo_surface_wrapper_flush (cairo_surface_wrapper_t *wrapper)
+ {
+ if (wrapper->target->backend->flush) {
+ return wrapper->target->backend->flush(wrapper->target);
+ }
++ return CAIRO_STATUS_SUCCESS;
+ }
diff --git a/gfx/cairo/fix-cairo-win32-print-gdi-error.diff b/gfx/cairo/fix-cairo-win32-print-gdi-error.diff
new file mode 100644
index 000000000..9387a87af
--- /dev/null
+++ b/gfx/cairo/fix-cairo-win32-print-gdi-error.diff
@@ -0,0 +1,26 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -95,20 +95,21 @@ _cairo_win32_print_gdi_error (const char
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ last_error,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR) &lpMsgBuf,
+ 0, NULL)) {
+ fprintf (stderr, "%s: Unknown GDI error", context);
+ } else {
+- fwprintf (stderr, "%S: %s", context, (char *)lpMsgBuf);
++ fprintf (stderr, "%s: %S", context, (char *)lpMsgBuf);
+
+ LocalFree (lpMsgBuf);
+ }
++ fflush(stderr);
+
+ /* We should switch off of last_status, but we'd either return
+ * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there
+ * is no CAIRO_STATUS_UNKNOWN_ERROR.
+ */
+
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
diff --git a/gfx/cairo/fix-clip-copy.patch b/gfx/cairo/fix-clip-copy.patch
new file mode 100644
index 000000000..c2c3b2fc8
--- /dev/null
+++ b/gfx/cairo/fix-clip-copy.patch
@@ -0,0 +1,30 @@
+commit f49a9740350d2f0d69ed59e913f0263a899cfb2a
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Fri Jan 29 14:39:24 2010 -0500
+
+ Fix clip copy
+
+diff --git a/src/cairo-clip.c b/src/cairo-clip.c
+index 8d66a5f..6acbcff 100644
+--- a/src/cairo-clip.c
++++ b/src/cairo-clip.c
+@@ -280,13 +280,12 @@ cairo_clip_t *
+ _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
+ {
+ if (other != NULL) {
+- if (other->path == NULL) {
+- _cairo_clip_init (clip);
+- clip = NULL;
+- } else {
+- clip->all_clipped = other->all_clipped;
+- clip->path = _cairo_clip_path_reference (other->path);
+- }
++ clip->all_clipped = other->all_clipped;
++ clip->path = _cairo_clip_path_reference (other->path);
++
++ /* this guy is here because of the weird return semantics of _cairo_clip_init_copy */
++ if (!other->path)
++ return NULL;
+ } else {
+ _cairo_clip_init (clip);
+ }
diff --git a/gfx/cairo/fix-clip-region-simplification.patch b/gfx/cairo/fix-clip-region-simplification.patch
new file mode 100644
index 000000000..825130a2e
--- /dev/null
+++ b/gfx/cairo/fix-clip-region-simplification.patch
@@ -0,0 +1 @@
+stg show: fix-clip-region-simplication: Unknown patch or revision name
diff --git a/gfx/cairo/fix-clip-test.patch b/gfx/cairo/fix-clip-test.patch
new file mode 100644
index 000000000..9987b48bf
--- /dev/null
+++ b/gfx/cairo/fix-clip-test.patch
@@ -0,0 +1,15 @@
+Fix a clip test to test the right coordinate.
+
+Fixed upstream by 498c10032ea3f8631a928cd7df96766f2c8ddca4
+diff --git a/gfx/cairo/cairo/src/cairo-clip.c b/gfx/cairo/cairo/src/cairo-clip.c
+--- a/gfx/cairo/cairo/src/cairo-clip.c
++++ b/gfx/cairo/cairo/src/cairo-clip.c
+@@ -408,7 +408,7 @@ _cairo_clip_rectangle (cairo_clip_t
+ /* if a smaller clip has already been set, ignore the new path */
+ if (clip->path != NULL) {
+ if (rectangle->x <= clip->path->extents.x &&
+- rectangle->y <= clip->path->extents.x &&
++ rectangle->y <= clip->path->extents.y &&
+ rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width &&
+ rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height)
+ {
diff --git a/gfx/cairo/fix-ps-output.patch b/gfx/cairo/fix-ps-output.patch
new file mode 100644
index 000000000..b325f0bc5
--- /dev/null
+++ b/gfx/cairo/fix-ps-output.patch
@@ -0,0 +1,19 @@
+# HG changeset patch
+# User Jeff Muizelaar <jmuizelaar@mozilla.com>
+# Date 1276629019 14400
+# Node ID b5eb246c152dedb895d3010eb7192f88cce51146
+# Parent 792cd3e8aa59b24c333f2c90f938beda361b3336
+cairo: PS: Add missing 'q' when resetting clip path
+
+diff --git a/gfx/cairo/cairo/src/cairo-ps-surface.c b/gfx/cairo/cairo/src/cairo-ps-surface.c
+--- a/gfx/cairo/cairo/src/cairo-ps-surface.c
++++ b/gfx/cairo/cairo/src/cairo-ps-surface.c
+@@ -3696,7 +3696,7 @@ _cairo_ps_surface_set_paginated_mode (vo
+ if (surface->clipper.clip.path != NULL) {
+ status = _cairo_pdf_operators_flush (&surface->pdf_operators);
+
+- _cairo_output_stream_printf (surface->stream, "Q\n");
++ _cairo_output_stream_printf (surface->stream, "Q q\n");
+ _cairo_surface_clipper_reset (&surface->clipper);
+ }
+ }
diff --git a/gfx/cairo/fix-unnecessary-fallback.patch b/gfx/cairo/fix-unnecessary-fallback.patch
new file mode 100644
index 000000000..468801623
--- /dev/null
+++ b/gfx/cairo/fix-unnecessary-fallback.patch
@@ -0,0 +1,14 @@
+diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
+index 353cbcd..1a053d0 100644
+--- a/src/cairo-xlib-surface.c
++++ b/src/cairo-xlib-surface.c
+@@ -1818,7 +1817,8 @@ _recategorize_composite_operation (cairo_xlib_surface_t *dst,
+ return DO_XTILE;
+ }
+
+- if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT)
++ if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT &&
++ (src->width != 1 || src->height != 1))
+ return DO_UNSUPPORTED;
+
+ if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src))
diff --git a/gfx/cairo/fix-win32-font-assertion.patch b/gfx/cairo/fix-win32-font-assertion.patch
new file mode 100644
index 000000000..23cc5474b
--- /dev/null
+++ b/gfx/cairo/fix-win32-font-assertion.patch
@@ -0,0 +1,27 @@
+From: Jonathan Kew <jkew@mozilla.com>
+bug 838617 - don't assert equality with the key's hash unless it was actually initialized. r=roc
+
+diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c
+--- a/gfx/cairo/cairo/src/cairo-win32-font.c
++++ b/gfx/cairo/cairo/src/cairo-win32-font.c
+@@ -2104,19 +2104,19 @@ cairo_win32_font_face_create_for_logfont
+ font_face = 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);
+
+ if (!font) {
++ assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash);
+ status = _cairo_hash_table_insert (hash_table,
+ &font_face->base.hash_entry);
+ if (unlikely (status))
+ goto FAIL;
+ }
+
+ DONE:
+ if (!font) {
diff --git a/gfx/cairo/fix-win32-show-glyphs-clipping.patch b/gfx/cairo/fix-win32-show-glyphs-clipping.patch
new file mode 100644
index 000000000..91c5de2e1
--- /dev/null
+++ b/gfx/cairo/fix-win32-show-glyphs-clipping.patch
@@ -0,0 +1,19 @@
+commit 25fb70d4dc1e67553dba5973a79902e4cf81341f
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Wed Apr 21 11:23:27 2010 -0400
+
+ fix show glyphs clipping
+
+diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
+index c10e134..ff823a3 100644
+--- a/src/cairo-win32-surface.c
++++ b/src/cairo-win32-surface.c
+@@ -1603,6 +1603,8 @@ _cairo_win32_surface_show_glyphs (void *surface,
+
+ _cairo_win32_surface_set_clip_region (surface, clip_region);
+ }
++ } else {
++ _cairo_win32_surface_set_clip_region (surface, NULL);
+ }
+
+ solid_pattern = (cairo_solid_pattern_t *)source;
diff --git a/gfx/cairo/fix-xcopyarea-with-clips.patch b/gfx/cairo/fix-xcopyarea-with-clips.patch
new file mode 100644
index 000000000..6bf3320b6
--- /dev/null
+++ b/gfx/cairo/fix-xcopyarea-with-clips.patch
@@ -0,0 +1,38 @@
+From: Benjamin Otte <otte@redhat.com>
+Date: Thu, 29 Apr 2010 16:20:59 +0000
+Subject: xlib: Don't modify variables that are needed later
+
+In the XCopyArea region code, don't modify src_x/y when they are later
+used in the unbounded fixup code.
+
+Exposed by composite-integer-translate-source test.
+---
+diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
+index bedc3fd..30c08d3 100644
+--- a/gfx/cairo/cairo/src/cairo-xlib-surface.c
++++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+@@ -2322,10 +2322,10 @@ _cairo_xlib_surface_composite (cairo_operator_t op,
+ width, height,
+ dst_x, dst_y);
+ } else {
+- int n, num_rects;
++ int n, num_rects, x, y;
+
+- src_x += src_attr.x_offset + itx - dst_x;
+- src_y += src_attr.y_offset + ity - dst_y;
++ x = src_x + src_attr.x_offset + itx - dst_x;
++ y = src_y + src_attr.y_offset + ity - dst_y;
+
+ num_rects = cairo_region_num_rectangles (clip_region);
+ for (n = 0; n < num_rects; n++) {
+@@ -2333,7 +2333,7 @@ _cairo_xlib_surface_composite (cairo_operator_t op,
+
+ cairo_region_get_rectangle (clip_region, n, &rect);
+ XCopyArea (dst->dpy, src->drawable, dst->drawable, gc,
+- rect.x + src_x, rect.y + src_y,
++ rect.x + x, rect.y + y,
+ rect.width, rect.height,
+ rect.x, rect.y);
+ }
+--
+cgit v0.8.3-6-g21f6
diff --git a/gfx/cairo/fix-zero-length-gradient.patch b/gfx/cairo/fix-zero-length-gradient.patch
new file mode 100644
index 000000000..a920d76e9
--- /dev/null
+++ b/gfx/cairo/fix-zero-length-gradient.patch
@@ -0,0 +1 @@
+stg show: fix-zero-len-graident: Unknown patch or revision name
diff --git a/gfx/cairo/fixup-unbounded.patch b/gfx/cairo/fixup-unbounded.patch
new file mode 100644
index 000000000..09b3a9069
--- /dev/null
+++ b/gfx/cairo/fixup-unbounded.patch
@@ -0,0 +1,22 @@
+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
+@@ -1797,17 +1797,17 @@ _cairo_image_surface_fixup_unbounded_box
+ 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_region == NULL)
++ if (boxes->num_boxes < 1 && clip_region == NULL)
+ return _cairo_image_surface_fixup_unbounded (dst, extents, NULL);
+
+ _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);
diff --git a/gfx/cairo/ft-no-subpixel-if-surface-disables.patch b/gfx/cairo/ft-no-subpixel-if-surface-disables.patch
new file mode 100644
index 000000000..a53e5de2b
--- /dev/null
+++ b/gfx/cairo/ft-no-subpixel-if-surface-disables.patch
@@ -0,0 +1,46 @@
+# HG changeset patch
+# Parent 31c7eac3de3de324cb5c93bd19c4e16a693f1101
+# User Karl Tomlinson <karlt+@karlt.net>
+b=929451 don't use subpixel aa for ft fonts on surfaces that don't support it r?roc
+
+Also:
+* Prefer subpixel order provided by the surface over that from the font face.
+* Allow font face options to turn off subpixel aa.
+
+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
+@@ -1759,23 +1759,26 @@ static void
+
+ 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;
++ } else if (options->base.antialias != CAIRO_ANTIALIAS_GRAY) {
++ /* The surface supports subpixel aa, so let the font face options
++ * choose whether to use subpixel aa. If the surface has
++ * CAIRO_ANTIALIAS_GRAY (e.g. PS, PDF, SVG, translucent part of a
++ * CONTENT_COLOR_ALPHA surface), then don't accept subpixel aa. */
++ if (other->base.antialias != CAIRO_ANTIALIAS_DEFAULT)
++ options->base.antialias = other->base.antialias;
++ /* If the surface knows the subpixel order then use that. */
++ if (options->base.subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
++ options->base.subpixel_order = other->base.subpixel_order;
+ }
+
+ if (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;
+
diff --git a/gfx/cairo/gdi-RGB24-ARGB32.patch b/gfx/cairo/gdi-RGB24-ARGB32.patch
new file mode 100644
index 000000000..1df95f9ac
--- /dev/null
+++ b/gfx/cairo/gdi-RGB24-ARGB32.patch
@@ -0,0 +1,141 @@
+changeset: 106848:28db6dbdd9ea
+tag: gdi-patch
+tag: qbase
+tag: qtip
+tag: tip
+user: Jeff Muizelaar <jmuizelaar@mozilla.com>
+date: Wed Sep 12 22:52:06 2012 -0400
+summary: Bug 788794. Use BitBlt to do SOURCE and OVER from RGB24 to ARGB32. r=nical
+
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -884,16 +884,28 @@ static cairo_int_status_t
+ src_x, src_y,
+ src_w, src_h,
+ blend_function))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(AlphaBlend)");
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++/* makes the alpha channel in a RGB24 surface 0xff */
++static void
++make_opaque (cairo_image_surface_t *image, cairo_rectangle_int_t src_r)
++{
++ int x; int y;
++ for (y = src_r.y; y < src_r.height; y++) {
++ for (x = src_r.x; x < src_r.width; x++) {
++ image->data[y * image->stride + x*4 + 3] = 0xff;
++ }
++ }
++}
++
+ static cairo_int_status_t
+ _cairo_win32_surface_composite_inner (cairo_win32_surface_t *src,
+ cairo_image_surface_t *src_image,
+ cairo_win32_surface_t *dst,
+ cairo_rectangle_int_t src_extents,
+ cairo_rectangle_int_t src_r,
+ cairo_rectangle_int_t dst_r,
+ int alpha,
+@@ -935,16 +947,24 @@ static cairo_int_status_t
+ src_r.width, - (int) src_r.height,
+ src_image->data,
+ &bi,
+ DIB_RGB_COLORS,
+ SRCCOPY))
+ return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(StretchDIBits)");
+ }
+ } else if (!needs_alpha) {
++ if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) {
++ /* Because we store RGB24 & ARGB32 in the same way GDI has no way
++ * to ignore the alpha channel from a RGB24 source. Therefore, we set
++ * the alpha channel in our RGB24 source to opaque so that we can treat
++ * it like ARGB32. */
++ GdiFlush();
++ make_opaque(src->image, src_r);
++ }
+ /* BitBlt or StretchBlt? */
+ if (!needs_scale && (dst->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)) {
+ if (!BitBlt (dst->dc,
+ dst_r.x, dst_r.y,
+ dst_r.width, dst_r.height,
+ src->dc,
+ src_r.x, src_r.y,
+ SRCCOPY))
+@@ -1184,28 +1204,36 @@ static cairo_int_status_t
+ }
+ } else {
+ needs_repeat = TRUE;
+ }
+
+ /*
+ * Operations that we can do:
+ *
++ * AlphaBlend uses the following formula for alpha when not use the per-pixel alpha (AlphaFormat = 0)
++ * Dst.Alpha = Src.Alpha * (SCA/255.0) + Dst.Alpha * (1.0 - (SCA/255.0))
++ * This turns into Dst.Alpha = Src.Alpha when SCA = 255.
++ * (http://msdn.microsoft.com/en-us/library/aa921335.aspx)
++ *
+ * RGB OVER RGB -> BitBlt (same as SOURCE)
+- * RGB OVER ARGB -> UNSUPPORTED (AlphaBlend treats this as a BitBlt, even with SCA 255 and no AC_SRC_ALPHA)
++ * RGB OVER ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB
++ * by setting the alpha values of the source to 255.
+ * ARGB OVER ARGB -> AlphaBlend, with AC_SRC_ALPHA
+ * ARGB OVER RGB -> AlphaBlend, with AC_SRC_ALPHA; we'll have junk in the dst A byte
+ *
+ * RGB OVER RGB + mask -> AlphaBlend, no AC_SRC_ALPHA
+- * RGB OVER ARGB + mask -> UNSUPPORTED
++ * RGB OVER ARGB + mask -> Partially supported, We convert this operation into a ARGB OVER ARGB + mask
++ * by setting the alpha values of the source to 255.
+ * ARGB OVER ARGB + mask -> AlphaBlend, with AC_SRC_ALPHA
+ * ARGB OVER RGB + mask -> AlphaBlend, with AC_SRC_ALPHA; junk in the dst A byte
+ *
+ * RGB SOURCE RGB -> BitBlt
+- * RGB SOURCE ARGB -> UNSUPPORTED (AlphaBlend treats this as a BitBlt, even with SCA 255 and no AC_SRC_ALPHA)
++ * RGB SOURCE ARGB -> Partially supported, We convert this operation into a ARGB SOURCE ARGB
++ * by setting the alpha values of the source to 255.
+ * ARGB SOURCE ARGB -> BitBlt
+ * ARGB SOURCE RGB -> BitBlt
+ *
+ * RGB SOURCE RGB + mask -> unsupported
+ * RGB SOURCE ARGB + mask -> unsupported
+ * ARGB SOURCE ARGB + mask -> unsupported
+ * ARGB SOURCE RGB + mask -> unsupported
+ */
+@@ -1222,22 +1250,32 @@ static cairo_int_status_t
+ needs_alpha = FALSE;
+ } else {
+ needs_alpha = TRUE;
+ }
+ } else if (src_format == CAIRO_FORMAT_ARGB32 &&
+ dst->format == CAIRO_FORMAT_RGB24)
+ {
+ needs_alpha = TRUE;
++ } else if (src_format == CAIRO_FORMAT_RGB24 &&
++ dst->format == CAIRO_FORMAT_ARGB32 &&
++ src->image)
++ {
++ if (alpha == 255) {
++ needs_alpha = FALSE;
++ } else {
++ needs_alpha = TRUE;
++ }
+ } else {
+ goto UNSUPPORTED;
+ }
+ } else if (alpha == 255 && op == CAIRO_OPERATOR_SOURCE) {
+ if ((src_format == dst->format) ||
+- (src_format == CAIRO_FORMAT_ARGB32 && dst->format == CAIRO_FORMAT_RGB24))
++ (src_format == CAIRO_FORMAT_ARGB32 && dst->format == CAIRO_FORMAT_RGB24) ||
++ (src_format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32 && src->image))
+ {
+ needs_alpha = FALSE;
+ } else {
+ goto UNSUPPORTED;
+ }
+ } else {
+ goto UNSUPPORTED;
+ }
+
diff --git a/gfx/cairo/glitz/src/glitz.def b/gfx/cairo/glitz/src/glitz.def
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/gfx/cairo/glitz/src/glitz.def
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 000000000..e69de29bb
--- /dev/null
+++ b/gfx/cairo/glitz/src/wgl/glitz-wgl.def
diff --git a/gfx/cairo/handle-a1.patch b/gfx/cairo/handle-a1.patch
new file mode 100644
index 000000000..eaad3d69e
--- /dev/null
+++ b/gfx/cairo/handle-a1.patch
@@ -0,0 +1,25 @@
+commit 82aab44a9005047743538d52e9fbc27fd6ce408a
+Author: Chris Wilson <chris@chris-wilson.co.uk>
+Date: Fri Mar 19 17:23:20 2010 -0400
+
+ commit f07195860620959c27d43080a7b987e28222735a
+
+ xlib: Handle a1 image uploads through converter
+
+ Fixes test/large-source [xlib]
+
+diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
+index 1a053d0..8f773b0 100644
+--- a/src/cairo-xlib-surface.c
++++ b/src/cairo-xlib-surface.c
+@@ -1155,7 +1155,9 @@ _draw_image_surface (cairo_xlib_surface_t *surface,
+ int dither_adjustment = dither_row[x_off];
+ int a, r, g, b;
+
+- if (image_masks.bpp <= 8)
++ 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];
diff --git a/gfx/cairo/handle-multi-path-clip.patch b/gfx/cairo/handle-multi-path-clip.patch
new file mode 100644
index 000000000..fe4268885
--- /dev/null
+++ b/gfx/cairo/handle-multi-path-clip.patch
@@ -0,0 +1,57 @@
+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
+@@ -2885,16 +2885,18 @@ static cairo_status_t
+ cairo_bool_t need_clip_mask = FALSE;
+ cairo_status_t status;
+ struct _cairo_boxes_chunk *chunk;
+ uint32_t pixel;
+ int i;
+
+ if (clip != NULL) {
+ status = _cairo_clip_get_region (clip, &clip_region);
++ if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
++ return CAIRO_STATUS_SUCCESS;
+ need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED;
+ if (need_clip_mask &&
+ (op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
+@@ -3200,30 +3202,20 @@ static cairo_status_t
+ return _clip_and_composite (dst, op, src,
+ _composite_traps, &info,
+ extents, clip);
+ }
+
+ static cairo_clip_path_t *
+ _clip_get_single_path (cairo_clip_t *clip)
+ {
+- 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;
++ if (clip->path->prev == NULL)
++ return clip->path;
++
++ return NULL;
+ }
+
+ /* high level image interface */
+
+ static cairo_int_status_t
+ _cairo_image_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
diff --git a/gfx/cairo/ignore-rank0.patch b/gfx/cairo/ignore-rank0.patch
new file mode 100644
index 000000000..7e20f66fb
--- /dev/null
+++ b/gfx/cairo/ignore-rank0.patch
@@ -0,0 +1,20 @@
+diff --git a/gfx/cairo/cairo/src/cairo-gstate.c b/gfx/cairo/cairo/src/cairo-gstate.c
+index 0439bcf..32a5c4b 100644
+--- a/gfx/cairo/cairo/src/cairo-gstate.c
++++ b/gfx/cairo/cairo/src/cairo-gstate.c
+@@ -1271,8 +1271,13 @@ _cairo_gstate_set_font_matrix (cairo_gstate_t *gstate,
+ if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0)
+ return CAIRO_STATUS_SUCCESS;
+
+- if (! _cairo_matrix_is_invertible (matrix))
+- return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
++ if (! _cairo_matrix_is_invertible (matrix)) {
++ /* rank 0 matrices are ok even though they are not invertible */
++ if (!(matrix->xx == 0. && matrix->xy == 0. &&
++ matrix->yx == 0. && matrix->yy == 0.)) {
++ return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
++ }
++ }
+
+ _cairo_gstate_unset_scaled_font (gstate);
+
diff --git a/gfx/cairo/libpixman/AUTHORS b/gfx/cairo/libpixman/AUTHORS
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/gfx/cairo/libpixman/AUTHORS
diff --git a/gfx/cairo/libpixman/COPYING b/gfx/cairo/libpixman/COPYING
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/gfx/cairo/libpixman/COPYING
diff --git a/gfx/cairo/libpixman/INSTALL b/gfx/cairo/libpixman/INSTALL
new file mode 100644
index 000000000..5458714e1
--- /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/NEWS b/gfx/cairo/libpixman/NEWS
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/gfx/cairo/libpixman/NEWS
diff --git a/gfx/cairo/libpixman/README b/gfx/cairo/libpixman/README
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/gfx/cairo/libpixman/README
diff --git a/gfx/cairo/libpixman/TODO b/gfx/cairo/libpixman/TODO
new file mode 100644
index 000000000..6649c698a
--- /dev/null
+++ b/gfx/cairo/libpixman/TODO
@@ -0,0 +1,139 @@
+ - Go through things marked FIXME
+
+ - Add calls to prepare and finish access where necessary. grep for
+ ACCESS_MEM, and make sure they are correctly wrapped in prepare
+ and finish.
+
+ - restore READ/WRITE in the fbcompose combiners since they sometimes
+ store directly to destination drawables.
+
+ - It probably makes sense to move the more strange X region API
+ into pixman as well, but guarded with PIXMAN_XORG_COMPATIBILITY
+
+ - Reinstate the FbBits typedef? At the moment we don't
+ even have the FbBits type; we just use uint32_t everywhere.
+
+ Keith says in bug 2335:
+
+ The 64-bit code in fb (pixman) is probably broken; it hasn't been
+ used in quite some time as PCI (and AGP) is 32-bits wide, so
+ doing things 64-bits at a time is a net loss. To quickly fix
+ this, I suggest just using 32-bit datatypes by setting
+ IC_SHIFT to 5 for all machines.
+
+ - Consider whether calling regions region16 is really such a great
+ idea Vlad wants 32 bit regions for Cairo. This will break X server
+ ABI, but should otherwise be mostly harmless, though a
+ pixman_region_get_boxes16() may be useful.
+
+ - Make source clipping optional.
+ - done: source clipping happens through an indirection.
+ still needs to make the indirection settable. (And call it
+ from X)
+
+ - Consider optimizing the 8/16 bit solid fills in pixman-util.c by
+ storing more than one value at a time.
+
+ - Add an image cache to prevent excessive malloc/free. Note that pixman
+ needs to be thread safe when used from cairo.
+
+ - Review the pixman_format_code_t enum to make sure it will support
+ future formats. Some formats we will probably need:
+
+ ARGB/ABGR with 16/32/64 bit integer/floating channels
+ YUV2,
+ YV12
+
+ Also we may need the ability to distinguish between PICT_c8 and
+ PICT_x4c4. (This could be done by interpreting the A channel as
+ the depth for TYPE_COLOR and TYPE_GRAY formats).
+
+ A possibility may be to reserve the two top bits and make them
+ encode "number of places to shift the channel widths given" Since
+ these bits are 00 at the moment everything will continue to work,
+ but these additional widths will be allowed:
+
+ All even widths between 18-32
+ All multiples of four widths between 33 and 64
+ All multiples of eight between 64 and 128
+
+ This means things like r21g22b21 won't work - is that worth
+ worrying about? I don't think so. And of course the bpp field
+ can't handle a depth of over 256, so > 64 bit channels arent'
+ really all that useful.
+
+ We could reserve one extra bit to indicate floating point, but
+ we may also just add
+
+ PIXMAN_TYPE_ARGB_FLOAT
+ PIXMAN_TYPE_BGRA_FLOAT
+ PIXMAN_TYPE_A_FLOAT
+
+ image types. With five bits we can support up to 32 different
+ format types, which should be enough for everybody, even if we
+ decide to support all the various video formats here:
+
+ http://www.fourcc.org/yuv.php
+
+ It may make sense to have a PIXMAN_TYPE_YUV, and then use the
+ channel bits to specify the exact subtype.
+
+ What about color spaces such a linear vs. srGB etc.?
+
+
+done:
+
+- Run cairo test suite; fix bugs
+ - one bug in source-scale-clip
+
+ - Remove the warning suppression in the ACCESS_MEM macro and fix the
+ warnings that are real
+ - irrelevant now.
+
+- make the wrapper functions global instead of image specific
+ - this won't work since pixman is linked to both fb and wfb
+
+- Add non-mmx solid fill
+
+- Make sure the endian-ness macros are defined correctly.
+
+- The rectangles in a region probably shouldn't be returned const as
+ the X server will be changing them.
+
+- Right now we _always_ have a clip region, which is empty by default.
+ Why does this work at all? It probably doesn't. The server
+ distinguishes two cases, one where nothing is clipped (CT_NONE), and
+ one where there is a clip region (CT_REGION).
+
+- Default clip region should be the full image
+
+ - Test if pseudo color still works. It does, but it also shows that
+ copying a pixman_indexed_t on every composite operation is not
+ going to fly. So, for now set_indexed() does not copy the
+ indexed table.
+
+ Also just the malloc() to allocate a pixman image shows up pretty
+ high.
+
+ Options include
+
+ - Make all the setters not copy their arguments
+
+ - Possibly combined with going back to the stack allocated
+ approach that we already use for regions.
+
+ - Keep a cached pixman_image_t around for every picture. It would
+ have to be kept uptodate every time something changes about the
+ picture.
+
+ - Break the X server ABI and simply have the relevant parameter
+ stored in the pixman image. This would have the additional benefits
+ that:
+
+ - We can get rid of the annoying repeat field which is duplicated
+ elsewhere.
+
+ - We can use pixman_color_t and pixman_gradient_stop_t
+ etc. instead of the types that are defined in
+ renderproto.h
+
diff --git a/gfx/cairo/libpixman/src/Makefile.in b/gfx/cairo/libpixman/src/Makefile.in
new file mode 100644
index 000000000..f119333b4
--- /dev/null
+++ b/gfx/cairo/libpixman/src/Makefile.in
@@ -0,0 +1,10 @@
+# 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/.
+
+include $(topsrcdir)/config/rules.mk
+
+# The ARM asm functions here don't appreciate being called by functions
+# compiled with -mapcs-frame. See bug 832752.
+CXXFLAGS := $(filter-out -mapcs-frame,$(CXXFLAGS))
+CFLAGS := $(filter-out -mapcs-frame,$(CFLAGS))
diff --git a/gfx/cairo/libpixman/src/make-combine.pl b/gfx/cairo/libpixman/src/make-combine.pl
new file mode 100644
index 000000000..210a5da12
--- /dev/null
+++ b/gfx/cairo/libpixman/src/make-combine.pl
@@ -0,0 +1,86 @@
+$usage = "Usage: combine.pl { 8 | 16 } < pixman-combine.c.template";
+
+$#ARGV == 0 or die $usage;
+
+# Get the component size.
+$size = int($ARGV[0]);
+$size == 8 or $size == 16 or die $usage;
+
+$pixel_size = $size * 4;
+$half_pixel_size = $size * 2;
+
+sub mask {
+ my $str = shift;
+ my $suffix;
+ $suffix = "ULL" if $size > 8;
+
+ return "0x" . $str . $suffix;
+}
+
+# Generate mask strings.
+$nibbles = $size / 4;
+$mask = "f" x $nibbles;
+$zero_mask = "0" x $nibbles;
+$one_half = "8" . "0" x ($nibbles - 1);
+
+print "/* WARNING: This file is generated by combine.pl from combine.inc.\n";
+print " Please edit one of those files rather than this one. */\n";
+print "\n";
+
+print "#line 1 \"pixman-combine.c.template\"\n";
+
+$mask_ = mask($mask);
+$one_half_ = mask($one_half);
+$g_mask = mask($mask . $zero_mask);
+$b_mask = mask($mask . $zero_mask x 2);
+$a_mask = mask($mask . $zero_mask x 3);
+$rb_mask = mask($mask . $zero_mask . $mask);
+$ag_mask = mask($mask . $zero_mask . $mask . $zero_mask);
+$rb_one_half = mask($one_half . $zero_mask . $one_half);
+$rb_mask_plus_one = mask("1" . $zero_mask x 2 . "1" . $zero_mask);
+
+while (<STDIN>) {
+ # Mask and 1/2 value for a single component.
+ s/#define COMPONENT_SIZE\b/$& $size/;
+ s/#define MASK\b/$& $mask_/;
+ s/#define ONE_HALF\b/$& $one_half_/;
+
+ # Shifts and masks for green, blue, and alpha.
+ s/#define G_SHIFT\b/$& $size/;
+ s/#define R_SHIFT\b/$& $size * 2/;
+ s/#define A_SHIFT\b/$& $size * 3/;
+ s/#define G_MASK\b/$& $g_mask/;
+ s/#define R_MASK\b/$& $b_mask/;
+ s/#define A_MASK\b/$& $a_mask/;
+
+ # Special values for dealing with red + blue at the same time.
+ s/#define RB_MASK\b/$& $rb_mask/;
+ s/#define AG_MASK\b/$& $ag_mask/;
+ s/#define RB_ONE_HALF\b/$& $rb_one_half/;
+ s/#define RB_MASK_PLUS_ONE\b/$& $rb_mask_plus_one/;
+
+ # Add 32/64 suffix to combining function types.
+ s/\bCombineFunc\b/CombineFunc$pixel_size/;
+ s/\bFbComposeFunctions\b/FbComposeFunctions$pixel_size/;
+ s/combine_width/combine_$pixel_size/;
+ s/_pixman_setup_combiner_functions_width/_pixman_setup_combiner_functions_$pixel_size/;
+ s/UNc/UN$size/g;
+ s/ALPHA_c/ALPHA_$size/g;
+ s/RED_c/RED_$size/g;
+ s/GREEN_c/GREEN_$size/g;
+ s/BLUE_c/BLUE_$size/g;
+
+ # Convert comp*_t values into the appropriate real types.
+ s/comp1_t/uint${size}_t/g;
+ s/comp2_t/uint${half_pixel_size}_t/g;
+ s/comp4_t/uint${pixel_size}_t/g;
+
+ # Change the function table name for the 64-bit version.
+ s/pixman_composeFunctions/pixman_composeFunctions64/ if $size == 16;
+
+ # Change the header for the 64-bit version
+ s/pixman-combine.h/pixman-combine64.h/ if $size == 16;
+ s/pixman-combine.h/pixman-combine32.h/ if $size == 8;
+
+ print;
+}
diff --git a/gfx/cairo/libpixman/src/moz.build b/gfx/cairo/libpixman/src/moz.build
new file mode 100644
index 000000000..e9e43a12b
--- /dev/null
+++ b/gfx/cairo/libpixman/src/moz.build
@@ -0,0 +1,163 @@
+# -*- 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',
+]
+
+# Apple's arm assembler doesn't support the same syntax as
+# the standard GNU assembler, so use the C fallback paths for now.
+# This may be fixable if clang's ARM/iOS assembler improves into a
+# viable solution in the future.
+if CONFIG['OS_ARCH'] != 'Darwin' and CONFIG['GNU_CC']:
+ if CONFIG['HAVE_ARM_NEON']:
+ SOURCES += [
+ 'pixman-arm-neon-asm-bilinear.S',
+ 'pixman-arm-neon-asm.S',
+ ]
+ if CONFIG['HAVE_ARM_SIMD']:
+ SOURCES += [
+ 'pixman-arm-simd-asm-scaled.S',
+ 'pixman-arm-simd-asm.S',
+ ]
+ if CONFIG['CLANG_CXX']:
+ ASFLAGS += [
+ '-no-integrated-as',
+ ]
+
+SOURCES += [
+ 'pixman-access-accessors.c',
+ 'pixman-access.c',
+ 'pixman-arm.c',
+ 'pixman-bits-image.c',
+ 'pixman-combine-float.c',
+ 'pixman-combine16.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.
+ALLOW_COMPILER_WARNINGS = True
+
+FINAL_LIBRARY = 'gkmedias'
+LOCAL_INCLUDES += [
+ '../../cairo/src',
+]
+
+if CONFIG['MOZ_USE_PTHREADS']:
+ DEFINES['HAVE_PTHREAD_SETSPECIFIC'] = True
+
+if CONFIG['_MSC_VER']:
+ DEFINES['PIXMAN_USE_XP_DLL_TLS_WORKAROUND'] = True
+
+DEFINES['PACKAGE'] = 'mozpixman'
+
+DEFINES['_USE_MATH_DEFINES'] = True
+
+use_mmx = False
+use_sse2 = False
+use_vmx = False
+use_arm_simd_gcc = False
+use_arm_neon_gcc = False
+if '86' in CONFIG['OS_TEST']:
+ use_sse2 = True
+ if '64' not in CONFIG['OS_TEST']:
+ if CONFIG['_MSC_VER']:
+ use_mmx = True
+ if CONFIG['GNU_CC']:
+ use_mmx = True
+elif 'ppc' in CONFIG['OS_TEST']:
+ if CONFIG['GNU_CC']:
+ use_vmx = True
+# Apple's arm assembler doesn't support the same syntax as
+# the standard GNU assembler, so use the C fallback paths for now.
+# This may be fixable if clang's ARM/iOS assembler improves into a
+# viable solution in the future.
+elif 'arm' in CONFIG['OS_TEST']:
+ if CONFIG['OS_ARCH'] != 'Darwin':
+ if CONFIG['HAVE_ARM_SIMD']:
+ use_arm_simd_gcc = True
+ if CONFIG['HAVE_ARM_NEON']:
+ use_arm_neon_gcc = True
+
+if use_mmx:
+ DEFINES['USE_MMX'] = True
+ SOURCES += ['pixman-mmx.c']
+ SOURCES['pixman-mmx.c'].flags += CONFIG['MMX_FLAGS']
+ if CONFIG['GNU_CC']:
+ SOURCES['pixman-mmx.c'].flags += [
+ '-Winline',
+ '--param inline-unit-growth=10000',
+ '--param large-function-growth=10000',
+ ]
+
+if use_sse2:
+ DEFINES['USE_SSE'] = True
+ DEFINES['USE_SSE2'] = True
+ SOURCES += ['pixman-sse2.c']
+ SOURCES['pixman-sse2.c'].flags += CONFIG['SSE_FLAGS'] + CONFIG['SSE2_FLAGS']
+ if CONFIG['GNU_CC']:
+ SOURCES['pixman-sse2.c'].flags += ['-Winline']
+
+if use_vmx:
+ DEFINES['USE_VMX'] = True
+ SOURCES += ['pixman-vmx.c']
+ SOURCES['pixman-vmx.c'].flags += ['-maltivec']
+
+if use_arm_simd_gcc:
+ DEFINES['USE_ARM_SIMD'] = True
+ SOURCES += ['pixman-arm-simd.c']
+
+if use_arm_neon_gcc:
+ DEFINES['USE_ARM_NEON'] = True
+ SOURCES += ['pixman-arm-neon.c']
+ SOURCES['pixman-arm-neon.c'].flags += CONFIG['NEON_FLAGS']
+
+# Suppress warnings in third-party code.
+if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+ CFLAGS += [
+ '-Wno-address',
+ '-Wno-missing-field-initializers',
+ '-Wno-sign-compare',
+ '-Wno-unused', # too many unused warnings; ignore
+ ]
+if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
+ CFLAGS += [
+ '-Wno-incompatible-pointer-types',
+ '-Wno-tautological-compare',
+ '-Wno-tautological-constant-out-of-range-compare',
+ ]
+if CONFIG['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 000000000..3263582f1
--- /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 000000000..00a02140a
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-access.c
@@ -0,0 +1,1492 @@
+/*
+ *
+ * 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 <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#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) \
+ ((READ (img, (((uint8_t *)(l)) + ((o) * 3) + 0)) << 16) | \
+ (READ (img, (((uint8_t *)(l)) + ((o) * 3) + 1)) << 8) | \
+ (READ (img, (((uint8_t *)(l)) + ((o) * 3) + 2)) << 0))
+#else
+#define FETCH_24(img,l,o) \
+ ((READ (img, (((uint8_t *)(l)) + ((o) * 3) + 0)) << 0) | \
+ (READ (img, (((uint8_t *)(l)) + ((o) * 3) + 1)) << 8) | \
+ (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 = 1 << (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 = 1 << ((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 (pixman_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->bits.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 (pixman_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 (pixman_image_t *image, \
+ int x, \
+ int y, \
+ int width, \
+ uint32_t * buffer, \
+ const uint32_t *mask) \
+ { \
+ uint8_t *bits = \
+ (uint8_t *)(image->bits.bits + y * image->bits.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 ((pixman_image_t *)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 (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t * b,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + y * image->bits.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 (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t * b,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + y * image->bits.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 */
+static void
+fetch_scanline_x2r10g10b10_float (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t * b,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + y * image->bits.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 (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t * b,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + y * image->bits.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 (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t * b,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + y * image->bits.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 (pixman_image_t *image,
+ int x,
+ int line,
+ int width,
+ uint32_t * buffer,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + image->bits.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 (pixman_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 *****************************/
+
+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 ************************************/
+
+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)
+ {
+ uint16_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)
+ {
+ uint16_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)
+ {
+ uint16_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)
+ {
+ uint16_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)
+ {
+ uint8_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);
+ }
+}
+
+static void
+store_scanline_16 (bits_image_t * image,
+ int x,
+ int y,
+ int width,
+ const uint32_t *v)
+{
+ uint16_t *bits = (uint16_t*)(image->bits + image->rowstride * y);
+ uint16_t *values = (uint16_t *)v;
+ uint16_t *pixel = bits + x;
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ WRITE (image, pixel++, values[i]);
+ }
+}
+
+static void
+fetch_scanline_16 (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t * b,
+ const uint32_t *mask)
+{
+ const uint16_t *bits = (uint16_t*)(image->bits.bits + y * image->bits.rowstride);
+ const uint16_t *pixel = bits + x;
+ int i;
+ uint16_t *buffer = (uint16_t *)b;
+
+ for (i = 0; i < width; ++i)
+ {
+ *buffer++ = READ (image, pixel++);
+ }
+}
+
+
+/*
+ * 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 (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t * buffer,
+ const uint32_t *mask)
+{
+ image->bits.fetch_scanline_32 (image, x, y, width, buffer, NULL);
+
+ pixman_expand_to_float ((argb_t *)buffer, buffer, image->bits.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 (pixman_image_t *image,
+ int x,
+ int y,
+ int width,
+ uint32_t *buffer,
+ const uint32_t *mask)
+{
+ const uint32_t *bits = image->bits.bits + y * image->bits.rowstride;
+ const uint32_t *pixel = (uint32_t *)bits + x;
+ const uint32_t *end = pixel + width;
+ uint32_t tmp;
+
+ while (pixel < end)
+ {
+ uint8_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);
+ uint8_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)
+ {
+ uint8_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_16;
+ 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_16;
+ store_scanline_t store_scanline_32;
+ store_scanline_t store_scanline_float;
+} format_info_t;
+
+#define FORMAT_INFO(format) \
+ { \
+ PIXMAN_ ## format, \
+ NULL, \
+ fetch_scanline_ ## format, \
+ fetch_scanline_generic_float, \
+ fetch_pixel_ ## format, \
+ fetch_pixel_generic_float, \
+ NULL, \
+ store_scanline_ ## format, \
+ store_scanline_generic_float \
+ }
+#define FORMAT_INFO16(format) \
+ { \
+ PIXMAN_ ## format, \
+ fetch_scanline_16, \
+ fetch_scanline_ ## format, \
+ fetch_scanline_generic_float, \
+ fetch_pixel_ ## format, \
+ fetch_pixel_generic_float, \
+ store_scanline_16, \
+ 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,
+ NULL,
+ fetch_scanline_a8r8g8b8_32_sRGB, fetch_scanline_a8r8g8b8_sRGB_float,
+ fetch_pixel_a8r8g8b8_32_sRGB, fetch_pixel_a8r8g8b8_sRGB_float,
+ NULL,
+ store_scanline_a8r8g8b8_32_sRGB, store_scanline_a8r8g8b8_sRGB_float,
+ },
+
+/* 24bpp formats */
+ FORMAT_INFO (r8g8b8),
+ FORMAT_INFO (b8g8r8),
+
+/* 16bpp formats */
+ FORMAT_INFO16 (r5g6b5),
+ FORMAT_INFO16 (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 */
+
+ { PIXMAN_a2r10g10b10,
+ NULL, NULL, fetch_scanline_a2r10g10b10_float,
+ fetch_pixel_generic_lossy_32, fetch_pixel_a2r10g10b10_float,
+ NULL, NULL, store_scanline_a2r10g10b10_float },
+
+ { PIXMAN_x2r10g10b10,
+ NULL, NULL, fetch_scanline_x2r10g10b10_float,
+ fetch_pixel_generic_lossy_32, fetch_pixel_x2r10g10b10_float,
+ NULL, NULL, store_scanline_x2r10g10b10_float },
+
+ { PIXMAN_a2b10g10r10,
+ NULL, NULL, fetch_scanline_a2b10g10r10_float,
+ fetch_pixel_generic_lossy_32, fetch_pixel_a2b10g10r10_float,
+ NULL, NULL, store_scanline_a2b10g10r10_float },
+
+ { PIXMAN_x2b10g10r10,
+ NULL, NULL, fetch_scanline_x2b10g10r10_float,
+ fetch_pixel_generic_lossy_32, fetch_pixel_x2b10g10r10_float,
+ NULL, NULL, store_scanline_x2b10g10r10_float },
+
+/* YUV formats */
+ { PIXMAN_yuy2,
+ NULL, fetch_scanline_yuy2, fetch_scanline_generic_float,
+ fetch_pixel_yuy2, fetch_pixel_generic_float,
+ NULL, NULL, NULL },
+
+ { PIXMAN_yv12,
+ NULL, fetch_scanline_yv12, fetch_scanline_generic_float,
+ fetch_pixel_yv12, fetch_pixel_generic_float,
+ NULL, 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_16 = info->fetch_scanline_16;
+ 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_16 = info->store_scanline_16;
+ 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 000000000..8e0b03621
--- /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-common.h b/gfx/cairo/libpixman/src/pixman-arm-common.h
new file mode 100644
index 000000000..b598502bb
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-common.h
@@ -0,0 +1,428 @@
+/*
+ * 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)
+
+/* Provide entries for the fast path table */
+#define PIXMAN_ARM_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 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_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), \
+ 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, \
+ NULL, src_type, uint32_t, dst_type, COVER, FLAG_NONE) \
+FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_none_##op, \
+ scaled_bilinear_scanline_##cputype##_##name##_##op, \
+ NULL, src_type, uint32_t, dst_type, NONE, FLAG_NONE) \
+FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_pad_##op, \
+ scaled_bilinear_scanline_##cputype##_##name##_##op, \
+ NULL, src_type, uint32_t, dst_type, PAD, FLAG_NONE) \
+FAST_BILINEAR_MAINLOOP_COMMON (cputype##_##name##_normal_##op, \
+ scaled_bilinear_scanline_##cputype##_##name##_##op, \
+ NULL, 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, \
+ NULL, 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, \
+ NULL, 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, \
+ NULL, 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, \
+ NULL, src_type, uint8_t, dst_type, NORMAL, \
+ FLAG_HAVE_NON_SOLID_MASK)
+
+
+#endif
diff --git a/gfx/cairo/libpixman/src/pixman-arm-detect-win32.asm b/gfx/cairo/libpixman/src/pixman-arm-detect-win32.asm
new file mode 100644
index 000000000..8f5d5eb2a
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-detect-win32.asm
@@ -0,0 +1,21 @@
+ area pixman_msvc, code, readonly
+
+ export pixman_msvc_try_arm_simd_op
+
+pixman_msvc_try_arm_simd_op
+ ;; I don't think the msvc arm asm knows how to do SIMD insns
+ ;; uqadd8 r3,r3,r3
+ dcd 0xe6633f93
+ mov pc,lr
+ endp
+
+ export pixman_msvc_try_arm_neon_op
+
+pixman_msvc_try_arm_neon_op
+ ;; I don't think the msvc arm asm knows how to do NEON insns
+ ;; veor d0,d0,d0
+ dcd 0xf3000110
+ mov pc,lr
+ endp
+
+ end
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 000000000..e37b5c298
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm-bilinear.S
@@ -0,0 +1,1368 @@
+/*
+ * 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-neon-asm.h"
+
+/*
+ * Bilinear macros from pixman-arm-neon-asm.S
+ */
+
+/* Supplementary macro for setting function attributes */
+.macro pixman_asm_function fname
+ .func fname
+ .global fname
+#ifdef __ELF__
+ .hidden fname
+ .type fname, %function
+#endif
+fname:
+.endm
+
+/*
+ * 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
+
+.endfunc
+
+.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 000000000..d0e943d71
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.S
@@ -0,0 +1,3650 @@
+/*
+ * 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
+ .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-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 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_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
+ .vsave {d8-d15}
+ 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)
+ .vsave {d8-d15}
+ 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
+ .vsave {d8-d15}
+ 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
+ .vsave {d8-d15}
+ 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
+ .vsave {d8-d15}
+ 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
+ .vsave {d8-d15}
+ 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
+ .vsave {d8-d15}
+ 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
+ .vsave {d8-d15}
+ 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)
+ .vsave {d8-d15}
+ 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 */
+
+/******************************************************************************/
+
+/* Supplementary macro for setting function attributes */
+.macro pixman_asm_function fname
+ .func fname
+ .global fname
+#ifdef __ELF__
+ .hidden fname
+ .type fname, %function
+#endif
+fname:
+.endm
+
+/*
+ * 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
+
+ .fnstart
+ mov ip, sp
+ .save {r4, r5, r6, r7, r8, r9}
+ 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
+ .vsave {d8-d15}
+ 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
+ .fnend
+
+ .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
+.endfunc
+
+.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 000000000..f50ade3ef
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-neon-asm.h
@@ -0,0 +1,1204 @@
+/*
+ * 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 sofware
+ * 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
+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
+ 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
+
+ .func fname
+ .global fname
+ /* For ELF format also set function visibility to hidden */
+#ifdef __ELF__
+ .hidden fname
+ .type fname, %function
+#endif
+fname:
+ .fnstart
+ .save {r4-r12, lr}
+ 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
+ .save {r0, r1}
+ 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 */
+ .fnend
+
+ .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
+ .endfunc
+.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
+
+ .func fname
+ .global fname
+ /* For ELF format also set function visibility to hidden */
+#ifdef __ELF__
+ .hidden fname
+ .type fname, %function
+#endif
+fname:
+ .fnstart
+ .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]
+ .save {r4-r8, lr}
+ 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
+
+ .fnend
+ .endfunc
+.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
+ .vsave {d8-d15}
+ 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 000000000..d902193cf
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-neon.c
@@ -0,0 +1,513 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <string.h>
+#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)
+static force_inline void
+pixman_scaled_bilinear_scanline_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)
+{
+ pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon (dst, src_top, src_bottom, wt, wb, vx, unit_x, w);
+}
+
+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 */
+ uint32_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 inline void op_bilinear_over_8888_0565(uint16_t *dst, const uint32_t *mask, const uint32_t *src, int width)
+{
+ pixman_composite_over_8888_0565_asm_neon (width, 1, dst, 0, src, 0);
+}
+
+FAST_BILINEAR_MAINLOOP_COMMON (neon_8888_0565_cover_OVER,
+ pixman_scaled_bilinear_scanline_8888_8888_SRC, op_bilinear_over_8888_0565,
+ uint32_t, uint32_t, uint16_t,
+ COVER, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (neon_8888_0565_pad_OVER,
+ pixman_scaled_bilinear_scanline_8888_8888_SRC, op_bilinear_over_8888_0565,
+ uint32_t, uint32_t, uint16_t,
+ PAD, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (neon_8888_0565_none_OVER,
+ pixman_scaled_bilinear_scanline_8888_8888_SRC, op_bilinear_over_8888_0565,
+ uint32_t, uint32_t, uint16_t,
+ NONE, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (neon_8888_0565_normal_OVER,
+ pixman_scaled_bilinear_scanline_8888_8888_SRC, op_bilinear_over_8888_0565,
+ uint32_t, uint32_t, uint16_t,
+ NORMAL, FLAG_NONE)
+
+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, 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, a8r8g8b8, 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, 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, a8r8g8b8, a8r8g8b8, a8r8g8b8, neon_composite_add_8888_8888_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, 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, a8r8g8b8, neon_composite_out_reverse_8_8888),
+ PIXMAN_STD_FAST_PATH (OUT_REVERSE, a8, null, a8b8g8r8, neon_composite_out_reverse_8_8888),
+
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, neon_8888_8888),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, neon_8888_8888),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, x8r8g8b8, neon_8888_8888),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, x8b8g8r8, neon_8888_8888),
+
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8r8g8b8, r5g6b5, neon_8888_0565),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (OVER, a8b8g8r8, b5g6r5, neon_8888_0565),
+
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, r5g6b5, neon_8888_0565),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, r5g6b5, neon_8888_0565),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, b5g6r5, neon_8888_0565),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, x8b8g8r8, b5g6r5, neon_8888_0565),
+
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, b5g6r5, x8b8g8r8, neon_0565_8888),
+ PIXMAN_ARM_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_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_FAST_PATH (OVER, a8r8g8b8, r5g6b5, neon_8888_0565),
+
+ { 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 000000000..711099548
--- /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)
+ *
+ */
+
+/* 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
+
+/* Supplementary macro for setting function attributes */
+.macro pixman_asm_function fname
+ .func fname
+ .global fname
+#ifdef __ELF__
+ .hidden fname
+ .type fname, %function
+#endif
+fname:
+.endm
+
+/*
+ * 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
+ ldrne&t TMP1, [SRC, TMP1]
+ strne&t TMP1, [DST]
+ /* 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
+.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 000000000..c20968879
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.S
@@ -0,0 +1,613 @@
+/*
+ * 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)
+ *
+ */
+
+/* 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-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 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
+
+/******************************************************************************/
+
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 000000000..496e37e30
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-simd-asm.h
@@ -0,0 +1,912 @@
+/*
+ * 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
+
+/*
+ * 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
+
+/*
+ * 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
+ op&m&cond&ia base!, {WK&reg0,WK&reg1,WK&reg2,WK&reg3}
+ .endif
+ .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&reg0,WK&reg1}
+ .endif
+ .elseif numbytes == 4
+ op&r&cond WK&reg0, [base], #4
+ .elseif numbytes == 2
+ op&r&cond&h WK&reg0, [base], #2
+ .elseif numbytes == 1
+ op&r&cond&b WK&reg0, [base], #1
+ .else
+ .error "unsupported size: numbytes"
+ .endif
+.endm
+
+.macro pixst_baseupdated cond, numbytes, reg0, reg1, reg2, reg3, base
+ .if numbytes == 16
+ stm&cond&db base, {WK&reg0,WK&reg1,WK&reg2,WK&reg3}
+ .elseif numbytes == 8
+ stm&cond&db base, {WK&reg0,WK&reg1}
+ .elseif numbytes == 4
+ str&cond WK&reg0, [base, #-4]
+ .elseif numbytes == 2
+ str&cond&h WK&reg0, [base, #-2]
+ .elseif numbytes == 1
+ str&cond&b WK&reg0, [base, #-1]
+ .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, #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
+ movs SCRATCH, WK0, lsl #32-1 /* C,N = bits 1,0 of DST */
+.endm
+
+.macro test_bits_3_2_ptr
+ movs SCRATCH, WK0, lsl #32-3 /* C,N = bits 3, 2 of DST */
+.endm
+
+.macro leading_15bytes process_head, process_tail
+ /* On entry, WK0 bits 0-3 = number of bytes until destination is 16-byte aligned */
+ /* 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, 1
+ .elseif dst_w_bpp == 16
+ test_bits_1_0_ptr
+ conditional_process1 cs, process_head, process_tail, 2, 2, 1, 1, 1
+ .endif
+ conditional_process2 test_bits_3_2_ptr, mi, cs, process_head, process_tail, 4, 8, 1, 2, 1, 1, 1
+.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)
+ /* 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
+ b 112f
+111:
+ .endif
+ process_inner_loop process_head, process_tail, unaligned_src, unaligned_mask, 0
+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
+ preload_trailing dst_r_bpp, dst_bpp_shift, DST
+ 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
+
+ .func fname
+ .global fname
+ /* For ELF format also set function visibility to hidden */
+#ifdef __ELF__
+ .hidden fname
+ .type fname, %function
+#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 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) */
+
+fname:
+ .fnstart
+ .save {r4-r11, lr}
+ push {r4-r11, lr} /* save all registers */
+
+ subs Y, Y, #1
+ blo 199f
+
+#ifdef DEBUG_PARAMS
+ .pad #9*4
+ 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
+
+ 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
+ .endif
+151: /* New line */
+ newline
+ preload_leading_step1 src_bpp, WK1, SRC
+ preload_leading_step1 mask_bpp, WK2, MASK
+ preload_leading_step1 dst_r_bpp, WK3, DST
+
+ tst DST, #15
+ beq 154f
+ rsb WK0, DST, #0 /* bits 0-3 = number of leading bytes until destination aligned */
+ .if (src_bpp != 0 && src_bpp != 2*dst_w_bpp) || (mask_bpp != 0 && mask_bpp != 2*dst_w_bpp)
+ PF and, WK0, WK0, #15
+ .endif
+
+ preload_leading_step2 src_bpp, src_bpp_shift, WK1, SRC
+ preload_leading_step2 mask_bpp, mask_bpp_shift, WK2, MASK
+ preload_leading_step2 dst_r_bpp, dst_bpp_shift, WK3, DST
+
+ 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
+ .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
+ .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
+ preload_line 0, dst_r_bpp, dst_bpp_shift, DST
+
+ sub X, X, #128/dst_w_bpp /* simplifies inner loop termination */
+ tst DST, #15
+ beq 164f
+ rsb WK0, DST, #0 /* bits 0-3 = 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
+ preload_line 1, dst_r_bpp, dst_bpp_shift, DST
+
+ .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
+
+197:
+ .if (flags) & FLAG_SPILL_LINE_VARS
+ add sp, sp, #LINE_SAVED_REG_COUNT*4
+ .endif
+198:
+ cleanup
+
+#ifdef DEBUG_PARAMS
+ add sp, sp, #9*4 /* junk the debug copy of arguments */
+#endif
+199:
+ pop {r4-r11, pc} /* exit */
+ .fnend
+
+ .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
+ .endfunc
+.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 000000000..af062e19d
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm-simd.c
@@ -0,0 +1,257 @@
+/*
+ * 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 <config.h>
+#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, 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_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_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 (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 (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_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, r5g6b5, r5g6b5, armv6_0565_0565),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, b5g6r5, b5g6r5, armv6_0565_0565),
+
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, a8r8g8b8, armv6_8888_8888),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8r8g8b8, x8r8g8b8, armv6_8888_8888),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, x8r8g8b8, x8r8g8b8, armv6_8888_8888),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, a8b8g8r8, armv6_8888_8888),
+ PIXMAN_ARM_SIMPLE_NEAREST_FAST_PATH (SRC, a8b8g8r8, x8b8g8r8, armv6_8888_8888),
+ PIXMAN_ARM_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 000000000..145bd9050
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-arm.c
@@ -0,0 +1,229 @@
+/*
+ * 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 <config.h>
+#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 <windows.h>
+
+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 */
+
+static arm_cpu_features_t
+detect_cpu_features (void)
+{
+ arm_cpu_features_t features = 0;
+ char buf[1024];
+ char* pos;
+ const char* ver_token = "CPU architecture: ";
+ FILE* f = fopen("/proc/cpuinfo", "r");
+ if (!f) {
+ return features;
+ }
+
+ fread(buf, sizeof(char), sizeof(buf), f);
+ fclose(f);
+ pos = strstr(buf, ver_token);
+ if (pos) {
+ char vchar = *(pos + strlen(ver_token));
+ if (vchar >= '0' && vchar <= '9') {
+ int ver = vchar - '0';
+ if (ver >= 7)
+ features |= ARM_V7;
+ }
+ }
+ if (strstr(buf, "neon") != NULL)
+ features |= ARM_NEON;
+ if (strstr(buf, "vfp") != NULL)
+ features |= ARM_VFP;
+
+ return features;
+}
+
+#elif defined (__linux__) /* linux ELF */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <elf.h>
+
+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;
+}
+
+#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
+
+ return imp;
+}
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 000000000..e9d2fb69c
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-bits-image.c
@@ -0,0 +1,1849 @@
+/*
+ * 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á <andrelrt@gmail.com>
+ *
+ * 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 <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pixman-private.h"
+#include "pixman-combine32.h"
+#include "pixman-inlines.h"
+
+static uint32_t *
+_pixman_image_get_scanline_generic_float (pixman_iter_t * iter,
+ const uint32_t *mask)
+{
+ pixman_iter_get_scanline_t fetch_32 = iter->data;
+ uint32_t *buffer = iter->buffer;
+
+ fetch_32 (iter, NULL);
+
+ pixman_expand_to_float ((argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+
+ return iter->buffer;
+}
+
+/* Fetch functions */
+
+static force_inline uint32_t
+fetch_pixel_no_alpha (bits_image_t *image,
+ int x, int y, pixman_bool_t check_bounds)
+{
+ if (check_bounds &&
+ (x < 0 || x >= image->width || y < 0 || y >= image->height))
+ {
+ return 0;
+ }
+
+ return image->fetch_pixel_32 (image, x, y);
+}
+
+typedef uint32_t (* get_pixel_t) (bits_image_t *image,
+ int x, int y, pixman_bool_t check_bounds);
+
+static force_inline uint32_t
+bits_image_fetch_pixel_nearest (bits_image_t *image,
+ pixman_fixed_t x,
+ pixman_fixed_t y,
+ get_pixel_t get_pixel)
+{
+ 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);
+
+ return get_pixel (image, x0, y0, FALSE);
+ }
+ else
+ {
+ return get_pixel (image, x0, y0, TRUE);
+ }
+}
+
+static force_inline uint32_t
+bits_image_fetch_pixel_bilinear (bits_image_t *image,
+ pixman_fixed_t x,
+ pixman_fixed_t y,
+ get_pixel_t get_pixel)
+{
+ 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;
+
+ 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);
+
+ tl = get_pixel (image, x1, y1, FALSE);
+ bl = get_pixel (image, x1, y2, FALSE);
+ tr = get_pixel (image, x2, y1, FALSE);
+ br = get_pixel (image, x2, y2, FALSE);
+ }
+ else
+ {
+ tl = get_pixel (image, x1, y1, TRUE);
+ tr = get_pixel (image, x2, y1, TRUE);
+ bl = get_pixel (image, x1, y2, TRUE);
+ br = get_pixel (image, x2, y2, TRUE);
+ }
+
+ return bilinear_interpolation (tl, tr, bl, br, distx, disty);
+}
+
+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;
+}
+
+static force_inline uint32_t
+bits_image_fetch_pixel_convolution (bits_image_t *image,
+ pixman_fixed_t x,
+ pixman_fixed_t y,
+ get_pixel_t get_pixel)
+{
+ 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;
+ 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)
+ {
+ uint32_t pixel;
+
+ if (repeat_mode != PIXMAN_REPEAT_NONE)
+ {
+ repeat (repeat_mode, &rx, width);
+ repeat (repeat_mode, &ry, height);
+
+ pixel = get_pixel (image, rx, ry, FALSE);
+ }
+ else
+ {
+ pixel = get_pixel (image, rx, ry, TRUE);
+ }
+
+ srtot += (int)RED_8 (pixel) * f;
+ sgtot += (int)GREEN_8 (pixel) * f;
+ sbtot += (int)BLUE_8 (pixel) * f;
+ satot += (int)ALPHA_8 (pixel) * f;
+ }
+
+ params++;
+ }
+ }
+
+ 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);
+
+ return ((satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot));
+}
+
+static uint32_t
+bits_image_fetch_pixel_separable_convolution (bits_image_t *image,
+ pixman_fixed_t x,
+ pixman_fixed_t y,
+ get_pixel_t get_pixel)
+{
+ 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;
+ 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)
+ {
+ pixman_fixed_t f;
+ uint32_t pixel;
+
+ if (repeat_mode != PIXMAN_REPEAT_NONE)
+ {
+ repeat (repeat_mode, &rx, width);
+ repeat (repeat_mode, &ry, height);
+
+ pixel = get_pixel (image, rx, ry, FALSE);
+ }
+ else
+ {
+ pixel = get_pixel (image, rx, ry, TRUE);
+ }
+
+ f = (fy * fx + 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);
+
+ return ((satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot));
+}
+
+static force_inline uint32_t
+bits_image_fetch_pixel_filtered (bits_image_t *image,
+ pixman_fixed_t x,
+ pixman_fixed_t y,
+ get_pixel_t get_pixel)
+{
+ switch (image->common.filter)
+ {
+ case PIXMAN_FILTER_NEAREST:
+ case PIXMAN_FILTER_FAST:
+ return bits_image_fetch_pixel_nearest (image, x, y, get_pixel);
+ break;
+
+ case PIXMAN_FILTER_BILINEAR:
+ case PIXMAN_FILTER_GOOD:
+ case PIXMAN_FILTER_BEST:
+ return bits_image_fetch_pixel_bilinear (image, x, y, get_pixel);
+ break;
+
+ case PIXMAN_FILTER_CONVOLUTION:
+ return bits_image_fetch_pixel_convolution (image, x, y, get_pixel);
+ break;
+
+ case PIXMAN_FILTER_SEPARABLE_CONVOLUTION:
+ return bits_image_fetch_pixel_separable_convolution (image, x, y, get_pixel);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static uint32_t *
+bits_image_fetch_affine_no_alpha (pixman_iter_t * iter,
+ 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;
+
+ pixman_fixed_t x, y;
+ pixman_fixed_t ux, uy;
+ 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 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 || mask[i])
+ {
+ buffer[i] = bits_image_fetch_pixel_filtered (
+ &image->bits, x, y, fetch_pixel_no_alpha);
+ }
+
+ x += ux;
+ y += uy;
+ }
+
+ return buffer;
+}
+
+/* General fetcher */
+static force_inline uint32_t
+fetch_pixel_general (bits_image_t *image, int x, int y, pixman_bool_t check_bounds)
+{
+ uint32_t pixel;
+
+ if (check_bounds &&
+ (x < 0 || x >= image->width || y < 0 || y >= image->height))
+ {
+ return 0;
+ }
+
+ 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);
+ }
+
+ return pixel;
+}
+
+static uint32_t *
+bits_image_fetch_general (pixman_iter_t *iter,
+ 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;
+
+ 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 || mask[i])
+ {
+ if (w != 0)
+ {
+ x0 = ((pixman_fixed_48_16_t)x << 16) / w;
+ y0 = ((pixman_fixed_48_16_t)y << 16) / w;
+ }
+ else
+ {
+ x0 = 0;
+ y0 = 0;
+ }
+
+ buffer[i] = bits_image_fetch_pixel_filtered (
+ &image->bits, x0, y0, fetch_pixel_general);
+ }
+
+ x += ux;
+ y += uy;
+ w += uw;
+ }
+
+ return 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 * 4 * 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 * 4 * 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);
+
+ buffer[k] = (satot << 24) | (srtot << 16) | (sgtot << 8) | (sbtot << 0);
+
+ 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 * 4 * y1;
+ row2 = (uint8_t *)bits->bits + bits->rowstride * 4 * 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 * 4 * 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 * 4 * 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 * 4 * 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 *(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)
+
+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 ((pixman_image_t *)image, x, y, w, buffer, NULL);
+ else
+ image->fetch_scanline_32 ((pixman_image_t *)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 ((pixman_image_t *)image, x, y, w, buffer, NULL);
+ else
+ image->fetch_scanline_32 ((pixman_image_t *)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
+ },
+
+#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,
+ bits_image_fetch_bilinear_no_repeat_8888,
+ _pixman_image_get_scanline_generic_float
+ },
+
+ { PIXMAN_x8r8g8b8,
+ FAST_BILINEAR_FLAGS,
+ bits_image_fetch_bilinear_no_repeat_8888,
+ _pixman_image_get_scanline_generic_float
+ },
+
+#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, \
+ bits_image_fetch_separable_convolution_affine_ ## name, \
+ _pixman_image_get_scanline_generic_float \
+ },
+
+#define BILINEAR_AFFINE_FAST_PATH(name, format, repeat) \
+ { PIXMAN_ ## format, \
+ GENERAL_BILINEAR_FLAGS | FAST_PATH_ ## repeat ## _REPEAT, \
+ bits_image_fetch_bilinear_affine_ ## name, \
+ _pixman_image_get_scanline_generic_float \
+ },
+
+#define NEAREST_AFFINE_FAST_PATH(name, format, repeat) \
+ { PIXMAN_ ## format, \
+ GENERAL_NEAREST_FLAGS | FAST_PATH_ ## repeat ## _REPEAT, \
+ bits_image_fetch_nearest_affine_ ## name, \
+ _pixman_image_get_scanline_generic_float \
+ },
+
+#define AFFINE_FAST_PATHS(name, format, repeat) \
+ SEPARABLE_CONVOLUTION_AFFINE_FAST_PATH(name, format, repeat) \
+ BILINEAR_AFFINE_FAST_PATH(name, format, repeat) \
+ NEAREST_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)
+
+ /* Affine, no alpha */
+ { PIXMAN_any,
+ (FAST_PATH_NO_ALPHA_MAP | FAST_PATH_HAS_TRANSFORM | FAST_PATH_AFFINE_TRANSFORM),
+ bits_image_fetch_affine_no_alpha,
+ _pixman_image_get_scanline_generic_float
+ },
+
+ /* General */
+ { PIXMAN_any,
+ 0,
+ bits_image_fetch_general,
+ _pixman_image_get_scanline_generic_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->data = info->get_scanline_32;
+ 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_16 (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_16 (image, x, y, width, buffer, mask);
+
+ return iter->buffer;
+}
+
+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, 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 (
+ (pixman_image_t *)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 (
+ (pixman_image_t *)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 (
+ (pixman_image_t *)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_16 (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_16 (image, x, y, width, buffer);
+
+ iter->y++;
+}
+
+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 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;
+
+ 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_16)
+ {
+ 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_16;
+ }
+ iter->write_back = dest_write_back_16;
+ }
+ else 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 (!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.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 000000000..06ce2037e
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine-float.c
@@ -0,0 +1,1018 @@
+/* -*- 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 <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include <float.h>
+
+#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
+
+#define IS_ZERO(f) (-FLT_MIN < (f) && (f) < FLT_MIN)
+
+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 (IS_ZERO (da))
+ f = 1.0f;
+ else
+ f = CLAMP (sa / da);
+ break;
+
+ case DA_OVER_SA:
+ if (IS_ZERO (sa))
+ f = 1.0f;
+ else
+ f = CLAMP (da / sa);
+ break;
+
+ case INV_SA_OVER_DA:
+ if (IS_ZERO (da))
+ f = 1.0f;
+ else
+ f = CLAMP ((1.0f - sa) / da);
+ break;
+
+ case INV_DA_OVER_SA:
+ if (IS_ZERO (sa))
+ f = 1.0f;
+ else
+ f = CLAMP ((1.0f - da) / sa);
+ break;
+
+ case ONE_MINUS_SA_OVER_DA:
+ if (IS_ZERO (da))
+ f = 0.0f;
+ else
+ f = CLAMP (1.0f - sa / da);
+ break;
+
+ case ONE_MINUS_DA_OVER_SA:
+ if (IS_ZERO (sa))
+ f = 0.0f;
+ else
+ f = CLAMP (1.0f - da / sa);
+ break;
+
+ case ONE_MINUS_INV_DA_OVER_SA:
+ if (IS_ZERO (sa))
+ f = 0.0f;
+ else
+ f = CLAMP (1.0f - (1.0f - da) / sa);
+ break;
+
+ case ONE_MINUS_INV_SA_OVER_DA:
+ if (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/acrobat/pdfs/PDF32000_2008.pdf
+ * The relevant chapters are 11.3.5 and 11.3.6.
+ * 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() being the blend function.
+ * Note that OVER is a special case of this operation, using B(Cb, Cs) = Cs
+ *
+ * These blend modes should match the SVG filter draft specification, as
+ * it has been designed to mirror ISO 32000. Note that at the current point
+ * no released draft exists that shows this, as the formulas have not been
+ * updated yet after the release of ISO 32000.
+ *
+ * The default implementation here uses the PDF_SEPARABLE_BLEND_MODE and
+ * PDF_NON_SEPARABLE_BLEND_MODE macros, which take the blend function as an
+ * argument. Note that this implementation operates on premultiplied colors,
+ * while the PDF specification does not. Therefore the code uses the formula
+ * ar.Cra = (1 – as) . Dca + (1 – ad) . Sca + B(Dca, ad, Sca, as)
+ */
+
+#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)
+
+static force_inline float
+blend_multiply (float sa, float s, float da, float d)
+{
+ return d * s;
+}
+
+static force_inline float
+blend_screen (float sa, float s, float da, float d)
+{
+ return d * sa + s * da - s * d;
+}
+
+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);
+}
+
+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;
+}
+
+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;
+}
+
+static force_inline float
+blend_color_dodge (float sa, float s, float da, float d)
+{
+ if (IS_ZERO (d))
+ return 0.0f;
+ else if (d * sa >= sa * da - s * da)
+ return sa * da;
+ else if (IS_ZERO (sa - s))
+ return sa * da;
+ else
+ return sa * sa * d / (sa - 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 (IS_ZERO (s))
+ return 0.0f;
+ else
+ return sa * (da - sa * (da - d) / 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);
+}
+
+static force_inline float
+blend_soft_light (float sa, float s, float da, float d)
+{
+ if (2 * s < sa)
+ {
+ if (IS_ZERO (da))
+ return d * sa;
+ else
+ return d * sa - d * (da - d) * (sa - 2 * s) / da;
+ }
+ else
+ {
+ if (IS_ZERO (da))
+ {
+ return 0.0f;
+ }
+ 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);
+ }
+ }
+}
+
+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;
+}
+
+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.
+ *
+ * These 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, ra);
+ *
+ * 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 - LUM (r × 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 they 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, rs)
+ *
+ *
+ *
+ *
+ * So, for the non-separable PDF blend modes, we have (using s, d for
+ * non-premultiplied colors, and S, D for premultiplied:
+ *
+ * Color:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (S/a_s, LUM (D/a_d), 1)
+ * = set_lum (S * a_d, a_s * LUM (D), a_s * a_d)
+ *
+ *
+ * Luminosity:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (D/a_d, LUM(S/a_s), 1)
+ * = set_lum (a_s * D, a_d * LUM(S), a_s * a_d)
+ *
+ *
+ * Saturation:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (set_sat (D/a_d, SAT (S/a_s)), LUM (D/a_d), 1)
+ * = set_lum (a_s * a_d * set_sat (D/a_d, SAT (S/a_s)),
+ * a_s * LUM (D), a_s * a_d)
+ * = set_lum (set_sat (a_s * D, a_d * SAT (S), a_s * LUM (D), a_s * a_d))
+ *
+ * Hue:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (set_sat (S/a_s, SAT (D/a_d)), LUM (D/a_d), 1)
+ * = set_lum (set_sat (a_d * S, a_s * SAT (D)), a_s * LUM (D), a_s * a_d)
+ *
+ */
+
+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 (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 (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 (IS_ZERO (t))
+ {
+ *mid = *max = 0.0f;
+ }
+ else
+ {
+ *mid = ((*mid - *min) * sat) / t;
+ *max = sat;
+ }
+
+ *min = 0.0f;
+}
+
+/*
+ * Hue:
+ * B(Cb, Cs) = set_lum (set_sat (Cs, SAT (Cb)), LUM (Cb))
+ */
+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:
+ * B(Cb, Cs) = set_lum (set_sat (Cb, SAT (Cs)), LUM (Cb))
+ */
+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:
+ * B(Cb, Cs) = set_lum (Cs, LUM (Cb))
+ */
+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:
+ * B(Cb, Cs) = set_lum (Cb, LUM (Cs))
+ */
+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-combine.c.template b/gfx/cairo/libpixman/src/pixman-combine.c.template
new file mode 100644
index 000000000..cd008d967
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine.c.template
@@ -0,0 +1,2461 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "pixman-private.h"
+
+#include "pixman-combine.h"
+
+/*** per channel helper functions ***/
+
+static void
+combine_mask_ca (comp4_t *src, comp4_t *mask)
+{
+ comp4_t a = *mask;
+
+ comp4_t x;
+ comp2_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;
+ UNcx4_MUL_UNcx4 (x, a);
+ *(src) = x;
+
+ UNcx4_MUL_UNc (a, xa);
+ *(mask) = a;
+}
+
+static void
+combine_mask_value_ca (comp4_t *src, const comp4_t *mask)
+{
+ comp4_t a = *mask;
+ comp4_t x;
+
+ if (!a)
+ {
+ *(src) = 0;
+ return;
+ }
+
+ if (a == ~0)
+ return;
+
+ x = *(src);
+ UNcx4_MUL_UNcx4 (x, a);
+ *(src) = x;
+}
+
+static void
+combine_mask_alpha_ca (const comp4_t *src, comp4_t *mask)
+{
+ comp4_t a = *(mask);
+ comp4_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;
+ }
+
+ UNcx4_MUL_UNc (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 'C'. Similarly, functions which deal with
+ * this difference will have two versions using the same convention.
+ */
+
+/*
+ * All of the composing functions
+ */
+
+static force_inline comp4_t
+combine_mask (const comp4_t *src, const comp4_t *mask, int i)
+{
+ comp4_t s, m;
+
+ if (mask)
+ {
+ m = *(mask + i) >> A_SHIFT;
+
+ if (!m)
+ return 0;
+ }
+
+ s = *(src + i);
+
+ if (mask)
+ UNcx4_MUL_UNc (s, m);
+
+ return s;
+}
+
+static void
+combine_clear (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ memset (dest, 0, width * sizeof(comp4_t));
+}
+
+static void
+combine_dst (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ return;
+}
+
+static void
+combine_src_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ if (!mask)
+ memcpy (dest, src, width * sizeof (comp4_t));
+ else
+ {
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+
+ *(dest + i) = s;
+ }
+ }
+}
+
+/* if the Src is opaque, call combine_src_u */
+static void
+combine_over_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t ia = ALPHA_c (~s);
+
+ UNcx4_MUL_UNc_ADD_UNcx4 (d, ia, s);
+ *(dest + i) = d;
+ }
+}
+
+/* if the Dst is opaque, this is a noop */
+static void
+combine_over_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t ia = ALPHA_c (~*(dest + i));
+ UNcx4_MUL_UNc_ADD_UNcx4 (s, ia, d);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Dst is opaque, call combine_src_u */
+static void
+combine_in_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t a = ALPHA_c (*(dest + i));
+ UNcx4_MUL_UNc (s, a);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Src is opaque, this is a noop */
+static void
+combine_in_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t a = ALPHA_c (s);
+ UNcx4_MUL_UNc (d, a);
+ *(dest + i) = d;
+ }
+}
+
+/* if the Dst is opaque, call combine_clear */
+static void
+combine_out_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t a = ALPHA_c (~*(dest + i));
+ UNcx4_MUL_UNc (s, a);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Src is opaque, call combine_clear */
+static void
+combine_out_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t a = ALPHA_c (~s);
+ UNcx4_MUL_UNc (d, a);
+ *(dest + i) = d;
+ }
+}
+
+/* if the Src is opaque, call combine_in_u */
+/* if the Dst is opaque, call combine_over_u */
+/* if both the Src and Dst are opaque, call combine_src_u */
+static void
+combine_atop_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t dest_a = ALPHA_c (d);
+ comp4_t src_ia = ALPHA_c (~s);
+
+ UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc (s, dest_a, d, src_ia);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Src is opaque, call combine_over_reverse_u */
+/* if the Dst is opaque, call combine_in_reverse_u */
+/* if both the Src and Dst are opaque, call combine_dst_u */
+static void
+combine_atop_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t src_a = ALPHA_c (s);
+ comp4_t dest_ia = ALPHA_c (~d);
+
+ UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc (s, dest_ia, d, src_a);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Src is opaque, call combine_over_u */
+/* if the Dst is opaque, call combine_over_reverse_u */
+/* if both the Src and Dst are opaque, call combine_clear */
+static void
+combine_xor_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t src_ia = ALPHA_c (~s);
+ comp4_t dest_ia = ALPHA_c (~d);
+
+ UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc (s, dest_ia, d, src_ia);
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_add_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ UNcx4_ADD_UNcx4 (d, s);
+ *(dest + i) = d;
+ }
+}
+
+/* if the Src is opaque, call combine_add_u */
+/* if the Dst is opaque, call combine_add_u */
+/* if both the Src and Dst are opaque, call combine_add_u */
+static void
+combine_saturate_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp2_t sa, da;
+
+ sa = s >> A_SHIFT;
+ da = ~d >> A_SHIFT;
+ if (sa > da)
+ {
+ sa = DIV_UNc (da, sa);
+ UNcx4_MUL_UNc (s, sa);
+ }
+ ;
+ UNcx4_ADD_UNcx4 (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/acrobat/pdfs/PDF32000_2008.pdf
+ * The relevant chapters are 11.3.5 and 11.3.6.
+ * 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() being the blend function.
+ * Note that OVER is a special case of this operation, using B(Cb, Cs) = Cs
+ *
+ * These blend modes should match the SVG filter draft specification, as
+ * it has been designed to mirror ISO 32000. Note that at the current point
+ * no released draft exists that shows this, as the formulas have not been
+ * updated yet after the release of ISO 32000.
+ *
+ * The default implementation here uses the PDF_SEPARABLE_BLEND_MODE and
+ * PDF_NON_SEPARABLE_BLEND_MODE macros, which take the blend function as an
+ * argument. Note that this implementation operates on premultiplied colors,
+ * while the PDF specification does not. Therefore the code uses the formula
+ * Cra = (1 – as) . Dca + (1 – ad) . Sca + B(Dca, ad, Sca, as)
+ */
+
+/*
+ * Multiply
+ * B(Dca, ad, Sca, as) = Dca.Sca
+ */
+
+static void
+combine_multiply_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t ss = s;
+ comp4_t src_ia = ALPHA_c (~s);
+ comp4_t dest_ia = ALPHA_c (~d);
+
+ UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc (ss, dest_ia, d, src_ia);
+ UNcx4_MUL_UNcx4 (d, s);
+ UNcx4_ADD_UNcx4 (d, ss);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_multiply_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t m = *(mask + i);
+ comp4_t s = *(src + i);
+ comp4_t d = *(dest + i);
+ comp4_t r = d;
+ comp4_t dest_ia = ALPHA_c (~d);
+
+ combine_mask_value_ca (&s, &m);
+
+ UNcx4_MUL_UNcx4_ADD_UNcx4_MUL_UNc (r, ~m, s, dest_ia);
+ UNcx4_MUL_UNcx4 (d, s);
+ UNcx4_ADD_UNcx4 (r, d);
+
+ *(dest + i) = r;
+ }
+}
+
+#define PDF_SEPARABLE_BLEND_MODE(name) \
+ static void \
+ combine_ ## name ## _u (pixman_implementation_t *imp, \
+ pixman_op_t op, \
+ comp4_t * dest, \
+ const comp4_t * src, \
+ const comp4_t * mask, \
+ int width) \
+ { \
+ int i; \
+ for (i = 0; i < width; ++i) { \
+ comp4_t s = combine_mask (src, mask, i); \
+ comp4_t d = *(dest + i); \
+ comp1_t sa = ALPHA_c (s); \
+ comp1_t isa = ~sa; \
+ comp1_t da = ALPHA_c (d); \
+ comp1_t ida = ~da; \
+ comp4_t result; \
+ \
+ result = d; \
+ UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc (result, isa, s, ida); \
+ \
+ *(dest + i) = result + \
+ (DIV_ONE_UNc (sa * (comp4_t)da) << A_SHIFT) + \
+ (blend_ ## name (RED_c (d), da, RED_c (s), sa) << R_SHIFT) + \
+ (blend_ ## name (GREEN_c (d), da, GREEN_c (s), sa) << G_SHIFT) + \
+ (blend_ ## name (BLUE_c (d), da, BLUE_c (s), sa)); \
+ } \
+ } \
+ \
+ static void \
+ combine_ ## name ## _ca (pixman_implementation_t *imp, \
+ pixman_op_t op, \
+ comp4_t * dest, \
+ const comp4_t * src, \
+ const comp4_t * mask, \
+ int width) \
+ { \
+ int i; \
+ for (i = 0; i < width; ++i) { \
+ comp4_t m = *(mask + i); \
+ comp4_t s = *(src + i); \
+ comp4_t d = *(dest + i); \
+ comp1_t da = ALPHA_c (d); \
+ comp1_t ida = ~da; \
+ comp4_t result; \
+ \
+ combine_mask_value_ca (&s, &m); \
+ \
+ result = d; \
+ UNcx4_MUL_UNcx4_ADD_UNcx4_MUL_UNc (result, ~m, s, ida); \
+ \
+ result += \
+ (DIV_ONE_UNc (ALPHA_c (m) * (comp4_t)da) << A_SHIFT) + \
+ (blend_ ## name (RED_c (d), da, RED_c (s), RED_c (m)) << R_SHIFT) + \
+ (blend_ ## name (GREEN_c (d), da, GREEN_c (s), GREEN_c (m)) << G_SHIFT) + \
+ (blend_ ## name (BLUE_c (d), da, BLUE_c (s), BLUE_c (m))); \
+ \
+ *(dest + i) = result; \
+ } \
+ }
+
+/*
+ * Screen
+ * B(Dca, ad, Sca, as) = Dca.sa + Sca.da - Dca.Sca
+ */
+static inline comp4_t
+blend_screen (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ return DIV_ONE_UNc (sca * da + dca * sa - sca * dca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (screen)
+
+/*
+ * Overlay
+ * B(Dca, Da, Sca, Sa) =
+ * if 2.Dca < Da
+ * 2.Sca.Dca
+ * otherwise
+ * Sa.Da - 2.(Da - Dca).(Sa - Sca)
+ */
+static inline comp4_t
+blend_overlay (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ comp4_t rca;
+
+ if (2 * dca < da)
+ rca = 2 * sca * dca;
+ else
+ rca = sa * da - 2 * (da - dca) * (sa - sca);
+ return DIV_ONE_UNc (rca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (overlay)
+
+/*
+ * Darken
+ * B(Dca, Da, Sca, Sa) = min (Sca.Da, Dca.Sa)
+ */
+static inline comp4_t
+blend_darken (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ comp4_t s, d;
+
+ s = sca * da;
+ d = dca * sa;
+ return DIV_ONE_UNc (s > d ? d : s);
+}
+
+PDF_SEPARABLE_BLEND_MODE (darken)
+
+/*
+ * Lighten
+ * B(Dca, Da, Sca, Sa) = max (Sca.Da, Dca.Sa)
+ */
+static inline comp4_t
+blend_lighten (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ comp4_t s, d;
+
+ s = sca * da;
+ d = dca * sa;
+ return DIV_ONE_UNc (s > d ? s : d);
+}
+
+PDF_SEPARABLE_BLEND_MODE (lighten)
+
+/*
+ * Color dodge
+ * B(Dca, Da, Sca, Sa) =
+ * if Dca == 0
+ * 0
+ * if Sca == Sa
+ * Sa.Da
+ * otherwise
+ * Sa.Da. min (1, Dca / Da / (1 - Sca/Sa))
+ */
+static inline comp4_t
+blend_color_dodge (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ if (sca >= sa)
+ {
+ return dca == 0 ? 0 : DIV_ONE_UNc (sa * da);
+ }
+ else
+ {
+ comp4_t rca = dca * sa / (sa - sca);
+ return DIV_ONE_UNc (sa * MIN (rca, da));
+ }
+}
+
+PDF_SEPARABLE_BLEND_MODE (color_dodge)
+
+/*
+ * Color burn
+ * B(Dca, Da, Sca, Sa) =
+ * if Dca == Da
+ * Sa.Da
+ * if Sca == 0
+ * 0
+ * otherwise
+ * Sa.Da.(1 - min (1, (1 - Dca/Da).Sa / Sca))
+ */
+static inline comp4_t
+blend_color_burn (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ if (sca == 0)
+ {
+ return dca < da ? 0 : DIV_ONE_UNc (sa * da);
+ }
+ else
+ {
+ comp4_t rca = (da - dca) * sa / sca;
+ return DIV_ONE_UNc (sa * (MAX (rca, da) - rca));
+ }
+}
+
+PDF_SEPARABLE_BLEND_MODE (color_burn)
+
+/*
+ * Hard light
+ * B(Dca, Da, Sca, Sa) =
+ * if 2.Sca < Sa
+ * 2.Sca.Dca
+ * otherwise
+ * Sa.Da - 2.(Da - Dca).(Sa - Sca)
+ */
+static inline comp4_t
+blend_hard_light (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ if (2 * sca < sa)
+ return DIV_ONE_UNc (2 * sca * dca);
+ else
+ return DIV_ONE_UNc (sa * da - 2 * (da - dca) * (sa - sca));
+}
+
+PDF_SEPARABLE_BLEND_MODE (hard_light)
+
+/*
+ * Soft light
+ * B(Dca, Da, Sca, Sa) =
+ * if (2.Sca <= Sa)
+ * Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa))
+ * otherwise if Dca.4 <= Da
+ * Dca.(Sa + (2.Sca - Sa).((16.Dca/Da - 12).Dca/Da + 3)
+ * otherwise
+ * (Dca.Sa + (SQRT (Dca/Da).Da - Dca).(2.Sca - Sa))
+ */
+static inline comp4_t
+blend_soft_light (comp4_t dca_org,
+ comp4_t da_org,
+ comp4_t sca_org,
+ comp4_t sa_org)
+{
+ double dca = dca_org * (1.0 / MASK);
+ double da = da_org * (1.0 / MASK);
+ double sca = sca_org * (1.0 / MASK);
+ double sa = sa_org * (1.0 / MASK);
+ double rca;
+
+ if (2 * sca < sa)
+ {
+ if (da == 0)
+ rca = dca * sa;
+ else
+ rca = dca * sa - dca * (da - dca) * (sa - 2 * sca) / da;
+ }
+ else if (da == 0)
+ {
+ rca = 0;
+ }
+ else if (4 * dca <= da)
+ {
+ rca = dca * sa +
+ (2 * sca - sa) * dca * ((16 * dca / da - 12) * dca / da + 3);
+ }
+ else
+ {
+ rca = dca * sa + (sqrt (dca * da) - dca) * (2 * sca - sa);
+ }
+ return rca * MASK + 0.5;
+}
+
+PDF_SEPARABLE_BLEND_MODE (soft_light)
+
+/*
+ * Difference
+ * B(Dca, Da, Sca, Sa) = abs (Dca.Sa - Sca.Da)
+ */
+static inline comp4_t
+blend_difference (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ comp4_t dcasa = dca * sa;
+ comp4_t scada = sca * da;
+
+ if (scada < dcasa)
+ return DIV_ONE_UNc (dcasa - scada);
+ else
+ return DIV_ONE_UNc (scada - dcasa);
+}
+
+PDF_SEPARABLE_BLEND_MODE (difference)
+
+/*
+ * Exclusion
+ * B(Dca, Da, Sca, Sa) = (Sca.Da + Dca.Sa - 2.Sca.Dca)
+ */
+
+/* This can be made faster by writing it directly and not using
+ * PDF_SEPARABLE_BLEND_MODE, but that's a performance optimization */
+
+static inline comp4_t
+blend_exclusion (comp4_t dca, comp4_t da, comp4_t sca, comp4_t sa)
+{
+ return DIV_ONE_UNc (sca * da + dca * sa - 2 * dca * sca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (exclusion)
+
+#undef PDF_SEPARABLE_BLEND_MODE
+
+/*
+ * 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, ra);
+ *
+ * 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, rs)
+ *
+ */
+
+/* So, for the non-separable PDF blend modes, we have (using s, d for
+ * non-premultiplied colors, and S, D for premultiplied:
+ *
+ * Color:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (S/a_s, LUM (D/a_d), 1)
+ * = set_lum (S * a_d, a_s * LUM (D), a_s * a_d)
+ *
+ *
+ * Luminosity:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (D/a_d, LUM(S/a_s), 1)
+ * = set_lum (a_s * D, a_d * LUM(S), a_s * a_d)
+ *
+ *
+ * Saturation:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (set_sat (D/a_d, SAT (S/a_s)), LUM (D/a_d), 1)
+ * = set_lum (a_s * a_d * set_sat (D/a_d, SAT (S/a_s)),
+ * a_s * LUM (D), a_s * a_d)
+ * = set_lum (set_sat (a_s * D, a_d * SAT (S), a_s * LUM (D), a_s * a_d))
+ *
+ * Hue:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (set_sat (S/a_s, SAT (D/a_d)), LUM (D/a_d), 1)
+ * = set_lum (set_sat (a_d * S, a_s * SAT (D)), a_s * LUM (D), a_s * a_d)
+ *
+ */
+
+#define CH_MIN(c) (c[0] < c[1] ? (c[0] < c[2] ? c[0] : c[2]) : (c[1] < c[2] ? c[1] : c[2]))
+#define CH_MAX(c) (c[0] > c[1] ? (c[0] > c[2] ? c[0] : c[2]) : (c[1] > c[2] ? c[1] : c[2]))
+#define LUM(c) ((c[0] * 30 + c[1] * 59 + c[2] * 11) / 100)
+#define SAT(c) (CH_MAX (c) - CH_MIN (c))
+
+#define PDF_NON_SEPARABLE_BLEND_MODE(name) \
+ static void \
+ combine_ ## name ## _u (pixman_implementation_t *imp, \
+ pixman_op_t op, \
+ comp4_t *dest, \
+ const comp4_t *src, \
+ const comp4_t *mask, \
+ int width) \
+ { \
+ int i; \
+ for (i = 0; i < width; ++i) \
+ { \
+ comp4_t s = combine_mask (src, mask, i); \
+ comp4_t d = *(dest + i); \
+ comp1_t sa = ALPHA_c (s); \
+ comp1_t isa = ~sa; \
+ comp1_t da = ALPHA_c (d); \
+ comp1_t ida = ~da; \
+ comp4_t result; \
+ comp4_t sc[3], dc[3], c[3]; \
+ \
+ result = d; \
+ UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc (result, isa, s, ida); \
+ dc[0] = RED_c (d); \
+ sc[0] = RED_c (s); \
+ dc[1] = GREEN_c (d); \
+ sc[1] = GREEN_c (s); \
+ dc[2] = BLUE_c (d); \
+ sc[2] = BLUE_c (s); \
+ blend_ ## name (c, dc, da, sc, sa); \
+ \
+ *(dest + i) = result + \
+ (DIV_ONE_UNc (sa * (comp4_t)da) << A_SHIFT) + \
+ (DIV_ONE_UNc (c[0]) << R_SHIFT) + \
+ (DIV_ONE_UNc (c[1]) << G_SHIFT) + \
+ (DIV_ONE_UNc (c[2])); \
+ } \
+ }
+
+static void
+set_lum (comp4_t dest[3], comp4_t src[3], comp4_t sa, comp4_t lum)
+{
+ double a, l, min, max;
+ double tmp[3];
+
+ a = sa * (1.0 / MASK);
+
+ l = lum * (1.0 / MASK);
+ tmp[0] = src[0] * (1.0 / MASK);
+ tmp[1] = src[1] * (1.0 / MASK);
+ tmp[2] = src[2] * (1.0 / MASK);
+
+ l = l - LUM (tmp);
+ tmp[0] += l;
+ tmp[1] += l;
+ tmp[2] += l;
+
+ /* clip_color */
+ l = LUM (tmp);
+ min = CH_MIN (tmp);
+ max = CH_MAX (tmp);
+
+ if (min < 0)
+ {
+ if (l - min == 0.0)
+ {
+ tmp[0] = 0;
+ tmp[1] = 0;
+ tmp[2] = 0;
+ }
+ else
+ {
+ tmp[0] = l + (tmp[0] - l) * l / (l - min);
+ tmp[1] = l + (tmp[1] - l) * l / (l - min);
+ tmp[2] = l + (tmp[2] - l) * l / (l - min);
+ }
+ }
+ if (max > a)
+ {
+ if (max - l == 0.0)
+ {
+ tmp[0] = a;
+ tmp[1] = a;
+ tmp[2] = a;
+ }
+ else
+ {
+ tmp[0] = l + (tmp[0] - l) * (a - l) / (max - l);
+ tmp[1] = l + (tmp[1] - l) * (a - l) / (max - l);
+ tmp[2] = l + (tmp[2] - l) * (a - l) / (max - l);
+ }
+ }
+
+ dest[0] = tmp[0] * MASK + 0.5;
+ dest[1] = tmp[1] * MASK + 0.5;
+ dest[2] = tmp[2] * MASK + 0.5;
+}
+
+static void
+set_sat (comp4_t dest[3], comp4_t src[3], comp4_t sat)
+{
+ int id[3];
+ comp4_t min, max;
+
+ if (src[0] > src[1])
+ {
+ if (src[0] > src[2])
+ {
+ id[0] = 0;
+ if (src[1] > src[2])
+ {
+ id[1] = 1;
+ id[2] = 2;
+ }
+ else
+ {
+ id[1] = 2;
+ id[2] = 1;
+ }
+ }
+ else
+ {
+ id[0] = 2;
+ id[1] = 0;
+ id[2] = 1;
+ }
+ }
+ else
+ {
+ if (src[0] > src[2])
+ {
+ id[0] = 1;
+ id[1] = 0;
+ id[2] = 2;
+ }
+ else
+ {
+ id[2] = 0;
+ if (src[1] > src[2])
+ {
+ id[0] = 1;
+ id[1] = 2;
+ }
+ else
+ {
+ id[0] = 2;
+ id[1] = 1;
+ }
+ }
+ }
+
+ max = dest[id[0]];
+ min = dest[id[2]];
+ if (max > min)
+ {
+ dest[id[1]] = (dest[id[1]] - min) * sat / (max - min);
+ dest[id[0]] = sat;
+ dest[id[2]] = 0;
+ }
+ else
+ {
+ dest[0] = dest[1] = dest[2] = 0;
+ }
+}
+
+/*
+ * Hue:
+ * B(Cb, Cs) = set_lum (set_sat (Cs, SAT (Cb)), LUM (Cb))
+ */
+static inline void
+blend_hsl_hue (comp4_t c[3],
+ comp4_t dc[3],
+ comp4_t da,
+ comp4_t sc[3],
+ comp4_t sa)
+{
+ c[0] = sc[0] * da;
+ c[1] = sc[1] * da;
+ c[2] = sc[2] * da;
+ set_sat (c, c, SAT (dc) * sa);
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_hue)
+
+/*
+ * Saturation:
+ * B(Cb, Cs) = set_lum (set_sat (Cb, SAT (Cs)), LUM (Cb))
+ */
+static inline void
+blend_hsl_saturation (comp4_t c[3],
+ comp4_t dc[3],
+ comp4_t da,
+ comp4_t sc[3],
+ comp4_t sa)
+{
+ c[0] = dc[0] * sa;
+ c[1] = dc[1] * sa;
+ c[2] = dc[2] * sa;
+ set_sat (c, c, SAT (sc) * da);
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_saturation)
+
+/*
+ * Color:
+ * B(Cb, Cs) = set_lum (Cs, LUM (Cb))
+ */
+static inline void
+blend_hsl_color (comp4_t c[3],
+ comp4_t dc[3],
+ comp4_t da,
+ comp4_t sc[3],
+ comp4_t sa)
+{
+ c[0] = sc[0] * da;
+ c[1] = sc[1] * da;
+ c[2] = sc[2] * da;
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_color)
+
+/*
+ * Luminosity:
+ * B(Cb, Cs) = set_lum (Cb, LUM (Cs))
+ */
+static inline void
+blend_hsl_luminosity (comp4_t c[3],
+ comp4_t dc[3],
+ comp4_t da,
+ comp4_t sc[3],
+ comp4_t sa)
+{
+ c[0] = dc[0] * sa;
+ c[1] = dc[1] * sa;
+ c[2] = dc[2] * sa;
+ set_lum (c, c, sa * da, LUM (sc) * da);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_luminosity)
+
+#undef SAT
+#undef LUM
+#undef CH_MAX
+#undef CH_MIN
+#undef PDF_NON_SEPARABLE_BLEND_MODE
+
+/* All of the disjoint/conjoint composing functions
+ *
+ * The four entries in the first column indicate what source contributions
+ * come from each of the four areas of the picture -- areas covered by neither
+ * A nor B, areas covered only by A, areas covered only by B and finally
+ * areas covered by both A and B.
+ *
+ * Disjoint Conjoint
+ * Fa Fb Fa Fb
+ * (0,0,0,0) 0 0 0 0
+ * (0,A,0,A) 1 0 1 0
+ * (0,0,B,B) 0 1 0 1
+ * (0,A,B,A) 1 min((1-a)/b,1) 1 max(1-a/b,0)
+ * (0,A,B,B) min((1-b)/a,1) 1 max(1-b/a,0) 1
+ * (0,0,0,A) max(1-(1-b)/a,0) 0 min(1,b/a) 0
+ * (0,0,0,B) 0 max(1-(1-a)/b,0) 0 min(a/b,1)
+ * (0,A,0,0) min(1,(1-b)/a) 0 max(1-b/a,0) 0
+ * (0,0,B,0) 0 min(1,(1-a)/b) 0 max(1-a/b,0)
+ * (0,0,B,A) max(1-(1-b)/a,0) min(1,(1-a)/b) min(1,b/a) max(1-a/b,0)
+ * (0,A,0,B) min(1,(1-b)/a) max(1-(1-a)/b,0) max(1-b/a,0) min(1,a/b)
+ * (0,A,B,0) min(1,(1-b)/a) min(1,(1-a)/b) max(1-b/a,0) max(1-a/b,0)
+ *
+ * See http://marc.info/?l=xfree-render&m=99792000027857&w=2 for more
+ * information about these operators.
+ */
+
+#define COMBINE_A_OUT 1
+#define COMBINE_A_IN 2
+#define COMBINE_B_OUT 4
+#define COMBINE_B_IN 8
+
+#define COMBINE_CLEAR 0
+#define COMBINE_A (COMBINE_A_OUT | COMBINE_A_IN)
+#define COMBINE_B (COMBINE_B_OUT | COMBINE_B_IN)
+#define COMBINE_A_OVER (COMBINE_A_OUT | COMBINE_B_OUT | COMBINE_A_IN)
+#define COMBINE_B_OVER (COMBINE_A_OUT | COMBINE_B_OUT | COMBINE_B_IN)
+#define COMBINE_A_ATOP (COMBINE_B_OUT | COMBINE_A_IN)
+#define COMBINE_B_ATOP (COMBINE_A_OUT | COMBINE_B_IN)
+#define COMBINE_XOR (COMBINE_A_OUT | COMBINE_B_OUT)
+
+/* portion covered by a but not b */
+static comp1_t
+combine_disjoint_out_part (comp1_t a, comp1_t b)
+{
+ /* min (1, (1-b) / a) */
+
+ b = ~b; /* 1 - b */
+ if (b >= a) /* 1 - b >= a -> (1-b)/a >= 1 */
+ return MASK; /* 1 */
+ return DIV_UNc (b, a); /* (1-b) / a */
+}
+
+/* portion covered by both a and b */
+static comp1_t
+combine_disjoint_in_part (comp1_t a, comp1_t b)
+{
+ /* max (1-(1-b)/a,0) */
+ /* = - min ((1-b)/a - 1, 0) */
+ /* = 1 - min (1, (1-b)/a) */
+
+ b = ~b; /* 1 - b */
+ if (b >= a) /* 1 - b >= a -> (1-b)/a >= 1 */
+ return 0; /* 1 - 1 */
+ return ~DIV_UNc(b, a); /* 1 - (1-b) / a */
+}
+
+/* portion covered by a but not b */
+static comp1_t
+combine_conjoint_out_part (comp1_t a, comp1_t b)
+{
+ /* max (1-b/a,0) */
+ /* = 1-min(b/a,1) */
+
+ /* min (1, (1-b) / a) */
+
+ if (b >= a) /* b >= a -> b/a >= 1 */
+ return 0x00; /* 0 */
+ return ~DIV_UNc(b, a); /* 1 - b/a */
+}
+
+/* portion covered by both a and b */
+static comp1_t
+combine_conjoint_in_part (comp1_t a, comp1_t b)
+{
+ /* min (1,b/a) */
+
+ if (b >= a) /* b >= a -> b/a >= 1 */
+ return MASK; /* 1 */
+ return DIV_UNc (b, a); /* b/a */
+}
+
+#define GET_COMP(v, i) ((comp2_t) (comp1_t) ((v) >> i))
+
+#define ADD(x, y, i, t) \
+ ((t) = GET_COMP (x, i) + GET_COMP (y, i), \
+ (comp4_t) ((comp1_t) ((t) | (0 - ((t) >> G_SHIFT)))) << (i))
+
+#define GENERIC(x, y, i, ax, ay, t, u, v) \
+ ((t) = (MUL_UNc (GET_COMP (y, i), ay, (u)) + \
+ MUL_UNc (GET_COMP (x, i), ax, (v))), \
+ (comp4_t) ((comp1_t) ((t) | \
+ (0 - ((t) >> G_SHIFT)))) << (i))
+
+static void
+combine_disjoint_general_u (comp4_t * dest,
+ const comp4_t *src,
+ const comp4_t *mask,
+ int width,
+ comp1_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t m, n, o, p;
+ comp2_t Fa, Fb, t, u, v;
+ comp1_t sa = s >> A_SHIFT;
+ comp1_t da = d >> A_SHIFT;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ Fa = combine_disjoint_out_part (sa, da);
+ break;
+
+ case COMBINE_A_IN:
+ Fa = combine_disjoint_in_part (sa, da);
+ break;
+
+ case COMBINE_A:
+ Fa = MASK;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ Fb = combine_disjoint_out_part (da, sa);
+ break;
+
+ case COMBINE_B_IN:
+ Fb = combine_disjoint_in_part (da, sa);
+ break;
+
+ case COMBINE_B:
+ Fb = MASK;
+ break;
+ }
+ m = GENERIC (s, d, 0, Fa, Fb, t, u, v);
+ n = GENERIC (s, d, G_SHIFT, Fa, Fb, t, u, v);
+ o = GENERIC (s, d, R_SHIFT, Fa, Fb, t, u, v);
+ p = GENERIC (s, d, A_SHIFT, Fa, Fb, t, u, v);
+ s = m | n | o | p;
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_disjoint_over_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp2_t a = s >> A_SHIFT;
+
+ if (s != 0x00)
+ {
+ comp4_t d = *(dest + i);
+ a = combine_disjoint_out_part (d >> A_SHIFT, a);
+ UNcx4_MUL_UNc_ADD_UNcx4 (d, a, s);
+
+ *(dest + i) = d;
+ }
+ }
+}
+
+static void
+combine_disjoint_in_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_disjoint_in_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_disjoint_out_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_disjoint_out_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_disjoint_atop_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_disjoint_atop_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_disjoint_xor_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_XOR);
+}
+
+static void
+combine_conjoint_general_u (comp4_t * dest,
+ const comp4_t *src,
+ const comp4_t *mask,
+ int width,
+ comp1_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = combine_mask (src, mask, i);
+ comp4_t d = *(dest + i);
+ comp4_t m, n, o, p;
+ comp2_t Fa, Fb, t, u, v;
+ comp1_t sa = s >> A_SHIFT;
+ comp1_t da = d >> A_SHIFT;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ Fa = combine_conjoint_out_part (sa, da);
+ break;
+
+ case COMBINE_A_IN:
+ Fa = combine_conjoint_in_part (sa, da);
+ break;
+
+ case COMBINE_A:
+ Fa = MASK;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ Fb = combine_conjoint_out_part (da, sa);
+ break;
+
+ case COMBINE_B_IN:
+ Fb = combine_conjoint_in_part (da, sa);
+ break;
+
+ case COMBINE_B:
+ Fb = MASK;
+ break;
+ }
+
+ m = GENERIC (s, d, 0, Fa, Fb, t, u, v);
+ n = GENERIC (s, d, G_SHIFT, Fa, Fb, t, u, v);
+ o = GENERIC (s, d, R_SHIFT, Fa, Fb, t, u, v);
+ p = GENERIC (s, d, A_SHIFT, Fa, Fb, t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_conjoint_over_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_conjoint_over_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_OVER);
+}
+
+static void
+combine_conjoint_in_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_conjoint_in_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_conjoint_out_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_conjoint_out_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_conjoint_atop_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_conjoint_atop_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_conjoint_xor_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_XOR);
+}
+
+/************************************************************************/
+/*********************** Per Channel functions **************************/
+/************************************************************************/
+
+static void
+combine_clear_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ memset (dest, 0, width * sizeof(comp4_t));
+}
+
+static void
+combine_src_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = *(src + i);
+ comp4_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,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = *(src + i);
+ comp4_t m = *(mask + i);
+ comp4_t a;
+
+ combine_mask_ca (&s, &m);
+
+ a = ~m;
+ if (a)
+ {
+ comp4_t d = *(dest + i);
+ UNcx4_MUL_UNcx4_ADD_UNcx4 (d, a, s);
+ s = d;
+ }
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_over_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t d = *(dest + i);
+ comp4_t a = ~d >> A_SHIFT;
+
+ if (a)
+ {
+ comp4_t s = *(src + i);
+ comp4_t m = *(mask + i);
+
+ UNcx4_MUL_UNcx4 (s, m);
+ UNcx4_MUL_UNc_ADD_UNcx4 (s, a, d);
+
+ *(dest + i) = s;
+ }
+ }
+}
+
+static void
+combine_in_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t d = *(dest + i);
+ comp2_t a = d >> A_SHIFT;
+ comp4_t s = 0;
+
+ if (a)
+ {
+ comp4_t m = *(mask + i);
+
+ s = *(src + i);
+ combine_mask_value_ca (&s, &m);
+
+ if (a != MASK)
+ UNcx4_MUL_UNc (s, a);
+ }
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_in_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = *(src + i);
+ comp4_t m = *(mask + i);
+ comp4_t a;
+
+ combine_mask_alpha_ca (&s, &m);
+
+ a = m;
+ if (a != ~0)
+ {
+ comp4_t d = 0;
+
+ if (a)
+ {
+ d = *(dest + i);
+ UNcx4_MUL_UNcx4 (d, a);
+ }
+
+ *(dest + i) = d;
+ }
+ }
+}
+
+static void
+combine_out_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t d = *(dest + i);
+ comp2_t a = ~d >> A_SHIFT;
+ comp4_t s = 0;
+
+ if (a)
+ {
+ comp4_t m = *(mask + i);
+
+ s = *(src + i);
+ combine_mask_value_ca (&s, &m);
+
+ if (a != MASK)
+ UNcx4_MUL_UNc (s, a);
+ }
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_out_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = *(src + i);
+ comp4_t m = *(mask + i);
+ comp4_t a;
+
+ combine_mask_alpha_ca (&s, &m);
+
+ a = ~m;
+ if (a != ~0)
+ {
+ comp4_t d = 0;
+
+ if (a)
+ {
+ d = *(dest + i);
+ UNcx4_MUL_UNcx4 (d, a);
+ }
+
+ *(dest + i) = d;
+ }
+ }
+}
+
+static void
+combine_atop_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t d = *(dest + i);
+ comp4_t s = *(src + i);
+ comp4_t m = *(mask + i);
+ comp4_t ad;
+ comp2_t as = d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ ad = ~m;
+
+ UNcx4_MUL_UNcx4_ADD_UNcx4_MUL_UNc (d, ad, s, as);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_atop_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t d = *(dest + i);
+ comp4_t s = *(src + i);
+ comp4_t m = *(mask + i);
+ comp4_t ad;
+ comp2_t as = ~d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ ad = m;
+
+ UNcx4_MUL_UNcx4_ADD_UNcx4_MUL_UNc (d, ad, s, as);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_xor_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t d = *(dest + i);
+ comp4_t s = *(src + i);
+ comp4_t m = *(mask + i);
+ comp4_t ad;
+ comp2_t as = ~d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ ad = ~m;
+
+ UNcx4_MUL_UNcx4_ADD_UNcx4_MUL_UNc (d, ad, s, as);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_add_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s = *(src + i);
+ comp4_t m = *(mask + i);
+ comp4_t d = *(dest + i);
+
+ combine_mask_value_ca (&s, &m);
+
+ UNcx4_ADD_UNcx4 (d, s);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_saturate_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s, d;
+ comp2_t sa, sr, sg, sb, da;
+ comp2_t t, u, v;
+ comp4_t m, n, o, p;
+
+ d = *(dest + i);
+ s = *(src + i);
+ m = *(mask + i);
+
+ combine_mask_ca (&s, &m);
+
+ sa = (m >> A_SHIFT);
+ sr = (m >> R_SHIFT) & MASK;
+ sg = (m >> G_SHIFT) & MASK;
+ sb = m & MASK;
+ da = ~d >> A_SHIFT;
+
+ if (sb <= da)
+ m = ADD (s, d, 0, t);
+ else
+ m = GENERIC (s, d, 0, (da << G_SHIFT) / sb, MASK, t, u, v);
+
+ if (sg <= da)
+ n = ADD (s, d, G_SHIFT, t);
+ else
+ n = GENERIC (s, d, G_SHIFT, (da << G_SHIFT) / sg, MASK, t, u, v);
+
+ if (sr <= da)
+ o = ADD (s, d, R_SHIFT, t);
+ else
+ o = GENERIC (s, d, R_SHIFT, (da << G_SHIFT) / sr, MASK, t, u, v);
+
+ if (sa <= da)
+ p = ADD (s, d, A_SHIFT, t);
+ else
+ p = GENERIC (s, d, A_SHIFT, (da << G_SHIFT) / sa, MASK, t, u, v);
+
+ *(dest + i) = m | n | o | p;
+ }
+}
+
+static void
+combine_disjoint_general_ca (comp4_t * dest,
+ const comp4_t *src,
+ const comp4_t *mask,
+ int width,
+ comp1_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s, d;
+ comp4_t m, n, o, p;
+ comp4_t Fa, Fb;
+ comp2_t t, u, v;
+ comp4_t sa;
+ comp1_t da;
+
+ s = *(src + i);
+ m = *(mask + i);
+ d = *(dest + i);
+ da = d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ sa = m;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ m = (comp4_t)combine_disjoint_out_part ((comp1_t) (sa >> 0), da);
+ n = (comp4_t)combine_disjoint_out_part ((comp1_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (comp4_t)combine_disjoint_out_part ((comp1_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (comp4_t)combine_disjoint_out_part ((comp1_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A_IN:
+ m = (comp4_t)combine_disjoint_in_part ((comp1_t) (sa >> 0), da);
+ n = (comp4_t)combine_disjoint_in_part ((comp1_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (comp4_t)combine_disjoint_in_part ((comp1_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (comp4_t)combine_disjoint_in_part ((comp1_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A:
+ Fa = ~0;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ m = (comp4_t)combine_disjoint_out_part (da, (comp1_t) (sa >> 0));
+ n = (comp4_t)combine_disjoint_out_part (da, (comp1_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (comp4_t)combine_disjoint_out_part (da, (comp1_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (comp4_t)combine_disjoint_out_part (da, (comp1_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B_IN:
+ m = (comp4_t)combine_disjoint_in_part (da, (comp1_t) (sa >> 0));
+ n = (comp4_t)combine_disjoint_in_part (da, (comp1_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (comp4_t)combine_disjoint_in_part (da, (comp1_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (comp4_t)combine_disjoint_in_part (da, (comp1_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B:
+ Fb = ~0;
+ break;
+ }
+ m = GENERIC (s, d, 0, GET_COMP (Fa, 0), GET_COMP (Fb, 0), t, u, v);
+ n = GENERIC (s, d, G_SHIFT, GET_COMP (Fa, G_SHIFT), GET_COMP (Fb, G_SHIFT), t, u, v);
+ o = GENERIC (s, d, R_SHIFT, GET_COMP (Fa, R_SHIFT), GET_COMP (Fb, R_SHIFT), t, u, v);
+ p = GENERIC (s, d, A_SHIFT, GET_COMP (Fa, A_SHIFT), GET_COMP (Fb, A_SHIFT), t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_disjoint_over_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_disjoint_in_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_disjoint_in_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_disjoint_out_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_disjoint_out_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_disjoint_atop_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_disjoint_atop_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_disjoint_xor_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_XOR);
+}
+
+static void
+combine_conjoint_general_ca (comp4_t * dest,
+ const comp4_t *src,
+ const comp4_t *mask,
+ int width,
+ comp1_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ comp4_t s, d;
+ comp4_t m, n, o, p;
+ comp4_t Fa, Fb;
+ comp2_t t, u, v;
+ comp4_t sa;
+ comp1_t da;
+
+ s = *(src + i);
+ m = *(mask + i);
+ d = *(dest + i);
+ da = d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ sa = m;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ m = (comp4_t)combine_conjoint_out_part ((comp1_t) (sa >> 0), da);
+ n = (comp4_t)combine_conjoint_out_part ((comp1_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (comp4_t)combine_conjoint_out_part ((comp1_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (comp4_t)combine_conjoint_out_part ((comp1_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A_IN:
+ m = (comp4_t)combine_conjoint_in_part ((comp1_t) (sa >> 0), da);
+ n = (comp4_t)combine_conjoint_in_part ((comp1_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (comp4_t)combine_conjoint_in_part ((comp1_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (comp4_t)combine_conjoint_in_part ((comp1_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A:
+ Fa = ~0;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ m = (comp4_t)combine_conjoint_out_part (da, (comp1_t) (sa >> 0));
+ n = (comp4_t)combine_conjoint_out_part (da, (comp1_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (comp4_t)combine_conjoint_out_part (da, (comp1_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (comp4_t)combine_conjoint_out_part (da, (comp1_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B_IN:
+ m = (comp4_t)combine_conjoint_in_part (da, (comp1_t) (sa >> 0));
+ n = (comp4_t)combine_conjoint_in_part (da, (comp1_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (comp4_t)combine_conjoint_in_part (da, (comp1_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (comp4_t)combine_conjoint_in_part (da, (comp1_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B:
+ Fb = ~0;
+ break;
+ }
+ m = GENERIC (s, d, 0, GET_COMP (Fa, 0), GET_COMP (Fb, 0), t, u, v);
+ n = GENERIC (s, d, G_SHIFT, GET_COMP (Fa, G_SHIFT), GET_COMP (Fb, G_SHIFT), t, u, v);
+ o = GENERIC (s, d, R_SHIFT, GET_COMP (Fa, R_SHIFT), GET_COMP (Fb, R_SHIFT), t, u, v);
+ p = GENERIC (s, d, A_SHIFT, GET_COMP (Fa, A_SHIFT), GET_COMP (Fb, A_SHIFT), t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_conjoint_over_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_conjoint_over_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_OVER);
+}
+
+static void
+combine_conjoint_in_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_conjoint_in_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_conjoint_out_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_conjoint_out_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_conjoint_atop_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_conjoint_atop_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_conjoint_xor_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ comp4_t * dest,
+ const comp4_t * src,
+ const comp4_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_XOR);
+}
+
+void
+_pixman_setup_combiner_functions_width (pixman_implementation_t *imp)
+{
+ /* Unified alpha */
+ imp->combine_width[PIXMAN_OP_CLEAR] = combine_clear;
+ imp->combine_width[PIXMAN_OP_SRC] = combine_src_u;
+ imp->combine_width[PIXMAN_OP_DST] = combine_dst;
+ imp->combine_width[PIXMAN_OP_OVER] = combine_over_u;
+ imp->combine_width[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_u;
+ imp->combine_width[PIXMAN_OP_IN] = combine_in_u;
+ imp->combine_width[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_u;
+ imp->combine_width[PIXMAN_OP_OUT] = combine_out_u;
+ imp->combine_width[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_u;
+ imp->combine_width[PIXMAN_OP_ATOP] = combine_atop_u;
+ imp->combine_width[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_u;
+ imp->combine_width[PIXMAN_OP_XOR] = combine_xor_u;
+ imp->combine_width[PIXMAN_OP_ADD] = combine_add_u;
+ imp->combine_width[PIXMAN_OP_SATURATE] = combine_saturate_u;
+
+ /* Disjoint, unified */
+ imp->combine_width[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear;
+ imp->combine_width[PIXMAN_OP_DISJOINT_SRC] = combine_src_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_DST] = combine_dst;
+ imp->combine_width[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_saturate_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_u;
+ imp->combine_width[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_u;
+
+ /* Conjoint, unified */
+ imp->combine_width[PIXMAN_OP_CONJOINT_CLEAR] = combine_clear;
+ imp->combine_width[PIXMAN_OP_CONJOINT_SRC] = combine_src_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_DST] = combine_dst;
+ imp->combine_width[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_u;
+ imp->combine_width[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_u;
+
+ imp->combine_width[PIXMAN_OP_MULTIPLY] = combine_multiply_u;
+ imp->combine_width[PIXMAN_OP_SCREEN] = combine_screen_u;
+ imp->combine_width[PIXMAN_OP_OVERLAY] = combine_overlay_u;
+ imp->combine_width[PIXMAN_OP_DARKEN] = combine_darken_u;
+ imp->combine_width[PIXMAN_OP_LIGHTEN] = combine_lighten_u;
+ imp->combine_width[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_u;
+ imp->combine_width[PIXMAN_OP_COLOR_BURN] = combine_color_burn_u;
+ imp->combine_width[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_u;
+ imp->combine_width[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_u;
+ imp->combine_width[PIXMAN_OP_DIFFERENCE] = combine_difference_u;
+ imp->combine_width[PIXMAN_OP_EXCLUSION] = combine_exclusion_u;
+ imp->combine_width[PIXMAN_OP_HSL_HUE] = combine_hsl_hue_u;
+ imp->combine_width[PIXMAN_OP_HSL_SATURATION] = combine_hsl_saturation_u;
+ imp->combine_width[PIXMAN_OP_HSL_COLOR] = combine_hsl_color_u;
+ imp->combine_width[PIXMAN_OP_HSL_LUMINOSITY] = combine_hsl_luminosity_u;
+
+ /* Component alpha combiners */
+ imp->combine_width_ca[PIXMAN_OP_CLEAR] = combine_clear_ca;
+ imp->combine_width_ca[PIXMAN_OP_SRC] = combine_src_ca;
+ /* dest */
+ imp->combine_width_ca[PIXMAN_OP_OVER] = combine_over_ca;
+ imp->combine_width_ca[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_IN] = combine_in_ca;
+ imp->combine_width_ca[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_OUT] = combine_out_ca;
+ imp->combine_width_ca[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_ATOP] = combine_atop_ca;
+ imp->combine_width_ca[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_XOR] = combine_xor_ca;
+ imp->combine_width_ca[PIXMAN_OP_ADD] = combine_add_ca;
+ imp->combine_width_ca[PIXMAN_OP_SATURATE] = combine_saturate_ca;
+
+ /* Disjoint CA */
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_SRC] = combine_src_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_DST] = combine_dst;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_saturate_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_ca;
+
+ /* Conjoint CA */
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_CLEAR] = combine_clear_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_SRC] = combine_src_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_DST] = combine_dst;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_ca;
+ imp->combine_width_ca[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_ca;
+
+ imp->combine_width_ca[PIXMAN_OP_MULTIPLY] = combine_multiply_ca;
+ imp->combine_width_ca[PIXMAN_OP_SCREEN] = combine_screen_ca;
+ imp->combine_width_ca[PIXMAN_OP_OVERLAY] = combine_overlay_ca;
+ imp->combine_width_ca[PIXMAN_OP_DARKEN] = combine_darken_ca;
+ imp->combine_width_ca[PIXMAN_OP_LIGHTEN] = combine_lighten_ca;
+ imp->combine_width_ca[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_ca;
+ imp->combine_width_ca[PIXMAN_OP_COLOR_BURN] = combine_color_burn_ca;
+ imp->combine_width_ca[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_ca;
+ imp->combine_width_ca[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_ca;
+ imp->combine_width_ca[PIXMAN_OP_DIFFERENCE] = combine_difference_ca;
+ imp->combine_width_ca[PIXMAN_OP_EXCLUSION] = combine_exclusion_ca;
+
+ /* It is not clear that these make sense, so make them noops for now */
+ imp->combine_width_ca[PIXMAN_OP_HSL_HUE] = combine_dst;
+ imp->combine_width_ca[PIXMAN_OP_HSL_SATURATION] = combine_dst;
+ imp->combine_width_ca[PIXMAN_OP_HSL_COLOR] = combine_dst;
+ imp->combine_width_ca[PIXMAN_OP_HSL_LUMINOSITY] = combine_dst;
+}
+
diff --git a/gfx/cairo/libpixman/src/pixman-combine.h.template b/gfx/cairo/libpixman/src/pixman-combine.h.template
new file mode 100644
index 000000000..20f784b5b
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine.h.template
@@ -0,0 +1,226 @@
+
+#define COMPONENT_SIZE
+#define MASK
+#define ONE_HALF
+
+#define A_SHIFT
+#define R_SHIFT
+#define G_SHIFT
+#define A_MASK
+#define R_MASK
+#define G_MASK
+
+#define RB_MASK
+#define AG_MASK
+#define RB_ONE_HALF
+#define RB_MASK_PLUS_ONE
+
+#define ALPHA_c(x) ((x) >> A_SHIFT)
+#define RED_c(x) (((x) >> R_SHIFT) & MASK)
+#define GREEN_c(x) (((x) >> G_SHIFT) & MASK)
+#define BLUE_c(x) ((x) & MASK)
+
+/*
+ * Helper macros.
+ */
+
+#define MUL_UNc(a, b, t) \
+ ((t) = (a) * (comp2_t)(b) + ONE_HALF, ((((t) >> G_SHIFT ) + (t) ) >> G_SHIFT ))
+
+#define DIV_UNc(a, b) \
+ (((comp2_t) (a) * MASK + ((b) / 2)) / (b))
+
+#define ADD_UNc(x, y, t) \
+ ((t) = (x) + (y), \
+ (comp4_t) (comp1_t) ((t) | (0 - ((t) >> G_SHIFT))))
+
+#define DIV_ONE_UNc(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 UNc_rb_MUL_UNc(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)
+ */
+#define UNc_rb_ADD_UNc_rb(x, y, t) \
+ do \
+ { \
+ t = ((x) + (y)); \
+ t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); \
+ x = (t & RB_MASK); \
+ } while (0)
+
+/*
+ * x_rb = (x_rb * a_rb) / 255
+ */
+#define UNc_rb_MUL_UNc_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 UNcx4_MUL_UNc(x, a) \
+ do \
+ { \
+ comp4_t r1__, r2__, t__; \
+ \
+ r1__ = (x); \
+ UNc_rb_MUL_UNc (r1__, (a), t__); \
+ \
+ r2__ = (x) >> G_SHIFT; \
+ UNc_rb_MUL_UNc (r2__, (a), t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a) / 255 + y_c
+ */
+#define UNcx4_MUL_UNc_ADD_UNcx4(x, a, y) \
+ do \
+ { \
+ comp4_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (y) & RB_MASK; \
+ UNc_rb_MUL_UNc (r1__, (a), t__); \
+ UNc_rb_ADD_UNc_rb (r1__, r2__, t__); \
+ \
+ r2__ = (x) >> G_SHIFT; \
+ r3__ = ((y) >> G_SHIFT) & RB_MASK; \
+ UNc_rb_MUL_UNc (r2__, (a), t__); \
+ UNc_rb_ADD_UNc_rb (r2__, r3__, t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a + y_c * b) / 255
+ */
+#define UNcx4_MUL_UNc_ADD_UNcx4_MUL_UNc(x, a, y, b) \
+ do \
+ { \
+ comp4_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (y); \
+ UNc_rb_MUL_UNc (r1__, (a), t__); \
+ UNc_rb_MUL_UNc (r2__, (b), t__); \
+ UNc_rb_ADD_UNc_rb (r1__, r2__, t__); \
+ \
+ r2__ = ((x) >> G_SHIFT); \
+ r3__ = ((y) >> G_SHIFT); \
+ UNc_rb_MUL_UNc (r2__, (a), t__); \
+ UNc_rb_MUL_UNc (r3__, (b), t__); \
+ UNc_rb_ADD_UNc_rb (r2__, r3__, t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a_c) / 255
+ */
+#define UNcx4_MUL_UNcx4(x, a) \
+ do \
+ { \
+ comp4_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (a); \
+ UNc_rb_MUL_UNc_rb (r1__, r2__, t__); \
+ \
+ r2__ = (x) >> G_SHIFT; \
+ r3__ = (a) >> G_SHIFT; \
+ UNc_rb_MUL_UNc_rb (r2__, r3__, t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a_c) / 255 + y_c
+ */
+#define UNcx4_MUL_UNcx4_ADD_UNcx4(x, a, y) \
+ do \
+ { \
+ comp4_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (a); \
+ UNc_rb_MUL_UNc_rb (r1__, r2__, t__); \
+ r2__ = (y) & RB_MASK; \
+ UNc_rb_ADD_UNc_rb (r1__, r2__, t__); \
+ \
+ r2__ = ((x) >> G_SHIFT); \
+ r3__ = ((a) >> G_SHIFT); \
+ UNc_rb_MUL_UNc_rb (r2__, r3__, t__); \
+ r3__ = ((y) >> G_SHIFT) & RB_MASK; \
+ UNc_rb_ADD_UNc_rb (r2__, r3__, t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a_c + y_c * b) / 255
+ */
+#define UNcx4_MUL_UNcx4_ADD_UNcx4_MUL_UNc(x, a, y, b) \
+ do \
+ { \
+ comp4_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (a); \
+ UNc_rb_MUL_UNc_rb (r1__, r2__, t__); \
+ r2__ = (y); \
+ UNc_rb_MUL_UNc (r2__, (b), t__); \
+ UNc_rb_ADD_UNc_rb (r1__, r2__, t__); \
+ \
+ r2__ = (x) >> G_SHIFT; \
+ r3__ = (a) >> G_SHIFT; \
+ UNc_rb_MUL_UNc_rb (r2__, r3__, t__); \
+ r3__ = (y) >> G_SHIFT; \
+ UNc_rb_MUL_UNc (r3__, (b), t__); \
+ UNc_rb_ADD_UNc_rb (r2__, r3__, t__); \
+ \
+ x = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ x_c = min(x_c + y_c, 255)
+*/
+#define UNcx4_ADD_UNcx4(x, y) \
+ do \
+ { \
+ comp4_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x) & RB_MASK; \
+ r2__ = (y) & RB_MASK; \
+ UNc_rb_ADD_UNc_rb (r1__, r2__, t__); \
+ \
+ r2__ = ((x) >> G_SHIFT) & RB_MASK; \
+ r3__ = ((y) >> G_SHIFT) & RB_MASK; \
+ UNc_rb_ADD_UNc_rb (r2__, r3__, t__); \
+ \
+ x = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
diff --git a/gfx/cairo/libpixman/src/pixman-combine16.c b/gfx/cairo/libpixman/src/pixman-combine16.c
new file mode 100644
index 000000000..9ba439fe5
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine16.c
@@ -0,0 +1,114 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "pixman-private.h"
+
+#include "pixman-combine32.h"
+
+static force_inline uint32_t
+combine_mask (const uint32_t src, const uint32_t mask)
+{
+ uint32_t s, m;
+
+ m = mask >> A_SHIFT;
+
+ if (!m)
+ return 0;
+ s = src;
+
+ UN8x4_MUL_UN8 (s, m);
+
+ return s;
+}
+
+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 (uint16_t));
+ else
+ {
+ uint16_t *d = (uint16_t*)dest;
+ uint16_t *src16 = (uint16_t*)src;
+ for (i = 0; i < width; ++i)
+ {
+ if ((*mask & 0xff000000) == 0xff000000) {
+ // it's likely worth special casing
+ // fully opaque because it avoids
+ // the cost of conversion as well the multiplication
+ *(d + i) = *src16;
+ } else {
+ // the mask is still 32bits
+ uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
+ *(d + i) = convert_8888_to_0565(s);
+ }
+ mask++;
+ src16++;
+ }
+ }
+
+}
+
+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)
+ memcpy (dest, src, width * sizeof (uint16_t));
+ else
+ {
+ uint16_t *d = (uint16_t*)dest;
+ uint16_t *src16 = (uint16_t*)src;
+ for (i = 0; i < width; ++i)
+ {
+ if ((*mask & 0xff000000) == 0xff000000) {
+ // it's likely worth special casing
+ // fully opaque because it avoids
+ // the cost of conversion as well the multiplication
+ *(d + i) = *src16;
+ } else if ((*mask & 0xff000000) == 0x00000000) {
+ // keep the dest the same
+ } else {
+ // the mask is still 32bits
+ uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
+ uint32_t ia = ALPHA_8 (~s);
+ uint32_t d32 = convert_0565_to_8888(*(d + i));
+ UN8x4_MUL_UN8_ADD_UN8x4 (d32, ia, s);
+ *(d + i) = convert_8888_to_0565(d32);
+ }
+ mask++;
+ src16++;
+ }
+ }
+
+}
+
+
+void
+_pixman_setup_combiner_functions_16 (pixman_implementation_t *imp)
+{
+ int i;
+ for (i = 0; i < PIXMAN_N_OPERATORS; i++) {
+ imp->combine_16[i] = NULL;
+ }
+ imp->combine_16[PIXMAN_OP_SRC] = combine_src_u;
+ imp->combine_16[PIXMAN_OP_OVER] = combine_over_u;
+}
+
diff --git a/gfx/cairo/libpixman/src/pixman-combine32.c b/gfx/cairo/libpixman/src/pixman-combine32.c
new file mode 100644
index 000000000..3ac7576bd
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine32.c
@@ -0,0 +1,2504 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#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;
+ }
+}
+
+static void
+combine_saturate_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);
+ uint16_t sa, da;
+
+ sa = s >> A_SHIFT;
+ da = ~d >> A_SHIFT;
+ if (sa > da)
+ {
+ sa = DIV_UN8 (da, sa);
+ UN8x4_MUL_UN8 (s, sa);
+ }
+ ;
+ 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/acrobat/pdfs/PDF32000_2008.pdf
+ * The relevant chapters are 11.3.5 and 11.3.6.
+ * 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() being the blend function.
+ * Note that OVER is a special case of this operation, using B(Cb, Cs) = Cs
+ *
+ * These blend modes should match the SVG filter draft specification, as
+ * it has been designed to mirror ISO 32000. Note that at the current point
+ * no released draft exists that shows this, as the formulas have not been
+ * updated yet after the release of ISO 32000.
+ *
+ * The default implementation here uses the PDF_SEPARABLE_BLEND_MODE and
+ * PDF_NON_SEPARABLE_BLEND_MODE macros, which take the blend function as an
+ * argument. Note that this implementation operates on premultiplied colors,
+ * while the PDF specification does not. Therefore the code uses the formula
+ * Cra = (1 – as) . Dca + (1 – ad) . Sca + B(Dca, ad, Sca, as)
+ */
+
+/*
+ * Multiply
+ * B(Dca, ad, Sca, as) = Dca.Sca
+ */
+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 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 result; \
+ \
+ result = d; \
+ UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (result, isa, s, ida); \
+ \
+ *(dest + i) = result + \
+ (DIV_ONE_UN8 (sa * (uint32_t)da) << A_SHIFT) + \
+ (blend_ ## name (RED_8 (d), da, RED_8 (s), sa) << R_SHIFT) + \
+ (blend_ ## name (GREEN_8 (d), da, GREEN_8 (s), sa) << G_SHIFT) + \
+ (blend_ ## name (BLUE_8 (d), da, BLUE_8 (s), sa)); \
+ } \
+ } \
+ \
+ 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 result; \
+ \
+ combine_mask_ca (&s, &m); \
+ \
+ result = d; \
+ UN8x4_MUL_UN8x4_ADD_UN8x4_MUL_UN8 (result, ~m, s, ida); \
+ \
+ result += \
+ (DIV_ONE_UN8 (ALPHA_8 (m) * (uint32_t)da) << A_SHIFT) + \
+ (blend_ ## name (RED_8 (d), da, RED_8 (s), RED_8 (m)) << R_SHIFT) + \
+ (blend_ ## name (GREEN_8 (d), da, GREEN_8 (s), GREEN_8 (m)) << G_SHIFT) + \
+ (blend_ ## name (BLUE_8 (d), da, BLUE_8 (s), BLUE_8 (m))); \
+ \
+ *(dest + i) = result; \
+ } \
+ }
+
+/*
+ * Screen
+ * B(Dca, ad, Sca, as) = Dca.sa + Sca.da - Dca.Sca
+ */
+static inline uint32_t
+blend_screen (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ return DIV_ONE_UN8 (sca * da + dca * sa - sca * dca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (screen)
+
+/*
+ * Overlay
+ * B(Dca, Da, Sca, Sa) =
+ * if 2.Dca < Da
+ * 2.Sca.Dca
+ * otherwise
+ * Sa.Da - 2.(Da - Dca).(Sa - Sca)
+ */
+static inline uint32_t
+blend_overlay (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ uint32_t rca;
+
+ if (2 * dca < da)
+ rca = 2 * sca * dca;
+ else
+ rca = sa * da - 2 * (da - dca) * (sa - sca);
+ return DIV_ONE_UN8 (rca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (overlay)
+
+/*
+ * Darken
+ * B(Dca, Da, Sca, Sa) = min (Sca.Da, Dca.Sa)
+ */
+static inline uint32_t
+blend_darken (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ uint32_t s, d;
+
+ s = sca * da;
+ d = dca * sa;
+ return DIV_ONE_UN8 (s > d ? d : s);
+}
+
+PDF_SEPARABLE_BLEND_MODE (darken)
+
+/*
+ * Lighten
+ * B(Dca, Da, Sca, Sa) = max (Sca.Da, Dca.Sa)
+ */
+static inline uint32_t
+blend_lighten (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ uint32_t s, d;
+
+ s = sca * da;
+ d = dca * sa;
+ return DIV_ONE_UN8 (s > d ? s : d);
+}
+
+PDF_SEPARABLE_BLEND_MODE (lighten)
+
+/*
+ * Color dodge
+ * B(Dca, Da, Sca, Sa) =
+ * if Dca == 0
+ * 0
+ * if Sca == Sa
+ * Sa.Da
+ * otherwise
+ * Sa.Da. min (1, Dca / Da / (1 - Sca/Sa))
+ */
+static inline uint32_t
+blend_color_dodge (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ if (sca >= sa)
+ {
+ return dca == 0 ? 0 : DIV_ONE_UN8 (sa * da);
+ }
+ else
+ {
+ uint32_t rca = dca * sa / (sa - sca);
+ return DIV_ONE_UN8 (sa * MIN (rca, da));
+ }
+}
+
+PDF_SEPARABLE_BLEND_MODE (color_dodge)
+
+/*
+ * Color burn
+ * B(Dca, Da, Sca, Sa) =
+ * if Dca == Da
+ * Sa.Da
+ * if Sca == 0
+ * 0
+ * otherwise
+ * Sa.Da.(1 - min (1, (1 - Dca/Da).Sa / Sca))
+ */
+static inline uint32_t
+blend_color_burn (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ if (sca == 0)
+ {
+ return dca < da ? 0 : DIV_ONE_UN8 (sa * da);
+ }
+ else
+ {
+ uint32_t rca = (da - dca) * sa / sca;
+ return DIV_ONE_UN8 (sa * (MAX (rca, da) - rca));
+ }
+}
+
+PDF_SEPARABLE_BLEND_MODE (color_burn)
+
+/*
+ * Hard light
+ * B(Dca, Da, Sca, Sa) =
+ * if 2.Sca < Sa
+ * 2.Sca.Dca
+ * otherwise
+ * Sa.Da - 2.(Da - Dca).(Sa - Sca)
+ */
+static inline uint32_t
+blend_hard_light (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ if (2 * sca < sa)
+ return DIV_ONE_UN8 (2 * sca * dca);
+ else
+ return DIV_ONE_UN8 (sa * da - 2 * (da - dca) * (sa - sca));
+}
+
+PDF_SEPARABLE_BLEND_MODE (hard_light)
+
+/*
+ * Soft light
+ * B(Dca, Da, Sca, Sa) =
+ * if (2.Sca <= Sa)
+ * Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa))
+ * otherwise if Dca.4 <= Da
+ * Dca.(Sa + (2.Sca - Sa).((16.Dca/Da - 12).Dca/Da + 3)
+ * otherwise
+ * (Dca.Sa + (SQRT (Dca/Da).Da - Dca).(2.Sca - Sa))
+ */
+static inline uint32_t
+blend_soft_light (uint32_t dca_org,
+ uint32_t da_org,
+ uint32_t sca_org,
+ uint32_t sa_org)
+{
+ double dca = dca_org * (1.0 / MASK);
+ double da = da_org * (1.0 / MASK);
+ double sca = sca_org * (1.0 / MASK);
+ double sa = sa_org * (1.0 / MASK);
+ double rca;
+
+ if (2 * sca < sa)
+ {
+ if (da == 0)
+ rca = dca * sa;
+ else
+ rca = dca * sa - dca * (da - dca) * (sa - 2 * sca) / da;
+ }
+ else if (da == 0)
+ {
+ rca = 0;
+ }
+ else if (4 * dca <= da)
+ {
+ rca = dca * sa +
+ (2 * sca - sa) * dca * ((16 * dca / da - 12) * dca / da + 3);
+ }
+ else
+ {
+ rca = dca * sa + (sqrt (dca * da) - dca) * (2 * sca - sa);
+ }
+ return rca * MASK + 0.5;
+}
+
+PDF_SEPARABLE_BLEND_MODE (soft_light)
+
+/*
+ * Difference
+ * B(Dca, Da, Sca, Sa) = abs (Dca.Sa - Sca.Da)
+ */
+static inline uint32_t
+blend_difference (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ uint32_t dcasa = dca * sa;
+ uint32_t scada = sca * da;
+
+ if (scada < dcasa)
+ return DIV_ONE_UN8 (dcasa - scada);
+ else
+ return DIV_ONE_UN8 (scada - dcasa);
+}
+
+PDF_SEPARABLE_BLEND_MODE (difference)
+
+/*
+ * Exclusion
+ * B(Dca, Da, Sca, Sa) = (Sca.Da + Dca.Sa - 2.Sca.Dca)
+ */
+
+/* This can be made faster by writing it directly and not using
+ * PDF_SEPARABLE_BLEND_MODE, but that's a performance optimization */
+
+static inline uint32_t
+blend_exclusion (uint32_t dca, uint32_t da, uint32_t sca, uint32_t sa)
+{
+ return DIV_ONE_UN8 (sca * da + dca * sa - 2 * dca * sca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (exclusion)
+
+#undef PDF_SEPARABLE_BLEND_MODE
+
+/*
+ * 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, ra);
+ *
+ * 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, rs)
+ *
+ */
+
+/* So, for the non-separable PDF blend modes, we have (using s, d for
+ * non-premultiplied colors, and S, D for premultiplied:
+ *
+ * Color:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (S/a_s, LUM (D/a_d), 1)
+ * = set_lum (S * a_d, a_s * LUM (D), a_s * a_d)
+ *
+ *
+ * Luminosity:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (D/a_d, LUM(S/a_s), 1)
+ * = set_lum (a_s * D, a_d * LUM(S), a_s * a_d)
+ *
+ *
+ * Saturation:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (set_sat (D/a_d, SAT (S/a_s)), LUM (D/a_d), 1)
+ * = set_lum (a_s * a_d * set_sat (D/a_d, SAT (S/a_s)),
+ * a_s * LUM (D), a_s * a_d)
+ * = set_lum (set_sat (a_s * D, a_d * SAT (S), a_s * LUM (D), a_s * a_d))
+ *
+ * Hue:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (set_sat (S/a_s, SAT (D/a_d)), LUM (D/a_d), 1)
+ * = set_lum (set_sat (a_d * S, a_s * SAT (D)), a_s * LUM (D), a_s * a_d)
+ *
+ */
+
+#define CH_MIN(c) (c[0] < c[1] ? (c[0] < c[2] ? c[0] : c[2]) : (c[1] < c[2] ? c[1] : c[2]))
+#define CH_MAX(c) (c[0] > c[1] ? (c[0] > c[2] ? c[0] : c[2]) : (c[1] > c[2] ? c[1] : c[2]))
+#define LUM(c) ((c[0] * 30 + c[1] * 59 + c[2] * 11) / 100)
+#define SAT(c) (CH_MAX (c) - CH_MIN (c))
+
+#define PDF_NON_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 result; \
+ uint32_t sc[3], dc[3], c[3]; \
+ \
+ result = d; \
+ UN8x4_MUL_UN8_ADD_UN8x4_MUL_UN8 (result, isa, s, ida); \
+ dc[0] = RED_8 (d); \
+ sc[0] = RED_8 (s); \
+ dc[1] = GREEN_8 (d); \
+ sc[1] = GREEN_8 (s); \
+ dc[2] = BLUE_8 (d); \
+ sc[2] = BLUE_8 (s); \
+ blend_ ## name (c, dc, da, sc, sa); \
+ \
+ *(dest + i) = result + \
+ (DIV_ONE_UN8 (sa * (uint32_t)da) << A_SHIFT) + \
+ (DIV_ONE_UN8 (c[0]) << R_SHIFT) + \
+ (DIV_ONE_UN8 (c[1]) << G_SHIFT) + \
+ (DIV_ONE_UN8 (c[2])); \
+ } \
+ }
+
+static void
+set_lum (uint32_t dest[3], uint32_t src[3], uint32_t sa, uint32_t lum)
+{
+ double a, l, min, max;
+ double tmp[3];
+
+ a = sa * (1.0 / MASK);
+
+ l = lum * (1.0 / MASK);
+ tmp[0] = src[0] * (1.0 / MASK);
+ tmp[1] = src[1] * (1.0 / MASK);
+ tmp[2] = src[2] * (1.0 / MASK);
+
+ l = l - LUM (tmp);
+ tmp[0] += l;
+ tmp[1] += l;
+ tmp[2] += l;
+
+ /* clip_color */
+ l = LUM (tmp);
+ min = CH_MIN (tmp);
+ max = CH_MAX (tmp);
+
+ if (min < 0)
+ {
+ if (l - min == 0.0)
+ {
+ tmp[0] = 0;
+ tmp[1] = 0;
+ tmp[2] = 0;
+ }
+ else
+ {
+ tmp[0] = l + (tmp[0] - l) * l / (l - min);
+ tmp[1] = l + (tmp[1] - l) * l / (l - min);
+ tmp[2] = l + (tmp[2] - l) * l / (l - min);
+ }
+ }
+ if (max > a)
+ {
+ if (max - l == 0.0)
+ {
+ tmp[0] = a;
+ tmp[1] = a;
+ tmp[2] = a;
+ }
+ else
+ {
+ tmp[0] = l + (tmp[0] - l) * (a - l) / (max - l);
+ tmp[1] = l + (tmp[1] - l) * (a - l) / (max - l);
+ tmp[2] = l + (tmp[2] - l) * (a - l) / (max - l);
+ }
+ }
+
+ dest[0] = tmp[0] * MASK + 0.5;
+ dest[1] = tmp[1] * MASK + 0.5;
+ dest[2] = tmp[2] * MASK + 0.5;
+}
+
+static void
+set_sat (uint32_t dest[3], uint32_t src[3], uint32_t sat)
+{
+ int id[3];
+ uint32_t min, max;
+
+ if (src[0] > src[1])
+ {
+ if (src[0] > src[2])
+ {
+ id[0] = 0;
+ if (src[1] > src[2])
+ {
+ id[1] = 1;
+ id[2] = 2;
+ }
+ else
+ {
+ id[1] = 2;
+ id[2] = 1;
+ }
+ }
+ else
+ {
+ id[0] = 2;
+ id[1] = 0;
+ id[2] = 1;
+ }
+ }
+ else
+ {
+ if (src[0] > src[2])
+ {
+ id[0] = 1;
+ id[1] = 0;
+ id[2] = 2;
+ }
+ else
+ {
+ id[2] = 0;
+ if (src[1] > src[2])
+ {
+ id[0] = 1;
+ id[1] = 2;
+ }
+ else
+ {
+ id[0] = 2;
+ id[1] = 1;
+ }
+ }
+ }
+
+ max = dest[id[0]];
+ min = dest[id[2]];
+ if (max > min)
+ {
+ dest[id[1]] = (dest[id[1]] - min) * sat / (max - min);
+ dest[id[0]] = sat;
+ dest[id[2]] = 0;
+ }
+ else
+ {
+ dest[0] = dest[1] = dest[2] = 0;
+ }
+}
+
+/*
+ * Hue:
+ * B(Cb, Cs) = set_lum (set_sat (Cs, SAT (Cb)), LUM (Cb))
+ */
+static inline void
+blend_hsl_hue (uint32_t c[3],
+ uint32_t dc[3],
+ uint32_t da,
+ uint32_t sc[3],
+ uint32_t sa)
+{
+ c[0] = sc[0] * da;
+ c[1] = sc[1] * da;
+ c[2] = sc[2] * da;
+ set_sat (c, c, SAT (dc) * sa);
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_hue)
+
+/*
+ * Saturation:
+ * B(Cb, Cs) = set_lum (set_sat (Cb, SAT (Cs)), LUM (Cb))
+ */
+static inline void
+blend_hsl_saturation (uint32_t c[3],
+ uint32_t dc[3],
+ uint32_t da,
+ uint32_t sc[3],
+ uint32_t sa)
+{
+ c[0] = dc[0] * sa;
+ c[1] = dc[1] * sa;
+ c[2] = dc[2] * sa;
+ set_sat (c, c, SAT (sc) * da);
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_saturation)
+
+/*
+ * Color:
+ * B(Cb, Cs) = set_lum (Cs, LUM (Cb))
+ */
+static inline void
+blend_hsl_color (uint32_t c[3],
+ uint32_t dc[3],
+ uint32_t da,
+ uint32_t sc[3],
+ uint32_t sa)
+{
+ c[0] = sc[0] * da;
+ c[1] = sc[1] * da;
+ c[2] = sc[2] * da;
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_color)
+
+/*
+ * Luminosity:
+ * B(Cb, Cs) = set_lum (Cb, LUM (Cs))
+ */
+static inline void
+blend_hsl_luminosity (uint32_t c[3],
+ uint32_t dc[3],
+ uint32_t da,
+ uint32_t sc[3],
+ uint32_t sa)
+{
+ c[0] = dc[0] * sa;
+ c[1] = dc[1] * sa;
+ c[2] = dc[2] * sa;
+ set_lum (c, c, sa * da, LUM (sc) * da);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_luminosity)
+
+#undef SAT
+#undef LUM
+#undef CH_MAX
+#undef CH_MIN
+#undef PDF_NON_SEPARABLE_BLEND_MODE
+
+/* All of the disjoint/conjoint composing functions
+ *
+ * The four entries in the first column indicate what source contributions
+ * come from each of the four areas of the picture -- areas covered by neither
+ * A nor B, areas covered only by A, areas covered only by B and finally
+ * areas covered by both A and B.
+ *
+ * Disjoint Conjoint
+ * Fa Fb Fa Fb
+ * (0,0,0,0) 0 0 0 0
+ * (0,A,0,A) 1 0 1 0
+ * (0,0,B,B) 0 1 0 1
+ * (0,A,B,A) 1 min((1-a)/b,1) 1 max(1-a/b,0)
+ * (0,A,B,B) min((1-b)/a,1) 1 max(1-b/a,0) 1
+ * (0,0,0,A) max(1-(1-b)/a,0) 0 min(1,b/a) 0
+ * (0,0,0,B) 0 max(1-(1-a)/b,0) 0 min(a/b,1)
+ * (0,A,0,0) min(1,(1-b)/a) 0 max(1-b/a,0) 0
+ * (0,0,B,0) 0 min(1,(1-a)/b) 0 max(1-a/b,0)
+ * (0,0,B,A) max(1-(1-b)/a,0) min(1,(1-a)/b) min(1,b/a) max(1-a/b,0)
+ * (0,A,0,B) min(1,(1-b)/a) max(1-(1-a)/b,0) max(1-b/a,0) min(1,a/b)
+ * (0,A,B,0) min(1,(1-b)/a) min(1,(1-a)/b) max(1-b/a,0) max(1-a/b,0)
+ *
+ * See http://marc.info/?l=xfree-render&m=99792000027857&w=2 for more
+ * information about these operators.
+ */
+
+#define COMBINE_A_OUT 1
+#define COMBINE_A_IN 2
+#define COMBINE_B_OUT 4
+#define COMBINE_B_IN 8
+
+#define COMBINE_CLEAR 0
+#define COMBINE_A (COMBINE_A_OUT | COMBINE_A_IN)
+#define COMBINE_B (COMBINE_B_OUT | COMBINE_B_IN)
+#define COMBINE_A_OVER (COMBINE_A_OUT | COMBINE_B_OUT | COMBINE_A_IN)
+#define COMBINE_B_OVER (COMBINE_A_OUT | COMBINE_B_OUT | COMBINE_B_IN)
+#define COMBINE_A_ATOP (COMBINE_B_OUT | COMBINE_A_IN)
+#define COMBINE_B_ATOP (COMBINE_A_OUT | COMBINE_B_IN)
+#define COMBINE_XOR (COMBINE_A_OUT | COMBINE_B_OUT)
+
+/* portion covered by a but not b */
+static uint8_t
+combine_disjoint_out_part (uint8_t a, uint8_t b)
+{
+ /* min (1, (1-b) / a) */
+
+ b = ~b; /* 1 - b */
+ if (b >= a) /* 1 - b >= a -> (1-b)/a >= 1 */
+ return MASK; /* 1 */
+ return DIV_UN8 (b, a); /* (1-b) / a */
+}
+
+/* portion covered by both a and b */
+static uint8_t
+combine_disjoint_in_part (uint8_t a, uint8_t b)
+{
+ /* max (1-(1-b)/a,0) */
+ /* = - min ((1-b)/a - 1, 0) */
+ /* = 1 - min (1, (1-b)/a) */
+
+ b = ~b; /* 1 - b */
+ if (b >= a) /* 1 - b >= a -> (1-b)/a >= 1 */
+ return 0; /* 1 - 1 */
+ return ~DIV_UN8(b, a); /* 1 - (1-b) / a */
+}
+
+/* portion covered by a but not b */
+static uint8_t
+combine_conjoint_out_part (uint8_t a, uint8_t b)
+{
+ /* max (1-b/a,0) */
+ /* = 1-min(b/a,1) */
+
+ /* min (1, (1-b) / a) */
+
+ if (b >= a) /* b >= a -> b/a >= 1 */
+ return 0x00; /* 0 */
+ return ~DIV_UN8(b, a); /* 1 - b/a */
+}
+
+/* portion covered by both a and b */
+static uint8_t
+combine_conjoint_in_part (uint8_t a, uint8_t b)
+{
+ /* min (1,b/a) */
+
+ if (b >= a) /* b >= a -> b/a >= 1 */
+ return MASK; /* 1 */
+ return DIV_UN8 (b, a); /* b/a */
+}
+
+#define GET_COMP(v, i) ((uint16_t) (uint8_t) ((v) >> i))
+
+#define ADD(x, y, i, t) \
+ ((t) = GET_COMP (x, i) + GET_COMP (y, i), \
+ (uint32_t) ((uint8_t) ((t) | (0 - ((t) >> G_SHIFT)))) << (i))
+
+#define GENERIC(x, y, i, ax, ay, t, u, v) \
+ ((t) = (MUL_UN8 (GET_COMP (y, i), ay, (u)) + \
+ MUL_UN8 (GET_COMP (x, i), ax, (v))), \
+ (uint32_t) ((uint8_t) ((t) | \
+ (0 - ((t) >> G_SHIFT)))) << (i))
+
+static void
+combine_disjoint_general_u (uint32_t * dest,
+ const uint32_t *src,
+ const uint32_t *mask,
+ int width,
+ uint8_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint32_t s = combine_mask (src, mask, i);
+ uint32_t d = *(dest + i);
+ uint32_t m, n, o, p;
+ uint16_t Fa, Fb, t, u, v;
+ uint8_t sa = s >> A_SHIFT;
+ uint8_t da = d >> A_SHIFT;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ Fa = combine_disjoint_out_part (sa, da);
+ break;
+
+ case COMBINE_A_IN:
+ Fa = combine_disjoint_in_part (sa, da);
+ break;
+
+ case COMBINE_A:
+ Fa = MASK;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ Fb = combine_disjoint_out_part (da, sa);
+ break;
+
+ case COMBINE_B_IN:
+ Fb = combine_disjoint_in_part (da, sa);
+ break;
+
+ case COMBINE_B:
+ Fb = MASK;
+ break;
+ }
+ m = GENERIC (s, d, 0, Fa, Fb, t, u, v);
+ n = GENERIC (s, d, G_SHIFT, Fa, Fb, t, u, v);
+ o = GENERIC (s, d, R_SHIFT, Fa, Fb, t, u, v);
+ p = GENERIC (s, d, A_SHIFT, Fa, Fb, t, u, v);
+ s = m | n | o | p;
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_disjoint_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;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint32_t s = combine_mask (src, mask, i);
+ uint16_t a = s >> A_SHIFT;
+
+ if (s != 0x00)
+ {
+ uint32_t d = *(dest + i);
+ a = combine_disjoint_out_part (d >> A_SHIFT, a);
+ UN8x4_MUL_UN8_ADD_UN8x4 (d, a, s);
+
+ *(dest + i) = d;
+ }
+ }
+}
+
+static void
+combine_disjoint_in_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_disjoint_in_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_disjoint_out_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_disjoint_out_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_disjoint_atop_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_disjoint_atop_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_disjoint_xor_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_XOR);
+}
+
+static void
+combine_conjoint_general_u (uint32_t * dest,
+ const uint32_t *src,
+ const uint32_t *mask,
+ int width,
+ uint8_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint32_t s = combine_mask (src, mask, i);
+ uint32_t d = *(dest + i);
+ uint32_t m, n, o, p;
+ uint16_t Fa, Fb, t, u, v;
+ uint8_t sa = s >> A_SHIFT;
+ uint8_t da = d >> A_SHIFT;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ Fa = combine_conjoint_out_part (sa, da);
+ break;
+
+ case COMBINE_A_IN:
+ Fa = combine_conjoint_in_part (sa, da);
+ break;
+
+ case COMBINE_A:
+ Fa = MASK;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ Fb = combine_conjoint_out_part (da, sa);
+ break;
+
+ case COMBINE_B_IN:
+ Fb = combine_conjoint_in_part (da, sa);
+ break;
+
+ case COMBINE_B:
+ Fb = MASK;
+ break;
+ }
+
+ m = GENERIC (s, d, 0, Fa, Fb, t, u, v);
+ n = GENERIC (s, d, G_SHIFT, Fa, Fb, t, u, v);
+ o = GENERIC (s, d, R_SHIFT, Fa, Fb, t, u, v);
+ p = GENERIC (s, d, A_SHIFT, Fa, Fb, t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_conjoint_over_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_conjoint_over_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_OVER);
+}
+
+static void
+combine_conjoint_in_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_conjoint_in_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_conjoint_out_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_conjoint_out_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_conjoint_atop_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_conjoint_atop_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_conjoint_xor_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_XOR);
+}
+
+
+/* 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;
+ }
+}
+
+static void
+combine_saturate_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, d;
+ uint16_t sa, sr, sg, sb, da;
+ uint16_t t, u, v;
+ uint32_t m, n, o, p;
+
+ d = *(dest + i);
+ s = *(src + i);
+ m = *(mask + i);
+
+ combine_mask_ca (&s, &m);
+
+ sa = (m >> A_SHIFT);
+ sr = (m >> R_SHIFT) & MASK;
+ sg = (m >> G_SHIFT) & MASK;
+ sb = m & MASK;
+ da = ~d >> A_SHIFT;
+
+ if (sb <= da)
+ m = ADD (s, d, 0, t);
+ else
+ m = GENERIC (s, d, 0, (da << G_SHIFT) / sb, MASK, t, u, v);
+
+ if (sg <= da)
+ n = ADD (s, d, G_SHIFT, t);
+ else
+ n = GENERIC (s, d, G_SHIFT, (da << G_SHIFT) / sg, MASK, t, u, v);
+
+ if (sr <= da)
+ o = ADD (s, d, R_SHIFT, t);
+ else
+ o = GENERIC (s, d, R_SHIFT, (da << G_SHIFT) / sr, MASK, t, u, v);
+
+ if (sa <= da)
+ p = ADD (s, d, A_SHIFT, t);
+ else
+ p = GENERIC (s, d, A_SHIFT, (da << G_SHIFT) / sa, MASK, t, u, v);
+
+ *(dest + i) = m | n | o | p;
+ }
+}
+
+static void
+combine_disjoint_general_ca (uint32_t * dest,
+ const uint32_t *src,
+ const uint32_t *mask,
+ int width,
+ uint8_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint32_t s, d;
+ uint32_t m, n, o, p;
+ uint32_t Fa, Fb;
+ uint16_t t, u, v;
+ uint32_t sa;
+ uint8_t da;
+
+ s = *(src + i);
+ m = *(mask + i);
+ d = *(dest + i);
+ da = d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ sa = m;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ m = (uint32_t)combine_disjoint_out_part ((uint8_t) (sa >> 0), da);
+ n = (uint32_t)combine_disjoint_out_part ((uint8_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (uint32_t)combine_disjoint_out_part ((uint8_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (uint32_t)combine_disjoint_out_part ((uint8_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A_IN:
+ m = (uint32_t)combine_disjoint_in_part ((uint8_t) (sa >> 0), da);
+ n = (uint32_t)combine_disjoint_in_part ((uint8_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (uint32_t)combine_disjoint_in_part ((uint8_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (uint32_t)combine_disjoint_in_part ((uint8_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A:
+ Fa = ~0;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ m = (uint32_t)combine_disjoint_out_part (da, (uint8_t) (sa >> 0));
+ n = (uint32_t)combine_disjoint_out_part (da, (uint8_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (uint32_t)combine_disjoint_out_part (da, (uint8_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (uint32_t)combine_disjoint_out_part (da, (uint8_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B_IN:
+ m = (uint32_t)combine_disjoint_in_part (da, (uint8_t) (sa >> 0));
+ n = (uint32_t)combine_disjoint_in_part (da, (uint8_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (uint32_t)combine_disjoint_in_part (da, (uint8_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (uint32_t)combine_disjoint_in_part (da, (uint8_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B:
+ Fb = ~0;
+ break;
+ }
+ m = GENERIC (s, d, 0, GET_COMP (Fa, 0), GET_COMP (Fb, 0), t, u, v);
+ n = GENERIC (s, d, G_SHIFT, GET_COMP (Fa, G_SHIFT), GET_COMP (Fb, G_SHIFT), t, u, v);
+ o = GENERIC (s, d, R_SHIFT, GET_COMP (Fa, R_SHIFT), GET_COMP (Fb, R_SHIFT), t, u, v);
+ p = GENERIC (s, d, A_SHIFT, GET_COMP (Fa, A_SHIFT), GET_COMP (Fb, A_SHIFT), t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_disjoint_over_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_disjoint_in_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_disjoint_in_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_disjoint_out_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_disjoint_out_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_disjoint_atop_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_disjoint_atop_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_disjoint_xor_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_XOR);
+}
+
+static void
+combine_conjoint_general_ca (uint32_t * dest,
+ const uint32_t *src,
+ const uint32_t *mask,
+ int width,
+ uint8_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint32_t s, d;
+ uint32_t m, n, o, p;
+ uint32_t Fa, Fb;
+ uint16_t t, u, v;
+ uint32_t sa;
+ uint8_t da;
+
+ s = *(src + i);
+ m = *(mask + i);
+ d = *(dest + i);
+ da = d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ sa = m;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ m = (uint32_t)combine_conjoint_out_part ((uint8_t) (sa >> 0), da);
+ n = (uint32_t)combine_conjoint_out_part ((uint8_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (uint32_t)combine_conjoint_out_part ((uint8_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (uint32_t)combine_conjoint_out_part ((uint8_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A_IN:
+ m = (uint32_t)combine_conjoint_in_part ((uint8_t) (sa >> 0), da);
+ n = (uint32_t)combine_conjoint_in_part ((uint8_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (uint32_t)combine_conjoint_in_part ((uint8_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (uint32_t)combine_conjoint_in_part ((uint8_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A:
+ Fa = ~0;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ m = (uint32_t)combine_conjoint_out_part (da, (uint8_t) (sa >> 0));
+ n = (uint32_t)combine_conjoint_out_part (da, (uint8_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (uint32_t)combine_conjoint_out_part (da, (uint8_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (uint32_t)combine_conjoint_out_part (da, (uint8_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B_IN:
+ m = (uint32_t)combine_conjoint_in_part (da, (uint8_t) (sa >> 0));
+ n = (uint32_t)combine_conjoint_in_part (da, (uint8_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (uint32_t)combine_conjoint_in_part (da, (uint8_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (uint32_t)combine_conjoint_in_part (da, (uint8_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B:
+ Fb = ~0;
+ break;
+ }
+ m = GENERIC (s, d, 0, GET_COMP (Fa, 0), GET_COMP (Fb, 0), t, u, v);
+ n = GENERIC (s, d, G_SHIFT, GET_COMP (Fa, G_SHIFT), GET_COMP (Fb, G_SHIFT), t, u, v);
+ o = GENERIC (s, d, R_SHIFT, GET_COMP (Fa, R_SHIFT), GET_COMP (Fb, R_SHIFT), t, u, v);
+ p = GENERIC (s, d, A_SHIFT, GET_COMP (Fa, A_SHIFT), GET_COMP (Fb, A_SHIFT), t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_conjoint_over_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_conjoint_over_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_OVER);
+}
+
+static void
+combine_conjoint_in_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_conjoint_in_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_conjoint_out_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_conjoint_out_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_conjoint_atop_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_conjoint_atop_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_conjoint_xor_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint32_t * dest,
+ const uint32_t * src,
+ const uint32_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_XOR);
+}
+
+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_SATURATE] = combine_saturate_u;
+
+ /* Disjoint, unified */
+ imp->combine_32[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear;
+ imp->combine_32[PIXMAN_OP_DISJOINT_SRC] = combine_src_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_DST] = combine_dst;
+ imp->combine_32[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_saturate_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_u;
+ imp->combine_32[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_u;
+
+ /* Conjoint, unified */
+ imp->combine_32[PIXMAN_OP_CONJOINT_CLEAR] = combine_clear;
+ imp->combine_32[PIXMAN_OP_CONJOINT_SRC] = combine_src_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_DST] = combine_dst;
+ imp->combine_32[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_u;
+ imp->combine_32[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_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_COLOR_DODGE] = combine_color_dodge_u;
+ imp->combine_32[PIXMAN_OP_COLOR_BURN] = combine_color_burn_u;
+ imp->combine_32[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_u;
+ imp->combine_32[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_u;
+ imp->combine_32[PIXMAN_OP_DIFFERENCE] = combine_difference_u;
+ imp->combine_32[PIXMAN_OP_EXCLUSION] = combine_exclusion_u;
+ imp->combine_32[PIXMAN_OP_HSL_HUE] = combine_hsl_hue_u;
+ imp->combine_32[PIXMAN_OP_HSL_SATURATION] = combine_hsl_saturation_u;
+ imp->combine_32[PIXMAN_OP_HSL_COLOR] = combine_hsl_color_u;
+ imp->combine_32[PIXMAN_OP_HSL_LUMINOSITY] = combine_hsl_luminosity_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_SATURATE] = combine_saturate_ca;
+
+ /* Disjoint CA */
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_SRC] = combine_src_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_DST] = combine_dst;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_saturate_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_ca;
+ imp->combine_32_ca[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_ca;
+
+ /* Conjoint CA */
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_CLEAR] = combine_clear_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_SRC] = combine_src_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_DST] = combine_dst;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_ca;
+ imp->combine_32_ca[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_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_COLOR_DODGE] = combine_color_dodge_ca;
+ imp->combine_32_ca[PIXMAN_OP_COLOR_BURN] = combine_color_burn_ca;
+ imp->combine_32_ca[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_ca;
+ imp->combine_32_ca[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_ca;
+ imp->combine_32_ca[PIXMAN_OP_DIFFERENCE] = combine_difference_ca;
+ imp->combine_32_ca[PIXMAN_OP_EXCLUSION] = combine_exclusion_ca;
+
+ /* It is not clear that these make sense, so make them noops for now */
+ imp->combine_32_ca[PIXMAN_OP_HSL_HUE] = combine_dst;
+ imp->combine_32_ca[PIXMAN_OP_HSL_SATURATION] = combine_dst;
+ imp->combine_32_ca[PIXMAN_OP_HSL_COLOR] = combine_dst;
+ imp->combine_32_ca[PIXMAN_OP_HSL_LUMINOSITY] = combine_dst;
+}
diff --git a/gfx/cairo/libpixman/src/pixman-combine32.h b/gfx/cairo/libpixman/src/pixman-combine32.h
new file mode 100644
index 000000000..cdd56a61a
--- /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 0x10000100
+
+#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-combine64.c b/gfx/cairo/libpixman/src/pixman-combine64.c
new file mode 100644
index 000000000..1c85af8ee
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine64.c
@@ -0,0 +1,2465 @@
+/* WARNING: This file is generated by combine.pl from combine.inc.
+ Please edit one of those files rather than this one. */
+
+#line 1 "pixman-combine.c.template"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "pixman-private.h"
+
+#include "pixman-combine64.h"
+
+/*** per channel helper functions ***/
+
+static void
+combine_mask_ca (uint64_t *src, uint64_t *mask)
+{
+ uint64_t a = *mask;
+
+ uint64_t x;
+ uint32_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;
+ UN16x4_MUL_UN16x4 (x, a);
+ *(src) = x;
+
+ UN16x4_MUL_UN16 (a, xa);
+ *(mask) = a;
+}
+
+static void
+combine_mask_value_ca (uint64_t *src, const uint64_t *mask)
+{
+ uint64_t a = *mask;
+ uint64_t x;
+
+ if (!a)
+ {
+ *(src) = 0;
+ return;
+ }
+
+ if (a == ~0)
+ return;
+
+ x = *(src);
+ UN16x4_MUL_UN16x4 (x, a);
+ *(src) = x;
+}
+
+static void
+combine_mask_alpha_ca (const uint64_t *src, uint64_t *mask)
+{
+ uint64_t a = *(mask);
+ uint64_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;
+ }
+
+ UN16x4_MUL_UN16 (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 'C'. Similarly, functions which deal with
+ * this difference will have two versions using the same convention.
+ */
+
+/*
+ * All of the composing functions
+ */
+
+static force_inline uint64_t
+combine_mask (const uint64_t *src, const uint64_t *mask, int i)
+{
+ uint64_t s, m;
+
+ if (mask)
+ {
+ m = *(mask + i) >> A_SHIFT;
+
+ if (!m)
+ return 0;
+ }
+
+ s = *(src + i);
+
+ if (mask)
+ UN16x4_MUL_UN16 (s, m);
+
+ return s;
+}
+
+static void
+combine_clear (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ memset (dest, 0, width * sizeof(uint64_t));
+}
+
+static void
+combine_dst (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ return;
+}
+
+static void
+combine_src_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ if (!mask)
+ memcpy (dest, src, width * sizeof (uint64_t));
+ else
+ {
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+
+ *(dest + i) = s;
+ }
+ }
+}
+
+/* if the Src is opaque, call combine_src_u */
+static void
+combine_over_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t ia = ALPHA_16 (~s);
+
+ UN16x4_MUL_UN16_ADD_UN16x4 (d, ia, s);
+ *(dest + i) = d;
+ }
+}
+
+/* if the Dst is opaque, this is a noop */
+static void
+combine_over_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t ia = ALPHA_16 (~*(dest + i));
+ UN16x4_MUL_UN16_ADD_UN16x4 (s, ia, d);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Dst is opaque, call combine_src_u */
+static void
+combine_in_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t a = ALPHA_16 (*(dest + i));
+ UN16x4_MUL_UN16 (s, a);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Src is opaque, this is a noop */
+static void
+combine_in_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t a = ALPHA_16 (s);
+ UN16x4_MUL_UN16 (d, a);
+ *(dest + i) = d;
+ }
+}
+
+/* if the Dst is opaque, call combine_clear */
+static void
+combine_out_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t a = ALPHA_16 (~*(dest + i));
+ UN16x4_MUL_UN16 (s, a);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Src is opaque, call combine_clear */
+static void
+combine_out_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t a = ALPHA_16 (~s);
+ UN16x4_MUL_UN16 (d, a);
+ *(dest + i) = d;
+ }
+}
+
+/* if the Src is opaque, call combine_in_u */
+/* if the Dst is opaque, call combine_over_u */
+/* if both the Src and Dst are opaque, call combine_src_u */
+static void
+combine_atop_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t dest_a = ALPHA_16 (d);
+ uint64_t src_ia = ALPHA_16 (~s);
+
+ UN16x4_MUL_UN16_ADD_UN16x4_MUL_UN16 (s, dest_a, d, src_ia);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Src is opaque, call combine_over_reverse_u */
+/* if the Dst is opaque, call combine_in_reverse_u */
+/* if both the Src and Dst are opaque, call combine_dst_u */
+static void
+combine_atop_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t src_a = ALPHA_16 (s);
+ uint64_t dest_ia = ALPHA_16 (~d);
+
+ UN16x4_MUL_UN16_ADD_UN16x4_MUL_UN16 (s, dest_ia, d, src_a);
+ *(dest + i) = s;
+ }
+}
+
+/* if the Src is opaque, call combine_over_u */
+/* if the Dst is opaque, call combine_over_reverse_u */
+/* if both the Src and Dst are opaque, call combine_clear */
+static void
+combine_xor_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t src_ia = ALPHA_16 (~s);
+ uint64_t dest_ia = ALPHA_16 (~d);
+
+ UN16x4_MUL_UN16_ADD_UN16x4_MUL_UN16 (s, dest_ia, d, src_ia);
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_add_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ UN16x4_ADD_UN16x4 (d, s);
+ *(dest + i) = d;
+ }
+}
+
+/* if the Src is opaque, call combine_add_u */
+/* if the Dst is opaque, call combine_add_u */
+/* if both the Src and Dst are opaque, call combine_add_u */
+static void
+combine_saturate_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint32_t sa, da;
+
+ sa = s >> A_SHIFT;
+ da = ~d >> A_SHIFT;
+ if (sa > da)
+ {
+ sa = DIV_UN16 (da, sa);
+ UN16x4_MUL_UN16 (s, sa);
+ }
+ ;
+ UN16x4_ADD_UN16x4 (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/acrobat/pdfs/PDF32000_2008.pdf
+ * The relevant chapters are 11.3.5 and 11.3.6.
+ * 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() being the blend function.
+ * Note that OVER is a special case of this operation, using B(Cb, Cs) = Cs
+ *
+ * These blend modes should match the SVG filter draft specification, as
+ * it has been designed to mirror ISO 32000. Note that at the current point
+ * no released draft exists that shows this, as the formulas have not been
+ * updated yet after the release of ISO 32000.
+ *
+ * The default implementation here uses the PDF_SEPARABLE_BLEND_MODE and
+ * PDF_NON_SEPARABLE_BLEND_MODE macros, which take the blend function as an
+ * argument. Note that this implementation operates on premultiplied colors,
+ * while the PDF specification does not. Therefore the code uses the formula
+ * Cra = (1 – as) . Dca + (1 – ad) . Sca + B(Dca, ad, Sca, as)
+ */
+
+/*
+ * Multiply
+ * B(Dca, ad, Sca, as) = Dca.Sca
+ */
+
+static void
+combine_multiply_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t ss = s;
+ uint64_t src_ia = ALPHA_16 (~s);
+ uint64_t dest_ia = ALPHA_16 (~d);
+
+ UN16x4_MUL_UN16_ADD_UN16x4_MUL_UN16 (ss, dest_ia, d, src_ia);
+ UN16x4_MUL_UN16x4 (d, s);
+ UN16x4_ADD_UN16x4 (d, ss);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_multiply_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t m = *(mask + i);
+ uint64_t s = *(src + i);
+ uint64_t d = *(dest + i);
+ uint64_t r = d;
+ uint64_t dest_ia = ALPHA_16 (~d);
+
+ combine_mask_value_ca (&s, &m);
+
+ UN16x4_MUL_UN16x4_ADD_UN16x4_MUL_UN16 (r, ~m, s, dest_ia);
+ UN16x4_MUL_UN16x4 (d, s);
+ UN16x4_ADD_UN16x4 (r, d);
+
+ *(dest + i) = r;
+ }
+}
+
+#define PDF_SEPARABLE_BLEND_MODE(name) \
+ static void \
+ combine_ ## name ## _u (pixman_implementation_t *imp, \
+ pixman_op_t op, \
+ uint64_t * dest, \
+ const uint64_t * src, \
+ const uint64_t * mask, \
+ int width) \
+ { \
+ int i; \
+ for (i = 0; i < width; ++i) { \
+ uint64_t s = combine_mask (src, mask, i); \
+ uint64_t d = *(dest + i); \
+ uint16_t sa = ALPHA_16 (s); \
+ uint16_t isa = ~sa; \
+ uint16_t da = ALPHA_16 (d); \
+ uint16_t ida = ~da; \
+ uint64_t result; \
+ \
+ result = d; \
+ UN16x4_MUL_UN16_ADD_UN16x4_MUL_UN16 (result, isa, s, ida); \
+ \
+ *(dest + i) = result + \
+ (DIV_ONE_UN16 (sa * (uint64_t)da) << A_SHIFT) + \
+ (blend_ ## name (RED_16 (d), da, RED_16 (s), sa) << R_SHIFT) + \
+ (blend_ ## name (GREEN_16 (d), da, GREEN_16 (s), sa) << G_SHIFT) + \
+ (blend_ ## name (BLUE_16 (d), da, BLUE_16 (s), sa)); \
+ } \
+ } \
+ \
+ static void \
+ combine_ ## name ## _ca (pixman_implementation_t *imp, \
+ pixman_op_t op, \
+ uint64_t * dest, \
+ const uint64_t * src, \
+ const uint64_t * mask, \
+ int width) \
+ { \
+ int i; \
+ for (i = 0; i < width; ++i) { \
+ uint64_t m = *(mask + i); \
+ uint64_t s = *(src + i); \
+ uint64_t d = *(dest + i); \
+ uint16_t da = ALPHA_16 (d); \
+ uint16_t ida = ~da; \
+ uint64_t result; \
+ \
+ combine_mask_value_ca (&s, &m); \
+ \
+ result = d; \
+ UN16x4_MUL_UN16x4_ADD_UN16x4_MUL_UN16 (result, ~m, s, ida); \
+ \
+ result += \
+ (DIV_ONE_UN16 (ALPHA_16 (m) * (uint64_t)da) << A_SHIFT) + \
+ (blend_ ## name (RED_16 (d), da, RED_16 (s), RED_16 (m)) << R_SHIFT) + \
+ (blend_ ## name (GREEN_16 (d), da, GREEN_16 (s), GREEN_16 (m)) << G_SHIFT) + \
+ (blend_ ## name (BLUE_16 (d), da, BLUE_16 (s), BLUE_16 (m))); \
+ \
+ *(dest + i) = result; \
+ } \
+ }
+
+/*
+ * Screen
+ * B(Dca, ad, Sca, as) = Dca.sa + Sca.da - Dca.Sca
+ */
+static inline uint64_t
+blend_screen (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ return DIV_ONE_UN16 (sca * da + dca * sa - sca * dca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (screen)
+
+/*
+ * Overlay
+ * B(Dca, Da, Sca, Sa) =
+ * if 2.Dca < Da
+ * 2.Sca.Dca
+ * otherwise
+ * Sa.Da - 2.(Da - Dca).(Sa - Sca)
+ */
+static inline uint64_t
+blend_overlay (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ uint64_t rca;
+
+ if (2 * dca < da)
+ rca = 2 * sca * dca;
+ else
+ rca = sa * da - 2 * (da - dca) * (sa - sca);
+ return DIV_ONE_UN16 (rca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (overlay)
+
+/*
+ * Darken
+ * B(Dca, Da, Sca, Sa) = min (Sca.Da, Dca.Sa)
+ */
+static inline uint64_t
+blend_darken (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ uint64_t s, d;
+
+ s = sca * da;
+ d = dca * sa;
+ return DIV_ONE_UN16 (s > d ? d : s);
+}
+
+PDF_SEPARABLE_BLEND_MODE (darken)
+
+/*
+ * Lighten
+ * B(Dca, Da, Sca, Sa) = max (Sca.Da, Dca.Sa)
+ */
+static inline uint64_t
+blend_lighten (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ uint64_t s, d;
+
+ s = sca * da;
+ d = dca * sa;
+ return DIV_ONE_UN16 (s > d ? s : d);
+}
+
+PDF_SEPARABLE_BLEND_MODE (lighten)
+
+/*
+ * Color dodge
+ * B(Dca, Da, Sca, Sa) =
+ * if Dca == 0
+ * 0
+ * if Sca == Sa
+ * Sa.Da
+ * otherwise
+ * Sa.Da. min (1, Dca / Da / (1 - Sca/Sa))
+ */
+static inline uint64_t
+blend_color_dodge (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ if (sca >= sa)
+ {
+ return dca == 0 ? 0 : DIV_ONE_UN16 (sa * da);
+ }
+ else
+ {
+ uint64_t rca = dca * sa / (sa - sca);
+ return DIV_ONE_UN16 (sa * MIN (rca, da));
+ }
+}
+
+PDF_SEPARABLE_BLEND_MODE (color_dodge)
+
+/*
+ * Color burn
+ * B(Dca, Da, Sca, Sa) =
+ * if Dca == Da
+ * Sa.Da
+ * if Sca == 0
+ * 0
+ * otherwise
+ * Sa.Da.(1 - min (1, (1 - Dca/Da).Sa / Sca))
+ */
+static inline uint64_t
+blend_color_burn (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ if (sca == 0)
+ {
+ return dca < da ? 0 : DIV_ONE_UN16 (sa * da);
+ }
+ else
+ {
+ uint64_t rca = (da - dca) * sa / sca;
+ return DIV_ONE_UN16 (sa * (MAX (rca, da) - rca));
+ }
+}
+
+PDF_SEPARABLE_BLEND_MODE (color_burn)
+
+/*
+ * Hard light
+ * B(Dca, Da, Sca, Sa) =
+ * if 2.Sca < Sa
+ * 2.Sca.Dca
+ * otherwise
+ * Sa.Da - 2.(Da - Dca).(Sa - Sca)
+ */
+static inline uint64_t
+blend_hard_light (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ if (2 * sca < sa)
+ return DIV_ONE_UN16 (2 * sca * dca);
+ else
+ return DIV_ONE_UN16 (sa * da - 2 * (da - dca) * (sa - sca));
+}
+
+PDF_SEPARABLE_BLEND_MODE (hard_light)
+
+/*
+ * Soft light
+ * B(Dca, Da, Sca, Sa) =
+ * if (2.Sca <= Sa)
+ * Dca.(Sa - (1 - Dca/Da).(2.Sca - Sa))
+ * otherwise if Dca.4 <= Da
+ * Dca.(Sa + (2.Sca - Sa).((16.Dca/Da - 12).Dca/Da + 3)
+ * otherwise
+ * (Dca.Sa + (SQRT (Dca/Da).Da - Dca).(2.Sca - Sa))
+ */
+static inline uint64_t
+blend_soft_light (uint64_t dca_org,
+ uint64_t da_org,
+ uint64_t sca_org,
+ uint64_t sa_org)
+{
+ double dca = dca_org * (1.0 / MASK);
+ double da = da_org * (1.0 / MASK);
+ double sca = sca_org * (1.0 / MASK);
+ double sa = sa_org * (1.0 / MASK);
+ double rca;
+
+ if (2 * sca < sa)
+ {
+ if (da == 0)
+ rca = dca * sa;
+ else
+ rca = dca * sa - dca * (da - dca) * (sa - 2 * sca) / da;
+ }
+ else if (da == 0)
+ {
+ rca = 0;
+ }
+ else if (4 * dca <= da)
+ {
+ rca = dca * sa +
+ (2 * sca - sa) * dca * ((16 * dca / da - 12) * dca / da + 3);
+ }
+ else
+ {
+ rca = dca * sa + (sqrt (dca * da) - dca) * (2 * sca - sa);
+ }
+ return rca * MASK + 0.5;
+}
+
+PDF_SEPARABLE_BLEND_MODE (soft_light)
+
+/*
+ * Difference
+ * B(Dca, Da, Sca, Sa) = abs (Dca.Sa - Sca.Da)
+ */
+static inline uint64_t
+blend_difference (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ uint64_t dcasa = dca * sa;
+ uint64_t scada = sca * da;
+
+ if (scada < dcasa)
+ return DIV_ONE_UN16 (dcasa - scada);
+ else
+ return DIV_ONE_UN16 (scada - dcasa);
+}
+
+PDF_SEPARABLE_BLEND_MODE (difference)
+
+/*
+ * Exclusion
+ * B(Dca, Da, Sca, Sa) = (Sca.Da + Dca.Sa - 2.Sca.Dca)
+ */
+
+/* This can be made faster by writing it directly and not using
+ * PDF_SEPARABLE_BLEND_MODE, but that's a performance optimization */
+
+static inline uint64_t
+blend_exclusion (uint64_t dca, uint64_t da, uint64_t sca, uint64_t sa)
+{
+ return DIV_ONE_UN16 (sca * da + dca * sa - 2 * dca * sca);
+}
+
+PDF_SEPARABLE_BLEND_MODE (exclusion)
+
+#undef PDF_SEPARABLE_BLEND_MODE
+
+/*
+ * 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, ra);
+ *
+ * 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, rs)
+ *
+ */
+
+/* So, for the non-separable PDF blend modes, we have (using s, d for
+ * non-premultiplied colors, and S, D for premultiplied:
+ *
+ * Color:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (S/a_s, LUM (D/a_d), 1)
+ * = set_lum (S * a_d, a_s * LUM (D), a_s * a_d)
+ *
+ *
+ * Luminosity:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (D/a_d, LUM(S/a_s), 1)
+ * = set_lum (a_s * D, a_d * LUM(S), a_s * a_d)
+ *
+ *
+ * Saturation:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (set_sat (D/a_d, SAT (S/a_s)), LUM (D/a_d), 1)
+ * = set_lum (a_s * a_d * set_sat (D/a_d, SAT (S/a_s)),
+ * a_s * LUM (D), a_s * a_d)
+ * = set_lum (set_sat (a_s * D, a_d * SAT (S), a_s * LUM (D), a_s * a_d))
+ *
+ * Hue:
+ *
+ * a_s * a_d * B(s, d)
+ * = a_s * a_d * set_lum (set_sat (S/a_s, SAT (D/a_d)), LUM (D/a_d), 1)
+ * = set_lum (set_sat (a_d * S, a_s * SAT (D)), a_s * LUM (D), a_s * a_d)
+ *
+ */
+
+#define CH_MIN(c) (c[0] < c[1] ? (c[0] < c[2] ? c[0] : c[2]) : (c[1] < c[2] ? c[1] : c[2]))
+#define CH_MAX(c) (c[0] > c[1] ? (c[0] > c[2] ? c[0] : c[2]) : (c[1] > c[2] ? c[1] : c[2]))
+#define LUM(c) ((c[0] * 30 + c[1] * 59 + c[2] * 11) / 100)
+#define SAT(c) (CH_MAX (c) - CH_MIN (c))
+
+#define PDF_NON_SEPARABLE_BLEND_MODE(name) \
+ static void \
+ combine_ ## name ## _u (pixman_implementation_t *imp, \
+ pixman_op_t op, \
+ uint64_t *dest, \
+ const uint64_t *src, \
+ const uint64_t *mask, \
+ int width) \
+ { \
+ int i; \
+ for (i = 0; i < width; ++i) \
+ { \
+ uint64_t s = combine_mask (src, mask, i); \
+ uint64_t d = *(dest + i); \
+ uint16_t sa = ALPHA_16 (s); \
+ uint16_t isa = ~sa; \
+ uint16_t da = ALPHA_16 (d); \
+ uint16_t ida = ~da; \
+ uint64_t result; \
+ uint64_t sc[3], dc[3], c[3]; \
+ \
+ result = d; \
+ UN16x4_MUL_UN16_ADD_UN16x4_MUL_UN16 (result, isa, s, ida); \
+ dc[0] = RED_16 (d); \
+ sc[0] = RED_16 (s); \
+ dc[1] = GREEN_16 (d); \
+ sc[1] = GREEN_16 (s); \
+ dc[2] = BLUE_16 (d); \
+ sc[2] = BLUE_16 (s); \
+ blend_ ## name (c, dc, da, sc, sa); \
+ \
+ *(dest + i) = result + \
+ (DIV_ONE_UN16 (sa * (uint64_t)da) << A_SHIFT) + \
+ (DIV_ONE_UN16 (c[0]) << R_SHIFT) + \
+ (DIV_ONE_UN16 (c[1]) << G_SHIFT) + \
+ (DIV_ONE_UN16 (c[2])); \
+ } \
+ }
+
+static void
+set_lum (uint64_t dest[3], uint64_t src[3], uint64_t sa, uint64_t lum)
+{
+ double a, l, min, max;
+ double tmp[3];
+
+ a = sa * (1.0 / MASK);
+
+ l = lum * (1.0 / MASK);
+ tmp[0] = src[0] * (1.0 / MASK);
+ tmp[1] = src[1] * (1.0 / MASK);
+ tmp[2] = src[2] * (1.0 / MASK);
+
+ l = l - LUM (tmp);
+ tmp[0] += l;
+ tmp[1] += l;
+ tmp[2] += l;
+
+ /* clip_color */
+ l = LUM (tmp);
+ min = CH_MIN (tmp);
+ max = CH_MAX (tmp);
+
+ if (min < 0)
+ {
+ if (l - min == 0.0)
+ {
+ tmp[0] = 0;
+ tmp[1] = 0;
+ tmp[2] = 0;
+ }
+ else
+ {
+ tmp[0] = l + (tmp[0] - l) * l / (l - min);
+ tmp[1] = l + (tmp[1] - l) * l / (l - min);
+ tmp[2] = l + (tmp[2] - l) * l / (l - min);
+ }
+ }
+ if (max > a)
+ {
+ if (max - l == 0.0)
+ {
+ tmp[0] = a;
+ tmp[1] = a;
+ tmp[2] = a;
+ }
+ else
+ {
+ tmp[0] = l + (tmp[0] - l) * (a - l) / (max - l);
+ tmp[1] = l + (tmp[1] - l) * (a - l) / (max - l);
+ tmp[2] = l + (tmp[2] - l) * (a - l) / (max - l);
+ }
+ }
+
+ dest[0] = tmp[0] * MASK + 0.5;
+ dest[1] = tmp[1] * MASK + 0.5;
+ dest[2] = tmp[2] * MASK + 0.5;
+}
+
+static void
+set_sat (uint64_t dest[3], uint64_t src[3], uint64_t sat)
+{
+ int id[3];
+ uint64_t min, max;
+
+ if (src[0] > src[1])
+ {
+ if (src[0] > src[2])
+ {
+ id[0] = 0;
+ if (src[1] > src[2])
+ {
+ id[1] = 1;
+ id[2] = 2;
+ }
+ else
+ {
+ id[1] = 2;
+ id[2] = 1;
+ }
+ }
+ else
+ {
+ id[0] = 2;
+ id[1] = 0;
+ id[2] = 1;
+ }
+ }
+ else
+ {
+ if (src[0] > src[2])
+ {
+ id[0] = 1;
+ id[1] = 0;
+ id[2] = 2;
+ }
+ else
+ {
+ id[2] = 0;
+ if (src[1] > src[2])
+ {
+ id[0] = 1;
+ id[1] = 2;
+ }
+ else
+ {
+ id[0] = 2;
+ id[1] = 1;
+ }
+ }
+ }
+
+ max = dest[id[0]];
+ min = dest[id[2]];
+ if (max > min)
+ {
+ dest[id[1]] = (dest[id[1]] - min) * sat / (max - min);
+ dest[id[0]] = sat;
+ dest[id[2]] = 0;
+ }
+ else
+ {
+ dest[0] = dest[1] = dest[2] = 0;
+ }
+}
+
+/*
+ * Hue:
+ * B(Cb, Cs) = set_lum (set_sat (Cs, SAT (Cb)), LUM (Cb))
+ */
+static inline void
+blend_hsl_hue (uint64_t c[3],
+ uint64_t dc[3],
+ uint64_t da,
+ uint64_t sc[3],
+ uint64_t sa)
+{
+ c[0] = sc[0] * da;
+ c[1] = sc[1] * da;
+ c[2] = sc[2] * da;
+ set_sat (c, c, SAT (dc) * sa);
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_hue)
+
+/*
+ * Saturation:
+ * B(Cb, Cs) = set_lum (set_sat (Cb, SAT (Cs)), LUM (Cb))
+ */
+static inline void
+blend_hsl_saturation (uint64_t c[3],
+ uint64_t dc[3],
+ uint64_t da,
+ uint64_t sc[3],
+ uint64_t sa)
+{
+ c[0] = dc[0] * sa;
+ c[1] = dc[1] * sa;
+ c[2] = dc[2] * sa;
+ set_sat (c, c, SAT (sc) * da);
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_saturation)
+
+/*
+ * Color:
+ * B(Cb, Cs) = set_lum (Cs, LUM (Cb))
+ */
+static inline void
+blend_hsl_color (uint64_t c[3],
+ uint64_t dc[3],
+ uint64_t da,
+ uint64_t sc[3],
+ uint64_t sa)
+{
+ c[0] = sc[0] * da;
+ c[1] = sc[1] * da;
+ c[2] = sc[2] * da;
+ set_lum (c, c, sa * da, LUM (dc) * sa);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_color)
+
+/*
+ * Luminosity:
+ * B(Cb, Cs) = set_lum (Cb, LUM (Cs))
+ */
+static inline void
+blend_hsl_luminosity (uint64_t c[3],
+ uint64_t dc[3],
+ uint64_t da,
+ uint64_t sc[3],
+ uint64_t sa)
+{
+ c[0] = dc[0] * sa;
+ c[1] = dc[1] * sa;
+ c[2] = dc[2] * sa;
+ set_lum (c, c, sa * da, LUM (sc) * da);
+}
+
+PDF_NON_SEPARABLE_BLEND_MODE (hsl_luminosity)
+
+#undef SAT
+#undef LUM
+#undef CH_MAX
+#undef CH_MIN
+#undef PDF_NON_SEPARABLE_BLEND_MODE
+
+/* All of the disjoint/conjoint composing functions
+ *
+ * The four entries in the first column indicate what source contributions
+ * come from each of the four areas of the picture -- areas covered by neither
+ * A nor B, areas covered only by A, areas covered only by B and finally
+ * areas covered by both A and B.
+ *
+ * Disjoint Conjoint
+ * Fa Fb Fa Fb
+ * (0,0,0,0) 0 0 0 0
+ * (0,A,0,A) 1 0 1 0
+ * (0,0,B,B) 0 1 0 1
+ * (0,A,B,A) 1 min((1-a)/b,1) 1 max(1-a/b,0)
+ * (0,A,B,B) min((1-b)/a,1) 1 max(1-b/a,0) 1
+ * (0,0,0,A) max(1-(1-b)/a,0) 0 min(1,b/a) 0
+ * (0,0,0,B) 0 max(1-(1-a)/b,0) 0 min(a/b,1)
+ * (0,A,0,0) min(1,(1-b)/a) 0 max(1-b/a,0) 0
+ * (0,0,B,0) 0 min(1,(1-a)/b) 0 max(1-a/b,0)
+ * (0,0,B,A) max(1-(1-b)/a,0) min(1,(1-a)/b) min(1,b/a) max(1-a/b,0)
+ * (0,A,0,B) min(1,(1-b)/a) max(1-(1-a)/b,0) max(1-b/a,0) min(1,a/b)
+ * (0,A,B,0) min(1,(1-b)/a) min(1,(1-a)/b) max(1-b/a,0) max(1-a/b,0)
+ *
+ * See http://marc.info/?l=xfree-render&m=99792000027857&w=2 for more
+ * information about these operators.
+ */
+
+#define COMBINE_A_OUT 1
+#define COMBINE_A_IN 2
+#define COMBINE_B_OUT 4
+#define COMBINE_B_IN 8
+
+#define COMBINE_CLEAR 0
+#define COMBINE_A (COMBINE_A_OUT | COMBINE_A_IN)
+#define COMBINE_B (COMBINE_B_OUT | COMBINE_B_IN)
+#define COMBINE_A_OVER (COMBINE_A_OUT | COMBINE_B_OUT | COMBINE_A_IN)
+#define COMBINE_B_OVER (COMBINE_A_OUT | COMBINE_B_OUT | COMBINE_B_IN)
+#define COMBINE_A_ATOP (COMBINE_B_OUT | COMBINE_A_IN)
+#define COMBINE_B_ATOP (COMBINE_A_OUT | COMBINE_B_IN)
+#define COMBINE_XOR (COMBINE_A_OUT | COMBINE_B_OUT)
+
+/* portion covered by a but not b */
+static uint16_t
+combine_disjoint_out_part (uint16_t a, uint16_t b)
+{
+ /* min (1, (1-b) / a) */
+
+ b = ~b; /* 1 - b */
+ if (b >= a) /* 1 - b >= a -> (1-b)/a >= 1 */
+ return MASK; /* 1 */
+ return DIV_UN16 (b, a); /* (1-b) / a */
+}
+
+/* portion covered by both a and b */
+static uint16_t
+combine_disjoint_in_part (uint16_t a, uint16_t b)
+{
+ /* max (1-(1-b)/a,0) */
+ /* = - min ((1-b)/a - 1, 0) */
+ /* = 1 - min (1, (1-b)/a) */
+
+ b = ~b; /* 1 - b */
+ if (b >= a) /* 1 - b >= a -> (1-b)/a >= 1 */
+ return 0; /* 1 - 1 */
+ return ~DIV_UN16(b, a); /* 1 - (1-b) / a */
+}
+
+/* portion covered by a but not b */
+static uint16_t
+combine_conjoint_out_part (uint16_t a, uint16_t b)
+{
+ /* max (1-b/a,0) */
+ /* = 1-min(b/a,1) */
+
+ /* min (1, (1-b) / a) */
+
+ if (b >= a) /* b >= a -> b/a >= 1 */
+ return 0x00; /* 0 */
+ return ~DIV_UN16(b, a); /* 1 - b/a */
+}
+
+/* portion covered by both a and b */
+static uint16_t
+combine_conjoint_in_part (uint16_t a, uint16_t b)
+{
+ /* min (1,b/a) */
+
+ if (b >= a) /* b >= a -> b/a >= 1 */
+ return MASK; /* 1 */
+ return DIV_UN16 (b, a); /* b/a */
+}
+
+#define GET_COMP(v, i) ((uint32_t) (uint16_t) ((v) >> i))
+
+#define ADD(x, y, i, t) \
+ ((t) = GET_COMP (x, i) + GET_COMP (y, i), \
+ (uint64_t) ((uint16_t) ((t) | (0 - ((t) >> G_SHIFT)))) << (i))
+
+#define GENERIC(x, y, i, ax, ay, t, u, v) \
+ ((t) = (MUL_UN16 (GET_COMP (y, i), ay, (u)) + \
+ MUL_UN16 (GET_COMP (x, i), ax, (v))), \
+ (uint64_t) ((uint16_t) ((t) | \
+ (0 - ((t) >> G_SHIFT)))) << (i))
+
+static void
+combine_disjoint_general_u (uint64_t * dest,
+ const uint64_t *src,
+ const uint64_t *mask,
+ int width,
+ uint16_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t m, n, o, p;
+ uint32_t Fa, Fb, t, u, v;
+ uint16_t sa = s >> A_SHIFT;
+ uint16_t da = d >> A_SHIFT;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ Fa = combine_disjoint_out_part (sa, da);
+ break;
+
+ case COMBINE_A_IN:
+ Fa = combine_disjoint_in_part (sa, da);
+ break;
+
+ case COMBINE_A:
+ Fa = MASK;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ Fb = combine_disjoint_out_part (da, sa);
+ break;
+
+ case COMBINE_B_IN:
+ Fb = combine_disjoint_in_part (da, sa);
+ break;
+
+ case COMBINE_B:
+ Fb = MASK;
+ break;
+ }
+ m = GENERIC (s, d, 0, Fa, Fb, t, u, v);
+ n = GENERIC (s, d, G_SHIFT, Fa, Fb, t, u, v);
+ o = GENERIC (s, d, R_SHIFT, Fa, Fb, t, u, v);
+ p = GENERIC (s, d, A_SHIFT, Fa, Fb, t, u, v);
+ s = m | n | o | p;
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_disjoint_over_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint32_t a = s >> A_SHIFT;
+
+ if (s != 0x00)
+ {
+ uint64_t d = *(dest + i);
+ a = combine_disjoint_out_part (d >> A_SHIFT, a);
+ UN16x4_MUL_UN16_ADD_UN16x4 (d, a, s);
+
+ *(dest + i) = d;
+ }
+ }
+}
+
+static void
+combine_disjoint_in_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_disjoint_in_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_disjoint_out_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_disjoint_out_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_disjoint_atop_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_disjoint_atop_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_disjoint_xor_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_u (dest, src, mask, width, COMBINE_XOR);
+}
+
+static void
+combine_conjoint_general_u (uint64_t * dest,
+ const uint64_t *src,
+ const uint64_t *mask,
+ int width,
+ uint16_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = combine_mask (src, mask, i);
+ uint64_t d = *(dest + i);
+ uint64_t m, n, o, p;
+ uint32_t Fa, Fb, t, u, v;
+ uint16_t sa = s >> A_SHIFT;
+ uint16_t da = d >> A_SHIFT;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ Fa = combine_conjoint_out_part (sa, da);
+ break;
+
+ case COMBINE_A_IN:
+ Fa = combine_conjoint_in_part (sa, da);
+ break;
+
+ case COMBINE_A:
+ Fa = MASK;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ Fb = combine_conjoint_out_part (da, sa);
+ break;
+
+ case COMBINE_B_IN:
+ Fb = combine_conjoint_in_part (da, sa);
+ break;
+
+ case COMBINE_B:
+ Fb = MASK;
+ break;
+ }
+
+ m = GENERIC (s, d, 0, Fa, Fb, t, u, v);
+ n = GENERIC (s, d, G_SHIFT, Fa, Fb, t, u, v);
+ o = GENERIC (s, d, R_SHIFT, Fa, Fb, t, u, v);
+ p = GENERIC (s, d, A_SHIFT, Fa, Fb, t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_conjoint_over_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_conjoint_over_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_OVER);
+}
+
+static void
+combine_conjoint_in_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_conjoint_in_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_conjoint_out_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_conjoint_out_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_conjoint_atop_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_conjoint_atop_reverse_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_conjoint_xor_u (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_u (dest, src, mask, width, COMBINE_XOR);
+}
+
+/************************************************************************/
+/*********************** Per Channel functions **************************/
+/************************************************************************/
+
+static void
+combine_clear_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ memset (dest, 0, width * sizeof(uint64_t));
+}
+
+static void
+combine_src_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = *(src + i);
+ uint64_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,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = *(src + i);
+ uint64_t m = *(mask + i);
+ uint64_t a;
+
+ combine_mask_ca (&s, &m);
+
+ a = ~m;
+ if (a)
+ {
+ uint64_t d = *(dest + i);
+ UN16x4_MUL_UN16x4_ADD_UN16x4 (d, a, s);
+ s = d;
+ }
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_over_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t d = *(dest + i);
+ uint64_t a = ~d >> A_SHIFT;
+
+ if (a)
+ {
+ uint64_t s = *(src + i);
+ uint64_t m = *(mask + i);
+
+ UN16x4_MUL_UN16x4 (s, m);
+ UN16x4_MUL_UN16_ADD_UN16x4 (s, a, d);
+
+ *(dest + i) = s;
+ }
+ }
+}
+
+static void
+combine_in_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t d = *(dest + i);
+ uint32_t a = d >> A_SHIFT;
+ uint64_t s = 0;
+
+ if (a)
+ {
+ uint64_t m = *(mask + i);
+
+ s = *(src + i);
+ combine_mask_value_ca (&s, &m);
+
+ if (a != MASK)
+ UN16x4_MUL_UN16 (s, a);
+ }
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_in_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = *(src + i);
+ uint64_t m = *(mask + i);
+ uint64_t a;
+
+ combine_mask_alpha_ca (&s, &m);
+
+ a = m;
+ if (a != ~0)
+ {
+ uint64_t d = 0;
+
+ if (a)
+ {
+ d = *(dest + i);
+ UN16x4_MUL_UN16x4 (d, a);
+ }
+
+ *(dest + i) = d;
+ }
+ }
+}
+
+static void
+combine_out_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t d = *(dest + i);
+ uint32_t a = ~d >> A_SHIFT;
+ uint64_t s = 0;
+
+ if (a)
+ {
+ uint64_t m = *(mask + i);
+
+ s = *(src + i);
+ combine_mask_value_ca (&s, &m);
+
+ if (a != MASK)
+ UN16x4_MUL_UN16 (s, a);
+ }
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_out_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = *(src + i);
+ uint64_t m = *(mask + i);
+ uint64_t a;
+
+ combine_mask_alpha_ca (&s, &m);
+
+ a = ~m;
+ if (a != ~0)
+ {
+ uint64_t d = 0;
+
+ if (a)
+ {
+ d = *(dest + i);
+ UN16x4_MUL_UN16x4 (d, a);
+ }
+
+ *(dest + i) = d;
+ }
+ }
+}
+
+static void
+combine_atop_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t d = *(dest + i);
+ uint64_t s = *(src + i);
+ uint64_t m = *(mask + i);
+ uint64_t ad;
+ uint32_t as = d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ ad = ~m;
+
+ UN16x4_MUL_UN16x4_ADD_UN16x4_MUL_UN16 (d, ad, s, as);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_atop_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t d = *(dest + i);
+ uint64_t s = *(src + i);
+ uint64_t m = *(mask + i);
+ uint64_t ad;
+ uint32_t as = ~d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ ad = m;
+
+ UN16x4_MUL_UN16x4_ADD_UN16x4_MUL_UN16 (d, ad, s, as);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_xor_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t d = *(dest + i);
+ uint64_t s = *(src + i);
+ uint64_t m = *(mask + i);
+ uint64_t ad;
+ uint32_t as = ~d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ ad = ~m;
+
+ UN16x4_MUL_UN16x4_ADD_UN16x4_MUL_UN16 (d, ad, s, as);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_add_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s = *(src + i);
+ uint64_t m = *(mask + i);
+ uint64_t d = *(dest + i);
+
+ combine_mask_value_ca (&s, &m);
+
+ UN16x4_ADD_UN16x4 (d, s);
+
+ *(dest + i) = d;
+ }
+}
+
+static void
+combine_saturate_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s, d;
+ uint32_t sa, sr, sg, sb, da;
+ uint32_t t, u, v;
+ uint64_t m, n, o, p;
+
+ d = *(dest + i);
+ s = *(src + i);
+ m = *(mask + i);
+
+ combine_mask_ca (&s, &m);
+
+ sa = (m >> A_SHIFT);
+ sr = (m >> R_SHIFT) & MASK;
+ sg = (m >> G_SHIFT) & MASK;
+ sb = m & MASK;
+ da = ~d >> A_SHIFT;
+
+ if (sb <= da)
+ m = ADD (s, d, 0, t);
+ else
+ m = GENERIC (s, d, 0, (da << G_SHIFT) / sb, MASK, t, u, v);
+
+ if (sg <= da)
+ n = ADD (s, d, G_SHIFT, t);
+ else
+ n = GENERIC (s, d, G_SHIFT, (da << G_SHIFT) / sg, MASK, t, u, v);
+
+ if (sr <= da)
+ o = ADD (s, d, R_SHIFT, t);
+ else
+ o = GENERIC (s, d, R_SHIFT, (da << G_SHIFT) / sr, MASK, t, u, v);
+
+ if (sa <= da)
+ p = ADD (s, d, A_SHIFT, t);
+ else
+ p = GENERIC (s, d, A_SHIFT, (da << G_SHIFT) / sa, MASK, t, u, v);
+
+ *(dest + i) = m | n | o | p;
+ }
+}
+
+static void
+combine_disjoint_general_ca (uint64_t * dest,
+ const uint64_t *src,
+ const uint64_t *mask,
+ int width,
+ uint16_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s, d;
+ uint64_t m, n, o, p;
+ uint64_t Fa, Fb;
+ uint32_t t, u, v;
+ uint64_t sa;
+ uint16_t da;
+
+ s = *(src + i);
+ m = *(mask + i);
+ d = *(dest + i);
+ da = d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ sa = m;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ m = (uint64_t)combine_disjoint_out_part ((uint16_t) (sa >> 0), da);
+ n = (uint64_t)combine_disjoint_out_part ((uint16_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (uint64_t)combine_disjoint_out_part ((uint16_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (uint64_t)combine_disjoint_out_part ((uint16_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A_IN:
+ m = (uint64_t)combine_disjoint_in_part ((uint16_t) (sa >> 0), da);
+ n = (uint64_t)combine_disjoint_in_part ((uint16_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (uint64_t)combine_disjoint_in_part ((uint16_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (uint64_t)combine_disjoint_in_part ((uint16_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A:
+ Fa = ~0;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ m = (uint64_t)combine_disjoint_out_part (da, (uint16_t) (sa >> 0));
+ n = (uint64_t)combine_disjoint_out_part (da, (uint16_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (uint64_t)combine_disjoint_out_part (da, (uint16_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (uint64_t)combine_disjoint_out_part (da, (uint16_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B_IN:
+ m = (uint64_t)combine_disjoint_in_part (da, (uint16_t) (sa >> 0));
+ n = (uint64_t)combine_disjoint_in_part (da, (uint16_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (uint64_t)combine_disjoint_in_part (da, (uint16_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (uint64_t)combine_disjoint_in_part (da, (uint16_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B:
+ Fb = ~0;
+ break;
+ }
+ m = GENERIC (s, d, 0, GET_COMP (Fa, 0), GET_COMP (Fb, 0), t, u, v);
+ n = GENERIC (s, d, G_SHIFT, GET_COMP (Fa, G_SHIFT), GET_COMP (Fb, G_SHIFT), t, u, v);
+ o = GENERIC (s, d, R_SHIFT, GET_COMP (Fa, R_SHIFT), GET_COMP (Fb, R_SHIFT), t, u, v);
+ p = GENERIC (s, d, A_SHIFT, GET_COMP (Fa, A_SHIFT), GET_COMP (Fb, A_SHIFT), t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_disjoint_over_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_disjoint_in_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_disjoint_in_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_disjoint_out_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_disjoint_out_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_disjoint_atop_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_disjoint_atop_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_disjoint_xor_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_disjoint_general_ca (dest, src, mask, width, COMBINE_XOR);
+}
+
+static void
+combine_conjoint_general_ca (uint64_t * dest,
+ const uint64_t *src,
+ const uint64_t *mask,
+ int width,
+ uint16_t combine)
+{
+ int i;
+
+ for (i = 0; i < width; ++i)
+ {
+ uint64_t s, d;
+ uint64_t m, n, o, p;
+ uint64_t Fa, Fb;
+ uint32_t t, u, v;
+ uint64_t sa;
+ uint16_t da;
+
+ s = *(src + i);
+ m = *(mask + i);
+ d = *(dest + i);
+ da = d >> A_SHIFT;
+
+ combine_mask_ca (&s, &m);
+
+ sa = m;
+
+ switch (combine & COMBINE_A)
+ {
+ default:
+ Fa = 0;
+ break;
+
+ case COMBINE_A_OUT:
+ m = (uint64_t)combine_conjoint_out_part ((uint16_t) (sa >> 0), da);
+ n = (uint64_t)combine_conjoint_out_part ((uint16_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (uint64_t)combine_conjoint_out_part ((uint16_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (uint64_t)combine_conjoint_out_part ((uint16_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A_IN:
+ m = (uint64_t)combine_conjoint_in_part ((uint16_t) (sa >> 0), da);
+ n = (uint64_t)combine_conjoint_in_part ((uint16_t) (sa >> G_SHIFT), da) << G_SHIFT;
+ o = (uint64_t)combine_conjoint_in_part ((uint16_t) (sa >> R_SHIFT), da) << R_SHIFT;
+ p = (uint64_t)combine_conjoint_in_part ((uint16_t) (sa >> A_SHIFT), da) << A_SHIFT;
+ Fa = m | n | o | p;
+ break;
+
+ case COMBINE_A:
+ Fa = ~0;
+ break;
+ }
+
+ switch (combine & COMBINE_B)
+ {
+ default:
+ Fb = 0;
+ break;
+
+ case COMBINE_B_OUT:
+ m = (uint64_t)combine_conjoint_out_part (da, (uint16_t) (sa >> 0));
+ n = (uint64_t)combine_conjoint_out_part (da, (uint16_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (uint64_t)combine_conjoint_out_part (da, (uint16_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (uint64_t)combine_conjoint_out_part (da, (uint16_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B_IN:
+ m = (uint64_t)combine_conjoint_in_part (da, (uint16_t) (sa >> 0));
+ n = (uint64_t)combine_conjoint_in_part (da, (uint16_t) (sa >> G_SHIFT)) << G_SHIFT;
+ o = (uint64_t)combine_conjoint_in_part (da, (uint16_t) (sa >> R_SHIFT)) << R_SHIFT;
+ p = (uint64_t)combine_conjoint_in_part (da, (uint16_t) (sa >> A_SHIFT)) << A_SHIFT;
+ Fb = m | n | o | p;
+ break;
+
+ case COMBINE_B:
+ Fb = ~0;
+ break;
+ }
+ m = GENERIC (s, d, 0, GET_COMP (Fa, 0), GET_COMP (Fb, 0), t, u, v);
+ n = GENERIC (s, d, G_SHIFT, GET_COMP (Fa, G_SHIFT), GET_COMP (Fb, G_SHIFT), t, u, v);
+ o = GENERIC (s, d, R_SHIFT, GET_COMP (Fa, R_SHIFT), GET_COMP (Fb, R_SHIFT), t, u, v);
+ p = GENERIC (s, d, A_SHIFT, GET_COMP (Fa, A_SHIFT), GET_COMP (Fb, A_SHIFT), t, u, v);
+
+ s = m | n | o | p;
+
+ *(dest + i) = s;
+ }
+}
+
+static void
+combine_conjoint_over_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_OVER);
+}
+
+static void
+combine_conjoint_over_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_OVER);
+}
+
+static void
+combine_conjoint_in_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_IN);
+}
+
+static void
+combine_conjoint_in_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_IN);
+}
+
+static void
+combine_conjoint_out_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_OUT);
+}
+
+static void
+combine_conjoint_out_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_OUT);
+}
+
+static void
+combine_conjoint_atop_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_A_ATOP);
+}
+
+static void
+combine_conjoint_atop_reverse_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_B_ATOP);
+}
+
+static void
+combine_conjoint_xor_ca (pixman_implementation_t *imp,
+ pixman_op_t op,
+ uint64_t * dest,
+ const uint64_t * src,
+ const uint64_t * mask,
+ int width)
+{
+ combine_conjoint_general_ca (dest, src, mask, width, COMBINE_XOR);
+}
+
+void
+_pixman_setup_combiner_functions_64 (pixman_implementation_t *imp)
+{
+ /* Unified alpha */
+ imp->combine_64[PIXMAN_OP_CLEAR] = combine_clear;
+ imp->combine_64[PIXMAN_OP_SRC] = combine_src_u;
+ imp->combine_64[PIXMAN_OP_DST] = combine_dst;
+ imp->combine_64[PIXMAN_OP_OVER] = combine_over_u;
+ imp->combine_64[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_u;
+ imp->combine_64[PIXMAN_OP_IN] = combine_in_u;
+ imp->combine_64[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_u;
+ imp->combine_64[PIXMAN_OP_OUT] = combine_out_u;
+ imp->combine_64[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_u;
+ imp->combine_64[PIXMAN_OP_ATOP] = combine_atop_u;
+ imp->combine_64[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_u;
+ imp->combine_64[PIXMAN_OP_XOR] = combine_xor_u;
+ imp->combine_64[PIXMAN_OP_ADD] = combine_add_u;
+ imp->combine_64[PIXMAN_OP_SATURATE] = combine_saturate_u;
+
+ /* Disjoint, unified */
+ imp->combine_64[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear;
+ imp->combine_64[PIXMAN_OP_DISJOINT_SRC] = combine_src_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_DST] = combine_dst;
+ imp->combine_64[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_saturate_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_u;
+ imp->combine_64[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_u;
+
+ /* Conjoint, unified */
+ imp->combine_64[PIXMAN_OP_CONJOINT_CLEAR] = combine_clear;
+ imp->combine_64[PIXMAN_OP_CONJOINT_SRC] = combine_src_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_DST] = combine_dst;
+ imp->combine_64[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_u;
+ imp->combine_64[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_u;
+
+ imp->combine_64[PIXMAN_OP_MULTIPLY] = combine_multiply_u;
+ imp->combine_64[PIXMAN_OP_SCREEN] = combine_screen_u;
+ imp->combine_64[PIXMAN_OP_OVERLAY] = combine_overlay_u;
+ imp->combine_64[PIXMAN_OP_DARKEN] = combine_darken_u;
+ imp->combine_64[PIXMAN_OP_LIGHTEN] = combine_lighten_u;
+ imp->combine_64[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_u;
+ imp->combine_64[PIXMAN_OP_COLOR_BURN] = combine_color_burn_u;
+ imp->combine_64[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_u;
+ imp->combine_64[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_u;
+ imp->combine_64[PIXMAN_OP_DIFFERENCE] = combine_difference_u;
+ imp->combine_64[PIXMAN_OP_EXCLUSION] = combine_exclusion_u;
+ imp->combine_64[PIXMAN_OP_HSL_HUE] = combine_hsl_hue_u;
+ imp->combine_64[PIXMAN_OP_HSL_SATURATION] = combine_hsl_saturation_u;
+ imp->combine_64[PIXMAN_OP_HSL_COLOR] = combine_hsl_color_u;
+ imp->combine_64[PIXMAN_OP_HSL_LUMINOSITY] = combine_hsl_luminosity_u;
+
+ /* Component alpha combiners */
+ imp->combine_64_ca[PIXMAN_OP_CLEAR] = combine_clear_ca;
+ imp->combine_64_ca[PIXMAN_OP_SRC] = combine_src_ca;
+ /* dest */
+ imp->combine_64_ca[PIXMAN_OP_OVER] = combine_over_ca;
+ imp->combine_64_ca[PIXMAN_OP_OVER_REVERSE] = combine_over_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_IN] = combine_in_ca;
+ imp->combine_64_ca[PIXMAN_OP_IN_REVERSE] = combine_in_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_OUT] = combine_out_ca;
+ imp->combine_64_ca[PIXMAN_OP_OUT_REVERSE] = combine_out_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_ATOP] = combine_atop_ca;
+ imp->combine_64_ca[PIXMAN_OP_ATOP_REVERSE] = combine_atop_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_XOR] = combine_xor_ca;
+ imp->combine_64_ca[PIXMAN_OP_ADD] = combine_add_ca;
+ imp->combine_64_ca[PIXMAN_OP_SATURATE] = combine_saturate_ca;
+
+ /* Disjoint CA */
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_CLEAR] = combine_clear_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_SRC] = combine_src_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_DST] = combine_dst;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_OVER] = combine_disjoint_over_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_OVER_REVERSE] = combine_saturate_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_IN] = combine_disjoint_in_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_IN_REVERSE] = combine_disjoint_in_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_OUT] = combine_disjoint_out_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_OUT_REVERSE] = combine_disjoint_out_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_ATOP] = combine_disjoint_atop_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_ATOP_REVERSE] = combine_disjoint_atop_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_DISJOINT_XOR] = combine_disjoint_xor_ca;
+
+ /* Conjoint CA */
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_CLEAR] = combine_clear_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_SRC] = combine_src_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_DST] = combine_dst;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_OVER] = combine_conjoint_over_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_OVER_REVERSE] = combine_conjoint_over_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_IN] = combine_conjoint_in_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_IN_REVERSE] = combine_conjoint_in_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_OUT] = combine_conjoint_out_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_OUT_REVERSE] = combine_conjoint_out_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_ATOP] = combine_conjoint_atop_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_ATOP_REVERSE] = combine_conjoint_atop_reverse_ca;
+ imp->combine_64_ca[PIXMAN_OP_CONJOINT_XOR] = combine_conjoint_xor_ca;
+
+ imp->combine_64_ca[PIXMAN_OP_MULTIPLY] = combine_multiply_ca;
+ imp->combine_64_ca[PIXMAN_OP_SCREEN] = combine_screen_ca;
+ imp->combine_64_ca[PIXMAN_OP_OVERLAY] = combine_overlay_ca;
+ imp->combine_64_ca[PIXMAN_OP_DARKEN] = combine_darken_ca;
+ imp->combine_64_ca[PIXMAN_OP_LIGHTEN] = combine_lighten_ca;
+ imp->combine_64_ca[PIXMAN_OP_COLOR_DODGE] = combine_color_dodge_ca;
+ imp->combine_64_ca[PIXMAN_OP_COLOR_BURN] = combine_color_burn_ca;
+ imp->combine_64_ca[PIXMAN_OP_HARD_LIGHT] = combine_hard_light_ca;
+ imp->combine_64_ca[PIXMAN_OP_SOFT_LIGHT] = combine_soft_light_ca;
+ imp->combine_64_ca[PIXMAN_OP_DIFFERENCE] = combine_difference_ca;
+ imp->combine_64_ca[PIXMAN_OP_EXCLUSION] = combine_exclusion_ca;
+
+ /* It is not clear that these make sense, so make them noops for now */
+ imp->combine_64_ca[PIXMAN_OP_HSL_HUE] = combine_dst;
+ imp->combine_64_ca[PIXMAN_OP_HSL_SATURATION] = combine_dst;
+ imp->combine_64_ca[PIXMAN_OP_HSL_COLOR] = combine_dst;
+ imp->combine_64_ca[PIXMAN_OP_HSL_LUMINOSITY] = combine_dst;
+}
+
diff --git a/gfx/cairo/libpixman/src/pixman-combine64.h b/gfx/cairo/libpixman/src/pixman-combine64.h
new file mode 100644
index 000000000..00413a85f
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine64.h
@@ -0,0 +1,230 @@
+/* WARNING: This file is generated by combine.pl from combine.inc.
+ Please edit one of those files rather than this one. */
+
+#line 1 "pixman-combine.c.template"
+
+#define COMPONENT_SIZE 16
+#define MASK 0xffffULL
+#define ONE_HALF 0x8000ULL
+
+#define A_SHIFT 16 * 3
+#define R_SHIFT 16 * 2
+#define G_SHIFT 16
+#define A_MASK 0xffff000000000000ULL
+#define R_MASK 0xffff00000000ULL
+#define G_MASK 0xffff0000ULL
+
+#define RB_MASK 0xffff0000ffffULL
+#define AG_MASK 0xffff0000ffff0000ULL
+#define RB_ONE_HALF 0x800000008000ULL
+#define RB_MASK_PLUS_ONE 0x10000000010000ULL
+
+#define ALPHA_16(x) ((x) >> A_SHIFT)
+#define RED_16(x) (((x) >> R_SHIFT) & MASK)
+#define GREEN_16(x) (((x) >> G_SHIFT) & MASK)
+#define BLUE_16(x) ((x) & MASK)
+
+/*
+ * Helper macros.
+ */
+
+#define MUL_UN16(a, b, t) \
+ ((t) = (a) * (uint32_t)(b) + ONE_HALF, ((((t) >> G_SHIFT ) + (t) ) >> G_SHIFT ))
+
+#define DIV_UN16(a, b) \
+ (((uint32_t) (a) * MASK + ((b) / 2)) / (b))
+
+#define ADD_UN16(x, y, t) \
+ ((t) = (x) + (y), \
+ (uint64_t) (uint16_t) ((t) | (0 - ((t) >> G_SHIFT))))
+
+#define DIV_ONE_UN16(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 UN16_rb_MUL_UN16(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)
+ */
+#define UN16_rb_ADD_UN16_rb(x, y, t) \
+ do \
+ { \
+ t = ((x) + (y)); \
+ t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK); \
+ x = (t & RB_MASK); \
+ } while (0)
+
+/*
+ * x_rb = (x_rb * a_rb) / 255
+ */
+#define UN16_rb_MUL_UN16_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 UN16x4_MUL_UN16(x, a) \
+ do \
+ { \
+ uint64_t r1__, r2__, t__; \
+ \
+ r1__ = (x); \
+ UN16_rb_MUL_UN16 (r1__, (a), t__); \
+ \
+ r2__ = (x) >> G_SHIFT; \
+ UN16_rb_MUL_UN16 (r2__, (a), t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a) / 255 + y_c
+ */
+#define UN16x4_MUL_UN16_ADD_UN16x4(x, a, y) \
+ do \
+ { \
+ uint64_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (y) & RB_MASK; \
+ UN16_rb_MUL_UN16 (r1__, (a), t__); \
+ UN16_rb_ADD_UN16_rb (r1__, r2__, t__); \
+ \
+ r2__ = (x) >> G_SHIFT; \
+ r3__ = ((y) >> G_SHIFT) & RB_MASK; \
+ UN16_rb_MUL_UN16 (r2__, (a), t__); \
+ UN16_rb_ADD_UN16_rb (r2__, r3__, t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a + y_c * b) / 255
+ */
+#define UN16x4_MUL_UN16_ADD_UN16x4_MUL_UN16(x, a, y, b) \
+ do \
+ { \
+ uint64_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (y); \
+ UN16_rb_MUL_UN16 (r1__, (a), t__); \
+ UN16_rb_MUL_UN16 (r2__, (b), t__); \
+ UN16_rb_ADD_UN16_rb (r1__, r2__, t__); \
+ \
+ r2__ = ((x) >> G_SHIFT); \
+ r3__ = ((y) >> G_SHIFT); \
+ UN16_rb_MUL_UN16 (r2__, (a), t__); \
+ UN16_rb_MUL_UN16 (r3__, (b), t__); \
+ UN16_rb_ADD_UN16_rb (r2__, r3__, t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a_c) / 255
+ */
+#define UN16x4_MUL_UN16x4(x, a) \
+ do \
+ { \
+ uint64_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (a); \
+ UN16_rb_MUL_UN16_rb (r1__, r2__, t__); \
+ \
+ r2__ = (x) >> G_SHIFT; \
+ r3__ = (a) >> G_SHIFT; \
+ UN16_rb_MUL_UN16_rb (r2__, r3__, t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a_c) / 255 + y_c
+ */
+#define UN16x4_MUL_UN16x4_ADD_UN16x4(x, a, y) \
+ do \
+ { \
+ uint64_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (a); \
+ UN16_rb_MUL_UN16_rb (r1__, r2__, t__); \
+ r2__ = (y) & RB_MASK; \
+ UN16_rb_ADD_UN16_rb (r1__, r2__, t__); \
+ \
+ r2__ = ((x) >> G_SHIFT); \
+ r3__ = ((a) >> G_SHIFT); \
+ UN16_rb_MUL_UN16_rb (r2__, r3__, t__); \
+ r3__ = ((y) >> G_SHIFT) & RB_MASK; \
+ UN16_rb_ADD_UN16_rb (r2__, r3__, t__); \
+ \
+ (x) = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ * x_c = (x_c * a_c + y_c * b) / 255
+ */
+#define UN16x4_MUL_UN16x4_ADD_UN16x4_MUL_UN16(x, a, y, b) \
+ do \
+ { \
+ uint64_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x); \
+ r2__ = (a); \
+ UN16_rb_MUL_UN16_rb (r1__, r2__, t__); \
+ r2__ = (y); \
+ UN16_rb_MUL_UN16 (r2__, (b), t__); \
+ UN16_rb_ADD_UN16_rb (r1__, r2__, t__); \
+ \
+ r2__ = (x) >> G_SHIFT; \
+ r3__ = (a) >> G_SHIFT; \
+ UN16_rb_MUL_UN16_rb (r2__, r3__, t__); \
+ r3__ = (y) >> G_SHIFT; \
+ UN16_rb_MUL_UN16 (r3__, (b), t__); \
+ UN16_rb_ADD_UN16_rb (r2__, r3__, t__); \
+ \
+ x = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
+
+/*
+ x_c = min(x_c + y_c, 255)
+*/
+#define UN16x4_ADD_UN16x4(x, y) \
+ do \
+ { \
+ uint64_t r1__, r2__, r3__, t__; \
+ \
+ r1__ = (x) & RB_MASK; \
+ r2__ = (y) & RB_MASK; \
+ UN16_rb_ADD_UN16_rb (r1__, r2__, t__); \
+ \
+ r2__ = ((x) >> G_SHIFT) & RB_MASK; \
+ r3__ = ((y) >> G_SHIFT) & RB_MASK; \
+ UN16_rb_ADD_UN16_rb (r2__, r3__, t__); \
+ \
+ x = r1__ | (r2__ << G_SHIFT); \
+ } while (0)
diff --git a/gfx/cairo/libpixman/src/pixman-compiler.h b/gfx/cairo/libpixman/src/pixman-compiler.h
new file mode 100644
index 000000000..fb674c5f0
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-compiler.h
@@ -0,0 +1,247 @@
+/* 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 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
+#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__) || defined(PIXMAN_USE_XP_DLL_TLS_WORKAROUND)
+
+# define _NO_W32_PSEUDO_MODIFIERS
+# include <windows.h>
+#undef IN
+#undef OUT
+
+# 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_PTHREAD_SETSPECIFIC)
+
+#include <pthread.h>
+
+# 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 000000000..8bb46aecd
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-conical-gradient.c
@@ -0,0 +1,212 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#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_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;
+
+ gradient_t *gradient = (gradient_t *)image;
+ conical_gradient_t *conical = (conical_gradient_t *)image;
+ uint32_t *end = buffer + width;
+ 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);
+
+ *buffer = _pixman_gradient_walker_pixel (
+ &walker, (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
+ }
+
+ ++buffer;
+
+ 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);
+
+ *buffer = _pixman_gradient_walker_pixel (
+ &walker, (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
+ }
+
+ ++buffer;
+
+ rx += cx;
+ ry += cy;
+ rz += cz;
+ }
+ }
+
+ iter->y++;
+ return iter->buffer;
+}
+
+static uint32_t *
+conical_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+ uint32_t *buffer = conical_get_scanline_narrow (iter, NULL);
+
+ pixman_expand_to_float (
+ (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+
+ return buffer;
+}
+
+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-cpu.c b/gfx/cairo/libpixman/src/pixman-cpu.c
new file mode 100644
index 000000000..0eabb4e25
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-cpu.c
@@ -0,0 +1,799 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#if defined(USE_ARM_SIMD) && defined(_MSC_VER)
+/* Needed for EXCEPTION_ILLEGAL_INSTRUCTION */
+#include <windows.h>
+#endif
+
+#if defined(__APPLE__)
+#include "TargetConditionals.h"
+#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.
+ */
+static pixman_bool_t initialized = FALSE;
+static volatile pixman_bool_t have_vmx = TRUE;
+
+#ifdef __APPLE__
+#include <sys/sysctl.h>
+
+static pixman_bool_t
+pixman_have_vmx (void)
+{
+ if (!initialized)
+ {
+ size_t length = sizeof(have_vmx);
+ int error =
+ sysctlbyname ("hw.optional.altivec", &have_vmx, &length, NULL, 0);
+
+ if (error)
+ have_vmx = FALSE;
+
+ initialized = TRUE;
+ }
+ return have_vmx;
+}
+
+#elif defined (__OpenBSD__)
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <machine/cpu.h>
+
+static pixman_bool_t
+pixman_have_vmx (void)
+{
+ if (!initialized)
+ {
+ int mib[2] = { CTL_MACHDEP, CPU_ALTIVEC };
+ size_t length = sizeof(have_vmx);
+ int error =
+ sysctl (mib, 2, &have_vmx, &length, NULL, 0);
+
+ if (error != 0)
+ have_vmx = FALSE;
+
+ initialized = TRUE;
+ }
+ return have_vmx;
+}
+
+#elif defined (__linux__)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <linux/auxvec.h>
+#include <asm/cputable.h>
+
+static pixman_bool_t
+pixman_have_vmx (void)
+{
+ if (!initialized)
+ {
+ char fname[64];
+ unsigned long buf[64];
+ ssize_t count = 0;
+ pid_t pid;
+ int fd, i;
+
+ pid = getpid ();
+ snprintf (fname, sizeof(fname) - 1, "/proc/%d/auxv", pid);
+
+ fd = open (fname, O_RDONLY);
+ if (fd >= 0)
+ {
+ for (i = 0; i <= (count / sizeof(unsigned long)); i += 2)
+ {
+ /* Read more if buf is empty... */
+ if (i == (count / sizeof(unsigned long)))
+ {
+ count = read (fd, buf, sizeof(buf));
+ if (count <= 0)
+ break;
+ i = 0;
+ }
+
+ if (buf[i] == AT_HWCAP)
+ {
+ have_vmx = !!(buf[i + 1] & PPC_FEATURE_HAS_ALTIVEC);
+ initialized = TRUE;
+ break;
+ }
+ else if (buf[i] == AT_NULL)
+ {
+ break;
+ }
+ }
+ close (fd);
+ }
+ }
+ if (!initialized)
+ {
+ /* Something went wrong. Assume 'no' rather than playing
+ fragile tricks with catching SIGILL. */
+ have_vmx = FALSE;
+ initialized = TRUE;
+ }
+
+ return have_vmx;
+}
+
+#else /* !__APPLE__ && !__OpenBSD__ && !__linux__ */
+#include <signal.h>
+#include <setjmp.h>
+
+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;
+
+ if (!initialized)
+ {
+ 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);
+ have_vmx = (jmp_result == 0);
+ initialized = TRUE;
+ }
+ return have_vmx;
+}
+
+#endif /* __APPLE__ */
+#endif /* USE_VMX */
+
+#if defined(USE_ARM_SIMD) || defined(USE_ARM_NEON) || defined(USE_ARM_IWMMXT)
+
+#if defined(_MSC_VER)
+
+#if defined(USE_ARM_SIMD)
+extern int pixman_msvc_try_arm_simd_op ();
+
+pixman_bool_t
+pixman_have_arm_simd (void)
+{
+ static pixman_bool_t initialized = FALSE;
+ static pixman_bool_t have_arm_simd = FALSE;
+
+ if (!initialized)
+ {
+ __try {
+ pixman_msvc_try_arm_simd_op ();
+ have_arm_simd = TRUE;
+ } __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION) {
+ have_arm_simd = FALSE;
+ }
+ initialized = TRUE;
+ }
+
+ return have_arm_simd;
+}
+
+#endif /* USE_ARM_SIMD */
+
+#if defined(USE_ARM_NEON)
+extern int pixman_msvc_try_arm_neon_op ();
+
+pixman_bool_t
+pixman_have_arm_neon (void)
+{
+ static pixman_bool_t initialized = FALSE;
+ static pixman_bool_t have_arm_neon = FALSE;
+
+ if (!initialized)
+ {
+ __try
+ {
+ pixman_msvc_try_arm_neon_op ();
+ have_arm_neon = TRUE;
+ }
+ __except (GetExceptionCode () == EXCEPTION_ILLEGAL_INSTRUCTION)
+ {
+ have_arm_neon = FALSE;
+ }
+ initialized = TRUE;
+ }
+
+ return have_arm_neon;
+}
+
+#endif /* USE_ARM_NEON */
+
+#elif (defined (__APPLE__) && defined(TARGET_OS_IPHONE)) /* iOS (iPhone/iPad/iPod touch) */
+
+/* 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.
+ */
+
+pixman_bool_t
+pixman_have_arm_simd (void)
+{
+#if defined(USE_ARM_SIMD)
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+pixman_bool_t
+pixman_have_arm_neon (void)
+{
+#if defined(USE_ARM_NEON) && defined(__ARM_NEON__)
+ /* This is an armv7 cpu build */
+ return TRUE;
+#else
+ /* This is an armv6 cpu build */
+ return FALSE;
+#endif
+}
+
+pixman_bool_t
+pixman_have_arm_iwmmxt (void)
+{
+#if defined(USE_ARM_IWMMXT)
+ return FALSE;
+#else
+ return FALSE;
+#endif
+}
+
+#elif defined (__linux__) || defined(__ANDROID__) || defined(ANDROID) /* linux ELF or ANDROID */
+
+static pixman_bool_t arm_has_v7 = FALSE;
+static pixman_bool_t arm_has_v6 = FALSE;
+static pixman_bool_t arm_has_vfp = FALSE;
+static pixman_bool_t arm_has_neon = FALSE;
+static pixman_bool_t arm_has_iwmmxt = FALSE;
+static pixman_bool_t arm_tests_initialized = FALSE;
+
+#if defined(__ANDROID__) || defined(ANDROID) /* Android device support */
+
+static void
+pixman_arm_read_auxv_or_cpu_features ()
+{
+ char buf[1024];
+ char* pos;
+ const char* ver_token = "CPU architecture: ";
+ FILE* f = fopen("/proc/cpuinfo", "r");
+ if (!f) {
+ arm_tests_initialized = TRUE;
+ return;
+ }
+
+ fread(buf, sizeof(char), sizeof(buf), f);
+ fclose(f);
+ pos = strstr(buf, ver_token);
+ if (pos) {
+ char vchar = *(pos + strlen(ver_token));
+ if (vchar >= '0' && vchar <= '9') {
+ int ver = vchar - '0';
+ arm_has_v7 = ver >= 7;
+ arm_has_v6 = ver >= 6;
+ }
+ }
+ arm_has_neon = strstr(buf, "neon") != NULL;
+ arm_has_vfp = strstr(buf, "vfp") != NULL;
+ arm_has_iwmmxt = strstr(buf, "iwmmxt") != NULL;
+ arm_tests_initialized = TRUE;
+}
+
+#elif defined (__linux__) /* linux ELF */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <elf.h>
+
+static void
+pixman_arm_read_auxv_or_cpu_features ()
+{
+ int fd;
+ Elf32_auxv_t aux;
+
+ 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
+ */
+ arm_has_vfp = (hwcap & 64) != 0;
+ arm_has_iwmmxt = (hwcap & 512) != 0;
+ /* this flag is only present on kernel 2.6.29 */
+ arm_has_neon = (hwcap & 4096) != 0;
+ }
+ else if (aux.a_type == AT_PLATFORM)
+ {
+ const char *plat = (const char*) aux.a_un.a_val;
+ if (strncmp (plat, "v7l", 3) == 0)
+ {
+ arm_has_v7 = TRUE;
+ arm_has_v6 = TRUE;
+ }
+ else if (strncmp (plat, "v6l", 3) == 0)
+ {
+ arm_has_v6 = TRUE;
+ }
+ }
+ }
+ close (fd);
+ }
+
+ arm_tests_initialized = TRUE;
+}
+
+#endif /* Linux elf */
+
+#if defined(USE_ARM_SIMD)
+pixman_bool_t
+pixman_have_arm_simd (void)
+{
+ if (!arm_tests_initialized)
+ pixman_arm_read_auxv_or_cpu_features ();
+
+ return arm_has_v6;
+}
+
+#endif /* USE_ARM_SIMD */
+
+#if defined(USE_ARM_NEON)
+pixman_bool_t
+pixman_have_arm_neon (void)
+{
+ if (!arm_tests_initialized)
+ pixman_arm_read_auxv_or_cpu_features ();
+
+ return arm_has_neon;
+}
+
+#endif /* USE_ARM_NEON */
+
+#if defined(USE_ARM_IWMMXT)
+pixman_bool_t
+pixman_have_arm_iwmmxt (void)
+{
+ if (!arm_tests_initialized)
+ pixman_arm_read_auxv_or_cpu_features ();
+
+ return arm_has_iwmmxt;
+}
+
+#endif /* USE_ARM_IWMMXT */
+
+#else /* !_MSC_VER && !Linux elf && !Android */
+
+#define pixman_have_arm_simd() FALSE
+#define pixman_have_arm_neon() FALSE
+#define pixman_have_arm_iwmmxt() FALSE
+
+#endif
+
+#endif /* USE_ARM_SIMD || USE_ARM_NEON || USE_ARM_IWMMXT */
+
+#if defined(USE_MIPS_DSPR2)
+
+#if defined (__linux__) /* linux ELF */
+
+pixman_bool_t
+pixman_have_mips_dspr2 (void)
+{
+ const char *search_string = "MIPS 74K";
+ const char *file_name = "/proc/cpuinfo";
+ /* Simple detection of MIPS DSP ASE (revision 2) 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.
+ *
+ * Only currently available MIPS core that supports DSPr2 is 74K.
+ */
+
+ 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);
+
+ /* Did not find string in the proc file. */
+ return FALSE;
+}
+
+#else /* linux ELF */
+
+#define pixman_have_mips_dspr2() FALSE
+
+#endif /* linux ELF */
+
+#endif /* USE_MIPS_DSPR2 */
+
+#if defined(USE_X86_MMX) || defined(USE_SSE2)
+/* 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.
+ */
+#if !defined(__amd64__) && !defined(__x86_64__) && !defined(_M_AMD64)
+
+#ifdef HAVE_GETISAX
+#include <sys/auxv.h>
+#endif
+
+typedef enum
+{
+ NO_FEATURES = 0,
+ MMX = 0x1,
+ MMX_EXTENSIONS = 0x2,
+ SSE = 0x6,
+ SSE2 = 0x8,
+ CMOV = 0x10
+} cpu_features_t;
+
+
+static unsigned int
+detect_cpu_features (void)
+{
+ unsigned int features = 0;
+ unsigned int result = 0;
+
+#ifdef HAVE_GETISAX
+ if (getisax (&result, 1))
+ {
+ if (result & AV_386_CMOV)
+ features |= CMOV;
+ if (result & AV_386_MMX)
+ features |= MMX;
+ if (result & AV_386_AMD_MMX)
+ features |= MMX_EXTENSIONS;
+ if (result & AV_386_SSE)
+ features |= SSE;
+ if (result & AV_386_SSE2)
+ features |= SSE2;
+ }
+#else
+ char vendor[13];
+#ifdef _MSC_VER
+ int vendor0 = 0, vendor1, vendor2;
+#endif
+ vendor[0] = 0;
+ vendor[12] = 0;
+
+#ifdef __GNUC__
+ /* see p. 118 of amd64 instruction set manual Vol3 */
+ /* We need to be careful about the handling of %ebx and
+ * %esp here. 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 they have their
+ * original values when we access the output operands.
+ */
+ __asm__ (
+ "pushf\n"
+ "pop %%eax\n"
+ "mov %%eax, %%ecx\n"
+ "xor $0x00200000, %%eax\n"
+ "push %%eax\n"
+ "popf\n"
+ "pushf\n"
+ "pop %%eax\n"
+ "mov $0x0, %%edx\n"
+ "xor %%ecx, %%eax\n"
+ "jz 1f\n"
+
+ "mov $0x00000000, %%eax\n"
+ "push %%ebx\n"
+ "cpuid\n"
+ "mov %%ebx, %%eax\n"
+ "pop %%ebx\n"
+ "mov %%eax, %1\n"
+ "mov %%edx, %2\n"
+ "mov %%ecx, %3\n"
+ "mov $0x00000001, %%eax\n"
+ "push %%ebx\n"
+ "cpuid\n"
+ "pop %%ebx\n"
+ "1:\n"
+ "mov %%edx, %0\n"
+ : "=r" (result),
+ "=m" (vendor[0]),
+ "=m" (vendor[4]),
+ "=m" (vendor[8])
+ :
+ : "%eax", "%ecx", "%edx"
+ );
+
+#elif defined (_MSC_VER)
+
+ _asm {
+ pushfd
+ pop eax
+ mov ecx, eax
+ xor eax, 00200000h
+ push eax
+ popfd
+ pushfd
+ pop eax
+ mov edx, 0
+ xor eax, ecx
+ jz nocpuid
+
+ mov eax, 0
+ push ebx
+ cpuid
+ mov eax, ebx
+ pop ebx
+ mov vendor0, eax
+ mov vendor1, edx
+ mov vendor2, ecx
+ mov eax, 1
+ push ebx
+ cpuid
+ pop ebx
+ nocpuid:
+ mov result, edx
+ }
+ memmove (vendor + 0, &vendor0, 4);
+ memmove (vendor + 4, &vendor1, 4);
+ memmove (vendor + 8, &vendor2, 4);
+
+#else
+# error unsupported compiler
+#endif
+
+ features = 0;
+ if (result)
+ {
+ /* result now contains the standard feature bits */
+ if (result & (1 << 15))
+ features |= CMOV;
+ if (result & (1 << 23))
+ features |= MMX;
+ if (result & (1 << 25))
+ features |= SSE;
+ if (result & (1 << 26))
+ features |= SSE2;
+ if ((features & MMX) && !(features & SSE) &&
+ (strcmp (vendor, "AuthenticAMD") == 0 ||
+ strcmp (vendor, "Geode by NSC") == 0))
+ {
+ /* check for AMD MMX extensions */
+#ifdef __GNUC__
+ __asm__ (
+ " push %%ebx\n"
+ " mov $0x80000000, %%eax\n"
+ " cpuid\n"
+ " xor %%edx, %%edx\n"
+ " cmp $0x1, %%eax\n"
+ " jge 2f\n"
+ " mov $0x80000001, %%eax\n"
+ " cpuid\n"
+ "2:\n"
+ " pop %%ebx\n"
+ " mov %%edx, %0\n"
+ : "=r" (result)
+ :
+ : "%eax", "%ecx", "%edx"
+ );
+#elif defined _MSC_VER
+ _asm {
+ push ebx
+ mov eax, 80000000h
+ cpuid
+ xor edx, edx
+ cmp eax, 1
+ jge notamd
+ mov eax, 80000001h
+ cpuid
+ notamd:
+ pop ebx
+ mov result, edx
+ }
+#endif
+ if (result & (1 << 22))
+ features |= MMX_EXTENSIONS;
+ }
+ }
+#endif /* HAVE_GETISAX */
+
+ return features;
+}
+
+#ifdef USE_X86_MMX
+static pixman_bool_t
+pixman_have_mmx (void)
+{
+ static pixman_bool_t initialized = FALSE;
+ static pixman_bool_t mmx_present;
+
+ if (!initialized)
+ {
+ unsigned int features = detect_cpu_features ();
+ mmx_present = (features & (MMX | MMX_EXTENSIONS)) == (MMX | MMX_EXTENSIONS);
+ initialized = TRUE;
+ }
+
+ return mmx_present;
+}
+#endif
+
+#ifdef USE_SSE2
+static pixman_bool_t
+pixman_have_sse2 (void)
+{
+ static pixman_bool_t initialized = FALSE;
+ static pixman_bool_t sse2_present;
+
+ if (!initialized)
+ {
+ unsigned int features = detect_cpu_features ();
+ sse2_present = (features & (MMX | MMX_EXTENSIONS | SSE | SSE2)) == (MMX | MMX_EXTENSIONS | SSE | SSE2);
+ initialized = TRUE;
+ }
+
+ return sse2_present;
+}
+
+#endif
+
+#else /* __amd64__ */
+#ifdef USE_X86_MMX
+#define pixman_have_mmx() TRUE
+#endif
+#ifdef USE_SSE2
+#define pixman_have_sse2() TRUE
+#endif
+#endif /* __amd64__ */
+#endif
+
+static pixman_bool_t
+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;
+}
+
+pixman_implementation_t *
+_pixman_choose_implementation (void)
+{
+ pixman_implementation_t *imp;
+
+ imp = _pixman_implementation_create_general();
+
+ if (!disabled ("fast"))
+ imp = _pixman_implementation_create_fast_path (imp);
+
+#ifdef USE_X86_MMX
+ if (!disabled ("mmx") && pixman_have_mmx ())
+ imp = _pixman_implementation_create_mmx (imp);
+#endif
+
+#ifdef USE_SSE2
+ if (!disabled ("sse2") && pixman_have_sse2 ())
+ imp = _pixman_implementation_create_sse2 (imp);
+#endif
+
+#ifdef USE_ARM_SIMD
+ if (!disabled ("arm-simd") && pixman_have_arm_simd ())
+ imp = _pixman_implementation_create_arm_simd (imp);
+#endif
+
+#ifdef USE_ARM_IWMMXT
+ if (!disabled ("arm-iwmmxt") && pixman_have_arm_iwmmxt ())
+ imp = _pixman_implementation_create_mmx (imp);
+#endif
+
+#ifdef USE_ARM_NEON
+ if (!disabled ("arm-neon") && pixman_have_arm_neon ())
+ imp = _pixman_implementation_create_arm_neon (imp);
+#endif
+
+#ifdef USE_MIPS_DSPR2
+ if (!disabled ("mips-dspr2") && pixman_have_mips_dspr2 ())
+ imp = _pixman_implementation_create_mips_dspr2 (imp);
+#endif
+
+#ifdef USE_VMX
+ if (!disabled ("vmx") && pixman_have_vmx ())
+ imp = _pixman_implementation_create_vmx (imp);
+#endif
+
+ imp = _pixman_implementation_create_noop (imp);
+
+ return imp;
+}
+
diff --git a/gfx/cairo/libpixman/src/pixman-dither.h b/gfx/cairo/libpixman/src/pixman-dither.h
new file mode 100644
index 000000000..ead9f38d8
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-dither.h
@@ -0,0 +1,51 @@
+#define R16_BITS 5
+#define G16_BITS 6
+#define B16_BITS 5
+
+#define R16_SHIFT (B16_BITS + G16_BITS)
+#define G16_SHIFT (B16_BITS)
+#define B16_SHIFT 0
+
+#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 0x10000100
+
+#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)
+
+// This uses the same dithering technique that Skia does.
+// It is essentially preturbing the lower bit based on the
+// high bit
+static inline uint16_t dither_32_to_16(uint32_t c)
+{
+ uint8_t b = BLUE_8(c);
+ uint8_t g = GREEN_8(c);
+ uint8_t r = RED_8(c);
+ r = ((r << 1) - ((r >> (8 - R16_BITS) << (8 - R16_BITS)) | (r >> R16_BITS))) >> (8 - R16_BITS);
+ g = ((g << 1) - ((g >> (8 - G16_BITS) << (8 - G16_BITS)) | (g >> G16_BITS))) >> (8 - G16_BITS);
+ b = ((b << 1) - ((b >> (8 - B16_BITS) << (8 - B16_BITS)) | (b >> B16_BITS))) >> (8 - B16_BITS);
+ return ((r << R16_SHIFT) | (g << G16_SHIFT) | (b << B16_SHIFT));
+}
+
+static inline uint16_t dither_8888_to_0565(uint32_t color, pixman_bool_t toggle)
+{
+ // alternate between a preturbed truncation and a regular truncation
+ if (toggle) {
+ return dither_32_to_16(color);
+ } else {
+ return convert_8888_to_0565(color);
+ }
+}
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 000000000..ea3a31e2f
--- /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 000000000..a47098a89
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-edge-imp.h
@@ -0,0 +1,183 @@
+/*
+ * 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)
+ */
+ /* we cast to unsigned to get defined behaviour for overflow */
+ lx = (unsigned)lx + X_FRAC_FIRST(1) - pixman_fixed_e;
+ rx = (unsigned)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 000000000..ad6dfc4cf
--- /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 <config.h>
+#endif
+
+#include <string.h>
+
+#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 000000000..9b22d7b46
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-fast-path.c
@@ -0,0 +1,2590 @@
+/* -*- 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 <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#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) (1 << (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)
+
+static force_inline void
+scaled_bilinear_scanline_8888_565_OVER (uint16_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)
+{
+ while ((w -= 1) >= 0)
+ {
+ uint32_t tl = src_top [pixman_fixed_to_int (vx)];
+ uint32_t tr = src_top [pixman_fixed_to_int (vx) + 1];
+ uint32_t bl = src_bottom [pixman_fixed_to_int (vx)];
+ uint32_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
+ uint32_t src, result;
+ uint16_t d;
+ d = *dst;
+ src = bilinear_interpolation (tl, tr,
+ bl, br,
+ pixman_fixed_to_bilinear_weight(vx),
+ wb);
+ vx += unit_x;
+ result = over (src, convert_0565_to_0888 (d));
+ *dst++ = convert_8888_to_0565 (result);
+ }
+}
+
+static force_inline void
+scaled_bilinear_scanline_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)
+{
+ while ((w -= 1) >= 0)
+ {
+ uint32_t tl = src_top [pixman_fixed_to_int (vx)];
+ uint32_t tr = src_top [pixman_fixed_to_int (vx) + 1];
+ uint32_t bl = src_bottom [pixman_fixed_to_int (vx)];
+ uint32_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
+ uint32_t src;
+ uint32_t d;
+ uint32_t result;
+ d = *dst;
+ src = bilinear_interpolation (tl, tr,
+ bl, br,
+ pixman_fixed_to_bilinear_weight(vx),
+ wb);
+ vx += unit_x;
+ *dst++ = over (src, d);
+ }
+}
+
+#ifndef LOWER_QUALITY_INTERPOLATION
+
+static force_inline void
+scaled_bilinear_scanline_565_565_SRC (uint16_t * dst,
+ const uint32_t * mask,
+ const uint16_t * src_top,
+ const uint16_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)
+{
+ while ((w -= 1) >= 0)
+ {
+ uint16_t tl = src_top [pixman_fixed_to_int (vx)];
+ uint16_t tr = src_top [pixman_fixed_to_int (vx) + 1];
+ uint16_t bl = src_bottom [pixman_fixed_to_int (vx)];
+ uint16_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
+ uint32_t d;
+ d = bilinear_interpolation(convert_0565_to_8888 (tl),
+ convert_0565_to_8888 (tr),
+ convert_0565_to_8888 (bl),
+ convert_0565_to_8888 (br),
+ pixman_fixed_to_bilinear_weight (vx),
+ wb);
+ vx += unit_x;
+ *dst++ = convert_8888_to_0565 (d);
+ }
+}
+
+#else
+
+/* This is a clever low resolution bilinear interpolation inspired by the code
+ in Skia */
+
+/* This takes the green component from the 565 representation and moves it:
+ 00000000 00000000 rrrrrggg gggbbbbb
+
+ 00000ggg ggg00000 rrrrr000 000bbbbb
+
+ This gives us 5 extra bits of space before each component to let us do
+ SWAR style optimizations
+*/
+
+#define GREEN_MASK (((1 << 6) - 1) << 5)
+
+static inline uint32_t
+expand_rgb_565 (uint16_t c) {
+ return ((c & GREEN_MASK) << 16) | (c & ~GREEN_MASK);
+}
+
+static inline uint16_t
+compact_rgb_565 (uint32_t c) {
+ return ((c >> 16) & GREEN_MASK) | (c & ~GREEN_MASK);
+}
+
+static inline uint16_t
+bilinear_interpolation_565(uint16_t tl, uint16_t tr,
+ uint16_t bl, uint16_t br,
+ int x, int y)
+{
+ int xy;
+ uint32_t a00 = expand_rgb_565 (tl);
+ uint32_t a01 = expand_rgb_565 (tr);
+ uint32_t a10 = expand_rgb_565 (bl);
+ uint32_t a11 = expand_rgb_565 (br);
+
+ xy = (x * y) >> 3;
+ return compact_rgb_565 ((a00 * (32 - 2*y - 2*x + xy) +
+ a01 * (2*x - xy) +
+ a10 * (2*y - xy) +
+ a11 * xy) >> 5);
+}
+
+static force_inline void
+scaled_bilinear_scanline_565_565_SRC (uint16_t * dst,
+ const uint32_t * mask,
+ const uint16_t * src_top,
+ const uint16_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)
+{
+ while ((w -= 1) >= 0)
+ {
+ uint16_t tl = src_top [pixman_fixed_to_int (vx)];
+ uint16_t tr = src_top [pixman_fixed_to_int (vx) + 1];
+ uint16_t bl = src_bottom [pixman_fixed_to_int (vx)];
+ uint16_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
+
+ uint16_t d = bilinear_interpolation_565 (tl, tr, bl, br,
+ pixman_fixed_to_bilinear_weight(vx),
+ wb);
+ vx += unit_x;
+ *dst++ = d;
+ }
+}
+
+#endif
+
+FAST_BILINEAR_MAINLOOP_COMMON (565_565_cover_SRC,
+ scaled_bilinear_scanline_565_565_SRC, NULL,
+ uint16_t, uint32_t, uint16_t,
+ COVER, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (565_565_pad_SRC,
+ scaled_bilinear_scanline_565_565_SRC, NULL,
+ uint16_t, uint32_t, uint16_t,
+ PAD, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (565_565_none_SRC,
+ scaled_bilinear_scanline_565_565_SRC, NULL,
+ uint16_t, uint32_t, uint16_t,
+ NONE, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (565_565_normal_SRC,
+ scaled_bilinear_scanline_565_565_SRC, NULL,
+ uint16_t, uint32_t, uint16_t,
+ NORMAL, FLAG_NONE)
+
+FAST_BILINEAR_MAINLOOP_COMMON (8888_565_cover_OVER,
+ scaled_bilinear_scanline_8888_565_OVER, NULL,
+ uint32_t, uint32_t, uint16_t,
+ COVER, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (8888_565_pad_OVER,
+ scaled_bilinear_scanline_8888_565_OVER, NULL,
+ uint32_t, uint32_t, uint16_t,
+ PAD, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (8888_565_none_OVER,
+ scaled_bilinear_scanline_8888_565_OVER, NULL,
+ uint32_t, uint32_t, uint16_t,
+ NONE, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (8888_565_normal_OVER,
+ scaled_bilinear_scanline_8888_565_OVER, NULL,
+ uint32_t, uint32_t, uint16_t,
+ NORMAL, FLAG_NONE)
+
+FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_cover_OVER,
+ scaled_bilinear_scanline_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ COVER, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_pad_OVER,
+ scaled_bilinear_scanline_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ PAD, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_none_OVER,
+ scaled_bilinear_scanline_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ NONE, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_normal_OVER,
+ scaled_bilinear_scanline_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ NORMAL, FLAG_NONE)
+
+#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
+ },
+
+ SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, r5g6b5, 565_565),
+ SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, r5g6b5, 8888_565),
+ SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, 8888_8888),
+
+ { 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
+{
+ pixman_format_code_t format;
+ pixman_iter_get_scanline_t get_scanline;
+ pixman_iter_write_back_t write_back;
+} fetcher_info_t;
+
+static const fetcher_info_t fetchers[] =
+{
+ { PIXMAN_r5g6b5, fast_fetch_r5g6b5, fast_write_back_r5g6b5 },
+ { PIXMAN_null }
+};
+
+static pixman_bool_t
+fast_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
+{
+ pixman_image_t *image = iter->image;
+
+#define FLAGS \
+ (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \
+ FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST)
+
+ if (iter->iter_flags & ITER_16)
+ return FALSE;
+
+ if ((iter->iter_flags & ITER_NARROW) &&
+ (iter->image_flags & FLAGS) == FLAGS)
+ {
+ const fetcher_info_t *f;
+
+ for (f = &fetchers[0]; f->format != PIXMAN_null; f++)
+ {
+ if (image->common.extended_format_code == f->format)
+ {
+ 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 (f->format) / 8;
+ iter->stride = s;
+
+ iter->get_scanline = f->get_scanline;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static pixman_bool_t
+fast_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
+{
+ pixman_image_t *image = iter->image;
+
+ if (iter->iter_flags & ITER_16)
+ return FALSE;
+
+ if ((iter->iter_flags & ITER_NARROW) &&
+ (iter->image_flags & FAST_PATH_STD_DEST_FLAGS) == FAST_PATH_STD_DEST_FLAGS)
+ {
+ const fetcher_info_t *f;
+
+ for (f = &fetchers[0]; f->format != PIXMAN_null; f++)
+ {
+ if (image->common.extended_format_code == f->format)
+ {
+ 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 (f->format) / 8;
+ iter->stride = s;
+
+ if ((iter->iter_flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) ==
+ (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA))
+ {
+ iter->get_scanline = fast_dest_fetch_noop;
+ }
+ else
+ {
+ iter->get_scanline = f->get_scanline;
+ }
+ iter->write_back = f->write_back;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+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->src_iter_init = fast_src_iter_init;
+ imp->dest_iter_init = fast_dest_iter_init;
+
+ return imp;
+}
diff --git a/gfx/cairo/libpixman/src/pixman-fast-path.h b/gfx/cairo/libpixman/src/pixman-fast-path.h
new file mode 100644
index 000000000..1885d47e7
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-fast-path.h
@@ -0,0 +1,1022 @@
+/* -*- 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
+
+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;
+}
+
+/*
+ * 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 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 max_vx, \
+ 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 = vx >> 16; \
+ vx += unit_x; \
+ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \
+ { \
+ /* This works because we know that unit_x is positive */ \
+ while (vx >= max_vx) \
+ vx -= max_vx; \
+ } \
+ s1 = src[x1]; \
+ \
+ x2 = vx >> 16; \
+ vx += unit_x; \
+ if (PIXMAN_REPEAT_ ## repeat_mode == PIXMAN_REPEAT_NORMAL) \
+ { \
+ /* This works because we know that unit_x is positive */ \
+ while (vx >= max_vx) \
+ vx -= max_vx; \
+ } \
+ 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 = vx >> 16; \
+ 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_op_t op, \
+ pixman_image_t * src_image, \
+ pixman_image_t * mask_image, \
+ pixman_image_t * dst_image, \
+ int32_t src_x, \
+ int32_t src_y, \
+ int32_t mask_x, \
+ int32_t mask_y, \
+ int32_t dst_x, \
+ int32_t dst_y, \
+ int32_t width, \
+ int32_t height) \
+{ \
+ dst_type_t *dst_line; \
+ mask_type_t *mask_line; \
+ src_type_t *src_first_line; \
+ int y; \
+ pixman_fixed_t max_vx = INT32_MAX; /* suppress uninitialized variable warning */ \
+ 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 (dst_image, dst_x, dst_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, dst_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) \
+ { \
+ /* Clamp repeating positions inside the actual samples */ \
+ max_vx = src_image->bits.width << 16; \
+ max_vy = src_image->bits.height << 16; \
+ \
+ repeat (PIXMAN_REPEAT_NORMAL, &vx, max_vx); \
+ 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 = vy >> 16; \
+ 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, left_pad, 0, 0, 0, FALSE); \
+ } \
+ if (width > 0) \
+ { \
+ scanline_func (mask + (mask_is_solid ? 0 : left_pad), \
+ dst + left_pad, src, width, vx, unit_x, 0, FALSE); \
+ } \
+ if (right_pad > 0) \
+ { \
+ scanline_func (mask + (mask_is_solid ? 0 : left_pad + width), \
+ dst + left_pad + width, src + src_image->bits.width - 1, \
+ right_pad, 0, 0, 0, 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, left_pad + width + right_pad, 0, 0, 0, TRUE); \
+ continue; \
+ } \
+ src = src_first_line + src_stride * y; \
+ if (left_pad > 0) \
+ { \
+ scanline_func (mask, dst, zero, left_pad, 0, 0, 0, TRUE); \
+ } \
+ if (width > 0) \
+ { \
+ scanline_func (mask + (mask_is_solid ? 0 : left_pad), \
+ dst + left_pad, src, width, vx, unit_x, 0, FALSE); \
+ } \
+ if (right_pad > 0) \
+ { \
+ scanline_func (mask + (mask_is_solid ? 0 : left_pad + width), \
+ dst + left_pad + width, zero, right_pad, 0, 0, 0, TRUE); \
+ } \
+ } \
+ else \
+ { \
+ src = src_first_line + src_stride * y; \
+ scanline_func (mask, dst, src, width, vx, unit_x, max_vx, 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, \
+ 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, \
+ 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, \
+ 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)
+
+/*****************************************************************************/
+
+/*
+ * 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 256,
+ * 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 in 0-255
+ * range and can fit into unsigned byte or be used with 8-bit SIMD
+ * multiplication instructions.
+ */
+#define FAST_BILINEAR_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_bilinear ## scale_func_name (pixman_implementation_t *imp, \
+ pixman_op_t op, \
+ pixman_image_t * src_image, \
+ pixman_image_t * mask_image, \
+ pixman_image_t * dst_image, \
+ int32_t src_x, \
+ int32_t src_y, \
+ int32_t mask_x, \
+ int32_t mask_y, \
+ int32_t dst_x, \
+ int32_t dst_y, \
+ int32_t width, \
+ int32_t height) \
+{ \
+ 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; \
+ \
+ PIXMAN_IMAGE_GET_LINE (dst_image, dst_x, dst_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, dst_image->bits.format); \
+ mask_stride = 0; \
+ } \
+ 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]; \
+ \
+ 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; \
+ } \
+ \
+ while (--height >= 0) \
+ { \
+ int weight1, weight2; \
+ dst = dst_line; \
+ dst_line += dst_stride; \
+ vx = v.vector[0]; \
+ if (have_mask && !mask_is_solid) \
+ { \
+ mask = mask_line; \
+ mask_line += mask_stride; \
+ } \
+ \
+ y1 = pixman_fixed_to_int (vy); \
+ weight2 = (vy >> 8) & 0xff; \
+ if (weight2) \
+ { \
+ /* normal case, both row weights are in 0-255 range and fit unsigned byte */ \
+ y2 = y1 + 1; \
+ weight1 = 256 - weight2; \
+ } \
+ else \
+ { \
+ /* set both top and bottom row to the same scanline, and weights to 128+128 */ \
+ y2 = y1; \
+ weight1 = weight2 = 128; \
+ } \
+ 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 (have_mask && !mask_is_solid) \
+ mask += left_pad; \
+ } \
+ if (width > 0) \
+ { \
+ scanline_func (dst, mask, \
+ src1, src2, width, weight1, weight2, vx, unit_x, 0, FALSE); \
+ dst += width; \
+ if (have_mask && !mask_is_solid) \
+ 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 (have_mask && !mask_is_solid) \
+ 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 (have_mask && !mask_is_solid) \
+ 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 (have_mask && !mask_is_solid) \
+ 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 (have_mask && !mask_is_solid) \
+ 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 \
+ { \
+ 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, have_mask, mask_is_solid) \
+ FAST_BILINEAR_MAINLOOP_INT(_ ## scale_func_name, scanline_func, src_type_t, mask_type_t,\
+ dst_type_t, repeat_mode, have_mask, mask_is_solid)
+
+#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, \
+ PIXMAN_null, 0, \
+ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \
+ fast_composite_scaled_bilinear_ ## func ## _cover ## _ ## 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, \
+ 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_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, \
+ PIXMAN_solid, MASK_FLAGS (solid, FAST_PATH_UNIFIED_ALPHA), \
+ PIXMAN_ ## d, FAST_PATH_STD_DEST_FLAGS, \
+ fast_composite_scaled_bilinear_ ## func ## _cover ## _ ## 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)
+
+#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)
+
+#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)
+
+#endif
diff --git a/gfx/cairo/libpixman/src/pixman-filter.c b/gfx/cairo/libpixman/src/pixman-filter.c
new file mode 100644
index 000000000..5ff7b6eaa
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-filter.c
@@ -0,0 +1,350 @@
+/*
+ * 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 <soren.sandmann@gmail.com>
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <assert.h>
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 * ax * ax +
+ (-18 + 12 * B + 6 * C) * ax * ax + (6 - 2 * B)) / 6;
+ }
+ else if (ax >= 1 && ax < 2)
+ {
+ return ((-B - 6 * C) * ax * ax * ax +
+ (6 * B + 30 * C) * ax * 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, 6 * SIGMA },
+ { 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 the integration interval crosses zero, break it into
+ * two separate integrals. This ensures that filters such
+ * as LINEAR that are not differentiable at 0 will still
+ * integrate properly.
+ */
+ if (x1 < 0 && x1 + width > 0)
+ {
+ return
+ integral (kernel1, x1, kernel2, scale, x2, - x1) +
+ integral (kernel1, 0, kernel2, scale, x2 - x1, width + x1);
+ }
+ else if (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 */
+#define N_SEGMENTS 128
+#define SAMPLE(a1, a2) \
+ (filters[kernel1].func ((a1)) * filters[kernel2].func ((a2) * scale))
+
+ double s = 0.0;
+ double h = width / (double)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 += 2 * SAMPLE (a1, a2);
+
+ if (i >= 2 && i < N_SEGMENTS - 1)
+ s += 4 * SAMPLE (a1, a2);
+ }
+
+ s += SAMPLE (x1 + width, x2 + width);
+
+ return h * s * (1.0 / 3.0);
+ }
+}
+
+static pixman_fixed_t *
+create_1d_filter (int *width,
+ pixman_kernel_t reconstruct,
+ pixman_kernel_t sample,
+ double scale,
+ int n_phases)
+{
+ pixman_fixed_t *params, *p;
+ double step;
+ double size;
+ int i;
+
+ size = scale * filters[sample].width + filters[reconstruct].width;
+ *width = ceil (size);
+
+ p = params = malloc (*width * n_phases * sizeof (pixman_fixed_t));
+ if (!params)
+ return NULL;
+
+ 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;
+
+ /* 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);
+ }
+
+ total += c;
+ *p++ = (pixman_fixed_t)(c * 65535.0 + 0.5);
+ }
+
+ /* Normalize */
+ p -= *width;
+ total = 1 / total;
+ new_total = 0;
+ for (x = x1; x < x2; ++x)
+ {
+ pixman_fixed_t t = (*p) * total + 0.5;
+
+ new_total += t;
+ *p++ = t;
+ }
+
+ if (new_total != pixman_fixed_1)
+ *(p - *width / 2) += (pixman_fixed_1 - new_total);
+ }
+
+ return params;
+}
+
+/* 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 *horz = NULL, *vert = NULL, *params = NULL;
+ int subsample_x, subsample_y;
+ int width, height;
+
+ subsample_x = (1 << subsample_bits_x);
+ subsample_y = (1 << subsample_bits_y);
+
+ horz = create_1d_filter (&width, reconstruct_x, sample_x, sx, subsample_x);
+ vert = create_1d_filter (&height, reconstruct_y, sample_y, sy, subsample_y);
+
+ if (!horz || !vert)
+ goto out;
+
+ *n_values = 4 + width * subsample_x + height * subsample_y;
+
+ params = malloc (*n_values * sizeof (pixman_fixed_t));
+ if (!params)
+ goto out;
+
+ 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);
+
+ memcpy (params + 4, horz,
+ width * subsample_x * sizeof (pixman_fixed_t));
+ memcpy (params + 4 + width * subsample_x, vert,
+ height * subsample_y * sizeof (pixman_fixed_t));
+
+out:
+ free (horz);
+ free (vert);
+
+ 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 000000000..2a551e3a5
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-general.c
@@ -0,0 +1,243 @@
+/*
+ * 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 <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pixman-private.h"
+
+static pixman_bool_t
+general_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
+{
+ pixman_image_t *image = iter->image;
+
+ if (image->type == LINEAR)
+ _pixman_linear_gradient_iter_init (image, iter);
+ else if (image->type == RADIAL)
+ _pixman_radial_gradient_iter_init (image, iter);
+ else if (image->type == CONICAL)
+ _pixman_conical_gradient_iter_init (image, iter);
+ else if (image->type == BITS)
+ _pixman_bits_image_src_iter_init (image, iter);
+ else if (image->type == SOLID)
+ _pixman_log_error (FUNC, "Solid image not handled by noop");
+ else
+ _pixman_log_error (FUNC, "Pixman bug: unknown image type\n");
+
+ return TRUE;
+}
+
+static pixman_bool_t
+general_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
+{
+ if (iter->image->type == BITS)
+ {
+ _pixman_bits_image_dest_iter_init (iter->image, iter);
+
+ return TRUE;
+ }
+ else
+ {
+ _pixman_log_error (FUNC, "Trying to write to a non-writable image");
+
+ return FALSE;
+ }
+}
+
+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 void
+general_composite_rect (pixman_implementation_t *imp,
+ pixman_composite_info_t *info)
+{
+ PIXMAN_COMPOSITE_ARGS (info);
+ uint64_t stack_scanline_buffer[(SCANLINE_BUFFER_LENGTH * 3 + 7) / 8];
+ 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 narrow, src_iter_flags;
+ iter_flags_t rgb16;
+ 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))
+ {
+ narrow = ITER_NARROW;
+ Bpp = 4;
+ }
+ else
+ {
+ narrow = 0;
+ Bpp = 16;
+ }
+
+ // XXX: This special casing is bad. Ideally, we'd keep the general code general perhaps
+ // by having it deal more specifically with different intermediate formats
+ if (
+ (dest_image->common.flags & FAST_PATH_16_FORMAT && (src_image->type == LINEAR || src_image->type == RADIAL)) &&
+ ( op == PIXMAN_OP_SRC ||
+ (op == PIXMAN_OP_OVER && (src_image->common.flags & FAST_PATH_IS_OPAQUE))
+ )
+ ) {
+ rgb16 = ITER_16;
+ } else {
+ rgb16 = 0;
+ }
+
+
+ if (width * Bpp > SCANLINE_BUFFER_LENGTH)
+ {
+ scanline_buffer = pixman_malloc_abc (width, 3, Bpp);
+
+ if (!scanline_buffer)
+ return;
+ }
+
+ src_buffer = scanline_buffer;
+ mask_buffer = src_buffer + width * Bpp;
+ dest_buffer = mask_buffer + width * Bpp;
+
+ if (!narrow)
+ {
+ /* 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 = narrow | op_flags[op].src | rgb16;
+
+ _pixman_implementation_src_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.type == BITS &&
+ mask_image->common.component_alpha &&
+ PIXMAN_FORMAT_RGB (mask_image->bits.format);
+
+ _pixman_implementation_src_iter_init (
+ imp->toplevel, &mask_iter, mask_image, mask_x, mask_y, width, height,
+ mask_buffer, narrow | (component_alpha? 0 : ITER_IGNORE_RGB), info->mask_flags);
+
+ /* dest iter */
+ _pixman_implementation_dest_iter_init (
+ imp->toplevel, &dest_iter, dest_image, dest_x, dest_y, width, height,
+ dest_buffer, narrow | op_flags[op].dst | rgb16, info->dest_flags);
+
+ compose = _pixman_implementation_lookup_combiner (
+ imp->toplevel, op, component_alpha, narrow, !!rgb16);
+
+ 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 (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_16 (imp);
+ _pixman_setup_combiner_functions_32 (imp);
+ _pixman_setup_combiner_functions_float (imp);
+
+ imp->src_iter_init = general_src_iter_init;
+ imp->dest_iter_init = general_dest_iter_init;
+
+ 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 000000000..5a271b64b
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-glyph.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright 2010, 2012, Soren Sandmann <sandmann@cs.au.dk>
+ * 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 <sandmann@cs.au.dk>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "pixman-private.h"
+
+#include <stdlib.h>
+
+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;
+}
+
+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 (&region);
+ if (!_pixman_compute_composite_region32 (
+ &region,
+ 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 (&region, &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 (&region);
+}
+
+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.
+ */
+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 000000000..e7e724fa6
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-gradient-walker.c
@@ -0,0 +1,172 @@
+/*
+ *
+ * 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 <config.h>
+#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->stepper = 0;
+ walker->left_ag = 0;
+ walker->left_rb = 0;
+ walker->right_ag = 0;
+ walker->right_rb = 0;
+ walker->repeat = repeat;
+
+ walker->need_reset = TRUE;
+}
+
+static void
+gradient_walker_reset (pixman_gradient_walker_t *walker,
+ pixman_fixed_48_16_t pos)
+{
+ int32_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;
+
+ 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;
+ }
+
+ walker->left_x = left_x;
+ walker->right_x = right_x;
+ walker->left_ag = ((left_c->alpha >> 8) << 16) | (left_c->green >> 8);
+ walker->left_rb = ((left_c->red & 0xff00) << 8) | (left_c->blue >> 8);
+ walker->right_ag = ((right_c->alpha >> 8) << 16) | (right_c->green >> 8);
+ walker->right_rb = ((right_c->red & 0xff00) << 8) | (right_c->blue >> 8);
+
+ if (walker->left_x == walker->right_x ||
+ (walker->left_ag == walker->right_ag &&
+ walker->left_rb == walker->right_rb))
+ {
+ walker->stepper = 0;
+ }
+ else
+ {
+ int32_t width = right_x - left_x;
+ walker->stepper = ((1 << 24) + width / 2) / width;
+ }
+
+ walker->need_reset = FALSE;
+}
+
+uint32_t
+_pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
+ pixman_fixed_48_16_t x)
+{
+ int dist, idist;
+ uint32_t t1, t2, a, color;
+
+ if (walker->need_reset || x < walker->left_x || x >= walker->right_x)
+ gradient_walker_reset (walker, x);
+
+ dist = ((int)(x - walker->left_x) * walker->stepper) >> 16;
+ idist = 256 - dist;
+
+ /* combined INTERPOLATE and premultiply */
+ t1 = walker->left_rb * idist + walker->right_rb * dist;
+ t1 = (t1 >> 8) & 0xff00ff;
+
+ t2 = walker->left_ag * idist + walker->right_ag * dist;
+ t2 &= 0xff00ff00;
+
+ color = t2 & 0xff000000;
+ a = t2 >> 24;
+
+ t1 = t1 * a + 0x800080;
+ t1 = (t1 + ((t1 >> 8) & 0xff00ff)) >> 8;
+
+ t2 = (t2 >> 8) * a + 0x800080;
+ t2 = (t2 + ((t2 >> 8) & 0xff00ff));
+
+ return (color | (t1 & 0xff00ff) | (t2 & 0xff00));
+}
+
diff --git a/gfx/cairo/libpixman/src/pixman-image.c b/gfx/cairo/libpixman/src/pixman-image.c
new file mode 100644
index 000000000..f43e07f41
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-image.c
@@ -0,0 +1,967 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pixman-private.h"
+
+static const pixman_color_t transparent_black = { 0, 0, 0, 0 };
+
+/**
+ ** bug 1293598 - clean up every pointer after free to avoid
+ ** "dereferencing freed memory" problem
+ **/
+#define PIXMAN_POSION
+
+static void
+free_memory (void** p)
+{
+#ifdef PIXMAN_POISON
+ if (*p) {
+#endif
+ free (*p);
+#ifdef PIXMAN_POISON
+ *p = NULL;
+ }
+#endif
+}
+
+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_memory (&common->transform);
+ free_memory (&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 */
+ void *addr = image->gradient.stops - 1;
+ free_memory (&addr);
+ }
+
+ /* 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_memory (&image->bits.free_me);
+ image->bits.bits = NULL;
+ }
+
+
+ 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_memory (&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 (
+ /* affine and integer translation components in matrix ... */
+ ((flags & FAST_PATH_AFFINE_TRANSFORM) &&
+ !pixman_fixed_frac (image->common.transform->matrix[0][2] |
+ image->common.transform->matrix[1][2])) &&
+ (
+ /* ... combined with a simple rotation */
+ (flags & (FAST_PATH_ROTATE_90_TRANSFORM |
+ FAST_PATH_ROTATE_180_TRANSFORM |
+ FAST_PATH_ROTATE_270_TRANSFORM)) ||
+ /* ... or combined with a simple non-rotated translation */
+ (image->common.transform->matrix[0][0] == pixman_fixed_1 &&
+ image->common.transform->matrix[1][1] == pixman_fixed_1 &&
+ image->common.transform->matrix[0][1] == 0 &&
+ image->common.transform->matrix[1][0] == 0)
+ )
+ )
+ {
+ /* 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;
+
+ if (image->bits.format == PIXMAN_r5g6b5)
+ flags |= FAST_PATH_16_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 map */
+ if (!image->common.alpha_map)
+ {
+ 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 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)
+ {
+ 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 = (*(uint8_t *)image->bits.bits) << 24;
+ else
+ goto otherwise;
+ }
+ else
+ {
+ pixman_iter_t iter;
+
+ otherwise:
+ _pixman_implementation_src_iter_init (
+ imp, &iter, image, 0, 0, 1, 1,
+ (uint8_t *)&result,
+ ITER_NARROW, image->common.flags);
+
+ result = *iter.get_scanline (&iter, NULL);
+ }
+
+ /* 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 000000000..44d409785
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-implementation.c
@@ -0,0 +1,405 @@
+/*
+ * 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 <config.h>
+#endif
+#include <stdlib.h>
+#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);
+
+ /* Bug 1324130 - For compatibility with Windows XP, we have to use Tls
+ * functions for the per-thread fast-path cache instead of the safer
+ * __declspec(thread) mechanism. If the Tls functions fail to set up
+ * the storage for some reason, cache will end up null here. As a
+ * temporary workaround, just check that cache is not null before
+ * using it. The implementation lookup will still function without the
+ * fast-path cache, however, it will incur a slow linear search.
+ */
+ if (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 known composite function\n");
+ *out_imp = NULL;
+ *out_func = dummy_composite_rect;
+
+update_cache:
+ if (cache && 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,
+ pixman_bool_t rgb16)
+{
+ 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 (rgb16)
+ f = (pixman_combine_32_func_t *)imp->combine_16[op];
+
+ 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;
+}
+
+pixman_bool_t
+_pixman_implementation_src_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)
+{
+ 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;
+
+ while (imp)
+ {
+ if (imp->src_iter_init && (*imp->src_iter_init) (imp, iter))
+ return TRUE;
+
+ imp = imp->fallback;
+ }
+
+ return FALSE;
+}
+
+pixman_bool_t
+_pixman_implementation_dest_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)
+{
+ 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;
+
+ while (imp)
+ {
+ if (imp->dest_iter_init && (*imp->dest_iter_init) (imp, iter))
+ return TRUE;
+
+ imp = imp->fallback;
+ }
+
+ return FALSE;
+}
+
+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;
+}
+
+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);
+
+ 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 000000000..6d78aa7cb
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-inlines.h
@@ -0,0 +1,1421 @@
+/* -*- 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
+
+#ifdef LOW_QUALITY_INTERPOLATION
+/* Based on Filter_32_opaque_portable from Skia */
+static force_inline uint32_t
+bilinear_interpolation(uint32_t a00, uint32_t a01,
+ uint32_t a10, uint32_t a11,
+ int x, int y)
+{
+ int xy = x * y;
+ static const uint32_t mask = 0xff00ff;
+
+ int scale = 256 - 16*y - 16*x + xy;
+ uint32_t lo = (a00 & mask) * scale;
+ uint32_t hi = ((a00 >> 8) & mask) * scale;
+
+ scale = 16*x - xy;
+ lo += (a01 & mask) * scale;
+ hi += ((a01 >> 8) & mask) * scale;
+
+ scale = 16*y - xy;
+ lo += (a10 & mask) * scale;
+ hi += ((a10 >> 8) & mask) * scale;
+
+ lo += (a11 & mask) * xy;
+ hi += ((a11 >> 8) & mask) * xy;
+
+ return ((lo >> 8) & mask) | (hi & ~mask);
+}
+#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
+#endif // BILINEAR_INTERPOLATION_BITS <= 4
+
+/*
+ * 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)
+
+/*****************************************************************************/
+
+/*
+ * 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.
+ */
+
+/* Replace a single "scanline_func" with "fetch_func" & "op_func" to allow optional
+ * two stage processing (bilinear fetch to a temp buffer, followed by unscaled
+ * combine), "op_func" may be NULL, in this case we keep old behavior.
+ * This is ugly and gcc issues some warnings, but works.
+ *
+ * An advice: clang has much better error reporting than gcc for deeply nested macros.
+ */
+
+#define scanline_func(dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buf, mask, src_top, src_bottom, width, \
+ weight_top, weight_bottom, vx, unit_x, max_vx, zero_src) \
+ do { \
+ if (op_func != NULL) \
+ { \
+ fetch_func ((void *)scanline_buf, (mask), (src_top), (src_bottom), (width), \
+ (weight_top), (weight_bottom), (vx), (unit_x), (max_vx), (zero_src)); \
+ ((void (*)(dst_type_t *, const mask_type_t *, const src_type_t *, int)) op_func)\
+ ((dst), (mask), (src_type_t *)scanline_buf, (width)); \
+ } \
+ else \
+ { \
+ fetch_func ((void*)(dst), (mask), (src_top), (src_bottom), (width), (weight_top), \
+ (weight_bottom), (vx), (unit_x), (max_vx), (zero_src)); \
+ } \
+ } while (0)
+
+
+#define SCANLINE_BUFFER_LENGTH 3072
+
+#define FAST_BILINEAR_MAINLOOP_INT(scale_func_name, fetch_func, op_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; \
+ \
+ uint64_t stack_scanline_buffer[SCANLINE_BUFFER_LENGTH]; \
+ uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer; \
+ \
+ 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); \
+ } \
+ \
+ if (op_func != NULL && width * sizeof(src_type_t) > sizeof(stack_scanline_buffer)) \
+ { \
+ scanline_buffer = pixman_malloc_ab (width, sizeof(src_type_t)); \
+ \
+ if (!scanline_buffer) \
+ return; \
+ } \
+ \
+ 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, 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; i<src_width;) \
+ { \
+ for (j=0; j<src_image->bits.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_type_t, mask_type_t, src_type_t, fetch_func, op_func, \
+ dst, scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, \
+ dst, scanline_buffer, 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_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
+ scanline_buffer, mask, \
+ src_first_line + src_stride * y1, \
+ src_first_line + src_stride * y2, width, \
+ weight1, weight2, vx, unit_x, max_vx, FALSE); \
+ } \
+ } \
+ if (scanline_buffer != (uint8_t *) stack_scanline_buffer) \
+ free (scanline_buffer); \
+}
+
+/* A workaround for old sun studio, see: https://bugs.freedesktop.org/show_bug.cgi?id=32764 */
+#define FAST_BILINEAR_MAINLOOP_COMMON(scale_func_name, fetch_func, op_func, src_type_t, mask_type_t,\
+ dst_type_t, repeat_mode, flags) \
+ FAST_BILINEAR_MAINLOOP_INT(_ ## scale_func_name, fetch_func, op_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 000000000..f5ba51b60
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
@@ -0,0 +1,444 @@
+/* -*- 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 <config.h>
+#endif
+#include <stdlib.h>
+#include "pixman-private.h"
+
+#include "pixman-dither.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_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;
+
+ 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;
+ 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)
+ {
+ register uint32_t color;
+
+ color = _pixman_gradient_walker_pixel (&walker, t);
+ while (buffer < end)
+ *buffer++ = color;
+ }
+ else
+ {
+ int i;
+
+ i = 0;
+ while (buffer < end)
+ {
+ if (!mask || *mask++)
+ {
+ *buffer = _pixman_gradient_walker_pixel (&walker,
+ t + next_inc);
+ }
+ i++;
+ next_inc = inc * i;
+ buffer++;
+ }
+ }
+ }
+ 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;
+ }
+
+ *buffer = _pixman_gradient_walker_pixel (&walker, t);
+ }
+
+ ++buffer;
+
+ 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_16 (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;
+ uint16_t * buffer = (uint16_t*)iter->buffer;
+ pixman_bool_t toggle = ((x ^ y) & 1);
+
+ 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;
+ uint16_t *end = buffer + width;
+ 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)
+ {
+ register uint32_t color;
+ uint16_t dither_diff;
+ uint16_t color16;
+ uint16_t color16b;
+
+ color = _pixman_gradient_walker_pixel (&walker, t);
+ color16 = dither_8888_to_0565(color, toggle);
+ color16b = dither_8888_to_0565(color, toggle^1);
+ // compute the difference
+ dither_diff = color16 ^ color16b;
+ while (buffer < end) {
+ *buffer++ = color16;
+ // use dither_diff to toggle between color16 and color16b
+ color16 ^= dither_diff;
+ toggle ^= 1;
+ }
+ }
+ else
+ {
+ int i;
+
+ i = 0;
+ while (buffer < end)
+ {
+ if (!mask || *mask++)
+ {
+ *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
+ t + next_inc),
+ toggle);
+ }
+ toggle ^= 1;
+ i++;
+ next_inc = inc * i;
+ buffer++;
+ }
+ }
+ }
+ 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;
+ }
+
+ *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t),
+ toggle);
+ }
+ toggle ^= 1;
+
+ ++buffer;
+
+ 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_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+ uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
+
+ pixman_expand_to_float (
+ (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+
+ return buffer;
+}
+
+void
+_pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+{
+ // XXX: we can't use this optimization when dithering
+ if (0 && linear_gradient_is_horizontal (
+ iter->image, iter->x, iter->y, iter->width, iter->height))
+ {
+ if (iter->iter_flags & ITER_16)
+ linear_get_scanline_16 (iter, NULL);
+ else 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_16)
+ iter->get_scanline = linear_get_scanline_16;
+ 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 000000000..89b96826b
--- /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 <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include "pixman-private.h"
+
+#define F(x) pixman_int_to_fixed (x)
+
+static force_inline int
+count_leading_zeros (uint32_t x)
+{
+#ifdef __GNUC__
+ 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 = (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 000000000..ddfacef62
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.S
@@ -0,0 +1,3373 @@
+/*
+ * 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 (nlukic@mips.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)
+
+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
+ */
+
+ SAVE_REGS_ON_STACK 8, s0, s1, s2, s3, s4, s5
+ beqz a3, 4f
+ nop
+ li t6, 0xff
+ addiu t7, zero, -1 /* t7 = 0xffffffff */
+ srl t8, a1, 24 /* t8 = srca */
+ li t9, 0x00ff00ff
+ addiu t1, a3, -1
+ beqz t1, 3f /* last pixel */
+ nop
+ beq t8, t6, 2f /* if (srca == 0xff) */
+ nop
+1:
+ /* a1 = src */
+ lw t0, 0(a2) /* t0 = mask */
+ lw t1, 4(a2) /* t1 = mask */
+ or t2, t0, t1
+ beqz t2, 12f /* if (t0 == 0) && (t1 == 0) */
+ addiu a2, a2, 8
+ and t3, t0, t1
+ move t4, a1 /* t4 = src */
+ move t5, a1 /* t5 = src */
+ lw t2, 0(a0) /* t2 = dst */
+ beq t3, t7, 11f /* if (t0 == 0xffffffff) && (t1 == 0xffffffff) */
+ 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
+11:
+ 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)
+12:
+ addiu a3, a3, -2
+ addiu t1, a3, -1
+ bgtz t1, 1b
+ addiu a0, a0, 8
+ b 3f
+ nop
+2:
+ /* a1 = src */
+ lw t0, 0(a2) /* t0 = mask */
+ lw t1, 4(a2) /* t1 = mask */
+ or t2, t0, t1
+ beqz t2, 22f /* if (t0 == 0) & (t1 == 0) */
+ addiu a2, a2, 8
+ and t2, t0, t1
+ move t4, a1
+ beq t2, t7, 21f /* if (t0 == 0xffffffff) && (t1 == 0xffffffff) */
+ move t5, a1
+ 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
+ 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 t4, t4, t2
+ addu_s.qb t5, t5, t3
+21:
+ sw t4, 0(a0)
+ sw t5, 4(a0)
+22:
+ addiu a3, a3, -2
+ addiu t1, a3, -1
+ bgtz t1, 2b
+ addiu a0, a0, 8
+3:
+ blez a3, 4f
+ nop
+ /* a1 = src */
+ lw t1, 0(a2) /* t1 = mask */
+ beqz t1, 4f
+ nop
+ move t2, a1 /* t2 = src */
+ beq t1, t7, 31f
+ lw t0, 0(a0) /* t0 = dst */
+
+ MIPS_UN8x4_MUL_UN8x4 a1, t1, t2, t9, t3, t4, t5, t6
+ MIPS_UN8x4_MUL_UN8 t1, t8, t1, t9, t3, t4, t5
+31:
+ not t1, t1
+ MIPS_UN8x4_MUL_UN8x4 t0, t1, t0, t9, t3, t4, t5, t6
+ addu_s.qb t0, t2, t0
+ sw t0, 0(a0)
+4:
+ RESTORE_REGS_FROM_STACK 8, s0, s1, s2, s3, s4, s5
+ 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
+ */
+
+ SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8
+ beqz a3, 4f
+ nop
+ li t5, 0xf800f800
+ li t6, 0x07e007e0
+ li t7, 0x001F001F
+ li t9, 0x00ff00ff
+
+ srl t8, a1, 24 /* t8 = srca */
+ addiu t1, a3, -1
+ beqz t1, 3f /* last pixel */
+ nop
+ li s0, 0xff /* s0 = 0xff */
+ addiu s1, zero, -1 /* s1 = 0xffffffff */
+
+ beq t8, s0, 2f /* if (srca == 0xff) */
+ nop
+1:
+ /* a1 = src */
+ lw t0, 0(a2) /* t0 = mask */
+ lw t1, 4(a2) /* t1 = mask */
+ or t2, t0, t1
+ beqz t2, 12f /* if (t0 == 0) && (t1 == 0) */
+ addiu a2, a2, 8
+ and t3, t0, t1
+ move s2, a1 /* s2 = src */
+ move s3, a1 /* s3 = src */
+ lhu t2, 0(a0) /* t2 = dst */
+ beq t3, s1, 11f /* if (t0 == 0xffffffff) && (t1 == 0xffffffff) */
+ lhu t3, 2(a0) /* t3 = dst */
+ MIPS_2xUN8x4_MUL_2xUN8x4 a1, a1, t0, t1, s2, s3, t9, t4, s4, s5, s6, s7, s8
+ MIPS_2xUN8x4_MUL_2xUN8 t0, t1, t8, t8, t0, t1, t9, t4, s4, s5, s6, s7, s8
+11:
+ not t0, t0
+ not t1, t1
+ CONVERT_2x0565_TO_2x8888 t2, t3, s4, s5, t6, t7, t4, s6, s7, s8
+ MIPS_2xUN8x4_MUL_2xUN8x4 s4, s5, t0, t1, s4, s5, t9, t4, s6, s7, s8, t0, t1
+ addu_s.qb s2, s2, s4
+ addu_s.qb s3, s3, s5
+ CONVERT_2x8888_TO_2x0565 s2, s3, t2, t3, t5, t6, t7, s4, s5
+ sh t2, 0(a0)
+ sh t3, 2(a0)
+12:
+ addiu a3, a3, -2
+ addiu t1, a3, -1
+ bgtz t1, 1b
+ addiu a0, a0, 4
+ b 3f
+ nop
+2:
+ /* a1 = src */
+ lw t0, 0(a2) /* t0 = mask */
+ lw t1, 4(a2) /* t1 = mask */
+ or t2, t0, t1
+ beqz t2, 22f /* if (t0 == 0) & (t1 == 0) */
+ addiu a2, a2, 8
+ and t3, t0, t1
+ move t2, a1
+ beq t3, s1, 21f /* if (t0 == 0xffffffff) && (t1 == 0xffffffff) */
+ move t3, a1
+ lhu t2, 0(a0) /* t2 = dst */
+ lhu t3, 2(a0) /* t3 = dst */
+ MIPS_2xUN8x4_MUL_2xUN8x4 a1, a1, t0, t1, s2, s3, t9, t4, s4, s5, s6, s7, s8
+ not t0, t0
+ not t1, t1
+ CONVERT_2x0565_TO_2x8888 t2, t3, s4, s5, t6, t7, t4, s6, s7, s8
+ MIPS_2xUN8x4_MUL_2xUN8x4 s4, s5, t0, t1, s4, s5, t9, t4, s6, s7, s8, t2, t3
+ addu_s.qb t2, s2, s4
+ addu_s.qb t3, s3, s5
+21:
+ CONVERT_2x8888_TO_2x0565 t2, t3, t0, t1, t5, t6, t7, s2, s3
+ sh t0, 0(a0)
+ sh t1, 2(a0)
+22:
+ addiu a3, a3, -2
+ addiu t1, a3, -1
+ bgtz t1, 2b
+ addiu a0, a0, 4
+3:
+ blez a3, 4f
+ nop
+ /* a1 = src */
+ lw t1, 0(a2) /* t1 = mask */
+ beqz t1, 4f
+ nop
+ move t2, a1 /* t2 = src */
+ beq t1, t7, 31f
+ lhu t0, 0(a0) /* t0 = dst */
+
+ MIPS_UN8x4_MUL_UN8x4 a1, t1, t2, t9, t3, t4, t5, t6
+ MIPS_UN8x4_MUL_UN8 t1, t8, t1, t9, t3, t4, t5
+31:
+ not t1, t1
+ CONVERT_1x0565_TO_1x8888 t0, s1, s2, s3
+ MIPS_UN8x4_MUL_UN8x4 s1, t1, t3, t9, t4, t5, t6, t7
+ addu_s.qb t0, t2, t3
+ CONVERT_1x8888_TO_1x0565 t0, s1, s2, s3
+ sh s1, 0(a0)
+4:
+ RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7, s8
+ j ra
+ nop
+
+END(pixman_composite_over_n_8888_0565_ca_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_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 (a8r8g8b8)
+ * a2 - w
+ */
+
+ beqz a2, 5f
+ nop
+
+ SAVE_REGS_ON_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7
+ move t7, a1
+ srl t5, t7, 24
+ replv.ph t5, t5
+ srl t9, a2, 2 /* t1 = how many multiples of 4 src pixels */
+ beqz t9, 2f /* branch if less than 4 src pixels */
+ nop
+
+1:
+ addiu t9, t9, -1
+ addiu a2, a2, -4
+ lbu t0, 0(a0)
+ lbu t1, 1(a0)
+ lbu t2, 2(a0)
+ lbu t3, 3(a0)
+
+ muleu_s.ph.qbl s0, t0, t5
+ muleu_s.ph.qbr s1, t0, t5
+ muleu_s.ph.qbl s2, t1, t5
+ muleu_s.ph.qbr s3, t1, t5
+ muleu_s.ph.qbl s4, t2, t5
+ muleu_s.ph.qbr s5, t2, t5
+ muleu_s.ph.qbl s6, t3, t5
+ muleu_s.ph.qbr s7, t3, t5
+
+ shrl.ph t4, s0, 8
+ shrl.ph t6, s1, 8
+ shrl.ph t7, s2, 8
+ shrl.ph t8, s3, 8
+ addq.ph t0, s0, t4
+ addq.ph t1, s1, t6
+ addq.ph t2, s2, t7
+ addq.ph t3, s3, t8
+ shra_r.ph t0, t0, 8
+ shra_r.ph t1, t1, 8
+ shra_r.ph t2, t2, 8
+ shra_r.ph t3, t3, 8
+ shrl.ph t4, s4, 8
+ shrl.ph t6, s5, 8
+ shrl.ph t7, s6, 8
+ shrl.ph t8, s7, 8
+ addq.ph s0, s4, t4
+ addq.ph s1, s5, t6
+ addq.ph s2, s6, t7
+ addq.ph s3, s7, t8
+ shra_r.ph t4, s0, 8
+ shra_r.ph t6, s1, 8
+ shra_r.ph t7, s2, 8
+ shra_r.ph t8, s3, 8
+
+ precr.qb.ph s0, t0, t1
+ precr.qb.ph s1, t2, t3
+ precr.qb.ph s2, t4, t6
+ precr.qb.ph s3, t7, t8
+
+ sb s0, 0(a0)
+ sb s1, 1(a0)
+ sb s2, 2(a0)
+ sb s3, 3(a0)
+ bgtz t9, 1b
+ addiu a0, a0, 4
+2:
+ beqz a2, 4f
+ nop
+3:
+ lbu t1, 0(a0)
+
+ muleu_s.ph.qbl t4, t1, t5
+ muleu_s.ph.qbr t7, t1, t5
+ shrl.ph t6, t4, 8
+ shrl.ph t0, t7, 8
+ addq.ph t8, t4, t6
+ addq.ph t9, t7, t0
+ shra_r.ph t8, t8, 8
+ shra_r.ph t9, t9, 8
+ precr.qb.ph t2, t8, t9
+ sb t2, 0(a0)
+ addiu a2, a2, -1
+ bnez a2, 3b
+ addiu a0, a0, 1
+4:
+ RESTORE_REGS_FROM_STACK 20, s0, s1, s2, s3, s4, s5, s6, s7
+5:
+ j ra
+ nop
+
+END(pixman_composite_in_n_8_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 000000000..b330c0f0d
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-mips-dspr2-asm.h
@@ -0,0 +1,681 @@
+/*
+ * 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 (nlukic@mips.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; \
+ .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
+ precrq.ph.w \scratch1, \in2_8888, \in1_8888
+ precr_sra.ph.w \in2_8888, \in1_8888, 0
+ shll.ph \scratch1, \scratch1, 8
+ srl \in2_8888, \in2_8888, 3
+ and \scratch2, \in2_8888, \maskB
+ and \scratch1, \scratch1, \maskR
+ srl \in2_8888, \in2_8888, 2
+ and \out2_565, \in2_8888, \maskG
+ or \out2_565, \out2_565, \scratch2
+ or \out1_565, \out2_565, \scratch1
+ 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
+
+.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 000000000..e14e1c43b
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-mips-dspr2.c
@@ -0,0 +1,411 @@
+/*
+ * 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 (nlukic@mips.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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)
+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, 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_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_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),
+ 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, 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 (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_A8_MASK_FAST_PATH (OVER, a8r8g8b8, r5g6b5, mips_8888_8_0565),
+ PIXMAN_MIPS_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, a8b8g8r8, b5g6r5, mips_8888_8_0565),
+
+ PIXMAN_MIPS_SIMPLE_NEAREST_A8_MASK_FAST_PATH (OVER, r5g6b5, r5g6b5, mips_0565_8_0565),
+ PIXMAN_MIPS_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 000000000..4ac9ff95d
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-mips-dspr2.h
@@ -0,0 +1,396 @@
+/*
+ * 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 (nlukic@mips.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_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)
+
+/* Provide entries for the fast path table */
+#define PIXMAN_MIPS_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 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 000000000..9ad6da537
--- /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 000000000..304881383
--- /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 <config.h>
+#endif
+
+#include "pixman-private.h"
+
+#if defined(USE_MIPS_DSPR2) || defined(USE_LOONGSON_MMI)
+
+#include <string.h>
+#include <stdlib.h>
+
+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 000000000..ca2ac83d9
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-mmx.c
@@ -0,0 +1,4084 @@
+/*
+ * 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 <config.h>
+#endif
+
+#if defined USE_X86_MMX || defined USE_ARM_IWMMXT || defined USE_LOONGSON_MMI
+
+#ifdef USE_LOONGSON_MMI
+#include <loongson-mmintrin.h>
+#else
+#include <mmintrin.h>
+#endif
+#include "pixman-private.h"
+#include "pixman-combine32.h"
+#include "pixman-inlines.h"
+
+#define no_vERBOSE
+
+#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))
+# include <xmmintrin.h>
+# 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;
+}
+
+# ifdef __OPTIMIZE__
+extern __inline __m64 __attribute__((__gnu_inline__, __always_inline__, __artificial__))
+_mm_shuffle_pi16 (__m64 __A, int8_t const __N)
+{
+ __m64 ret;
+
+ asm ("pshufw %2, %1, %0\n\t"
+ : "=y" (ret)
+ : "y" (__A), "K" (__N)
+ );
+
+ return ret;
+}
+# else
+# 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
+#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.
+ */
+#ifdef _MSC_VER
+# define M64_MEMBER m64_u64
+#elif defined(__ICC)
+# define USE_CVT_INTRINSICS
+#elif defined(USE_LOONGSON_MMI)
+# define USE_M64_DOUBLE
+#elif defined(__GNUC__)
+# 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));
+}
+
+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. */
+ return *(__m64 *)p;
+#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. */
+ return *p;
+#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 (&quot);
+ 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 ();
+}
+
+#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_BSHIFT = _mm_set_pi16 (BSHIFT, BSHIFT, BSHIFT, BSHIFT); \
+ 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); \
+ vx += unit_x; \
+ if (BILINEAR_INTERPOLATION_BITS < 8) \
+ { \
+ /* 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); \
+ lo = _mm_madd_pi16 (p, mm_wh); \
+ hi = _mm_madd_pi16 (q, mm_wh); \
+ } \
+ else \
+ { \
+ /* calculate horizontal weights */ \
+ __m64 mm_wh_lo = _mm_sub_pi16 (mm_BSHIFT, _mm_srli_pi16 (mm_x, \
+ 16 - BILINEAR_INTERPOLATION_BITS)); \
+ __m64 mm_wh_hi = _mm_srli_pi16 (mm_x, \
+ 16 - BILINEAR_INTERPOLATION_BITS); \
+ /* horizontal interpolation */ \
+ __m64 mm_lo_lo = _mm_mullo_pi16 (lo, mm_wh_lo); \
+ __m64 mm_lo_hi = _mm_mullo_pi16 (hi, mm_wh_hi); \
+ __m64 mm_hi_lo = _mm_mulhi_pu16 (lo, mm_wh_lo); \
+ __m64 mm_hi_hi = _mm_mulhi_pu16 (hi, mm_wh_hi); \
+ lo = _mm_add_pi32 (_mm_unpacklo_pi16 (mm_lo_lo, mm_hi_lo), \
+ _mm_unpacklo_pi16 (mm_lo_hi, mm_hi_hi)); \
+ hi = _mm_add_pi32 (_mm_unpackhi_pi16 (mm_lo_lo, mm_hi_lo), \
+ _mm_unpackhi_pi16 (mm_lo_hi, mm_hi_hi)); \
+ } \
+ 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++ = *(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++ = *(src++) << 24;
+ w--;
+ }
+
+ _mm_empty ();
+ return iter->buffer;
+}
+
+typedef struct
+{
+ pixman_format_code_t format;
+ pixman_iter_get_scanline_t get_scanline;
+} fetcher_info_t;
+
+static const fetcher_info_t fetchers[] =
+{
+ { PIXMAN_x8r8g8b8, mmx_fetch_x8r8g8b8 },
+ { PIXMAN_r5g6b5, mmx_fetch_r5g6b5 },
+ { PIXMAN_a8, mmx_fetch_a8 },
+ { PIXMAN_null }
+};
+
+static pixman_bool_t
+mmx_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
+{
+ pixman_image_t *image = iter->image;
+
+#define FLAGS \
+ (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \
+ FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST)
+
+ if ((iter->iter_flags & ITER_NARROW) &&
+ (iter->image_flags & FLAGS) == FLAGS)
+ {
+ const fetcher_info_t *f;
+
+ for (f = &fetchers[0]; f->format != PIXMAN_null; f++)
+ {
+ if (image->common.extended_format_code == f->format)
+ {
+ 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 (f->format) / 8;
+ iter->stride = s;
+
+ iter->get_scanline = f->get_scanline;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+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_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->src_iter_init = mmx_src_iter_init;
+
+ 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 000000000..e39996d9d
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-noop.c
@@ -0,0 +1,176 @@
+/* -*- 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 <config.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+#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 void
+dest_write_back_direct (pixman_iter_t *iter)
+{
+ iter->buffer += iter->image->bits.rowstride;
+}
+
+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 uint32_t *
+get_scanline_null (pixman_iter_t *iter, const uint32_t *mask)
+{
+ return NULL;
+}
+
+static pixman_bool_t
+noop_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
+{
+ pixman_image_t *image = iter->image;
+
+#define FLAGS \
+ (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM)
+
+ if (!image)
+ {
+ iter->get_scanline = get_scanline_null;
+ }
+ else if ((iter->iter_flags & (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) ==
+ (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB))
+ {
+ iter->get_scanline = _pixman_iter_get_scanline_noop;
+ }
+ else if (image->common.extended_format_code == PIXMAN_solid &&
+ (iter->image->type == SOLID ||
+ (iter->image_flags & FAST_PATH_NO_ALPHA_MAP)))
+ {
+ if (iter->iter_flags & ITER_NARROW)
+ {
+ uint32_t *buffer = iter->buffer;
+ uint32_t *end = buffer + iter->width;
+ uint32_t color;
+
+ if (image->type == SOLID)
+ color = image->solid.color_32;
+ else
+ color = image->bits.fetch_pixel_32 (&image->bits, 0, 0);
+
+ while (buffer < end)
+ *(buffer++) = color;
+ }
+ else
+ {
+ argb_t *buffer = (argb_t *)iter->buffer;
+ argb_t *end = buffer + iter->width;
+ argb_t color;
+
+ if (image->type == SOLID)
+ color = image->solid.color_float;
+ else
+ color = image->bits.fetch_pixel_float (&image->bits, 0, 0);
+
+ while (buffer < end)
+ *(buffer++) = color;
+ }
+
+ iter->get_scanline = _pixman_iter_get_scanline_noop;
+ }
+ else if (image->common.extended_format_code == PIXMAN_a8r8g8b8 &&
+ (iter->iter_flags & ITER_NARROW) &&
+ (iter->image_flags & FLAGS) == FLAGS &&
+ iter->x >= 0 && iter->y >= 0 &&
+ iter->x + iter->width <= image->bits.width &&
+ iter->y + iter->height <= image->bits.height)
+ {
+ iter->buffer =
+ image->bits.bits + iter->y * image->bits.rowstride + iter->x;
+
+ iter->get_scanline = noop_get_scanline;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static pixman_bool_t
+noop_dest_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
+{
+ pixman_image_t *image = iter->image;
+ uint32_t image_flags = iter->image_flags;
+ uint32_t iter_flags = iter->iter_flags;
+
+ if ((image_flags & FAST_PATH_STD_DEST_FLAGS) == FAST_PATH_STD_DEST_FLAGS &&
+ (iter_flags & ITER_NARROW) == ITER_NARROW &&
+ ((image->common.extended_format_code == PIXMAN_a8r8g8b8) ||
+ (image->common.extended_format_code == PIXMAN_x8r8g8b8 &&
+ (iter_flags & (ITER_LOCALIZED_ALPHA)))))
+ {
+ iter->buffer = image->bits.bits + iter->y * image->bits.rowstride + iter->x;
+
+ iter->get_scanline = _pixman_iter_get_scanline_noop;
+ iter->write_back = dest_write_back_direct;
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+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->src_iter_init = noop_src_iter_init;
+ imp->dest_iter_init = noop_dest_iter_init;
+
+ 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 000000000..a6e7bb0cf
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-ppc.c
@@ -0,0 +1,155 @@
+/*
+ * 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 <config.h>
+#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 <sys/sysctl.h>
+
+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 <sys/param.h>
+#include <sys/sysctl.h>
+#include <machine/cpu.h>
+
+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 (__linux__)
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <linux/auxvec.h>
+#include <asm/cputable.h>
+
+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 <signal.h>
+#include <setjmp.h>
+
+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 000000000..6af073b55
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-private.h
@@ -0,0 +1,1187 @@
+#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 <time.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+
+#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) (pixman_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 */
+
+ fetch_scanline_t fetch_scanline_16;
+
+ 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;
+
+ store_scanline_t store_scanline_16;
+
+ /* 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 enum
+{
+ ITER_NARROW = (1 << 0),
+
+ /* "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 << 1),
+ ITER_IGNORE_ALPHA = (1 << 2),
+ ITER_IGNORE_RGB = (1 << 3),
+
+ /* With the addition of ITER_16 we now have two flags that to represent
+ * 3 pipelines. This means that there can be an invalid state when
+ * both ITER_NARROW and ITER_16 are set. In this case
+ * ITER_16 overrides NARROW and we should use the 16 bit pipeline.
+ * Note: ITER_16 still has a 32 bit mask, which is a bit weird. */
+ ITER_16 = (1 << 4)
+} 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;
+
+ /* These fields are scratch data that implementations can use */
+ void * data;
+ uint8_t * bits;
+ int stride;
+};
+
+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
+{
+ uint32_t left_ag;
+ uint32_t left_rb;
+ uint32_t right_ag;
+ uint32_t right_rb;
+ pixman_fixed_t left_x;
+ pixman_fixed_t right_x;
+ pixman_fixed_t stepper;
+
+ 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);
+
+uint32_t
+_pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
+ pixman_fixed_48_16_t x);
+
+/*
+ * 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);
+typedef pixman_bool_t (*pixman_iter_init_func_t) (pixman_implementation_t *imp,
+ pixman_iter_t *iter);
+
+void _pixman_setup_combiner_functions_16 (pixman_implementation_t *imp);
+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;
+
+ pixman_blt_func_t blt;
+ pixman_fill_func_t fill;
+ pixman_iter_init_func_t src_iter_init;
+ pixman_iter_init_func_t dest_iter_init;
+
+ pixman_combine_32_func_t combine_16[PIXMAN_N_OPERATORS];
+ pixman_combine_32_func_t combine_16_ca[PIXMAN_N_OPERATORS];
+ 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 rgb16);
+
+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);
+
+pixman_bool_t
+_pixman_implementation_src_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);
+
+pixman_bool_t
+_pixman_implementation_dest_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_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_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);
+
+/* 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_16_FORMAT (1 << 27)
+
+#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);
+
+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)))
+
+/* 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
+
+#ifdef DEBUG
+
+void
+_pixman_log_error (const char *function, const char *message);
+
+#define return_if_fail(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ { \
+ _pixman_log_error (FUNC, "The expression " # expr " was false"); \
+ return; \
+ } \
+ } \
+ while (0)
+
+#define return_val_if_fail(expr, retval) \
+ do \
+ { \
+ if (!(expr)) \
+ { \
+ _pixman_log_error (FUNC, "The expression " # expr " was false"); \
+ return (retval); \
+ } \
+ } \
+ while (0)
+
+#define critical_if_fail(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ _pixman_log_error (FUNC, "The expression " # expr " was false"); \
+ } \
+ while (0)
+
+
+#else
+
+#define _pixman_log_error(f,m) do { } while (0)
+
+#define return_if_fail(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ return; \
+ } \
+ while (0)
+
+#define return_val_if_fail(expr, retval) \
+ do \
+ { \
+ if (!(expr)) \
+ return (retval); \
+ } \
+ while (0)
+
+#define critical_if_fail(expr) \
+ do \
+ { \
+ } \
+ while (0)
+#endif
+
+/*
+ * Matrix
+ */
+
+typedef struct { pixman_fixed_48_16_t v[3]; } pixman_vector_48_16_t;
+
+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);
+
+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);
+
+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 000000000..3d539b1c8
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
@@ -0,0 +1,727 @@
+/* -*- 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 <config.h>
+#endif
+#include <stdlib.h>
+#include <math.h>
+#include "pixman-private.h"
+
+#include "pixman-dither.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 uint32_t
+radial_compute_color (double a,
+ double b,
+ double c,
+ double inva,
+ double dr,
+ double mindr,
+ pixman_gradient_walker_t *walker,
+ pixman_repeat_t repeat)
+{
+ /*
+ * 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)
+ return 0;
+
+ t = pixman_fixed_1 / 2 * c / b;
+ if (repeat == PIXMAN_REPEAT_NONE)
+ {
+ if (0 <= t && t <= pixman_fixed_1)
+ return _pixman_gradient_walker_pixel (walker, t);
+ }
+ else
+ {
+ if (t * dr >= mindr)
+ return _pixman_gradient_walker_pixel (walker, t);
+ }
+
+ return 0;
+ }
+
+ 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)
+ return _pixman_gradient_walker_pixel (walker, t0);
+ else if (0 <= t1 && t1 <= pixman_fixed_1)
+ return _pixman_gradient_walker_pixel (walker, t1);
+ }
+ else
+ {
+ if (t0 * dr >= mindr)
+ return _pixman_gradient_walker_pixel (walker, t0);
+ else if (t1 * dr >= mindr)
+ return _pixman_gradient_walker_pixel (walker, t1);
+ }
+ }
+
+ return 0;
+}
+
+static uint32_t *
+radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
+{
+ /*
+ * 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;
+ 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++)
+ {
+ *buffer = radial_compute_color (radial->a, b, c,
+ radial->inva,
+ radial->delta.radius,
+ radial->mindr,
+ &walker,
+ image->common.repeat);
+ }
+
+ b += db;
+ c += dc;
+ dc += ddc;
+ ++buffer;
+ }
+ }
+ 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 */
+
+ *buffer = radial_compute_color (radial->a, b, c,
+ radial->inva,
+ radial->delta.radius,
+ radial->mindr,
+ &walker,
+ image->common.repeat);
+ }
+ else
+ {
+ *buffer = 0;
+ }
+ }
+
+ ++buffer;
+
+ 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_16 (pixman_iter_t *iter, const uint32_t *mask)
+{
+ /*
+ * 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;
+ uint16_t *buffer = iter->buffer;
+ pixman_bool_t toggle = ((x ^ y) & 1);
+
+ gradient_t *gradient = (gradient_t *)image;
+ radial_gradient_t *radial = (radial_gradient_t *)image;
+ uint16_t *end = buffer + width;
+ 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++)
+ {
+ *buffer = dither_8888_to_0565(
+ radial_compute_color (radial->a, b, c,
+ radial->inva,
+ radial->delta.radius,
+ radial->mindr,
+ &walker,
+ image->common.repeat),
+ toggle);
+ }
+
+ toggle ^= 1;
+ b += db;
+ c += dc;
+ dc += ddc;
+ ++buffer;
+ }
+ }
+ 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 */
+
+ *buffer = dither_8888_to_0565 (
+ radial_compute_color (radial->a, b, c,
+ radial->inva,
+ radial->delta.radius,
+ radial->mindr,
+ &walker,
+ image->common.repeat),
+ toggle);
+ }
+ else
+ {
+ *buffer = 0;
+ }
+ }
+
+ ++buffer;
+ toggle ^= 1;
+
+ 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_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+ uint32_t *buffer = radial_get_scanline_narrow (iter, NULL);
+
+ pixman_expand_to_float (
+ (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+
+ return buffer;
+}
+
+void
+_pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+{
+ if (iter->iter_flags & ITER_16)
+ iter->get_scanline = radial_get_scanline_16;
+ else 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 000000000..7f2e29b15
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-region.c
@@ -0,0 +1,2808 @@
+/*
+ * 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 <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#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) \
+ : &(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) (region_type_t *reg1, region_type_t *reg2)
+{
+ int i;
+ box_type_t *rects1;
+ box_type_t *rects2;
+
+ /*
+ * If the region is empty the extents are undefined so we need to check
+ * for empty before comparing the extents.
+ */
+ if (PIXREGION_NIL (reg1) && PIXREGION_NIL(reg2))
+ return TRUE;
+
+ 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 (&region->extents))
+ {
+ if (BAD_RECT (&region->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, 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) (region_type_t *region)
+{
+ return PIXREGION_NUMRECTS (region);
+}
+
+PIXMAN_EXPORT box_type_t *
+PREFIX (_rectangles) (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, 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 */
+ region_type_t * reg1, /* First region in operation */
+ 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,
+ region_type_t * reg1,
+ region_type_t * reg2)
+{
+ GOOD (reg1);
+ GOOD (reg2);
+ GOOD (new_reg);
+
+ /* check for trivial reject */
+ if (PIXREGION_NIL (reg1) || PIXREGION_NIL (reg2) ||
+ !EXTENTCHECK (&reg1->extents, &reg2->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 (&reg2->extents, &reg1->extents))
+ {
+ return PREFIX (_copy) (new_reg, reg1);
+ }
+ else if (!reg1->data && SUBSUMES (&reg1->extents, &reg2->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,
+ 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;
+
+ if (!GOOD_RECT (&region.extents))
+ {
+ if (BAD_RECT (&region.extents))
+ _pixman_log_error (FUNC, "Invalid rectangle passed");
+ FREE_DATA (dest);
+ PREFIX (_init) (dest);
+ return TRUE;
+ }
+
+ return PREFIX(_intersect) (dest, source, &region);
+}
+
+/* Convenience function for performing union of region with a
+ * single rectangle
+ */
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_union_rect) (region_type_t *dest,
+ 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 (&region.extents))
+ {
+ if (BAD_RECT (&region.extents))
+ _pixman_log_error (FUNC, "Invalid rectangle passed");
+ return PREFIX (_copy) (dest, source);
+ }
+
+ region.data = NULL;
+
+ return PREFIX (_union) (dest, source, &region);
+}
+
+PIXMAN_EXPORT pixman_bool_t
+PREFIX (_union) (region_type_t *new_reg,
+ region_type_t *reg1,
+ 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 (&reg1->extents, &reg2->extents))
+ {
+ if (new_reg != reg1)
+ return PREFIX (_copy) (new_reg, reg1);
+
+ return TRUE;
+ }
+
+ /*
+ * Region 2 completely subsumes region 1
+ */
+ if (!reg2->data && SUBSUMES (&reg2->extents, &reg1->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 preceeds 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,
+ region_type_t *reg_m,
+ 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 (&reg_m->extents, &reg_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 substraction 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 */
+ region_type_t *reg1, /* Region to invert */
+ 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, &reg1->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 substraction 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) (region_type_t * region,
+ 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 (&region->extents, prect))
+ return(PIXMAN_REGION_OUT);
+
+ if (numRects == 1)
+ {
+ /* We know that it must be PIXMAN_REGION_IN or PIXMAN_REGION_PART */
+ if (SUBSUMES (&region->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, 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) (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 (&region->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) (region_type_t * region)
+{
+ GOOD (region);
+
+ return(!PIXREGION_NIL (region));
+}
+
+PIXMAN_EXPORT box_type_t *
+PREFIX (_extents) (region_type_t * region)
+{
+ GOOD (region);
+
+ return(&region->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 000000000..d88d3380f
--- /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 <sandmann@redhat.com>
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#undef PIXMAN_DISABLE_DEPRECATED
+
+#include "pixman-private.h"
+
+#include <stdlib.h>
+
+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 000000000..abd6b1a93
--- /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 <sandmann@redhat.com>
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "pixman-private.h"
+
+#include <stdlib.h>
+
+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 000000000..5f9fef630
--- /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 <config.h>
+#endif
+#include "pixman-private.h"
+
+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 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 000000000..e4e668d38
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-sse2.c
@@ -0,0 +1,6560 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include <xmmintrin.h> /* for _mm_shuffle_pi16 and _MM_SHUFFLE */
+#include <emmintrin.h> /* 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 = *ps;
+
+ 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 m, 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)
+ {
+ m = *((uint32_t*)mask);
+
+ 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)
+ {
+ 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;
+ }
+
+ 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;
+ uint32_t m;
+
+ __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)
+ {
+ m = *((uint32_t*)mask);
+
+ 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;
+ uint32_t m;
+ __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)
+ {
+ 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)
+ {
+ xmm_dst = load_128_aligned ((__m128i*) dst);
+ unpack_565_128_4x128 (xmm_dst,
+ &xmm_dst0, &xmm_dst1, &xmm_dst2, &xmm_dst3);
+
+ m = *((uint32_t*)mask);
+ 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);
+ }
+
+ m = *((uint32_t*)mask);
+ 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)
+ {
+ 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, m;
+ 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))
+ {
+ m = (uint32_t) *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)
+ {
+ m = (uint32_t) *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 m, 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))
+ {
+ 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--;
+ }
+
+ 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)
+ {
+ 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 && (unsigned long)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 && ((unsigned long)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 = *(uint32_t*)mask;
+ 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))
+ {
+ *(uint16_t *)d = *(uint16_t *)s;
+ w -= 2;
+ s += 2;
+ d += 2;
+ }
+
+ while (w >= 4 && ((uintptr_t)d & 15))
+ {
+ *(uint32_t *)d = *(uint32_t *)s;
+
+ 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)
+ {
+ *(uint32_t *)d = *(uint32_t *)s;
+
+ w -= 4;
+ s += 4;
+ d += 4;
+ }
+
+ if (w >= 2)
+ {
+ *(uint16_t *)d = *(uint16_t *)s;
+ 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;
+ uint32_t m;
+ 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)
+ {
+ s = 0xff000000 | *src++;
+ m = (uint32_t) *mask++;
+ 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)
+ {
+ m = *(uint32_t*) mask;
+ 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)
+ {
+ m = (uint32_t) *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;
+ 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, 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;
+
+ s = *src++;
+ m = (uint32_t) *mask++;
+ 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)
+ {
+ m = *(uint32_t *) mask;
+
+ 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;
+
+ s = *src++;
+ m = (uint32_t) *mask++;
+ 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)
+
+#define BMSK ((1 << BILINEAR_INTERPOLATION_BITS) - 1)
+
+#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_xorc8 = _mm_set_epi16 (0, 0, 0, 0, BMSK, BMSK, BMSK, BMSK);\
+ const __m128i xmm_addc8 = _mm_set_epi16 (0, 0, 0, 0, 1, 1, 1, 1); \
+ const __m128i xmm_xorc7 = _mm_set_epi16 (0, BMSK, 0, BMSK, 0, BMSK, 0, BMSK);\
+ const __m128i xmm_addc7 = _mm_set_epi16 (0, 1, 0, 1, 0, 1, 0, 1); \
+ const __m128i xmm_ux = _mm_set_epi16 (unit_x, unit_x, unit_x, unit_x, \
+ unit_x, unit_x, unit_x, unit_x); \
+ const __m128i xmm_zero = _mm_setzero_si128 (); \
+ __m128i xmm_x = _mm_set_epi16 (vx, vx, vx, vx, vx, vx, vx, vx)
+
+#define BILINEAR_INTERPOLATE_ONE_PIXEL(pix) \
+do { \
+ __m128i xmm_wh, xmm_lo, xmm_hi, a; \
+ /* fetch 2x2 pixel block into sse2 registers */ \
+ __m128i tltr = _mm_loadl_epi64 ( \
+ (__m128i *)&src_top[pixman_fixed_to_int (vx)]); \
+ __m128i blbr = _mm_loadl_epi64 ( \
+ (__m128i *)&src_bottom[pixman_fixed_to_int (vx)]); \
+ vx += unit_x; \
+ /* vertical interpolation */ \
+ a = _mm_add_epi16 (_mm_mullo_epi16 (_mm_unpacklo_epi8 (tltr, xmm_zero), \
+ xmm_wt), \
+ _mm_mullo_epi16 (_mm_unpacklo_epi8 (blbr, xmm_zero), \
+ xmm_wb)); \
+ if (BILINEAR_INTERPOLATION_BITS < 8) \
+ { \
+ /* calculate horizontal weights */ \
+ xmm_wh = _mm_add_epi16 (xmm_addc7, _mm_xor_si128 (xmm_xorc7, \
+ _mm_srli_epi16 (xmm_x, 16 - BILINEAR_INTERPOLATION_BITS))); \
+ xmm_x = _mm_add_epi16 (xmm_x, xmm_ux); \
+ /* horizontal interpolation */ \
+ a = _mm_madd_epi16 (_mm_unpackhi_epi16 (_mm_shuffle_epi32 ( \
+ a, _MM_SHUFFLE (1, 0, 3, 2)), a), xmm_wh); \
+ } \
+ else \
+ { \
+ /* calculate horizontal weights */ \
+ xmm_wh = _mm_add_epi16 (xmm_addc8, _mm_xor_si128 (xmm_xorc8, \
+ _mm_srli_epi16 (xmm_x, 16 - BILINEAR_INTERPOLATION_BITS))); \
+ xmm_x = _mm_add_epi16 (xmm_x, xmm_ux); \
+ /* horizontal interpolation */ \
+ xmm_lo = _mm_mullo_epi16 (a, xmm_wh); \
+ xmm_hi = _mm_mulhi_epu16 (a, xmm_wh); \
+ a = _mm_add_epi32 (_mm_unpacklo_epi16 (xmm_lo, xmm_hi), \
+ _mm_unpackhi_epi16 (xmm_lo, xmm_hi)); \
+ } \
+ /* shift and pack the result */ \
+ a = _mm_srli_epi32 (a, BILINEAR_INTERPOLATION_BITS * 2); \
+ a = _mm_packs_epi32 (a, a); \
+ a = _mm_packus_epi16 (a, a); \
+ pix = _mm_cvtsi128_si32 (a); \
+} while (0)
+
+#define BILINEAR_SKIP_ONE_PIXEL() \
+do { \
+ vx += unit_x; \
+ xmm_x = _mm_add_epi16 (xmm_x, xmm_ux); \
+} 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)
+{
+ BILINEAR_DECLARE_VARIABLES;
+ uint32_t pix1, pix2, pix3, pix4;
+
+ while ((w -= 4) >= 0)
+ {
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix1);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix2);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix3);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix4);
+ *dst++ = pix1;
+ *dst++ = pix2;
+ *dst++ = pix3;
+ *dst++ = pix4;
+ }
+
+ 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;
+ }
+
+}
+
+/* Add extra NULL argument to the existing bilinear fast paths to indicate
+ * that we don't need two-pass processing */
+
+FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_cover_SRC,
+ scaled_bilinear_scanline_sse2_8888_8888_SRC, NULL,
+ 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, NULL,
+ 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, NULL,
+ 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, NULL,
+ 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, pix3, pix4;
+
+ while (w && ((uintptr_t)dst & 15))
+ {
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix1);
+ *dst++ = pix1 | 0xFF000000;
+ w--;
+ }
+
+ while ((w -= 4) >= 0) {
+ __m128i xmm_src;
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix1);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix2);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix3);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix4);
+
+ xmm_src = _mm_set_epi32 (pix4, pix3, pix2, pix1);
+ _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, NULL,
+ 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, NULL,
+ 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, NULL,
+ uint32_t, uint32_t, uint32_t,
+ NORMAL, FLAG_NONE)
+#if 0
+FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_pad_OVER,
+ scaled_bilinear_scanline_sse2_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ PAD, FLAG_NONE)
+#endif
+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)
+{
+ BILINEAR_DECLARE_VARIABLES;
+ uint32_t pix1, pix2, pix3, pix4;
+
+ 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_ONE_PIXEL (pix1);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix2);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix3);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix4);
+
+ xmm_src = _mm_set_epi32 (pix4, pix3, pix2, pix1);
+
+ 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, NULL,
+ 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, NULL,
+ 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, NULL,
+ 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, NULL,
+ uint32_t, uint32_t, uint32_t,
+ NORMAL, FLAG_NONE)
+
+
+/* An example of SSE2 two-stage bilinear_over_8888_0565 fast path, which is implemented
+ as scaled_bilinear_scanline_sse2_8888_8888_SRC + op_bilinear_over_8888_0565 */
+
+void op_bilinear_over_8888_0565(uint16_t *dst, const uint32_t *mask, const uint32_t *src, int width)
+{
+ /* Note: this is not really fast and should be based on 8 pixel loop from sse2_composite_over_8888_0565 */
+ while (--width >= 0)
+ {
+ *dst = composite_over_8888_0565pixel (*src, *dst);
+ src++;
+ dst++;
+ }
+}
+
+FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_0565_cover_OVER,
+ scaled_bilinear_scanline_sse2_8888_8888_SRC, op_bilinear_over_8888_0565,
+ uint32_t, uint32_t, uint16_t,
+ COVER, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_0565_pad_OVER,
+ scaled_bilinear_scanline_sse2_8888_8888_SRC, op_bilinear_over_8888_0565,
+ uint32_t, uint32_t, uint16_t,
+ PAD, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_0565_none_OVER,
+ scaled_bilinear_scanline_sse2_8888_8888_SRC, op_bilinear_over_8888_0565,
+ uint32_t, uint32_t, uint16_t,
+ NONE, FLAG_NONE)
+FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_0565_normal_OVER,
+ scaled_bilinear_scanline_sse2_8888_8888_SRC, op_bilinear_over_8888_0565,
+ uint32_t, uint32_t, uint16_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)
+{
+ BILINEAR_DECLARE_VARIABLES;
+ uint32_t pix1, pix2, pix3, pix4;
+ uint32_t m;
+
+ while (w && ((uintptr_t)dst & 15))
+ {
+ uint32_t sa;
+
+ m = (uint32_t) *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)
+ {
+ __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;
+
+ m = *(uint32_t*)mask;
+
+ if (m)
+ {
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix1);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix2);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix3);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix4);
+
+ xmm_src = _mm_set_epi32 (pix4, pix3, pix2, pix1);
+
+ 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_ONE_PIXEL ();
+ BILINEAR_SKIP_ONE_PIXEL ();
+ BILINEAR_SKIP_ONE_PIXEL ();
+ BILINEAR_SKIP_ONE_PIXEL ();
+ }
+
+ w -= 4;
+ dst += 4;
+ mask += 4;
+ }
+
+ while (w)
+ {
+ uint32_t sa;
+
+ m = (uint32_t) *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, NULL,
+ 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, NULL,
+ 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, NULL,
+ 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, NULL,
+ 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)
+{
+ BILINEAR_DECLARE_VARIABLES;
+ uint32_t pix1, pix2, pix3, pix4;
+ __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)
+ {
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix1);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix2);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix3);
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix4);
+
+ if (pix1 | pix2 | pix3 | pix4)
+ {
+ __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;
+
+ xmm_src = _mm_set_epi32 (pix4, pix3, pix2, pix1);
+
+ 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, NULL,
+ 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, NULL,
+ 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, NULL,
+ 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, NULL,
+ 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_COVER (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_COVER (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_COVER (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_COVER (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_NONE (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_NONE (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_NONE (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_NONE (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_PAD (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_PAD (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_PAD (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_PAD (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_NORMAL (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_NORMAL (OVER, a8b8g8r8, x8b8g8r8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_NORMAL (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888),
+ SIMPLE_NEAREST_FAST_PATH_NORMAL (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_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_n_8888),
+ SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_n_8888),
+ SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (OVER, a8r8g8b8, x8r8g8b8, sse2_8888_n_8888),
+ SIMPLE_NEAREST_SOLID_MASK_FAST_PATH_NORMAL (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),
+
+ /* and here the needed entries are added to the fast path table */
+
+ SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, r5g6b5, sse2_8888_0565),
+ SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, b5g6r5, sse2_8888_0565),
+
+ { 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++ = *(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++ = *(src++) << 24;
+ w--;
+ }
+
+ return iter->buffer;
+}
+
+typedef struct
+{
+ pixman_format_code_t format;
+ pixman_iter_get_scanline_t get_scanline;
+} fetcher_info_t;
+
+static const fetcher_info_t fetchers[] =
+{
+ { PIXMAN_x8r8g8b8, sse2_fetch_x8r8g8b8 },
+ { PIXMAN_r5g6b5, sse2_fetch_r5g6b5 },
+ { PIXMAN_a8, sse2_fetch_a8 },
+ { PIXMAN_null }
+};
+
+static pixman_bool_t
+sse2_src_iter_init (pixman_implementation_t *imp, pixman_iter_t *iter)
+{
+ pixman_image_t *image = iter->image;
+
+#define FLAGS \
+ (FAST_PATH_STANDARD_FLAGS | FAST_PATH_ID_TRANSFORM | \
+ FAST_PATH_BITS_IMAGE | FAST_PATH_SAMPLES_COVER_CLIP_NEAREST)
+
+ if ((iter->iter_flags & ITER_NARROW) &&
+ (iter->image_flags & FLAGS) == FLAGS)
+ {
+ const fetcher_info_t *f;
+
+ for (f = &fetchers[0]; f->format != PIXMAN_null; f++)
+ {
+ if (image->common.extended_format_code == f->format)
+ {
+ 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 (f->format) / 8;
+ iter->stride = s;
+
+ iter->get_scanline = f->get_scanline;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+#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->src_iter_init = sse2_src_iter_init;
+
+ 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 000000000..f5ae18e89
--- /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 <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#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 000000000..91766fdbf
--- /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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#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) == 0x8000)
+ {
+ 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 000000000..b2ffb8ca2
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-utils.c
@@ -0,0 +1,315 @@
+/*
+ * 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 <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#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 (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)
+ {
+ uint8_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;
+}
+
+#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 ();
+}
+
+#ifdef DEBUG
+
+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++;
+ }
+}
+
+#endif
diff --git a/gfx/cairo/libpixman/src/pixman-version.h b/gfx/cairo/libpixman/src/pixman-version.h
new file mode 100644
index 000000000..fac4225b0
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-version.h
@@ -0,0 +1,50 @@
+/*
+ * 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 <cworth@cworth.org>
+ */
+
+#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 27
+#define PIXMAN_VERSION_MICRO 1
+
+#define PIXMAN_VERSION_STRING "0.27.1"
+
+#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)
+
+#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 000000000..6868704a8
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-vmx.c
@@ -0,0 +1,1647 @@
+/*
+ * 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
+ */
+
+#include <config.h>
+#include "pixman-private.h"
+#include "pixman-combine32.h"
+#include <altivec.h>
+
+#define AVV(x...) {x}
+
+static force_inline vector unsigned int
+splat_alpha (vector unsigned int pix)
+{
+ 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));
+}
+
+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)
+ vec_mergeh ((vector unsigned char)AVV (0),
+ (vector unsigned char)p);
+
+ mod = (vector unsigned short)
+ vec_mergeh ((vector unsigned char)AVV (0),
+ (vector unsigned char)a);
+
+ 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)
+ vec_mergel ((vector unsigned char)AVV (0),
+ (vector unsigned char)p);
+ mod = (vector unsigned short)
+ vec_mergel ((vector unsigned char)AVV (0),
+ (vector unsigned char)a);
+
+ 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)
+
+
+#define COMPUTE_SHIFT_MASK(source) \
+ source ## _mask = vec_lvsl (0, source);
+
+#define COMPUTE_SHIFT_MASKS(dest, source) \
+ dest ## _mask = vec_lvsl (0, dest); \
+ source ## _mask = vec_lvsl (0, source); \
+ store_mask = vec_lvsr (0, dest);
+
+#define COMPUTE_SHIFT_MASKC(dest, source, mask) \
+ mask ## _mask = vec_lvsl (0, mask); \
+ dest ## _mask = vec_lvsl (0, dest); \
+ source ## _mask = vec_lvsl (0, source); \
+ store_mask = vec_lvsr (0, dest);
+
+/* notice you have to declare temp vars...
+ * Note: tmp3 and tmp4 must remain untouched!
+ */
+
+#define LOAD_VECTORS(dest, source) \
+ tmp1 = (typeof(tmp1))vec_ld (0, source); \
+ tmp2 = (typeof(tmp2))vec_ld (15, source); \
+ tmp3 = (typeof(tmp3))vec_ld (0, dest); \
+ v ## source = (typeof(v ## source)) \
+ vec_perm (tmp1, tmp2, source ## _mask); \
+ tmp4 = (typeof(tmp4))vec_ld (15, dest); \
+ v ## dest = (typeof(v ## dest)) \
+ vec_perm (tmp3, tmp4, dest ## _mask);
+
+#define LOAD_VECTORSC(dest, source, mask) \
+ tmp1 = (typeof(tmp1))vec_ld (0, source); \
+ tmp2 = (typeof(tmp2))vec_ld (15, source); \
+ tmp3 = (typeof(tmp3))vec_ld (0, dest); \
+ v ## source = (typeof(v ## source)) \
+ vec_perm (tmp1, tmp2, source ## _mask); \
+ tmp4 = (typeof(tmp4))vec_ld (15, dest); \
+ tmp1 = (typeof(tmp1))vec_ld (0, mask); \
+ v ## dest = (typeof(v ## dest)) \
+ vec_perm (tmp3, tmp4, dest ## _mask); \
+ tmp2 = (typeof(tmp2))vec_ld (15, mask); \
+ v ## mask = (typeof(v ## mask)) \
+ vec_perm (tmp1, tmp2, mask ## _mask);
+
+#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) \
+ edges = vec_perm (tmp4, tmp3, dest ## _mask); \
+ tmp3 = vec_perm ((vector unsigned char)v ## dest, edges, store_mask); \
+ tmp1 = vec_perm (edges, (vector unsigned char)v ## dest, store_mask); \
+ vec_st ((vector unsigned int) tmp3, 15, dest); \
+ vec_st ((vector unsigned int) tmp1, 0, dest);
+
+static void
+vmx_combine_over_u_no_mask (uint32_t * dest,
+ const uint32_t *src,
+ int width)
+{
+ int i;
+ vector unsigned int vdest, vsrc;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, src_mask, mask_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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;
+ vector unsigned char tmp1, tmp2, tmp3, tmp4, edges,
+ dest_mask, mask_mask, src_mask, store_mask;
+
+ 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 const pixman_fast_path_t vmx_fast_paths[] =
+{
+ { PIXMAN_OP_NONE },
+};
+
+pixman_implementation_t *
+_pixman_implementation_create_vmx (pixman_implementation_t *fallback)
+{
+ pixman_implementation_t *imp = _pixman_implementation_create (fallback, vmx_fast_paths);
+
+ /* 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;
+
+ return imp;
+}
diff --git a/gfx/cairo/libpixman/src/pixman-x64-mmx-emulation.h b/gfx/cairo/libpixman/src/pixman-x64-mmx-emulation.h
new file mode 100644
index 000000000..378019cf2
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-x64-mmx-emulation.h
@@ -0,0 +1,263 @@
+#ifndef MMX_X64_H_INCLUDED
+#define MMX_X64_H_INCLUDED
+
+/* Implementation of x64 MMX substitition functions, before
+ * pixman is reimplemented not to use __m64 type on Visual C++
+ *
+ * Copyright (C)2009 by George Yohng
+ * Released in public domain.
+ */
+
+#include <intrin.h>
+
+#define M64C(a) (*(const __m64 *)(&a))
+#define M64U(a) (*(const unsigned long long *)(&a))
+
+__inline __m64
+_m_from_int (int a)
+{
+ long long i64 = a;
+
+ return M64C (i64);
+}
+
+__inline __m64
+_mm_setzero_si64 ()
+{
+ long long i64 = 0;
+
+ return M64C (i64);
+}
+
+__inline __m64
+_mm_set_pi32 (int i1, int i0)
+{
+ unsigned long long i64 = ((unsigned)i0) + (((unsigned long long)(unsigned)i1) << 32);
+
+ return M64C (i64);
+}
+
+__inline void
+_m_empty ()
+{
+}
+
+__inline __m64
+_mm_set1_pi16 (short w)
+{
+ unsigned long long i64 = ((unsigned long long)(unsigned short)(w)) * 0x0001000100010001ULL;
+
+ return M64C (i64);
+}
+
+__inline int
+_m_to_int (__m64 m)
+{
+ return m.m64_i32[0];
+}
+
+__inline __m64
+_mm_movepi64_pi64 (__m128i a)
+{
+ return M64C (a.m128i_i64[0]);
+}
+
+__inline __m64
+_m_pand (__m64 a, __m64 b)
+{
+ unsigned long long i64 = M64U (a) & M64U (b);
+
+ return M64C (i64);
+}
+
+__inline __m64
+_m_por (__m64 a, __m64 b)
+{
+ unsigned long long i64 = M64U (a) | M64U (b);
+
+ return M64C (i64);
+}
+
+__inline __m64
+_m_pxor (__m64 a, __m64 b)
+{
+ unsigned long long i64 = M64U (a) ^ M64U (b);
+
+ return M64C (i64);
+}
+
+__inline __m64
+_m_pmulhuw (__m64 a, __m64 b) /* unoptimized */
+{
+ unsigned short d[4] =
+ {
+ (unsigned short)((((unsigned)a.m64_u16[0]) * b.m64_u16[0]) >> 16),
+ (unsigned short)((((unsigned)a.m64_u16[1]) * b.m64_u16[1]) >> 16),
+ (unsigned short)((((unsigned)a.m64_u16[2]) * b.m64_u16[2]) >> 16),
+ (unsigned short)((((unsigned)a.m64_u16[3]) * b.m64_u16[3]) >> 16)
+ };
+
+ return M64C (d[0]);
+}
+
+__inline __m64
+_m_pmullw2 (__m64 a, __m64 b) /* unoptimized */
+{
+ unsigned short d[4] =
+ {
+ (unsigned short)((((unsigned)a.m64_u16[0]) * b.m64_u16[0])),
+ (unsigned short)((((unsigned)a.m64_u16[1]) * b.m64_u16[1])),
+ (unsigned short)((((unsigned)a.m64_u16[2]) * b.m64_u16[2])),
+ (unsigned short)((((unsigned)a.m64_u16[3]) * b.m64_u16[3]))
+ };
+
+ return M64C (d[0]);
+}
+
+__inline __m64
+_m_pmullw (__m64 a, __m64 b) /* unoptimized */
+{
+ unsigned long long x =
+ ((unsigned long long)(unsigned short)((((unsigned)a.m64_u16[0]) * b.m64_u16[0]))) +
+ (((unsigned long long)(unsigned short)((((unsigned)a.m64_u16[1]) * b.m64_u16[1]))) << 16) +
+ (((unsigned long long)(unsigned short)((((unsigned)a.m64_u16[2]) * b.m64_u16[2]))) << 32) +
+ (((unsigned long long)(unsigned short)((((unsigned)a.m64_u16[3]) * b.m64_u16[3]))) << 48);
+
+ return M64C (x);
+}
+
+__inline __m64
+_m_paddusb (__m64 a, __m64 b) /* unoptimized */
+{
+ unsigned long long x = (M64U (a) & 0x00FF00FF00FF00FFULL) +
+ (M64U (b) & 0x00FF00FF00FF00FFULL);
+
+ unsigned long long y = ((M64U (a) >> 8) & 0x00FF00FF00FF00FFULL) +
+ ((M64U (b) >> 8) & 0x00FF00FF00FF00FFULL);
+
+ x |= ((x & 0xFF00FF00FF00FF00ULL) >> 8) * 0xFF;
+ y |= ((y & 0xFF00FF00FF00FF00ULL) >> 8) * 0xFF;
+
+ x = (x & 0x00FF00FF00FF00FFULL) | ((y & 0x00FF00FF00FF00FFULL) << 8);
+
+ return M64C (x);
+}
+
+__inline __m64
+_m_paddusw (__m64 a, __m64 b) /* unoptimized */
+{
+ unsigned long long x = (M64U (a) & 0x0000FFFF0000FFFFULL) +
+ (M64U (b) & 0x0000FFFF0000FFFFULL);
+
+ unsigned long long y = ((M64U (a) >> 16) & 0x0000FFFF0000FFFFULL) +
+ ((M64U (b) >> 16) & 0x0000FFFF0000FFFFULL);
+
+ x |= ((x & 0xFFFF0000FFFF0000) >> 16) * 0xFFFF;
+ y |= ((y & 0xFFFF0000FFFF0000) >> 16) * 0xFFFF;
+
+ x = (x & 0x0000FFFF0000FFFFULL) | ((y & 0x0000FFFF0000FFFFULL) << 16);
+
+ return M64C (x);
+}
+
+__inline __m64
+_m_pshufw (__m64 a, int n) /* unoptimized */
+{
+ unsigned short d[4] =
+ {
+ a.m64_u16[n & 3],
+ a.m64_u16[(n >> 2) & 3],
+ a.m64_u16[(n >> 4) & 3],
+ a.m64_u16[(n >> 6) & 3]
+ };
+
+ return M64C (d[0]);
+}
+
+__inline unsigned char
+sat16 (unsigned short d)
+{
+ if (d > 0xFF) return 0xFF;
+ else return d & 0xFF;
+}
+
+__inline __m64
+_m_packuswb (__m64 m1, __m64 m2) /* unoptimized */
+{
+ unsigned char d[8] =
+ {
+ sat16 (m1.m64_u16[0]),
+ sat16 (m1.m64_u16[1]),
+ sat16 (m1.m64_u16[2]),
+ sat16 (m1.m64_u16[3]),
+ sat16 (m2.m64_u16[0]),
+ sat16 (m2.m64_u16[1]),
+ sat16 (m2.m64_u16[2]),
+ sat16 (m2.m64_u16[3])
+ };
+
+ return M64C (d[0]);
+}
+
+__inline __m64 _m_punpcklbw (__m64 m1, __m64 m2) /* unoptimized */
+{
+ unsigned char d[8] =
+ {
+ m1.m64_u8[0],
+ m2.m64_u8[0],
+ m1.m64_u8[1],
+ m2.m64_u8[1],
+ m1.m64_u8[2],
+ m2.m64_u8[2],
+ m1.m64_u8[3],
+ m2.m64_u8[3],
+ };
+
+ return M64C (d[0]);
+}
+
+__inline __m64 _m_punpckhbw (__m64 m1, __m64 m2) /* unoptimized */
+{
+ unsigned char d[8] =
+ {
+ m1.m64_u8[4],
+ m2.m64_u8[4],
+ m1.m64_u8[5],
+ m2.m64_u8[5],
+ m1.m64_u8[6],
+ m2.m64_u8[6],
+ m1.m64_u8[7],
+ m2.m64_u8[7],
+ };
+
+ return M64C (d[0]);
+}
+
+__inline __m64 _m_psrlwi (__m64 a, int n) /* unoptimized */
+{
+ unsigned short d[4] =
+ {
+ a.m64_u16[0] >> n,
+ a.m64_u16[1] >> n,
+ a.m64_u16[2] >> n,
+ a.m64_u16[3] >> n
+ };
+
+ return M64C (d[0]);
+}
+
+__inline __m64 _m_psrlqi (__m64 m, int n)
+{
+ unsigned long long x = M64U (m) >> n;
+
+ return M64C (x);
+}
+
+__inline __m64 _m_psllqi (__m64 m, int n)
+{
+ unsigned long long x = M64U (m) << n;
+
+ return M64C (x);
+}
+
+#endif /* MMX_X64_H_INCLUDED */
diff --git a/gfx/cairo/libpixman/src/pixman-x86.c b/gfx/cairo/libpixman/src/pixman-x86.c
new file mode 100644
index 000000000..feea23e79
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-x86.c
@@ -0,0 +1,241 @@
+/*
+ * 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 <config.h>
+#endif
+
+#include "pixman-private.h"
+
+#if defined(USE_X86_MMX) || defined (USE_SSE2)
+
+/* 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)
+} cpu_features_t;
+
+#ifdef HAVE_GETISAX
+
+#include <sys/auxv.h>
+
+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;
+ }
+
+ 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 <intrin.h> /* 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;
+
+ /* 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, "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)
+
+#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
+
+ return imp;
+}
diff --git a/gfx/cairo/libpixman/src/pixman.c b/gfx/cairo/libpixman/src/pixman.c
new file mode 100644
index 000000000..184f0c4e6
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman.c
@@ -0,0 +1,1135 @@
+/* -*- 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 <config.h>
+#endif
+#include "pixman-private.h"
+
+#include <stdlib.h>
+
+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
+{
+ pixman_fixed_48_16_t x1;
+ pixman_fixed_48_16_t y1;
+ pixman_fixed_48_16_t x2;
+ pixman_fixed_48_16_t y2;
+} box_48_16_t;
+
+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;
+
+ /* Expand the source area by a tiny bit so account of different rounding that
+ * may happen during sampling. Note that (8 * pixman_fixed_e) is very far from
+ * 0.5 so this won't cause the area computed to be overly pessimistic.
+ */
+ transformed.x1 -= 8 * pixman_fixed_e;
+ transformed.y1 -= 8 * pixman_fixed_e;
+ transformed.x2 += 8 * pixman_fixed_e;
+ transformed.y2 += 8 * pixman_fixed_e;
+
+ if (image->common.type == BITS)
+ {
+ if (pixman_fixed_to_int (transformed.x1) >= 0 &&
+ pixman_fixed_to_int (transformed.y1) >= 0 &&
+ pixman_fixed_to_int (transformed.x2) < image->bits.width &&
+ pixman_fixed_to_int (transformed.y2) < 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;
+ }
+
+ 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 (&region);
+
+ if (!_pixman_compute_composite_region32 (
+ &region, src, mask, dest,
+ src_x, src_y, mask_x, mask_y, dest_x, dest_y, width, height))
+ {
+ goto out;
+ }
+
+ extents = *pixman_region32_extents (&region);
+
+ 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 (&region, &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 (&region);
+}
+
+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 (!(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:
+ *
+ * <informalexample><programlisting>
+ * if (pixman_version() >= PIXMAN_VERSION_ENCODE(X,Y,Z)) {...}
+ * </programlisting></informalexample>
+ *
+ * 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 000000000..1cbf62e21
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman.h
@@ -0,0 +1,1116 @@
+/***********************************************************
+
+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 <pixman-version.h>
+
+#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 <inttypes.h>
+/* 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 <sys/inttypes.h>
+#else
+# include <stdint.h>
+#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) ((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;
+
+void pixman_transform_init_identity (struct pixman_transform *matrix);
+pixman_bool_t pixman_transform_point_3d (const struct pixman_transform *transform,
+ struct pixman_vector *vector);
+pixman_bool_t pixman_transform_point (const struct pixman_transform *transform,
+ struct pixman_vector *vector);
+pixman_bool_t pixman_transform_multiply (struct pixman_transform *dst,
+ const struct pixman_transform *l,
+ const struct pixman_transform *r);
+void pixman_transform_init_scale (struct pixman_transform *t,
+ pixman_fixed_t sx,
+ pixman_fixed_t sy);
+pixman_bool_t pixman_transform_scale (struct pixman_transform *forward,
+ struct pixman_transform *reverse,
+ pixman_fixed_t sx,
+ pixman_fixed_t sy);
+void pixman_transform_init_rotate (struct pixman_transform *t,
+ pixman_fixed_t cos,
+ pixman_fixed_t sin);
+pixman_bool_t pixman_transform_rotate (struct pixman_transform *forward,
+ struct pixman_transform *reverse,
+ pixman_fixed_t c,
+ pixman_fixed_t s);
+void pixman_transform_init_translate (struct pixman_transform *t,
+ pixman_fixed_t tx,
+ pixman_fixed_t ty);
+pixman_bool_t pixman_transform_translate (struct pixman_transform *forward,
+ struct pixman_transform *reverse,
+ pixman_fixed_t tx,
+ pixman_fixed_t ty);
+pixman_bool_t pixman_transform_bounds (const struct pixman_transform *matrix,
+ struct pixman_box16 *b);
+pixman_bool_t pixman_transform_invert (struct pixman_transform *dst,
+ const struct pixman_transform *src);
+pixman_bool_t pixman_transform_is_identity (const struct pixman_transform *t);
+pixman_bool_t pixman_transform_is_scale (const struct pixman_transform *t);
+pixman_bool_t pixman_transform_is_int_translate (const struct pixman_transform *t);
+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_bool_t pixman_transform_from_pixman_f_transform (struct pixman_transform *t,
+ const struct pixman_f_transform *ft);
+void pixman_f_transform_from_pixman_transform (struct pixman_f_transform *ft,
+ const struct pixman_transform *t);
+pixman_bool_t pixman_f_transform_invert (struct pixman_f_transform *dst,
+ const struct pixman_f_transform *src);
+pixman_bool_t pixman_f_transform_point (const struct pixman_f_transform *t,
+ struct pixman_f_vector *v);
+void pixman_f_transform_point_3d (const struct pixman_f_transform *t,
+ struct pixman_f_vector *v);
+void pixman_f_transform_multiply (struct pixman_f_transform *dst,
+ const struct pixman_f_transform *l,
+ const struct pixman_f_transform *r);
+void pixman_f_transform_init_scale (struct pixman_f_transform *t,
+ double sx,
+ double sy);
+pixman_bool_t pixman_f_transform_scale (struct pixman_f_transform *forward,
+ struct pixman_f_transform *reverse,
+ double sx,
+ double sy);
+void pixman_f_transform_init_rotate (struct pixman_f_transform *t,
+ double cos,
+ double sin);
+pixman_bool_t pixman_f_transform_rotate (struct pixman_f_transform *forward,
+ struct pixman_f_transform *reverse,
+ double c,
+ double s);
+void pixman_f_transform_init_translate (struct pixman_f_transform *t,
+ double tx,
+ double ty);
+pixman_bool_t pixman_f_transform_translate (struct pixman_f_transform *forward,
+ struct pixman_f_transform *reverse,
+ double tx,
+ double ty);
+pixman_bool_t pixman_f_transform_bounds (const struct pixman_f_transform *t,
+ struct pixman_box16 *b);
+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_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.
+ */
+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 */
+void pixman_region_init (pixman_region16_t *region);
+void pixman_region_init_rect (pixman_region16_t *region,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height);
+pixman_bool_t pixman_region_init_rects (pixman_region16_t *region,
+ const pixman_box16_t *boxes,
+ int count);
+void pixman_region_init_with_extents (pixman_region16_t *region,
+ pixman_box16_t *extents);
+void pixman_region_init_from_image (pixman_region16_t *region,
+ pixman_image_t *image);
+void pixman_region_fini (pixman_region16_t *region);
+
+
+/* manipulation */
+void pixman_region_translate (pixman_region16_t *region,
+ int x,
+ int y);
+pixman_bool_t pixman_region_copy (pixman_region16_t *dest,
+ pixman_region16_t *source);
+pixman_bool_t pixman_region_intersect (pixman_region16_t *new_reg,
+ pixman_region16_t *reg1,
+ pixman_region16_t *reg2);
+pixman_bool_t pixman_region_union (pixman_region16_t *new_reg,
+ pixman_region16_t *reg1,
+ pixman_region16_t *reg2);
+pixman_bool_t pixman_region_union_rect (pixman_region16_t *dest,
+ pixman_region16_t *source,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height);
+pixman_bool_t pixman_region_intersect_rect (pixman_region16_t *dest,
+ pixman_region16_t *source,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height);
+pixman_bool_t pixman_region_subtract (pixman_region16_t *reg_d,
+ pixman_region16_t *reg_m,
+ pixman_region16_t *reg_s);
+pixman_bool_t pixman_region_inverse (pixman_region16_t *new_reg,
+ pixman_region16_t *reg1,
+ pixman_box16_t *inv_rect);
+pixman_bool_t pixman_region_contains_point (pixman_region16_t *region,
+ int x,
+ int y,
+ pixman_box16_t *box);
+pixman_region_overlap_t pixman_region_contains_rectangle (pixman_region16_t *region,
+ pixman_box16_t *prect);
+pixman_bool_t pixman_region_not_empty (pixman_region16_t *region);
+pixman_box16_t * pixman_region_extents (pixman_region16_t *region);
+int pixman_region_n_rects (pixman_region16_t *region);
+pixman_box16_t * pixman_region_rectangles (pixman_region16_t *region,
+ int *n_rects);
+pixman_bool_t pixman_region_equal (pixman_region16_t *region1,
+ pixman_region16_t *region2);
+pixman_bool_t pixman_region_selfcheck (pixman_region16_t *region);
+void pixman_region_reset (pixman_region16_t *region,
+ pixman_box16_t *box);
+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 */
+void pixman_region32_init (pixman_region32_t *region);
+void pixman_region32_init_rect (pixman_region32_t *region,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height);
+pixman_bool_t pixman_region32_init_rects (pixman_region32_t *region,
+ const pixman_box32_t *boxes,
+ int count);
+void pixman_region32_init_with_extents (pixman_region32_t *region,
+ pixman_box32_t *extents);
+void pixman_region32_init_from_image (pixman_region32_t *region,
+ pixman_image_t *image);
+void pixman_region32_fini (pixman_region32_t *region);
+
+
+/* manipulation */
+void pixman_region32_translate (pixman_region32_t *region,
+ int x,
+ int y);
+pixman_bool_t pixman_region32_copy (pixman_region32_t *dest,
+ pixman_region32_t *source);
+pixman_bool_t pixman_region32_intersect (pixman_region32_t *new_reg,
+ pixman_region32_t *reg1,
+ pixman_region32_t *reg2);
+pixman_bool_t pixman_region32_union (pixman_region32_t *new_reg,
+ pixman_region32_t *reg1,
+ pixman_region32_t *reg2);
+pixman_bool_t pixman_region32_intersect_rect (pixman_region32_t *dest,
+ pixman_region32_t *source,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height);
+pixman_bool_t pixman_region32_union_rect (pixman_region32_t *dest,
+ pixman_region32_t *source,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height);
+pixman_bool_t pixman_region32_subtract (pixman_region32_t *reg_d,
+ pixman_region32_t *reg_m,
+ pixman_region32_t *reg_s);
+pixman_bool_t pixman_region32_inverse (pixman_region32_t *new_reg,
+ pixman_region32_t *reg1,
+ pixman_box32_t *inv_rect);
+pixman_bool_t pixman_region32_contains_point (pixman_region32_t *region,
+ int x,
+ int y,
+ pixman_box32_t *box);
+pixman_region_overlap_t pixman_region32_contains_rectangle (pixman_region32_t *region,
+ pixman_box32_t *prect);
+pixman_bool_t pixman_region32_not_empty (pixman_region32_t *region);
+pixman_box32_t * pixman_region32_extents (pixman_region32_t *region);
+int pixman_region32_n_rects (pixman_region32_t *region);
+pixman_box32_t * pixman_region32_rectangles (pixman_region32_t *region,
+ int *n_rects);
+pixman_bool_t pixman_region32_equal (pixman_region32_t *region1,
+ pixman_region32_t *region2);
+pixman_bool_t pixman_region32_selfcheck (pixman_region32_t *region);
+void pixman_region32_reset (pixman_region32_t *region,
+ pixman_box32_t *box);
+void pixman_region32_clear (pixman_region32_t *region);
+
+
+/* Copy / Fill / Misc */
+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_bool_t pixman_fill (uint32_t *bits,
+ int stride,
+ int bpp,
+ int x,
+ int y,
+ int width,
+ int height,
+ uint32_t _xor);
+
+int pixman_version (void);
+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_BPP(f) (((f) >> 24) )
+#define PIXMAN_FORMAT_TYPE(f) (((f) >> 16) & 0xff)
+#define PIXMAN_FORMAT_A(f) (((f) >> 12) & 0x0f)
+#define PIXMAN_FORMAT_R(f) (((f) >> 8) & 0x0f)
+#define PIXMAN_FORMAT_G(f) (((f) >> 4) & 0x0f)
+#define PIXMAN_FORMAT_B(f) (((f) ) & 0x0f)
+#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_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)
+
+/* 32bpp formats */
+typedef enum {
+ 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_bool_t pixman_format_supported_destination (pixman_format_code_t format);
+pixman_bool_t pixman_format_supported_source (pixman_format_code_t format);
+
+/* Constructors */
+pixman_image_t *pixman_image_create_solid_fill (const pixman_color_t *color);
+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 *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 *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 *pixman_image_create_bits (pixman_format_code_t format,
+ int width,
+ int height,
+ uint32_t *bits,
+ int rowstride_bytes);
+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_image_t *pixman_image_ref (pixman_image_t *image);
+pixman_bool_t pixman_image_unref (pixman_image_t *image);
+
+void pixman_image_set_destroy_function (pixman_image_t *image,
+ pixman_image_destroy_func_t function,
+ void *data);
+void * pixman_image_get_destroy_data (pixman_image_t *image);
+
+/* Set properties */
+pixman_bool_t pixman_image_set_clip_region (pixman_image_t *image,
+ pixman_region16_t *region);
+pixman_bool_t pixman_image_set_clip_region32 (pixman_image_t *image,
+ pixman_region32_t *region);
+void pixman_image_set_has_client_clip (pixman_image_t *image,
+ pixman_bool_t clien_clip);
+pixman_bool_t pixman_image_set_transform (pixman_image_t *image,
+ const pixman_transform_t *transform);
+void pixman_image_set_repeat (pixman_image_t *image,
+ pixman_repeat_t repeat);
+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);
+void pixman_image_set_source_clipping (pixman_image_t *image,
+ pixman_bool_t source_clipping);
+void pixman_image_set_alpha_map (pixman_image_t *image,
+ pixman_image_t *alpha_map,
+ int16_t x,
+ int16_t y);
+void pixman_image_set_component_alpha (pixman_image_t *image,
+ pixman_bool_t component_alpha);
+pixman_bool_t pixman_image_get_component_alpha (pixman_image_t *image);
+void pixman_image_set_accessors (pixman_image_t *image,
+ pixman_read_memory_func_t read_func,
+ pixman_write_memory_func_t write_func);
+void pixman_image_set_indexed (pixman_image_t *image,
+ const pixman_indexed_t *indexed);
+uint32_t *pixman_image_get_data (pixman_image_t *image);
+int pixman_image_get_width (pixman_image_t *image);
+int pixman_image_get_height (pixman_image_t *image);
+int pixman_image_get_stride (pixman_image_t *image); /* in bytes */
+int pixman_image_get_depth (pixman_image_t *image);
+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_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_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_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_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);
+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);
+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.
+ */
+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_glyph_cache_t *pixman_glyph_cache_create (void);
+void pixman_glyph_cache_destroy (pixman_glyph_cache_t *cache);
+void pixman_glyph_cache_freeze (pixman_glyph_cache_t *cache);
+void pixman_glyph_cache_thaw (pixman_glyph_cache_t *cache);
+const void * pixman_glyph_cache_lookup (pixman_glyph_cache_t *cache,
+ void *font_key,
+ void *glyph_key);
+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);
+void pixman_glyph_cache_remove (pixman_glyph_cache_t *cache,
+ void *font_key,
+ void *glyph_key);
+void pixman_glyph_get_extents (pixman_glyph_cache_t *cache,
+ int n_glyphs,
+ pixman_glyph_t *glyphs,
+ pixman_box32_t *extents);
+pixman_format_code_t pixman_glyph_get_mask_format (pixman_glyph_cache_t *cache,
+ int n_glyphs,
+ const pixman_glyph_t *glyphs);
+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);
+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 && \
+ (int) ((t)->bottom - (t)->top) > 0)
+
+struct pixman_span_fix
+{
+ pixman_fixed_t l, r, y;
+};
+
+struct pixman_trap
+{
+ pixman_span_fix_t top, bot;
+};
+
+pixman_fixed_t pixman_sample_ceil_y (pixman_fixed_t y,
+ int bpp);
+pixman_fixed_t pixman_sample_floor_y (pixman_fixed_t y,
+ int bpp);
+void pixman_edge_step (pixman_edge_t *e,
+ int n);
+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);
+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);
+void pixman_rasterize_edges (pixman_image_t *image,
+ pixman_edge_t *l,
+ pixman_edge_t *r,
+ pixman_fixed_t t,
+ pixman_fixed_t b);
+void pixman_add_traps (pixman_image_t *image,
+ int16_t x_off,
+ int16_t y_off,
+ int ntrap,
+ const pixman_trap_t *traps);
+void pixman_add_trapezoids (pixman_image_t *image,
+ int16_t x_off,
+ int y_off,
+ int ntraps,
+ const pixman_trapezoid_t *traps);
+void pixman_rasterize_trapezoid (pixman_image_t *image,
+ const pixman_trapezoid_t *trap,
+ int x_off,
+ int y_off);
+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);
+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);
+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/refactor b/gfx/cairo/libpixman/src/refactor
new file mode 100644
index 000000000..52fceab17
--- /dev/null
+++ b/gfx/cairo/libpixman/src/refactor
@@ -0,0 +1,478 @@
+Roadmap
+
+- Move all the fetchers etc. into pixman-image to make pixman-compose.c
+ less intimidating.
+
+ DONE
+
+- Make combiners for unified alpha take a mask argument. That way
+ we won't need two separate paths for unified vs component in the
+ general compositing code.
+
+ DONE, except that the Altivec code needs to be updated. Luca is
+ looking into that.
+
+- Delete separate 'unified alpha' path
+
+ DONE
+
+- Split images into their own files
+
+ DONE
+
+- Split the gradient walker code out into its own file
+
+ DONE
+
+- Add scanline getters per image
+
+ DONE
+
+- Generic 64 bit fetcher
+
+ DONE
+
+- Split fast path tables into their respective architecture dependent
+ files.
+
+See "Render Algorithm" below for rationale
+
+Images will eventually have these virtual functions:
+
+ get_scanline()
+ get_scanline_wide()
+ get_pixel()
+ get_pixel_wide()
+ get_untransformed_pixel()
+ get_untransformed_pixel_wide()
+ get_unfiltered_pixel()
+ get_unfiltered_pixel_wide()
+
+ store_scanline()
+ store_scanline_wide()
+
+1.
+
+Initially we will just have get_scanline() and get_scanline_wide();
+these will be based on the ones in pixman-compose. Hopefully this will
+reduce the complexity in pixman_composite_rect_general().
+
+Note that there is access considerations - the compose function is
+being compiled twice.
+
+
+2.
+
+Split image types into their own source files. Export noop virtual
+reinit() call. Call this whenever a property of the image changes.
+
+
+3.
+
+Split the get_scanline() call into smaller functions that are
+initialized by the reinit() call.
+
+The Render Algorithm:
+ (first repeat, then filter, then transform, then clip)
+
+Starting from a destination pixel (x, y), do
+
+ 1 x = x - xDst + xSrc
+ y = y - yDst + ySrc
+
+ 2 reject pixel that is outside the clip
+
+ This treats clipping as something that happens after
+ transformation, which I think is correct for client clips. For
+ hierarchy clips it is wrong, but who really cares? Without
+ GraphicsExposes hierarchy clips are basically irrelevant. Yes,
+ you could imagine cases where the pixels of a subwindow of a
+ redirected, transformed window should be treated as
+ transparent. I don't really care
+
+ Basically, I think the render spec should say that pixels that
+ are unavailable due to the hierarcy have undefined content,
+ and that GraphicsExposes are not generated. Ie., basically
+ that using non-redirected windows as sources is fail. This is
+ at least consistent with the current implementation and we can
+ update the spec later if someone makes it work.
+
+ The implication for render is that it should stop passing the
+ hierarchy clip to pixman. In pixman, if a souce image has a
+ clip it should be used in computing the composite region and
+ nowhere else, regardless of what "has_client_clip" says. The
+ default should be for there to not be any clip.
+
+ I would really like to get rid of the client clip as well for
+ source images, but unfortunately there is at least one
+ application in the wild that uses them.
+
+ 3 Transform pixel: (x, y) = T(x, y)
+
+ 4 Call p = GetUntransformedPixel (x, y)
+
+ 5 If the image has an alpha map, then
+
+ Call GetUntransformedPixel (x, y) on the alpha map
+
+ add resulting alpha channel to p
+
+ return p
+
+ Where GetUnTransformedPixel is:
+
+ 6 switch (filter)
+ {
+ case NEAREST:
+ return GetUnfilteredPixel (x, y);
+ break;
+
+ case BILINEAR:
+ return GetUnfilteredPixel (...) // 4 times
+ break;
+
+ case CONVOLUTION:
+ return GetUnfilteredPixel (...) // as many times as necessary.
+ break;
+ }
+
+ Where GetUnfilteredPixel (x, y) is
+
+ 7 switch (repeat)
+ {
+ case REPEAT_NORMAL:
+ case REPEAT_PAD:
+ case REPEAT_REFLECT:
+ // adjust x, y as appropriate
+ break;
+
+ case REPEAT_NONE:
+ if (x, y) is outside image bounds
+ return 0;
+ break;
+ }
+
+ return GetRawPixel(x, y)
+
+ Where GetRawPixel (x, y) is
+
+ 8 Compute the pixel in question, depending on image type.
+
+For gradients, repeat has a totally different meaning, so
+UnfilteredPixel() and RawPixel() must be the same function so that
+gradients can do their own repeat algorithm.
+
+So, the GetRawPixel
+
+ for bits must deal with repeats
+ for gradients must deal with repeats (differently)
+ for solids, should ignore repeats.
+
+ for polygons, when we add them, either ignore repeats or do
+ something similar to bits (in which case, we may want an extra
+ layer of indirection to modify the coordinates).
+
+It is then possible to build things like "get scanline" or "get tile" on
+top of this. In the simplest case, just repeatedly calling GetPixel()
+would work, but specialized get_scanline()s or get_tile()s could be
+plugged in for common cases.
+
+By not plugging anything in for images with access functions, we only
+have to compile the pixel functions twice, not the scanline functions.
+
+And we can get rid of fetchers for the bizarre formats that no one
+uses. Such as b2g3r3 etc. r1g2b1? Seriously? It is also worth
+considering a generic format based pixel fetcher for these edge cases.
+
+Since the actual routines depend on the image attributes, the images
+must be notified when those change and update their function pointers
+appropriately. So there should probably be a virtual function called
+(* reinit) or something like that.
+
+There will also be wide fetchers for both pixels and lines. The line
+fetcher will just call the wide pixel fetcher. The wide pixel fetcher
+will just call expand, except for 10 bit formats.
+
+Rendering pipeline:
+
+Drawable:
+ 0. if (picture has alpha map)
+ 0.1. Position alpha map according to the alpha_x/alpha_y
+ 0.2. Where the two drawables intersect, the alpha channel
+ Replace the alpha channel of source with the one
+ from the alpha map. Replacement only takes place
+ in the intersection of the two drawables' geometries.
+ 1. Repeat the drawable according to the repeat attribute
+ 2. Reconstruct a continuous image according to the filter
+ 3. Transform according to the transform attribute
+ 4. Position image such that src_x, src_y is over dst_x, dst_y
+ 5. Sample once per destination pixel
+ 6. Clip. If a pixel is not within the source clip, then no
+ compositing takes place at that pixel. (Ie., it's *not*
+ treated as 0).
+
+ Sampling a drawable:
+
+ - If the channel does not have an alpha channel, the pixels in it
+ are treated as opaque.
+
+ Note on reconstruction:
+
+ - The top left pixel has coordinates (0.5, 0.5) and pixels are
+ spaced 1 apart.
+
+Gradient:
+ 1. Unless gradient type is conical, repeat the underlying (0, 1)
+ gradient according to the repeat attribute
+ 2. Integrate the gradient across the plane according to type.
+ 3. Transform according to transform attribute
+ 4. Position gradient
+ 5. Sample once per destination pixel.
+ 6. Clip
+
+Solid Fill:
+ 1. Repeat has no effect
+ 2. Image is already continuous and defined for the entire plane
+ 3. Transform has no effect
+ 4. Positioning has no effect
+ 5. Sample once per destination pixel.
+ 6. Clip
+
+Polygon:
+ 1. Repeat has no effect
+ 2. Image is already continuous and defined on the whole plane
+ 3. Transform according to transform attribute
+ 4. Position image
+ 5. Supersample 15x17 per destination pixel.
+ 6. Clip
+
+Possibly interesting additions:
+ - More general transformations, such as warping, or general
+ shading.
+
+ - Shader image where a function is called to generate the
+ pixel (ie., uploading assembly code).
+
+ - Resampling kernels
+
+ In principle the polygon image uses a 15x17 box filter for
+ resampling. If we allow general resampling filters, then we
+ get all the various antialiasing types for free.
+
+ Bilinear downsampling looks terrible and could be much
+ improved by a resampling filter. NEAREST reconstruction
+ combined with a box resampling filter is what GdkPixbuf
+ does, I believe.
+
+ Useful for high frequency gradients as well.
+
+ (Note that the difference between a reconstruction and a
+ resampling filter is mainly where in the pipeline they
+ occur. High quality resampling should use a correctly
+ oriented kernel so it should happen after transformation.
+
+ An implementation can transform the resampling kernel and
+ convolve it with the reconstruction if it so desires, but it
+ will need to deal with the fact that the resampling kernel
+ will not necessarily be pixel aligned.
+
+ "Output kernels"
+
+ One could imagine doing the resampling after compositing,
+ ie., for each destination pixel sample each source image 16
+ times, then composite those subpixels individually, then
+ finally apply a kernel.
+
+ However, this is effectively the same as full screen
+ antialiasing, which is a simpler way to think about it. So
+ resampling kernels may make sense for individual images, but
+ not as a post-compositing step.
+
+ Fullscreen AA is inefficient without chained compositing
+ though. Consider an (image scaled up to oversample size IN
+ some polygon) scaled down to screen size. With the current
+ implementation, there will be a huge temporary. With chained
+ compositing, the whole thing ends up being equivalent to the
+ output kernel from above.
+
+ - Color space conversion
+
+ The complete model here is that each surface has a color
+ space associated with it and that the compositing operation
+ also has one associated with it. Note also that gradients
+ should have associcated colorspaces.
+
+ - Dithering
+
+ If people dither something that is already dithered, it will
+ look terrible, but don't do that, then. (Dithering happens
+ after resampling if at all - what is the relationship
+ with color spaces? Presumably dithering should happen in linear
+ intensity space).
+
+ - Floating point surfaces, 16, 32 and possibly 64 bit per
+ channel.
+
+ Maybe crack:
+
+ - Glyph polygons
+
+ If glyphs could be given as polygons, they could be
+ positioned and rasterized more accurately. The glyph
+ structure would need subpixel positioning though.
+
+ - Luminance vs. coverage for the alpha channel
+
+ Whether the alpha channel should be interpreted as luminance
+ modulation or as coverage (intensity modulation). This is a
+ bit of a departure from the rendering model though. It could
+ also be considered whether it should be possible to have
+ both channels in the same drawable.
+
+ - Alternative for component alpha
+
+ - Set component-alpha on the output image.
+
+ - This means each of the components are sampled
+ independently and composited in the corresponding
+ channel only.
+
+ - Have 3 x oversampled mask
+
+ - Scale it down by 3 horizontally, with [ 1/3, 1/3, 1/3 ]
+ resampling filter.
+
+ Is this equivalent to just using a component alpha mask?
+
+ Incompatible changes:
+
+ - Gradients could be specified with premultiplied colors. (You
+ can use a mask to get things like gradients from solid red to
+ transparent red.
+
+Refactoring pixman
+
+The pixman code is not particularly nice to put it mildly. Among the
+issues are
+
+- inconsistent naming style (fb vs Fb, camelCase vs
+ underscore_naming). Sometimes there is even inconsistency *within*
+ one name.
+
+ fetchProc32 ACCESS(pixman_fetchProcForPicture32)
+
+ may be one of the uglies names ever created.
+
+ coding style:
+ use the one from cairo except that pixman uses this brace style:
+
+ while (blah)
+ {
+ }
+
+ Format do while like this:
+
+ do
+ {
+
+ }
+ while (...);
+
+- PIXMAN_COMPOSITE_RECT_GENERAL() is horribly complex
+
+- switch case logic in pixman-access.c
+
+ Instead it would be better to just store function pointers in the
+ image objects themselves,
+
+ get_pixel()
+ get_scanline()
+
+- Much of the scanline fetching code is for formats that no one
+ ever uses. a2r2g2b2 anyone?
+
+ It would probably be worthwhile having a generic fetcher for any
+ pixman format whatsoever.
+
+- Code related to particular image types should be split into individual
+ files.
+
+ pixman-bits-image.c
+ pixman-linear-gradient-image.c
+ pixman-radial-gradient-image.c
+ pixman-solid-image.c
+
+- Fast path code should be split into files based on architecture:
+
+ pixman-mmx-fastpath.c
+ pixman-sse2-fastpath.c
+ pixman-c-fastpath.c
+
+ etc.
+
+ Each of these files should then export a fastpath table, which would
+ be declared in pixman-private.h. This should allow us to get rid
+ of the pixman-mmx.h files.
+
+ The fast path table should describe each fast path. Ie there should
+ be bitfields indicating what things the fast path can handle, rather than
+ like now where it is only allowed to take one format per src/mask/dest. Ie.,
+
+ {
+ FAST_a8r8g8b8 | FAST_x8r8g8b8,
+ FAST_null,
+ FAST_x8r8g8b8,
+ FAST_repeat_normal | FAST_repeat_none,
+ the_fast_path
+ }
+
+There should then be *one* file that implements pixman_image_composite().
+This should do this:
+
+ optimize_operator();
+
+ convert 1x1 repeat to solid (actually this should be done at
+ image creation time).
+
+ is there a useful fastpath?
+
+There should be a file called pixman-cpu.c that contains all the
+architecture specific stuff to detect what CPU features we have.
+
+Issues that must be kept in mind:
+
+ - we need accessor code to be preserved
+
+ - maybe there should be a "store_scanline" too?
+
+ Is this sufficient?
+
+ We should preserve the optimization where the
+ compositing happens directly in the destination
+ whenever possible.
+
+ - It should be possible to create GPU samplers from the
+ images.
+
+The "horizontal" classification should be a bit in the image, the
+"vertical" classification should just happen inside the gradient
+file. Note though that
+
+ (a) these will change if the tranformation/repeat changes.
+
+ (b) at the moment the optimization for linear gradients
+ takes the source rectangle into account. Presumably
+ this is to also optimize the case where the gradient
+ is close enough to horizontal?
+
+Who is responsible for repeats? In principle it should be the scanline
+fetch. Right now NORMAL repeats are handled by walk_composite_region()
+while other repeats are handled by the scanline code.
+
+
+(Random note on filtering: do you filter before or after
+transformation? Hardware is going to filter after transformation;
+this is also what pixman does currently). It's not completely clear
+what filtering *after* transformation means. One thing that might look
+good would be to do *supersampling*, ie., compute multiple subpixels
+per destination pixel, then average them together.
diff --git a/gfx/cairo/lround-c99-only.patch b/gfx/cairo/lround-c99-only.patch
new file mode 100644
index 000000000..9002235f0
--- /dev/null
+++ b/gfx/cairo/lround-c99-only.patch
@@ -0,0 +1,46 @@
+Only use lround in C99 programs.
+
+diff --git a/gfx/cairo/cairo/src/cairo-misc.c b/gfx/cairo/cairo/src/cairo-misc.c
+--- a/gfx/cairo/cairo/src/cairo-misc.c
++++ b/gfx/cairo/cairo/src/cairo-misc.c
+@@ -478,17 +478,17 @@ _cairo_operator_bounded_by_either (cairo
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return 0;
+ }
+
+ }
+
+-#if DISABLE_SOME_FLOATING_POINT
++#if DISABLE_SOME_FLOATING_POINT || __STDC_VERSION__ < 199901L
+ /* 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
+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
+@@ -969,17 +969,17 @@ _cairo_restrict_value (double value, dou
+ * away from 0. _cairo_round rounds halfway cases toward negative infinity.
+ * This matches the rounding behaviour of _cairo_lround. */
+ static inline double cairo_const
+ _cairo_round (double r)
+ {
+ return floor (r + .5);
+ }
+
+-#if DISABLE_SOME_FLOATING_POINT
++#if DISABLE_SOME_FLOATING_POINT || __STDC_VERSION__ < 199901L
+ cairo_private int
+ _cairo_lround (double d) cairo_const;
+ #else
+ #define _cairo_lround lround
+ #endif
+
+ cairo_private uint16_t
+ _cairo_half_from_float (float f) cairo_const;
diff --git a/gfx/cairo/max-font-size.patch b/gfx/cairo/max-font-size.patch
new file mode 100644
index 000000000..99be23906
--- /dev/null
+++ b/gfx/cairo/max-font-size.patch
@@ -0,0 +1,28 @@
+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
+@@ -63,6 +63,10 @@
+ /* This is the max number of FT_face objects we keep open at once
+ */
+ #define MAX_OPEN_FACES 10
++
++/* This is the maximum font size we allow to be passed to FT_Set_Char_Size
++ */
++#define MAX_FONT_SIZE 1000
+
+ /*
+ * The simple 2x2 matrix is converted into separate scale and shape
+@@ -682,9 +686,11 @@ _cairo_ft_unscaled_font_set_scale (cairo
+ FT_Set_Transform(unscaled->face, &mat, NULL);
+
+ if ((unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) {
++ double x_scale = MIN(sf.x_scale, MAX_FONT_SIZE);
++ double y_scale = MIN(sf.y_scale, MAX_FONT_SIZE);
+ error = FT_Set_Char_Size (unscaled->face,
+- sf.x_scale * 64.0 + .5,
+- sf.y_scale * 64.0 + .5,
++ x_scale * 64.0 + .5,
++ y_scale * 64.0 + .5,
+ 0, 0);
+ if (error)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
diff --git a/gfx/cairo/missing-cairo-clip-init.diff b/gfx/cairo/missing-cairo-clip-init.diff
new file mode 100644
index 000000000..a3f46771f
--- /dev/null
+++ b/gfx/cairo/missing-cairo-clip-init.diff
@@ -0,0 +1,21 @@
+diff --git a/gfx/cairo/cairo/src/cairo-gstate.c b/gfx/cairo/cairo/src/cairo-gstate.c
+--- a/gfx/cairo/cairo/src/cairo-gstate.c
++++ b/gfx/cairo/cairo/src/cairo-gstate.c
+@@ -1841,16 +1841,17 @@ _cairo_gstate_show_text_glyphs (cairo_gs
+ transformed_glyphs,
+ &num_glyphs,
+ transformed_clusters);
+
+ if (status || num_glyphs == 0)
+ goto CLEANUP_GLYPHS;
+
+ _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
++ _cairo_clip_init(&clip);
+
+ /* For really huge font sizes, we can just do path;fill instead of
+ * show_glyphs, as show_glyphs would put excess pressure on the cache,
+ * not all components below us correctly handle huge font sizes, and
+ * path filling can be cheaper since parts of glyphs are likely to be
+ * clipped out. 256 seems like a good limit. But alas, seems like cairo's
+ * rasterizer is something like ten times slower than freetype's for huge
+ * sizes. So, no win just yet when we're using cairo's rasterizer.
diff --git a/gfx/cairo/moz.build b/gfx/cairo/moz.build
new file mode 100644
index 000000000..177484753
--- /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/native-clipping.patch b/gfx/cairo/native-clipping.patch
new file mode 100644
index 000000000..171998fe7
--- /dev/null
+++ b/gfx/cairo/native-clipping.patch
@@ -0,0 +1,189 @@
+commit 857df0583365228150b3319475efc43b22077d06
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Tue Apr 20 15:43:54 2010 -0400
+
+ native clipping
+
+diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
+index df063bf..819e53e 100644
+--- a/src/cairo-quartz-surface.c
++++ b/src/cairo-quartz-surface.c
+@@ -39,6 +39,8 @@
+
+ #include "cairo-quartz-private.h"
+ #include "cairo-surface-clipper-private.h"
++#include "cairo-gstate-private.h"
++#include "cairo-private.h"
+
+ #include <dlfcn.h>
+
+@@ -3095,6 +3097,61 @@ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
+ return quartz->cgContext;
+ }
+
++CGContextRef
++cairo_quartz_get_cg_context_with_clip (cairo_t *cr)
++{
++
++ cairo_surface_t *surface = cr->gstate->target;
++ cairo_clip_t *clip = &cr->gstate->clip;
++ cairo_status_t status;
++
++ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
++
++ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
++ return NULL;
++
++ if (!clip->path) {
++ if (clip->all_clipped) {
++ /* Save the state before we set an empty clip rect so that
++ * our previous clip will be restored */
++ CGContextSaveGState (quartz->cgContext);
++
++ /* _cairo_surface_clipper_set_clip doesn't deal with
++ * clip->all_clipped because drawing is normally discarded earlier */
++ CGRect empty = {{0,0}, {0,0}};
++ CGContextClipToRect (quartz->cgContext, empty);
++
++ return quartz->cgContext;
++ }
++
++ /* an empty clip is represented by NULL */
++ clip = NULL;
++ }
++
++ status = _cairo_surface_clipper_set_clip (&quartz->clipper, clip);
++
++ /* Save the state after we set the clip so that it persists
++ * after we restore */
++ CGContextSaveGState (quartz->cgContext);
++
++ if (unlikely (status))
++ return NULL;
++
++ return quartz->cgContext;
++}
++
++void
++cairo_quartz_finish_cg_context_with_clip (cairo_t *cr)
++{
++ cairo_surface_t *surface = cr->gstate->target;
++
++ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
++
++ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
++ return;
++
++ CGContextRestoreGState (quartz->cgContext);
++}
+
+ /* Debug stuff */
+
+diff --git a/src/cairo-quartz.h b/src/cairo-quartz.h
+index e8b71ba..aa1cdd2 100644
+--- a/src/cairo-quartz.h
++++ b/src/cairo-quartz.h
+@@ -57,6 +57,12 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
+ cairo_public CGContextRef
+ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+
++cairo_public CGContextRef
++cairo_quartz_get_cg_context_with_clip (cairo_t *cr);
++
++cairo_public void
++cairo_quartz_finish_cg_context_with_clip (cairo_t *cr);
++
+ #if CAIRO_HAS_QUARTZ_FONT
+
+ /*
+diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
+index d4575a3..c10e134 100644
+--- a/src/cairo-win32-surface.c
++++ b/src/cairo-win32-surface.c
+@@ -52,7 +52,9 @@
+ #include "cairo-win32-private.h"
+ #include "cairo-scaled-font-subsets-private.h"
+ #include "cairo-surface-fallback-private.h"
+-
++#include "cairo-surface-clipper-private.h"
++#include "cairo-gstate-private.h"
++#include "cairo-private.h"
+ #include <wchar.h>
+ #include <windows.h>
+
+@@ -1914,6 +1916,61 @@ cairo_win32_surface_get_dc (cairo_surface_t *surface)
+ return NULL;
+ }
+
++
++HDC
++cairo_win32_get_dc_with_clip (cairo_t *cr)
++{
++ cairo_surface_t *surface = cr->gstate->target;
++ cairo_clip_t clip;
++ _cairo_clip_init_copy(&clip, &cr->gstate->clip);
++
++ if (_cairo_surface_is_win32 (surface)){
++ cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) surface;
++ cairo_region_t *clip_region = NULL;
++ cairo_status_t status;
++
++ if (clip.path) {
++ status = _cairo_clip_get_region (&clip, &clip_region);
++ assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
++ if (status) {
++ _cairo_clip_fini(&clip);
++ return NULL;
++ }
++ }
++ _cairo_win32_surface_set_clip_region (winsurf, clip_region);
++
++ _cairo_clip_fini(&clip);
++ return winsurf->dc;
++ }
++
++ if (_cairo_surface_is_paginated (surface)) {
++ cairo_surface_t *target;
++
++ target = _cairo_paginated_surface_get_target (surface);
++
++#ifndef CAIRO_OMIT_WIN32_PRINTING
++ if (_cairo_surface_is_win32_printing (target)) {
++ cairo_status_t status;
++ cairo_win32_surface_t *winsurf = (cairo_win32_surface_t *) target;
++
++ status = _cairo_surface_clipper_set_clip (&winsurf->clipper, &clip);
++
++ _cairo_clip_fini(&clip);
++
++ if (status)
++ return NULL;
++
++ return winsurf->dc;
++ }
++#endif
++ }
++
++ _cairo_clip_fini(&clip);
++ return NULL;
++}
++
++
++
+ /**
+ * cairo_win32_surface_get_image
+ * @surface: a #cairo_surface_t
+diff --git a/src/cairo-win32.h b/src/cairo-win32.h
+index 7d04d2a..c304f92 100644
+--- a/src/cairo-win32.h
++++ b/src/cairo-win32.h
+@@ -65,6 +65,9 @@ cairo_win32_surface_create_with_dib (cairo_format_t format,
+ 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);
+
diff --git a/gfx/cairo/no-pixman-image-reuse-across-threads.patch b/gfx/cairo/no-pixman-image-reuse-across-threads.patch
new file mode 100644
index 000000000..ccaf5a5e6
--- /dev/null
+++ b/gfx/cairo/no-pixman-image-reuse-across-threads.patch
@@ -0,0 +1,242 @@
+From
+https://cgit.freedesktop.org/cairo/commit/?id=71e8a4c23019b01aa43b334fcb2784c70daae9b5
+https://bugs.freedesktop.org/show_bug.cgi?id=34177
+
+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
+@@ -870,17 +870,17 @@ static cairo_bool_t
+ *ty = _pixman_nearest_sample (*ty);
+ } else {
+ if (*tx != floor (*tx) || *ty != floor (*ty))
+ return FALSE;
+ }
+ return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT;
+ }
+
+-#if HAS_ATOMIC_OPS
++#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;
+@@ -964,56 +964,59 @@ static pixman_image_t *
+ pixman_image_ref (image);
+ }
+ } else {
+ pixman_image_ref (image);
+ }
+
+ return image;
+ }
+-#else
+-static pixman_image_t *
+-_pixman_transparent_image (void)
+-{
+- return _pixman_image_for_solid (&_cairo_pattern_clear);
+-}
+-static pixman_image_t *
+-_pixman_black_image (void)
+-{
+- return _pixman_image_for_solid (&_cairo_pattern_black);
+-}
+-static pixman_image_t *
+-_pixman_white_image (void)
+-{
+- return _pixman_image_for_solid (&_cairo_pattern_white);
+-}
+-#endif
+
+ static uint32_t
+ hars_petruska_f54_1_random (void)
+ {
+ #define rol(x,k) ((x << k) | (x >> (32-k)))
+ static uint32_t x;
+ return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+ #undef rol
+ }
+
+ static struct {
+ cairo_color_t color;
+ pixman_image_t *image;
+ } cache[16];
+ static int n_cached;
+
++#else /* !PIXMAN_HAS_ATOMIC_OPS */
++static pixman_image_t *
++_pixman_transparent_image (void)
++{
++ return _pixman_image_for_solid (&_cairo_pattern_clear);
++}
++
++static pixman_image_t *
++_pixman_black_image (void)
++{
++ return _pixman_image_for_solid (&_cairo_pattern_black);
++}
++
++static pixman_image_t *
++_pixman_white_image (void)
++{
++ return _pixman_image_for_solid (&_cairo_pattern_white);
++}
++#endif /* !PIXMAN_HAS_ATOMIC_OPS */
++
+ void
+ _cairo_image_reset_static_data (void)
+ {
++#if PIXMAN_HAS_ATOMIC_OPS
+ while (n_cached)
+ pixman_image_unref (cache[--n_cached].image);
+
+-#if HAS_ATOMIC_OPS
+ 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;
+@@ -1026,19 +1029,20 @@ void
+ #endif
+ }
+
+ static pixman_image_t *
+ _pixman_image_for_solid (const cairo_solid_pattern_t *pattern)
+ {
+ pixman_color_t color;
+ pixman_image_t *image;
++
++#if PIXMAN_HAS_ATOMIC_OPS
+ int i;
+
+-#if HAS_ATOMIC_OPS
+ if (pattern->color.alpha_short <= 0x00ff)
+ return _pixman_transparent_image ();
+
+ if (pattern->color.alpha_short >= 0xff00) {
+ if (pattern->color.red_short <= 0x00ff &&
+ pattern->color.green_short <= 0x00ff &&
+ pattern->color.blue_short <= 0x00ff)
+ {
+@@ -1047,46 +1051,48 @@ static pixman_image_t *
+
+ if (pattern->color.red_short >= 0xff00 &&
+ pattern->color.green_short >= 0xff00 &&
+ pattern->color.blue_short >= 0xff00)
+ {
+ return _pixman_white_image ();
+ }
+ }
+-#endif
+
+ CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
+ for (i = 0; i < n_cached; i++) {
+ if (_cairo_color_equal (&cache[i].color, &pattern->color)) {
+ image = pixman_image_ref (cache[i].image);
+ goto UNLOCK;
+ }
+ }
++#endif
+
+ color.red = pattern->color.red_short;
+ color.green = pattern->color.green_short;
+ color.blue = pattern->color.blue_short;
+ color.alpha = pattern->color.alpha_short;
+
+ image = pixman_image_create_solid_fill (&color);
++#if PIXMAN_HAS_ATOMIC_OPS
+ if (image == NULL)
+ goto UNLOCK;
+
+ if (n_cached < ARRAY_LENGTH (cache)) {
+ i = n_cached++;
+ } else {
+ i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
+ pixman_image_unref (cache[i].image);
+ }
+ cache[i].image = pixman_image_ref (image);
+ cache[i].color = pattern->color;
+
+ UNLOCK:
+ CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
++#endif
+ return image;
+ }
+
+ static double
+ clamp (double val, double min, double max)
+ {
+ return val < min ? min : (val > max ? max : val);
+ }
+@@ -1423,25 +1429,27 @@ static pixman_image_t *
+ return _pixman_transparent_image ();
+ }
+ else
+ {
+ return _pixel_to_solid (source, sample.x, sample.y);
+ }
+ }
+
++#if PIXMAN_HAS_ATOMIC_OPS
+ /* avoid allocating a 'pattern' image if we can reuse the original */
+ if (extend == CAIRO_EXTEND_NONE &&
+ _cairo_matrix_is_translation (&pattern->base.matrix) &&
+ _nearest_sample (filter, &tx, &ty))
+ {
+ *ix = tx;
+ *iy = ty;
+ return pixman_image_ref (source->pixman_image);
+ }
++#endif
+
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ source->width,
+ source->height,
+ (uint32_t *) source->data,
+ source->stride);
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+@@ -1466,31 +1474,36 @@ static pixman_image_t *
+ sub->extents.x + sample.x,
+ sub->extents.y + sample.y);
+ } else {
+ if (extend == CAIRO_EXTEND_NONE)
+ return _pixman_transparent_image ();
+ }
+ }
+
++#if PIXMAN_HAS_ATOMIC_OPS
+ if (is_contained &&
+ _cairo_matrix_is_translation (&pattern->base.matrix) &&
+ _nearest_sample (filter, &tx, &ty))
+ {
+ *ix = tx + sub->extents.x;
+ *iy = ty + sub->extents.y;
+ return pixman_image_ref (source->pixman_image);
+ }
++#endif
+
+ /* Avoid sub-byte offsets, force a copy in that case. */
+ if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
++ void *data = source->data
++ + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
++ + sub->extents.y * source->stride;
+ pixman_image = pixman_image_create_bits (source->pixman_format,
+ sub->extents.width,
+ sub->extents.height,
+- (uint32_t *) (source->data + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 + sub->extents.y * source->stride),
++ data,
+ source->stride);
+ if (unlikely (pixman_image == NULL))
+ return NULL;
+ }
+ }
+ }
+
+ if (pixman_image == NULL) {
diff --git a/gfx/cairo/nonfatal-assertions.patch b/gfx/cairo/nonfatal-assertions.patch
new file mode 100644
index 000000000..dcbb904ab
--- /dev/null
+++ b/gfx/cairo/nonfatal-assertions.patch
@@ -0,0 +1,17 @@
+diff -r b79d47dad1ea gfx/cairo/cairo/src/cairoint.h
+--- a/gfx/cairo/cairo/src/cairoint.h Fri Jun 08 18:09:53 2007 -0700
++++ b/gfx/cairo/cairo/src/cairoint.h Fri Jun 29 09:18:02 2007 +0200
+@@ -159,6 +159,13 @@ CAIRO_BEGIN_DECLS
+
+ #ifndef M_PI
+ #define M_PI 3.14159265358979323846
++#endif
++
++#ifndef NDEBUG
++#undef assert
++#define assert(expr) \
++ do { if (!(expr)) fprintf(stderr, "Assertion failed at %s:%d: %s\n", \
++ __FILE__, __LINE__, #expr); } while (0)
+ #endif
+
+ #undef ARRAY_LENGTH
diff --git a/gfx/cairo/on-edge.patch b/gfx/cairo/on-edge.patch
new file mode 100644
index 000000000..85e328ad8
--- /dev/null
+++ b/gfx/cairo/on-edge.patch
@@ -0,0 +1,70 @@
+commit a26655b3144ed273940486fc15ccdac12b0562ec
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Tue Mar 17 15:08:50 2009 -0400
+
+ Jeff Muizelaar noted that the treatment of edges differed with firefox's
+ canvas definition, which considers a point on any edge as inside. The
+ current implementation has a similar definition to that of flash, for
+ which the top and right edges are outside. Arguably, firefox has the more
+ intuitive definition here...
+
+diff --git a/src/cairo-path-in-fill.c b/src/cairo-path-in-fill.c
+index 21cd0bd..e641654 100644
+--- a/src/cairo-path-in-fill.c
++++ b/src/cairo-path-in-fill.c
+@@ -41,6 +41,7 @@ typedef struct cairo_in_fill {
+ int winding;
+
+ cairo_fixed_t x, y;
++ cairo_bool_t on_edge;
+
+ cairo_bool_t has_current_point;
+ cairo_point_t current_point;
+@@ -58,6 +59,7 @@ _cairo_in_fill_init (cairo_in_fill_t *in_fill,
+
+ in_fill->x = _cairo_fixed_from_double (x);
+ in_fill->y = _cairo_fixed_from_double (y);
++ in_fill->on_edge = FALSE;
+
+ in_fill->has_current_point = FALSE;
+ in_fill->current_point.x = 0;
+@@ -103,6 +105,9 @@ _cairo_in_fill_add_edge (cairo_in_fill_t *in_fill,
+ {
+ int dir;
+
++ if (in_fill->on_edge)
++ return;
++
+ /* count the number of edge crossing to -∞ */
+
+ dir = 1;
+@@ -116,6 +121,18 @@ _cairo_in_fill_add_edge (cairo_in_fill_t *in_fill,
+ dir = -1;
+ }
+
++ /* First check whether the query is on an edge */
++ if ((p1->x == in_fill->x && p1->x == in_fill->y) ||
++ (p2->x == in_fill->x && p2->x == 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;
+@@ -246,7 +263,9 @@ _cairo_path_fixed_in_fill (cairo_path_fixed_t *path,
+
+ _cairo_in_fill_close_path (&in_fill);
+
+- switch (fill_rule) {
++ 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;
diff --git a/gfx/cairo/pattern_get_surface-no-error.patch b/gfx/cairo/pattern_get_surface-no-error.patch
new file mode 100644
index 000000000..93f08512d
--- /dev/null
+++ b/gfx/cairo/pattern_get_surface-no-error.patch
@@ -0,0 +1,29 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1294019288 -46800
+# Node ID e427b4ea7e2ff980769e1acd92f4730c5ed3654f
+# Parent bacc54d452a9fddb5a0d6a1442ec7be4de81ffa7
+Bug 593604. Part 2.5: cairo_pattern_get_surface should not call cairo_error. r=jrmuizel,a=blocking
+
+diff --git a/gfx/cairo/cairo/src/cairo-pattern.c b/gfx/cairo/cairo/src/cairo-pattern.c
+--- a/gfx/cairo/cairo/src/cairo-pattern.c
++++ b/gfx/cairo/cairo/src/cairo-pattern.c
+@@ -2940,17 +2940,17 @@ cairo_pattern_get_surface (cairo_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);
++ return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
+
+ if (surface)
+ *surface = spat->surface;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /**
diff --git a/gfx/cairo/pixman-16-bit-pipeline.patch b/gfx/cairo/pixman-16-bit-pipeline.patch
new file mode 100644
index 000000000..8a7878ca2
--- /dev/null
+++ b/gfx/cairo/pixman-16-bit-pipeline.patch
@@ -0,0 +1,1242 @@
+diff --git a/gfx/cairo/libpixman/src/pixman-access.c b/gfx/cairo/libpixman/src/pixman-access.c
+--- a/gfx/cairo/libpixman/src/pixman-access.c
++++ b/gfx/cairo/libpixman/src/pixman-access.c
+@@ -933,16 +933,54 @@ store_scanline_x2b10g10r10 (bits_image_t
+ {
+ WRITE (image, pixel++,
+ ((values[i] >> 38) & 0x3ff) |
+ ((values[i] >> 12) & 0xffc00) |
+ ((values[i] << 14) & 0x3ff00000));
+ }
+ }
+
++static void
++store_scanline_16 (bits_image_t * image,
++ int x,
++ int y,
++ int width,
++ const uint32_t *v)
++{
++ uint16_t *bits = (uint16_t*)(image->bits + image->rowstride * y);
++ uint16_t *values = (uint16_t *)v;
++ uint16_t *pixel = bits + x;
++ int i;
++
++ for (i = 0; i < width; ++i)
++ {
++ WRITE (image, pixel++, values[i]);
++ }
++}
++
++static void
++fetch_scanline_16 (pixman_image_t *image,
++ int x,
++ int y,
++ int width,
++ uint32_t * b,
++ const uint32_t *mask)
++{
++ const uint16_t *bits = (uint16_t*)(image->bits.bits + y * image->bits.rowstride);
++ const uint16_t *pixel = bits + x;
++ int i;
++ uint16_t *buffer = (uint16_t *)b;
++
++ for (i = 0; i < width; ++i)
++ {
++ *buffer++ = READ (image, pixel++);
++ }
++}
++
++
+ /*
+ * Contracts a 64bpp image to 32bpp and then stores it using a regular 32-bit
+ * store proc. Despite the type, this function expects a uint64_t buffer.
+ */
+ static void
+ store_scanline_generic_64 (bits_image_t * image,
+ int x,
+ int y,
+@@ -1044,32 +1082,47 @@ fetch_pixel_generic_lossy_32 (bits_image
+ pixman_contract (&result, &pixel64, 1);
+
+ return result;
+ }
+
+ typedef struct
+ {
+ pixman_format_code_t format;
++ fetch_scanline_t fetch_scanline_16;
+ fetch_scanline_t fetch_scanline_32;
+ fetch_scanline_t fetch_scanline_64;
+ fetch_pixel_32_t fetch_pixel_32;
+ fetch_pixel_64_t fetch_pixel_64;
++ store_scanline_t store_scanline_16;
+ store_scanline_t store_scanline_32;
+ store_scanline_t store_scanline_64;
+ } format_info_t;
+
+ #define FORMAT_INFO(format) \
+ { \
+ PIXMAN_ ## format, \
++ NULL, \
+ fetch_scanline_ ## format, \
+ fetch_scanline_generic_64, \
+ fetch_pixel_ ## format, fetch_pixel_generic_64, \
++ NULL, \
+ store_scanline_ ## format, store_scanline_generic_64 \
+ }
++#define FORMAT_INFO16(format) \
++ { \
++ PIXMAN_ ## format, \
++ fetch_scanline_16, \
++ fetch_scanline_ ## format, \
++ fetch_scanline_generic_64, \
++ fetch_pixel_ ## format, fetch_pixel_generic_64, \
++ store_scanline_16, \
++ store_scanline_ ## format, store_scanline_generic_64 \
++ }
++
+
+ static const format_info_t accessors[] =
+ {
+ /* 32 bpp formats */
+ FORMAT_INFO (a8r8g8b8),
+ FORMAT_INFO (x8r8g8b8),
+ FORMAT_INFO (a8b8g8r8),
+ FORMAT_INFO (x8b8g8r8),
+@@ -1079,18 +1132,18 @@ static const format_info_t accessors[] =
+ FORMAT_INFO (r8g8b8x8),
+ FORMAT_INFO (x14r6g6b6),
+
+ /* 24bpp formats */
+ FORMAT_INFO (r8g8b8),
+ FORMAT_INFO (b8g8r8),
+
+ /* 16bpp formats */
+- FORMAT_INFO (r5g6b5),
+- FORMAT_INFO (b5g6r5),
++ FORMAT_INFO16 (r5g6b5),
++ FORMAT_INFO16 (b5g6r5),
+
+ FORMAT_INFO (a1r5g5b5),
+ FORMAT_INFO (x1r5g5b5),
+ FORMAT_INFO (a1b5g5r5),
+ FORMAT_INFO (x1b5g5r5),
+ FORMAT_INFO (a4r4g4b4),
+ FORMAT_INFO (x4r4g4b4),
+ FORMAT_INFO (a4b4g4r4),
+@@ -1132,62 +1185,64 @@ static const format_info_t accessors[] =
+
+ /* 1bpp formats */
+ FORMAT_INFO (a1),
+ FORMAT_INFO (g1),
+
+ /* Wide formats */
+
+ { PIXMAN_a2r10g10b10,
+- NULL, fetch_scanline_a2r10g10b10,
++ NULL, NULL, fetch_scanline_a2r10g10b10,
+ fetch_pixel_generic_lossy_32, fetch_pixel_a2r10g10b10,
+ NULL, store_scanline_a2r10g10b10 },
+
+ { PIXMAN_x2r10g10b10,
+- NULL, fetch_scanline_x2r10g10b10,
++ NULL, NULL, fetch_scanline_x2r10g10b10,
+ fetch_pixel_generic_lossy_32, fetch_pixel_x2r10g10b10,
+ NULL, store_scanline_x2r10g10b10 },
+
+ { PIXMAN_a2b10g10r10,
+- NULL, fetch_scanline_a2b10g10r10,
++ NULL, NULL, fetch_scanline_a2b10g10r10,
+ fetch_pixel_generic_lossy_32, fetch_pixel_a2b10g10r10,
+ NULL, store_scanline_a2b10g10r10 },
+
+ { PIXMAN_x2b10g10r10,
+- NULL, fetch_scanline_x2b10g10r10,
++ NULL, NULL, fetch_scanline_x2b10g10r10,
+ fetch_pixel_generic_lossy_32, fetch_pixel_x2b10g10r10,
+ NULL, store_scanline_x2b10g10r10 },
+
+ /* YUV formats */
+ { PIXMAN_yuy2,
+- fetch_scanline_yuy2, fetch_scanline_generic_64,
++ NULL, fetch_scanline_yuy2, fetch_scanline_generic_64,
+ fetch_pixel_yuy2, fetch_pixel_generic_64,
+ NULL, NULL },
+
+ { PIXMAN_yv12,
+- fetch_scanline_yv12, fetch_scanline_generic_64,
++ NULL, fetch_scanline_yv12, fetch_scanline_generic_64,
+ fetch_pixel_yv12, fetch_pixel_generic_64,
+ 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_16 = info->fetch_scanline_16;
+ image->fetch_scanline_32 = info->fetch_scanline_32;
+ image->fetch_scanline_64 = info->fetch_scanline_64;
+ image->fetch_pixel_32 = info->fetch_pixel_32;
+ image->fetch_pixel_64 = info->fetch_pixel_64;
++ image->store_scanline_16 = info->store_scanline_16;
+ image->store_scanline_32 = info->store_scanline_32;
+ image->store_scanline_64 = info->store_scanline_64;
+
+ return;
+ }
+
+ info++;
+ }
+diff --git a/gfx/cairo/libpixman/src/pixman-bits-image.c b/gfx/cairo/libpixman/src/pixman-bits-image.c
+--- a/gfx/cairo/libpixman/src/pixman-bits-image.c
++++ b/gfx/cairo/libpixman/src/pixman-bits-image.c
+@@ -1247,16 +1247,31 @@ src_get_scanline_wide (pixman_iter_t *it
+
+ void
+ _pixman_bits_image_src_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+ {
+ if (iter->flags & ITER_NARROW)
+ iter->get_scanline = src_get_scanline_narrow;
+ else
+ iter->get_scanline = src_get_scanline_wide;
++
++}
++
++static uint32_t *
++dest_get_scanline_16 (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_16 (image, x, y, width, buffer, mask);
++
++ return iter->buffer;
+ }
+
+ 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;
+@@ -1327,16 +1342,30 @@ dest_get_scanline_wide (pixman_iter_t *i
+ free (alpha);
+ }
+ }
+
+ return iter->buffer;
+ }
+
+ static void
++dest_write_back_16 (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_16 (image, x, y, width, buffer);
++
++ iter->y++;
++}
++
++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;
+
+@@ -1375,28 +1404,41 @@ dest_write_back_wide (pixman_iter_t *ite
+ }
+
+ iter->y++;
+ }
+
+ void
+ _pixman_bits_image_dest_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+ {
+- if (iter->flags & ITER_NARROW)
++ if (iter->flags & ITER_16)
++ {
++ if ((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_16;
++ }
++ iter->write_back = dest_write_back_16;
++ }
++ else if (iter->flags & ITER_NARROW)
+ {
+ if ((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;
+ }
+ }
+diff --git a/gfx/cairo/libpixman/src/pixman-combine16.c b/gfx/cairo/libpixman/src/pixman-combine16.c
+new file mode 100644
+--- /dev/null
++++ b/gfx/cairo/libpixman/src/pixman-combine16.c
+@@ -0,0 +1,124 @@
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <math.h>
++#include <string.h>
++
++#include "pixman-private.h"
++
++#include "pixman-combine32.h"
++
++static force_inline uint32_t
++combine_mask (const uint32_t src, const uint32_t mask)
++{
++ uint32_t s, m;
++
++ m = mask >> A_SHIFT;
++
++ if (!m)
++ return 0;
++ s = src;
++
++ UN8x4_MUL_UN8 (s, m);
++
++ return s;
++}
++
++static inline uint32_t convert_0565_to_8888(uint16_t color)
++{
++ return CONVERT_0565_TO_8888(color);
++}
++
++static inline uint16_t convert_8888_to_0565(uint32_t color)
++{
++ return CONVERT_8888_TO_0565(color);
++}
++
++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 (uint16_t));
++ else
++ {
++ uint16_t *d = (uint16_t*)dest;
++ uint16_t *src16 = (uint16_t*)src;
++ for (i = 0; i < width; ++i)
++ {
++ if ((*mask & 0xff000000) == 0xff000000) {
++ // it's likely worth special casing
++ // fully opaque because it avoids
++ // the cost of conversion as well the multiplication
++ *(d + i) = *src16;
++ } else {
++ // the mask is still 32bits
++ uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
++ *(d + i) = convert_8888_to_0565(s);
++ }
++ mask++;
++ src16++;
++ }
++ }
++
++}
++
++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)
++ memcpy (dest, src, width * sizeof (uint16_t));
++ else
++ {
++ uint16_t *d = (uint16_t*)dest;
++ uint16_t *src16 = (uint16_t*)src;
++ for (i = 0; i < width; ++i)
++ {
++ if ((*mask & 0xff000000) == 0xff000000) {
++ // it's likely worth special casing
++ // fully opaque because it avoids
++ // the cost of conversion as well the multiplication
++ *(d + i) = *src16;
++ } else if ((*mask & 0xff000000) == 0x00000000) {
++ // keep the dest the same
++ } else {
++ // the mask is still 32bits
++ uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
++ uint32_t ia = ALPHA_8 (~s);
++ uint32_t d32 = convert_0565_to_8888(*(d + i));
++ UN8x4_MUL_UN8_ADD_UN8x4 (d32, ia, s);
++ *(d + i) = convert_8888_to_0565(d32);
++ }
++ mask++;
++ src16++;
++ }
++ }
++
++}
++
++
++void
++_pixman_setup_combiner_functions_16 (pixman_implementation_t *imp)
++{
++ int i;
++ for (i = 0; i < PIXMAN_N_OPERATORS; i++) {
++ imp->combine_16[i] = NULL;
++ }
++ imp->combine_16[PIXMAN_OP_SRC] = combine_src_u;
++ imp->combine_16[PIXMAN_OP_OVER] = combine_over_u;
++}
++
+diff --git a/gfx/cairo/libpixman/src/pixman-general.c b/gfx/cairo/libpixman/src/pixman-general.c
+--- a/gfx/cairo/libpixman/src/pixman-general.c
++++ b/gfx/cairo/libpixman/src/pixman-general.c
+@@ -106,46 +106,61 @@ general_composite_rect (pixman_implemen
+ PIXMAN_COMPOSITE_ARGS (info);
+ uint64_t stack_scanline_buffer[(SCANLINE_BUFFER_LENGTH * 3 + 7) / 8];
+ 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 narrow, src_flags;
++ iter_flags_t rgb16;
+ 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))
+ {
+ narrow = ITER_NARROW;
+ Bpp = 4;
+ }
+ else
+ {
+ narrow = 0;
+ Bpp = 8;
+ }
+
++ // XXX: This special casing is bad. Ideally, we'd keep the general code general perhaps
++ // by having it deal more specifically with different intermediate formats
++ if (
++ (dest_image->common.flags & FAST_PATH_16_FORMAT && (src_image->type == LINEAR || src_image->type == RADIAL)) &&
++ ( op == PIXMAN_OP_SRC ||
++ (op == PIXMAN_OP_OVER && (src_image->common.flags & FAST_PATH_IS_OPAQUE))
++ )
++ ) {
++ rgb16 = ITER_16;
++ } else {
++ rgb16 = 0;
++ }
++
++
+ if (width * Bpp > SCANLINE_BUFFER_LENGTH)
+ {
+ scanline_buffer = pixman_malloc_abc (width, 3, Bpp);
+
+ if (!scanline_buffer)
+ return;
+ }
+
+ src_buffer = scanline_buffer;
+ mask_buffer = src_buffer + width * Bpp;
+ dest_buffer = mask_buffer + width * Bpp;
+
+ /* src iter */
+- src_flags = narrow | op_flags[op].src;
++ src_flags = narrow | op_flags[op].src | rgb16;
+
+ _pixman_implementation_src_iter_init (imp->toplevel, &src_iter, src_image,
+ src_x, src_y, width, height,
+ src_buffer, src_flags);
+
+ /* mask iter */
+ if ((src_flags & (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) ==
+ (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB))
+@@ -164,20 +179,20 @@ general_composite_rect (pixman_implemen
+
+ _pixman_implementation_src_iter_init (
+ imp->toplevel, &mask_iter, mask_image, mask_x, mask_y, width, height,
+ mask_buffer, narrow | (component_alpha? 0 : ITER_IGNORE_RGB));
+
+ /* dest iter */
+ _pixman_implementation_dest_iter_init (
+ imp->toplevel, &dest_iter, dest_image, dest_x, dest_y, width, height,
+- dest_buffer, narrow | op_flags[op].dst);
++ dest_buffer, narrow | op_flags[op].dst | rgb16);
+
+ compose = _pixman_implementation_lookup_combiner (
+- imp->toplevel, op, component_alpha, narrow);
++ imp->toplevel, op, component_alpha, narrow, !!rgb16);
+
+ if (!compose)
+ return;
+
+ for (i = 0; i < height; ++i)
+ {
+ uint32_t *s, *m, *d;
+
+@@ -234,16 +249,17 @@ general_fill (pixman_implementation_t *i
+ return FALSE;
+ }
+
+ pixman_implementation_t *
+ _pixman_implementation_create_general (void)
+ {
+ pixman_implementation_t *imp = _pixman_implementation_create (NULL, general_fast_path);
+
++ _pixman_setup_combiner_functions_16 (imp);
+ _pixman_setup_combiner_functions_32 (imp);
+ _pixman_setup_combiner_functions_64 (imp);
+
+ imp->blt = general_blt;
+ imp->fill = general_fill;
+ imp->src_iter_init = general_src_iter_init;
+ imp->dest_iter_init = general_dest_iter_init;
+
+diff --git a/gfx/cairo/libpixman/src/pixman-image.c b/gfx/cairo/libpixman/src/pixman-image.c
+--- a/gfx/cairo/libpixman/src/pixman-image.c
++++ b/gfx/cairo/libpixman/src/pixman-image.c
+@@ -451,16 +451,20 @@ compute_image_info (pixman_image_t *imag
+ 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;
++
++ if (image->bits.format == PIXMAN_r5g6b5)
++ flags |= FAST_PATH_16_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
+diff --git a/gfx/cairo/libpixman/src/pixman-implementation.c b/gfx/cairo/libpixman/src/pixman-implementation.c
+--- a/gfx/cairo/libpixman/src/pixman-implementation.c
++++ b/gfx/cairo/libpixman/src/pixman-implementation.c
+@@ -101,45 +101,51 @@ pixman_implementation_t *
+ imp->fill = delegate_fill;
+ imp->src_iter_init = delegate_src_iter_init;
+ imp->dest_iter_init = delegate_dest_iter_init;
+
+ imp->fast_paths = fast_paths;
+
+ for (i = 0; i < PIXMAN_N_OPERATORS; ++i)
+ {
++ imp->combine_16[i] = NULL;
+ imp->combine_32[i] = NULL;
+ imp->combine_64[i] = NULL;
+ imp->combine_32_ca[i] = NULL;
+ imp->combine_64_ca[i] = NULL;
+ }
+
+ return imp;
+ }
+
+ 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)
++ pixman_bool_t narrow,
++ pixman_bool_t rgb16)
+ {
+ pixman_combine_32_func_t f;
+
+ do
+ {
+ pixman_combine_32_func_t (*combiners[]) =
+ {
+ (pixman_combine_32_func_t *)imp->combine_64,
+ (pixman_combine_32_func_t *)imp->combine_64_ca,
+ imp->combine_32,
+ imp->combine_32_ca,
++ (pixman_combine_32_func_t *)imp->combine_16,
++ NULL,
+ };
+-
+- f = combiners[component_alpha | (narrow << 1)][op];
+-
++ if (rgb16) {
++ f = combiners[4][op];
++ } else {
++ f = combiners[component_alpha + (narrow << 1)][op];
++ }
+ imp = imp->delegate;
+ }
+ while (!f);
+
+ return f;
+ }
+
+ pixman_bool_t
+diff --git a/gfx/cairo/libpixman/src/pixman-linear-gradient.c b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+--- a/gfx/cairo/libpixman/src/pixman-linear-gradient.c
++++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+@@ -217,42 +217,185 @@ linear_get_scanline_narrow (pixman_iter_
+ }
+ }
+
+ iter->y++;
+
+ return iter->buffer;
+ }
+
++static uint16_t convert_8888_to_0565(uint32_t color)
++{
++ return CONVERT_8888_TO_0565(color);
++}
++
++static uint32_t *
++linear_get_scanline_16 (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;
++ uint16_t * buffer = (uint16_t*)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;
++ uint16_t *end = buffer + width;
++ 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)
++ {
++ register uint16_t color;
++
++ color = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
++ while (buffer < end)
++ *buffer++ = color;
++ }
++ else
++ {
++ int i;
++
++ i = 0;
++ while (buffer < end)
++ {
++ if (!mask || *mask++)
++ {
++ *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
++ t + next_inc));
++ }
++ i++;
++ next_inc = inc * i;
++ buffer++;
++ }
++ }
++ }
++ 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;
++ }
++
++ *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
++ }
++
++ ++buffer;
++
++ 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_wide (pixman_iter_t *iter, const uint32_t *mask)
+ {
+ uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
+
+ pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+
+ return buffer;
+ }
+
+ 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->flags & ITER_NARROW)
++ if (iter->flags & ITER_16)
++ linear_get_scanline_16 (iter, NULL);
++ else if (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->flags & ITER_NARROW)
++ if (iter->flags & ITER_16)
++ iter->get_scanline = linear_get_scanline_16;
++ else if (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 (pixman_point_fixed_t * p1,
+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
+@@ -152,24 +152,28 @@ struct bits_image
+ int height;
+ uint32_t * bits;
+ uint32_t * free_me;
+ int rowstride; /* in number of uint32_t's */
+
+ fetch_scanline_t get_scanline_32;
+ fetch_scanline_t get_scanline_64;
+
++ fetch_scanline_t fetch_scanline_16;
++
+ fetch_scanline_t fetch_scanline_32;
+ fetch_pixel_32_t fetch_pixel_32;
+ store_scanline_t store_scanline_32;
+
+ fetch_scanline_t fetch_scanline_64;
+ fetch_pixel_64_t fetch_pixel_64;
+ store_scanline_t store_scanline_64;
+
++ store_scanline_t store_scanline_16;
++
+ /* 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;
+@@ -202,17 +206,24 @@ typedef enum
+ * 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 << 1),
+ ITER_IGNORE_ALPHA = (1 << 2),
+- ITER_IGNORE_RGB = (1 << 3)
++ ITER_IGNORE_RGB = (1 << 3),
++
++ /* With the addition of ITER_16 we now have two flags that to represent
++ * 3 pipelines. This means that there can be an invalid state when
++ * both ITER_NARROW and ITER_16 are set. In this case
++ * ITER_16 overrides NARROW and we should use the 16 bit pipeline.
++ * Note: ITER_16 still has a 32 bit mask, which is a bit weird. */
++ ITER_16 = (1 << 4)
+ } 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;
+@@ -429,16 +440,17 @@ typedef pixman_bool_t (*pixman_fill_func
+ int x,
+ int y,
+ int width,
+ int height,
+ uint32_t xor);
+ typedef void (*pixman_iter_init_func_t) (pixman_implementation_t *imp,
+ pixman_iter_t *iter);
+
++void _pixman_setup_combiner_functions_16 (pixman_implementation_t *imp);
+ void _pixman_setup_combiner_functions_32 (pixman_implementation_t *imp);
+ void _pixman_setup_combiner_functions_64 (pixman_implementation_t *imp);
+
+ typedef struct
+ {
+ pixman_op_t op;
+ pixman_format_code_t src_format;
+ uint32_t src_flags;
+@@ -459,32 +471,34 @@ struct pixman_implementation_t
+ pixman_fill_func_t fill;
+ pixman_iter_init_func_t src_iter_init;
+ pixman_iter_init_func_t dest_iter_init;
+
+ pixman_combine_32_func_t combine_32[PIXMAN_N_OPERATORS];
+ pixman_combine_32_func_t combine_32_ca[PIXMAN_N_OPERATORS];
+ pixman_combine_64_func_t combine_64[PIXMAN_N_OPERATORS];
+ pixman_combine_64_func_t combine_64_ca[PIXMAN_N_OPERATORS];
++ pixman_combine_64_func_t combine_16[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 *delegate,
+ const pixman_fast_path_t *fast_paths);
+
+ 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 wide,
++ pixman_bool_t rgb16);
+
+ 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,
+@@ -613,16 +627,17 @@ uint32_t *
+ #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_16_FORMAT (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 | \
+diff --git a/gfx/cairo/libpixman/src/pixman-radial-gradient.c b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+--- a/gfx/cairo/libpixman/src/pixman-radial-gradient.c
++++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+@@ -395,35 +395,289 @@ radial_get_scanline_narrow (pixman_iter_
+ v.vector[2] += unit.vector[2];
+ }
+ }
+
+ iter->y++;
+ return iter->buffer;
+ }
+
++static uint16_t convert_8888_to_0565(uint32_t color)
++{
++ return CONVERT_8888_TO_0565(color);
++}
++
++static uint32_t *
++radial_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask)
++{
++ /*
++ * 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;
++ uint16_t *buffer = iter->buffer;
++
++ gradient_t *gradient = (gradient_t *)image;
++ radial_gradient_t *radial = (radial_gradient_t *)image;
++ uint16_t *end = buffer + width;
++ 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++)
++ {
++ *buffer = convert_8888_to_0565(
++ radial_compute_color (radial->a, b, c,
++ radial->inva,
++ radial->delta.radius,
++ radial->mindr,
++ &walker,
++ image->common.repeat));
++ }
++
++ b += db;
++ c += dc;
++ dc += ddc;
++ ++buffer;
++ }
++ }
++ 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 */
++
++ *buffer = convert_8888_to_0565 (
++ radial_compute_color (radial->a, b, c,
++ radial->inva,
++ radial->delta.radius,
++ radial->mindr,
++ &walker,
++ image->common.repeat));
++ }
++ else
++ {
++ *buffer = 0;
++ }
++ }
++
++ ++buffer;
++
++ 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_wide (pixman_iter_t *iter, const uint32_t *mask)
+ {
+ uint32_t *buffer = radial_get_scanline_narrow (iter, NULL);
+
+ pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+
+ return buffer;
+ }
+
+ void
+ _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+ {
+- if (iter->flags & ITER_NARROW)
++ if (iter->flags & ITER_16)
++ iter->get_scanline = radial_get_scanline_16;
++ else if (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 (pixman_point_fixed_t * inner,
+ pixman_point_fixed_t * outer,
+ pixman_fixed_t inner_radius,
+ pixman_fixed_t outer_radius,
+ const pixman_gradient_stop_t *stops,
+ int n_stops)
+ {
diff --git a/gfx/cairo/pixman-8888-over-565.patch b/gfx/cairo/pixman-8888-over-565.patch
new file mode 100644
index 000000000..d6ab4618f
--- /dev/null
+++ b/gfx/cairo/pixman-8888-over-565.patch
@@ -0,0 +1,712 @@
+changeset: 96613:3e003f0b8026
+tag: 2pass
+tag: qbase
+tag: qtip
+tag: tip
+user: Jeff Muizelaar <jmuizelaar@mozilla.com>
+date: Thu May 17 19:23:53 2012 -0400
+summary: Bug 757878. Add a fast path for 8888_over_565 with NEON. r=bgirard,joe
+
+diff --git a/gfx/cairo/libpixman/src/pixman-arm-common.h b/gfx/cairo/libpixman/src/pixman-arm-common.h
+--- a/gfx/cairo/libpixman/src/pixman-arm-common.h
++++ b/gfx/cairo/libpixman/src/pixman-arm-common.h
+@@ -355,26 +355,26 @@ scaled_bilinear_scanline_##cputype##_##n
+ 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) \
++ NULL, 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) \
++ NULL, 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) \
++ NULL, 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, \
++ NULL, 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, \
+@@ -404,25 +404,25 @@ scaled_bilinear_scanline_##cputype##_##n
+ 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, \
++ NULL, 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, \
++ NULL, 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, \
++ NULL, 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, \
++ NULL, src_type, uint8_t, dst_type, NORMAL, \
+ FLAG_HAVE_NON_SOLID_MASK)
+
+
+ #endif
+diff --git a/gfx/cairo/libpixman/src/pixman-arm-neon.c b/gfx/cairo/libpixman/src/pixman-arm-neon.c
+--- a/gfx/cairo/libpixman/src/pixman-arm-neon.c
++++ b/gfx/cairo/libpixman/src/pixman-arm-neon.c
+@@ -140,16 +140,33 @@ PIXMAN_ARM_BIND_SCALED_BILINEAR_SRC_DST
+ 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)
++static force_inline void
++pixman_scaled_bilinear_scanline_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)
++{
++ pixman_scaled_bilinear_scanline_8888_8888_SRC_asm_neon (dst, src_top, src_bottom, wt, wb, vx, unit_x, w);
++}
++
+ 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,
+@@ -261,16 +278,38 @@ pixman_blt_neon (uint32_t *src_bits,
+ (uint32_t *)(((char *) src_bits) +
+ src_y * src_stride * 4 + src_x * 4), src_stride);
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+
++static inline void op_bilinear_over_8888_0565(uint16_t *dst, const uint32_t *mask, const uint32_t *src, int width)
++{
++ pixman_composite_over_8888_0565_asm_neon (width, 1, dst, 0, src, 0);
++}
++
++FAST_BILINEAR_MAINLOOP_COMMON (neon_8888_0565_cover_OVER,
++ pixman_scaled_bilinear_scanline_8888_8888_SRC, op_bilinear_over_8888_0565,
++ uint32_t, uint32_t, uint16_t,
++ COVER, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (neon_8888_0565_pad_OVER,
++ pixman_scaled_bilinear_scanline_8888_8888_SRC, op_bilinear_over_8888_0565,
++ uint32_t, uint32_t, uint16_t,
++ PAD, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (neon_8888_0565_none_OVER,
++ pixman_scaled_bilinear_scanline_8888_8888_SRC, op_bilinear_over_8888_0565,
++ uint32_t, uint32_t, uint16_t,
++ NONE, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (neon_8888_0565_normal_OVER,
++ pixman_scaled_bilinear_scanline_8888_8888_SRC, op_bilinear_over_8888_0565,
++ uint32_t, uint32_t, uint16_t,
++ NORMAL, FLAG_NONE)
++
+ 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),
+@@ -414,16 +453,18 @@ static const pixman_fast_path_t arm_neon
+ 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_FAST_PATH (OVER, a8r8g8b8, r5g6b5, neon_8888_0565),
++
+ { PIXMAN_OP_NONE },
+ };
+
+ static pixman_bool_t
+ arm_neon_blt (pixman_implementation_t *imp,
+ uint32_t * src_bits,
+ uint32_t * dst_bits,
+ int src_stride,
+diff --git a/gfx/cairo/libpixman/src/pixman-fast-path.c b/gfx/cairo/libpixman/src/pixman-fast-path.c
+--- a/gfx/cairo/libpixman/src/pixman-fast-path.c
++++ b/gfx/cairo/libpixman/src/pixman-fast-path.c
+@@ -1356,63 +1356,63 @@ scaled_bilinear_scanline_565_565_SRC (ui
+ vx += unit_x;
+ *dst++ = d;
+ }
+ }
+
+ #endif
+
+ FAST_BILINEAR_MAINLOOP_COMMON (565_565_cover_SRC,
+- scaled_bilinear_scanline_565_565_SRC,
++ scaled_bilinear_scanline_565_565_SRC, NULL,
+ uint16_t, uint32_t, uint16_t,
+ COVER, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (565_565_pad_SRC,
+- scaled_bilinear_scanline_565_565_SRC,
++ scaled_bilinear_scanline_565_565_SRC, NULL,
+ uint16_t, uint32_t, uint16_t,
+ PAD, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (565_565_none_SRC,
+- scaled_bilinear_scanline_565_565_SRC,
++ scaled_bilinear_scanline_565_565_SRC, NULL,
+ uint16_t, uint32_t, uint16_t,
+ NONE, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (565_565_normal_SRC,
+- scaled_bilinear_scanline_565_565_SRC,
++ scaled_bilinear_scanline_565_565_SRC, NULL,
+ uint16_t, uint32_t, uint16_t,
+ NORMAL, FLAG_NONE)
+
+ FAST_BILINEAR_MAINLOOP_COMMON (8888_565_cover_OVER,
+- scaled_bilinear_scanline_8888_565_OVER,
++ scaled_bilinear_scanline_8888_565_OVER, NULL,
+ uint32_t, uint32_t, uint16_t,
+ COVER, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (8888_565_pad_OVER,
+- scaled_bilinear_scanline_8888_565_OVER,
++ scaled_bilinear_scanline_8888_565_OVER, NULL,
+ uint32_t, uint32_t, uint16_t,
+ PAD, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (8888_565_none_OVER,
+- scaled_bilinear_scanline_8888_565_OVER,
++ scaled_bilinear_scanline_8888_565_OVER, NULL,
+ uint32_t, uint32_t, uint16_t,
+ NONE, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (8888_565_normal_OVER,
+- scaled_bilinear_scanline_8888_565_OVER,
++ scaled_bilinear_scanline_8888_565_OVER, NULL,
+ uint32_t, uint32_t, uint16_t,
+ NORMAL, FLAG_NONE)
+
+ FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_cover_OVER,
+- scaled_bilinear_scanline_8888_8888_OVER,
++ scaled_bilinear_scanline_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ COVER, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_pad_OVER,
+- scaled_bilinear_scanline_8888_8888_OVER,
++ scaled_bilinear_scanline_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ PAD, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_none_OVER,
+- scaled_bilinear_scanline_8888_8888_OVER,
++ scaled_bilinear_scanline_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ NONE, FLAG_NONE)
+ FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_normal_OVER,
+- scaled_bilinear_scanline_8888_8888_OVER,
++ scaled_bilinear_scanline_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ NORMAL, FLAG_NONE)
+
+ #define REPEAT_MIN_WIDTH 32
+
+ static void
+ fast_composite_tiled_repeat (pixman_implementation_t *imp,
+ pixman_composite_info_t *info)
+diff --git a/gfx/cairo/libpixman/src/pixman-inlines.h b/gfx/cairo/libpixman/src/pixman-inlines.h
+--- a/gfx/cairo/libpixman/src/pixman-inlines.h
++++ b/gfx/cairo/libpixman/src/pixman-inlines.h
+@@ -816,18 +816,48 @@ bilinear_pad_repeat_get_scanline_bounds
+ *
+ * Note: normally the sum of 'weight_top' and 'weight_bottom' is equal to 256,
+ * 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 in 0-255
+ * range and can fit into unsigned byte or be used with 8-bit SIMD
+ * multiplication instructions.
+ */
+-#define FAST_BILINEAR_MAINLOOP_INT(scale_func_name, scanline_func, src_type_t, mask_type_t, \
+- dst_type_t, repeat_mode, flags) \
++
++/* Replace a single "scanline_func" with "fetch_func" & "op_func" to allow optional
++ * two stage processing (bilinear fetch to a temp buffer, followed by unscaled
++ * combine), "op_func" may be NULL, in this case we keep old behavior.
++ * This is ugly and gcc issues some warnings, but works.
++ *
++ * An advice: clang has much better error reporting than gcc for deeply nested macros.
++ */
++
++#define scanline_func(dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buf, mask, src_top, src_bottom, width, \
++ weight_top, weight_bottom, vx, unit_x, max_vx, zero_src) \
++ do { \
++ if (op_func != NULL) \
++ { \
++ fetch_func ((void *)scanline_buf, (mask), (src_top), (src_bottom), (width), \
++ (weight_top), (weight_bottom), (vx), (unit_x), (max_vx), (zero_src)); \
++ ((void (*)(dst_type_t *, const mask_type_t *, const src_type_t *, int)) op_func)\
++ ((dst), (mask), (src_type_t *)scanline_buf, (width)); \
++ } \
++ else \
++ { \
++ fetch_func ((void*)(dst), (mask), (src_top), (src_bottom), (width), (weight_top), \
++ (weight_bottom), (vx), (unit_x), (max_vx), (zero_src)); \
++ } \
++ } while (0)
++
++
++#define SCANLINE_BUFFER_LENGTH 3072
++
++#define FAST_BILINEAR_MAINLOOP_INT(scale_func_name, fetch_func, op_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; \
+@@ -842,16 +872,19 @@ fast_composite_scaled_bilinear ## scale_
+ 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; \
++ \
++ uint64_t stack_scanline_buffer[SCANLINE_BUFFER_LENGTH]; \
++ uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer; \
+ \
+ 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) \
+@@ -914,16 +947,24 @@ fast_composite_scaled_bilinear ## scale_
+ else \
+ { \
+ src_width = src_image->bits.width; \
+ need_src_extension = FALSE; \
+ } \
+ \
+ src_width_fixed = pixman_int_to_fixed (src_width); \
+ } \
++ \
++ if (op_func != NULL && width * sizeof(src_type_t) > sizeof(stack_scanline_buffer)) \
++ { \
++ scanline_buffer = pixman_malloc_ab (width, sizeof(src_type_t)); \
++ \
++ if (!scanline_buffer) \
++ return; \
++ } \
+ \
+ while (--height >= 0) \
+ { \
+ int weight1, weight2; \
+ dst = dst_line; \
+ dst_line += dst_stride; \
+ vx = v.vector[0]; \
+ if (flags & FLAG_HAVE_NON_SOLID_MASK) \
+@@ -956,36 +997,39 @@ fast_composite_scaled_bilinear ## scale_
+ 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); \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, 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); \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, 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); \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, 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 */ \
+@@ -1011,64 +1055,67 @@ fast_composite_scaled_bilinear ## scale_
+ } \
+ 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); \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, 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, \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, 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); \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, 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, \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, 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); \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, 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; \
+@@ -1120,17 +1167,18 @@ fast_composite_scaled_bilinear ## scale_
+ * 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, \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, \
++ dst, scanline_buffer, 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) \
+@@ -1149,41 +1197,47 @@ fast_composite_scaled_bilinear ## scale_
+ * 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); \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, \
++ dst, scanline_buffer, 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, \
++ scanline_func (dst_type_t, mask_type_t, src_type_t, fetch_func, op_func, dst, \
++ scanline_buffer, mask, \
++ src_first_line + src_stride * y1, \
+ src_first_line + src_stride * y2, width, \
+ weight1, weight2, vx, unit_x, max_vx, FALSE); \
+ } \
+ } \
++ if (scanline_buffer != (uint8_t *) stack_scanline_buffer) \
++ free (scanline_buffer); \
+ }
+
+ /* 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, \
++#define FAST_BILINEAR_MAINLOOP_COMMON(scale_func_name, fetch_func, op_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,\
++ FAST_BILINEAR_MAINLOOP_INT(_ ## scale_func_name, fetch_func, op_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)
+diff --git a/gfx/cairo/libpixman/src/pixman-sse2.c b/gfx/cairo/libpixman/src/pixman-sse2.c
+--- a/gfx/cairo/libpixman/src/pixman-sse2.c
++++ b/gfx/cairo/libpixman/src/pixman-sse2.c
+@@ -5404,30 +5404,33 @@ scaled_bilinear_scanline_sse2_8888_8888_
+ if (w & 1)
+ {
+ BILINEAR_INTERPOLATE_ONE_PIXEL (pix1);
+ *dst = pix1;
+ }
+
+ }
+
++/* Add extra NULL argument to the existing bilinear fast paths to indicate
++ * that we don't need two-pass processing */
++
+ FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_cover_SRC,
+- scaled_bilinear_scanline_sse2_8888_8888_SRC,
++ scaled_bilinear_scanline_sse2_8888_8888_SRC, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8888_SRC, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8888_SRC, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8888_SRC, NULL,
+ 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,
+@@ -5505,32 +5508,66 @@ scaled_bilinear_scanline_sse2_8888_8888_
+ }
+
+ w--;
+ dst++;
+ }
+ }
+
+ FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8888_cover_OVER,
+- scaled_bilinear_scanline_sse2_8888_8888_OVER,
++ scaled_bilinear_scanline_sse2_8888_8888_OVER, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8888_OVER, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8888_OVER, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8888_OVER, NULL,
+ uint32_t, uint32_t, uint32_t,
+ NORMAL, FLAG_NONE)
+
++
++/* An example of SSE2 two-stage bilinear_over_8888_0565 fast path, which is implemented
++ as scaled_bilinear_scanline_sse2_8888_8888_SRC + op_bilinear_over_8888_0565 */
++
++void op_bilinear_over_8888_0565(uint16_t *dst, const uint32_t *mask, const uint32_t *src, int width)
++{
++ /* Note: this is not really fast and should be based on 8 pixel loop from sse2_composite_over_8888_0565 */
++ while (--width >= 0)
++ {
++ *dst = composite_over_8888_0565pixel (*src, *dst);
++ src++;
++ dst++;
++ }
++}
++
++FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_0565_cover_OVER,
++ scaled_bilinear_scanline_sse2_8888_8888_SRC, op_bilinear_over_8888_0565,
++ uint32_t, uint32_t, uint16_t,
++ COVER, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_0565_pad_OVER,
++ scaled_bilinear_scanline_sse2_8888_8888_SRC, op_bilinear_over_8888_0565,
++ uint32_t, uint32_t, uint16_t,
++ PAD, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_0565_none_OVER,
++ scaled_bilinear_scanline_sse2_8888_8888_SRC, op_bilinear_over_8888_0565,
++ uint32_t, uint32_t, uint16_t,
++ NONE, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_0565_normal_OVER,
++ scaled_bilinear_scanline_sse2_8888_8888_SRC, op_bilinear_over_8888_0565,
++ uint32_t, uint32_t, uint16_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,
+@@ -5669,29 +5706,29 @@ scaled_bilinear_scanline_sse2_8888_8_888
+ }
+
+ w--;
+ dst++;
+ }
+ }
+
+ FAST_BILINEAR_MAINLOOP_COMMON (sse2_8888_8_8888_cover_OVER,
+- scaled_bilinear_scanline_sse2_8888_8_8888_OVER,
++ scaled_bilinear_scanline_sse2_8888_8_8888_OVER, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8_8888_OVER, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8_8888_OVER, NULL,
+ 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,
++ scaled_bilinear_scanline_sse2_8888_8_8888_OVER, NULL,
+ uint32_t, uint8_t, uint32_t,
+ NORMAL, FLAG_HAVE_NON_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),
+@@ -5808,16 +5845,21 @@ static const pixman_fast_path_t sse2_fas
+ SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, sse2_8888_8888),
+ SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, a8b8g8r8, sse2_8888_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),
+
++ /* and here the needed entries are added to the fast path table */
++
++ SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, r5g6b5, sse2_8888_0565),
++ SIMPLE_BILINEAR_FAST_PATH (OVER, a8b8g8r8, b5g6r5, sse2_8888_0565),
++
+ { PIXMAN_OP_NONE },
+ };
+
+ static pixman_bool_t
+ sse2_blt (pixman_implementation_t *imp,
+ uint32_t * src_bits,
+ uint32_t * dst_bits,
+ int src_stride,
+
diff --git a/gfx/cairo/pixman-android-cpu-detect.patch b/gfx/cairo/pixman-android-cpu-detect.patch
new file mode 100644
index 000000000..130af5130
--- /dev/null
+++ b/gfx/cairo/pixman-android-cpu-detect.patch
@@ -0,0 +1,29 @@
+diff --git a/gfx/cairo/libpixman/src/pixman-cpu.c b/gfx/cairo/libpixman/src/pixman-cpu.c
+--- a/gfx/cairo/libpixman/src/pixman-cpu.c
++++ b/gfx/cairo/libpixman/src/pixman-cpu.c
+@@ -257,19 +257,22 @@ pixman_arm_read_auxv()
+ arm_tests_initialized = TRUE;
+ return;
+ }
+
+ fread(buf, sizeof(char), 1024, f);
+ fclose(f);
+ pos = strstr(buf, ver_token);
+ if (pos) {
+- int ver = *(pos + strlen(ver_token)) - '0';
+- arm_has_v7 = ver >= 7;
+- arm_has_v6 = ver >= 6;
++ char vchar = *(pos + strlen(ver_token));
++ if (vchar >= '0' && vchar <= '9') {
++ int ver = vchar - '0';
++ arm_has_v7 = ver >= 7;
++ arm_has_v6 = ver >= 6;
++ }
+ }
+ arm_has_neon = strstr(buf, "neon") != NULL;
+ arm_has_vfp = strstr(buf, "vfp") != NULL;
+ arm_has_iwmmxt = strstr(buf, "iwmmxt") != NULL;
+ arm_tests_initialized = TRUE;
+ }
+
+ #else
diff --git a/gfx/cairo/pixman-bilinear-fastpath.patch b/gfx/cairo/pixman-bilinear-fastpath.patch
new file mode 100644
index 000000000..b2c5f270c
--- /dev/null
+++ b/gfx/cairo/pixman-bilinear-fastpath.patch
@@ -0,0 +1,287 @@
+changeset: 94061:73a9b24d863a
+tag: bilin
+tag: qbase
+tag: qtip
+tag: tip
+user: Jeff Muizelaar <jmuizelaar@mozilla.com>
+date: Tue May 15 18:26:16 2012 -0400
+summary: Bug 754364. Add bilinear non-repeat and repeat fast paths. r=joe
+
+diff --git a/gfx/cairo/libpixman/src/pixman-fast-path.c b/gfx/cairo/libpixman/src/pixman-fast-path.c
+--- a/gfx/cairo/libpixman/src/pixman-fast-path.c
++++ b/gfx/cairo/libpixman/src/pixman-fast-path.c
+@@ -1186,16 +1186,228 @@ FAST_NEAREST (8888_565_none, 8888, 0565,
+ 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)
+
++static force_inline void
++scaled_bilinear_scanline_8888_565_OVER (uint16_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)
++{
++ while ((w -= 1) >= 0)
++ {
++ uint32_t tl = src_top [pixman_fixed_to_int (vx)];
++ uint32_t tr = src_top [pixman_fixed_to_int (vx) + 1];
++ uint32_t bl = src_bottom [pixman_fixed_to_int (vx)];
++ uint32_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
++ uint32_t src, result;
++ uint16_t d;
++ d = *dst;
++ src = bilinear_interpolation (tl, tr,
++ bl, br,
++ interpolation_coord(vx),
++ wb >> (8 - INTERPOLATION_PRECISION_BITS));
++ vx += unit_x;
++ result = over (src, CONVERT_0565_TO_0888 (d));
++ *dst++ = CONVERT_8888_TO_0565(result);
++ }
++}
++
++static force_inline void
++scaled_bilinear_scanline_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)
++{
++ while ((w -= 1) >= 0)
++ {
++ uint32_t tl = src_top [pixman_fixed_to_int (vx)];
++ uint32_t tr = src_top [pixman_fixed_to_int (vx) + 1];
++ uint32_t bl = src_bottom [pixman_fixed_to_int (vx)];
++ uint32_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
++ uint32_t src;
++ uint32_t d;
++ uint32_t result;
++ d = *dst;
++ src = bilinear_interpolation (tl, tr,
++ bl, br,
++ interpolation_coord(vx),
++ wb >> (8 - INTERPOLATION_PRECISION_BITS));
++ vx += unit_x;
++ *dst++ = over (src, d);
++ }
++}
++
++#if 1
++
++static force_inline void
++scaled_bilinear_scanline_565_565_SRC (uint16_t * dst,
++ const uint32_t * mask,
++ const uint16_t * src_top,
++ const uint16_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)
++{
++ while ((w -= 1) >= 0)
++ {
++ uint16_t tl = src_top [pixman_fixed_to_int (vx)];
++ uint16_t tr = src_top [pixman_fixed_to_int (vx) + 1];
++ uint16_t bl = src_bottom [pixman_fixed_to_int (vx)];
++ uint16_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
++ uint32_t d;
++ d = bilinear_interpolation(CONVERT_0565_TO_8888(tl),
++ CONVERT_0565_TO_8888(tr),
++ CONVERT_0565_TO_8888(bl),
++ CONVERT_0565_TO_8888(br),
++ interpolation_coord(vx),
++ wb >> (8 - INTERPOLATION_PRECISION_BITS));
++ vx += unit_x;
++ *dst++ = CONVERT_8888_TO_0565(d);
++ }
++}
++
++#else
++
++#define SK_G16_MASK_IN_PLACE 0xfc0
++
++static inline uint32_t SkExpand_rgb_16(uint16_t c) {
++
++ return ((c & SK_G16_MASK_IN_PLACE) << 16) | (c & ~SK_G16_MASK_IN_PLACE);
++}
++
++/** Compress an expanded value (from SkExpand_rgb_16) back down to a 16bit
++ color value. The computation yields only 16bits of valid data, but we claim
++ to return 32bits, so that the compiler won't generate extra instructions to
++ "clean" the top 16bits. However, the top 16 can contain garbage, so it is
++ up to the caller to safely ignore them.
++*/
++static inline uint16_t SkCompact_rgb_16(uint32_t c) {
++ return ((c >> 16) & SK_G16_MASK_IN_PLACE) | (c & ~SK_G16_MASK_IN_PLACE);
++}
++// returns expanded * 5bits
++static inline uint32_t Filter_565_Expanded(unsigned x, unsigned y,
++ uint32_t a00, uint32_t a01,
++ uint32_t a10, uint32_t a11) {
++ a00 = SkExpand_rgb_16(a00);
++ a01 = SkExpand_rgb_16(a01);
++ a10 = SkExpand_rgb_16(a10);
++ a11 = SkExpand_rgb_16(a11);
++
++ int xy = x * y >> 3;
++ return a00 * (32 - 2*y - 2*x + xy) +
++ a01 * (2*x - xy) +
++ a10 * (2*y - xy) +
++ a11 * xy;
++}
++
++
++
++static force_inline void
++scaled_bilinear_scanline_565_565_SRC (uint16_t * dst,
++ const uint32_t * mask,
++ const uint16_t * src_top,
++ const uint16_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)
++{
++ while ((w -= 1) >= 0)
++ {
++ uint16_t tl = src_top [pixman_fixed_to_int (vx)];
++ uint16_t tr = src_top [pixman_fixed_to_int (vx) + 1];
++ uint16_t bl = src_bottom [pixman_fixed_to_int (vx)];
++ uint16_t br = src_bottom [pixman_fixed_to_int (vx) + 1];
++
++ uint32_t tmp = Filter_565_Expanded((vx>>12)&0xf, wb>>4, tl, tr, bl, br);
++ vx += unit_x;
++ *dst++ = SkCompact_rgb_16((tmp) >> 5);
++ }
++}
++
++
++#endif
++FAST_BILINEAR_MAINLOOP_COMMON (565_565_cover_SRC,
++ scaled_bilinear_scanline_565_565_SRC,
++ uint16_t, uint32_t, uint16_t,
++ COVER, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (565_565_pad_SRC,
++ scaled_bilinear_scanline_565_565_SRC,
++ uint16_t, uint32_t, uint16_t,
++ PAD, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (565_565_none_SRC,
++ scaled_bilinear_scanline_565_565_SRC,
++ uint16_t, uint32_t, uint16_t,
++ NONE, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (565_565_normal_SRC,
++ scaled_bilinear_scanline_565_565_SRC,
++ uint16_t, uint32_t, uint16_t,
++ NORMAL, FLAG_NONE)
++
++FAST_BILINEAR_MAINLOOP_COMMON (8888_565_cover_OVER,
++ scaled_bilinear_scanline_8888_565_OVER,
++ uint32_t, uint32_t, uint16_t,
++ COVER, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (8888_565_pad_OVER,
++ scaled_bilinear_scanline_8888_565_OVER,
++ uint32_t, uint32_t, uint16_t,
++ PAD, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (8888_565_none_OVER,
++ scaled_bilinear_scanline_8888_565_OVER,
++ uint32_t, uint32_t, uint16_t,
++ NONE, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (8888_565_normal_OVER,
++ scaled_bilinear_scanline_8888_565_OVER,
++ uint32_t, uint32_t, uint16_t,
++ NORMAL, FLAG_NONE)
++
++FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_cover_OVER,
++ scaled_bilinear_scanline_8888_8888_OVER,
++ uint32_t, uint32_t, uint32_t,
++ COVER, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_pad_OVER,
++ scaled_bilinear_scanline_8888_8888_OVER,
++ uint32_t, uint32_t, uint32_t,
++ PAD, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_none_OVER,
++ scaled_bilinear_scanline_8888_8888_OVER,
++ uint32_t, uint32_t, uint32_t,
++ NONE, FLAG_NONE)
++FAST_BILINEAR_MAINLOOP_COMMON (8888_8888_normal_OVER,
++ scaled_bilinear_scanline_8888_8888_OVER,
++ uint32_t, uint32_t, uint32_t,
++ NORMAL, FLAG_NONE)
++
+ #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;
+@@ -1960,16 +2172,20 @@ static const pixman_fast_path_t c_fast_p
+ 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
+ },
+
++ SIMPLE_BILINEAR_FAST_PATH (SRC, r5g6b5, r5g6b5, 565_565),
++ SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, r5g6b5, 8888_565),
++ SIMPLE_BILINEAR_FAST_PATH (OVER, a8r8g8b8, a8r8g8b8, 8888_8888),
++
+ { 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
+diff --git a/gfx/cairo/libpixman/src/pixman-inlines.h b/gfx/cairo/libpixman/src/pixman-inlines.h
+--- a/gfx/cairo/libpixman/src/pixman-inlines.h
++++ b/gfx/cairo/libpixman/src/pixman-inlines.h
+@@ -80,16 +80,21 @@ repeat (pixman_repeat_t repeat, int *c,
+ }
+ return TRUE;
+ }
+
+ #ifdef MOZ_GFX_OPTIMIZE_MOBILE
+ #define LOW_QUALITY_INTERPOLATION
+ #endif
+
++#ifdef LOW_QUALITY_INTERPOLATION
++#define INTERPOLATION_PRECISION_BITS 4
++#else
++#define INTERPOLATION_PRECISION_BITS 8
++#endif
+ static force_inline int32_t
+ interpolation_coord(pixman_fixed_t t)
+ {
+ #ifdef LOW_QUALITY_INTERPOLATION
+ return (t >> 12) & 0xf;
+ #else
+ return (t >> 8) & 0xff;
+ #endif
diff --git a/gfx/cairo/pixman-component-alpha.patch b/gfx/cairo/pixman-component-alpha.patch
new file mode 100644
index 000000000..f6a6f4a95
--- /dev/null
+++ b/gfx/cairo/pixman-component-alpha.patch
@@ -0,0 +1,34 @@
+commit d14b8c688f4cbe6c9289955d9ce7257c07869f9e
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Wed May 6 10:19:04 2009 -0400
+
+ create getter for component alpha
+
+diff --git a/gfx/cairo/libpixman/src/pixman-image.c b/gfx/cairo/libpixman/src/pixman-image.c
+index e80c479..2c49f92 100644
+--- a/gfx/cairo/libpixman/src/pixman-image.c
++++ b/gfx/cairo/libpixman/src/pixman-image.c
+@@ -563,6 +563,11 @@ pixman_image_set_component_alpha (pixman_image_t *image,
+ image->common.component_alpha = component_alpha;
+ }
+
++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,
+diff --git a/gfx/cairo/libpixman/src/pixman.h b/gfx/cairo/libpixman/src/pixman.h
+index 49c39d5..522a866 100644
+--- a/gfx/cairo/libpixman/src/pixman.h
++++ b/gfx/cairo/libpixman/src/pixman.h
+@@ -767,6 +767,7 @@ void pixman_image_set_alpha_map (pixman_image_t
+ int16_t y);
+ void pixman_image_set_component_alpha (pixman_image_t *image,
+ pixman_bool_t component_alpha);
++pixman_bool_t pixman_image_get_component_alpha (pixman_image_t *image);
+ void pixman_image_set_accessors (pixman_image_t *image,
+ pixman_read_memory_func_t read_func,
+ pixman_write_memory_func_t write_func);
diff --git a/gfx/cairo/pixman-dither.patch b/gfx/cairo/pixman-dither.patch
new file mode 100644
index 000000000..633a8d728
--- /dev/null
+++ b/gfx/cairo/pixman-dither.patch
@@ -0,0 +1,310 @@
+diff --git a/gfx/cairo/libpixman/src/pixman-dither.h b/gfx/cairo/libpixman/src/pixman-dither.h
+new file mode 100644
+--- /dev/null
++++ b/gfx/cairo/libpixman/src/pixman-dither.h
+@@ -0,0 +1,51 @@
++#define R16_BITS 5
++#define G16_BITS 6
++#define B16_BITS 5
++
++#define R16_SHIFT (B16_BITS + G16_BITS)
++#define G16_SHIFT (B16_BITS)
++#define B16_SHIFT 0
++
++#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 0x10000100
++
++#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)
++
++// This uses the same dithering technique that Skia does.
++// It is essentially preturbing the lower bit based on the
++// high bit
++static inline uint16_t dither_32_to_16(uint32_t c)
++{
++ uint8_t b = BLUE_8(c);
++ uint8_t g = GREEN_8(c);
++ uint8_t r = RED_8(c);
++ r = ((r << 1) - ((r >> (8 - R16_BITS) << (8 - R16_BITS)) | (r >> R16_BITS))) >> (8 - R16_BITS);
++ g = ((g << 1) - ((g >> (8 - G16_BITS) << (8 - G16_BITS)) | (g >> G16_BITS))) >> (8 - G16_BITS);
++ b = ((b << 1) - ((b >> (8 - B16_BITS) << (8 - B16_BITS)) | (b >> B16_BITS))) >> (8 - B16_BITS);
++ return ((r << R16_SHIFT) | (g << G16_SHIFT) | (b << B16_SHIFT));
++}
++
++static inline uint16_t dither_8888_to_0565(uint32_t color, pixman_bool_t toggle)
++{
++ // alternate between a preturbed truncation and a regular truncation
++ if (toggle) {
++ return dither_32_to_16(color);
++ } else {
++ return CONVERT_8888_TO_0565(color);
++ }
++}
+diff --git a/gfx/cairo/libpixman/src/pixman-linear-gradient.c b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+--- a/gfx/cairo/libpixman/src/pixman-linear-gradient.c
++++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+@@ -26,16 +26,18 @@
+ */
+
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+ #include <stdlib.h>
+ #include "pixman-private.h"
+
++#include "pixman-dither.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;
+@@ -222,25 +224,28 @@ linear_get_scanline_narrow (pixman_iter_
+ return iter->buffer;
+ }
+
+ static uint16_t convert_8888_to_0565(uint32_t color)
+ {
+ return CONVERT_8888_TO_0565(color);
+ }
+
++
++
+ static uint32_t *
+ linear_get_scanline_16 (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;
+ uint16_t * buffer = (uint16_t*)iter->buffer;
++ pixman_bool_t toggle = ((x ^ y) & 1);
+
+ 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;
+ uint16_t *end = buffer + width;
+ pixman_gradient_walker_t walker;
+@@ -294,34 +299,47 @@ linear_get_scanline_16 (pixman_iter_t *
+ 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)
+ {
+- register uint16_t color;
++ register uint32_t color;
++ uint16_t dither_diff;
++ uint16_t color16;
++ uint16_t color16b;
+
+- color = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
+- while (buffer < end)
+- *buffer++ = color;
++ color = _pixman_gradient_walker_pixel (&walker, t);
++ color16 = dither_8888_to_0565(color, toggle);
++ color16b = dither_8888_to_0565(color, toggle^1);
++ // compute the difference
++ dither_diff = color16 ^ color16b;
++ while (buffer < end) {
++ *buffer++ = color16;
++ // use dither_diff to toggle between color16 and color16b
++ color16 ^= dither_diff;
++ toggle ^= 1;
++ }
+ }
+ else
+ {
+ int i;
+
+ i = 0;
+ while (buffer < end)
+ {
+ if (!mask || *mask++)
+ {
+- *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
+- t + next_inc));
++ *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
++ t + next_inc),
++ toggle);
+ }
++ toggle ^= 1;
+ i++;
+ next_inc = inc * i;
+ buffer++;
+ }
+ }
+ }
+ else
+ {
+@@ -340,18 +358,20 @@ linear_get_scanline_16 (pixman_iter_t *
+
+ 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;
+ }
+
+- *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
++ *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t),
++ toggle);
+ }
++ toggle ^= 1;
+
+ ++buffer;
+
+ v.vector[0] += unit.vector[0];
+ v.vector[1] += unit.vector[1];
+ v.vector[2] += unit.vector[2];
+ }
+ }
+@@ -369,17 +389,18 @@ linear_get_scanline_wide (pixman_iter_t
+ pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+
+ return buffer;
+ }
+
+ void
+ _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+ {
+- if (linear_gradient_is_horizontal (
++ // XXX: we can't use this optimization when dithering
++ if (0 && linear_gradient_is_horizontal (
+ iter->image, iter->x, iter->y, iter->width, iter->height))
+ {
+ if (iter->flags & ITER_16)
+ linear_get_scanline_16 (iter, NULL);
+ else if (iter->flags & ITER_NARROW)
+ linear_get_scanline_narrow (iter, NULL);
+ else
+ linear_get_scanline_wide (iter, NULL);
+diff --git a/gfx/cairo/libpixman/src/pixman-radial-gradient.c b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+--- a/gfx/cairo/libpixman/src/pixman-radial-gradient.c
++++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+@@ -29,16 +29,18 @@
+
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+ #include <stdlib.h>
+ #include <math.h>
+ #include "pixman-private.h"
+
++#include "pixman-dither.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)
+ {
+@@ -489,16 +491,17 @@ radial_get_scanline_16 (pixman_iter_t *i
+ * <=> 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;
+ uint16_t *buffer = iter->buffer;
++ pixman_bool_t toggle = ((x ^ y) & 1);
+
+ gradient_t *gradient = (gradient_t *)image;
+ radial_gradient_t *radial = (radial_gradient_t *)image;
+ uint16_t *end = buffer + width;
+ pixman_gradient_walker_t walker;
+ pixman_vector_t v, unit;
+
+ /* reference point is the center of the pixel */
+@@ -575,25 +578,27 @@ radial_get_scanline_16 (pixman_iter_t *i
+ 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++)
+ {
+- *buffer = convert_8888_to_0565(
++ *buffer = dither_8888_to_0565(
+ radial_compute_color (radial->a, b, c,
+ radial->inva,
+ radial->delta.radius,
+ radial->mindr,
+ &walker,
+- image->common.repeat));
++ image->common.repeat),
++ toggle);
+ }
+
++ toggle ^= 1;
+ b += db;
+ c += dc;
+ dc += ddc;
+ ++buffer;
+ }
+ }
+ else
+ {
+@@ -621,31 +626,33 @@ radial_get_scanline_16 (pixman_iter_t *i
+ 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 */
+
+- *buffer = convert_8888_to_0565 (
++ *buffer = dither_8888_to_0565 (
+ radial_compute_color (radial->a, b, c,
+ radial->inva,
+ radial->delta.radius,
+ radial->mindr,
+ &walker,
+- image->common.repeat));
++ image->common.repeat),
++ toggle);
+ }
+ else
+ {
+ *buffer = 0;
+ }
+ }
+
+ ++buffer;
++ toggle ^= 1;
+
+ v.vector[0] += unit.vector[0];
+ v.vector[1] += unit.vector[1];
+ v.vector[2] += unit.vector[2];
+ }
+ }
+
+ iter->y++;
+
diff --git a/gfx/cairo/pixman-enable-altivec-acceleration.patch b/gfx/cairo/pixman-enable-altivec-acceleration.patch
new file mode 100644
index 000000000..d0685cce1
--- /dev/null
+++ b/gfx/cairo/pixman-enable-altivec-acceleration.patch
@@ -0,0 +1,38 @@
+# HG changeset patch
+# Parent d0b3d7af190b3c62397ece2bc33b429d903e455d
+
+diff -r d0b3d7af190b gfx/cairo/libpixman/src/Makefile.in
+--- a/gfx/cairo/libpixman/src/Makefile.in Sat Nov 24 14:26:11 2012 +0100
++++ b/gfx/cairo/libpixman/src/Makefile.in Thu Nov 29 22:20:59 2012 +0100
+@@ -42,6 +42,10 @@
+ endif
+
+ ifdef GNU_CC
++ifeq (ppc,$(findstring ppc,$(OS_TEST)))
++USE_VMX=1
++VMX_CFLAGS=-maltivec
++endif
+ ifeq (86,$(findstring 86,$(OS_TEST)))
+ USE_MMX=1
+ MMX_CFLAGS=-mmmx -Winline
+@@ -156,3 +160,6 @@
+ pixman-sse2.$(OBJ_SUFFIX): COMPILE_CFLAGS += $(SSE2_CFLAGS)
+
+ pixman-arm-neon.$(OBJ_SUFFIX): COMPILE_CFLAGS += $(ARM_NEON_CFLAGS)
++
++pixman-vmx.$(OBJ_SUFFIX): COMPILE_CFLAGS += $(VMX_CFLAGS)
++
+diff -r d0b3d7af190b gfx/cairo/libpixman/src/pixman-vmx.c
+--- a/gfx/cairo/libpixman/src/pixman-vmx.c Sat Nov 24 14:26:11 2012 +0100
++++ b/gfx/cairo/libpixman/src/pixman-vmx.c Thu Nov 29 22:20:59 2012 +0100
+@@ -25,7 +25,10 @@
+ * Based on fbmmx.c by Owen Taylor, Søren Sandmann and Nicholas Miell
+ */
+
++#ifdef HAVE_CONFIG_H
+ #include <config.h>
++#endif
++
+ #include "pixman-private.h"
+ #include "pixman-combine32.h"
+ #include <altivec.h>
diff --git a/gfx/cairo/pixman-export.patch b/gfx/cairo/pixman-export.patch
new file mode 100644
index 000000000..78ca8c669
--- /dev/null
+++ b/gfx/cairo/pixman-export.patch
@@ -0,0 +1,37 @@
+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
+@@ -59,26 +59,33 @@
+ # 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
++# define PIXMAN_EXPORT cairo_public
++#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
+ #else
+ # define PIXMAN_EXPORT
+ #endif
+
++#endif
++
+ /* TLS */
+ #if defined(PIXMAN_NO_TLS)
+
+ # define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \
+ static type name
+ # define PIXMAN_GET_THREAD_LOCAL(name) \
+ (&name)
+
diff --git a/gfx/cairo/pixman-image-transform.patch b/gfx/cairo/pixman-image-transform.patch
new file mode 100644
index 000000000..2f93d315a
--- /dev/null
+++ b/gfx/cairo/pixman-image-transform.patch
@@ -0,0 +1,52 @@
+# HG changeset patch
+# User Jeff Muizelaar <jmuizelaar@mozilla.com>
+# Date 1299543337 18000
+# Node ID 57f411f16517fa3abc31b6b081dd31420c4d9b45
+# Parent e56ecd8b3a68c158025207c5fd081d043e28f5ce
+Bug 637828. Reset the transform on the dest image. r=joe
+
+We can get into a situation where the destination image has a transform
+because we use it as source. The transform set when the image is a source
+sticks around and when we use it as a destination pixman gets confused.
+
+It seems like the code at fault here is really pixman. I think that pixman
+should probably not be using a transformed fetch on the destination image under
+any circumstances.
+
+For example, in this case we're fetching destination pixels from a different
+part of the image than we're storing them to. I can't see any reason for
+wanting this behaviour.
+
+However, reseting the transform seemed like the easiest solution.
+
+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
+@@ -1138,16 +1138,27 @@ _cairo_image_surface_composite (cairo_op
+ return status;
+
+ status = _cairo_image_surface_set_attributes (src, &src_attr,
+ dst_x + width / 2.,
+ dst_y + height / 2.);
+ if (unlikely (status))
+ goto CLEANUP_SURFACES;
+
++ /* we sometimes get destinations with transforms.
++ * we're not equiped to deal with this */
++ {
++ static const pixman_transform_t id = {
++ {{ pixman_fixed_1, 0, 0 },
++ { 0, pixman_fixed_1, 0 },
++ { 0, 0, pixman_fixed_1 }}
++ };
++ pixman_image_set_transform (dst->pixman_image, &id);
++ }
++
+ if (mask) {
+ status = _cairo_image_surface_set_attributes (mask, &mask_attr,
+ dst_x + width / 2.,
+ dst_y + height / 2.);
+ if (unlikely (status))
+ goto CLEANUP_SURFACES;
+
+ pixman_image_composite (_pixman_operator (op),
diff --git a/gfx/cairo/pixman-limits.patch b/gfx/cairo/pixman-limits.patch
new file mode 100644
index 000000000..bc820e673
--- /dev/null
+++ b/gfx/cairo/pixman-limits.patch
@@ -0,0 +1,18 @@
+commit 3c275c75e381184fb3fa0537c4bfce50b44690e7
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Fri Apr 20 00:12:32 2012 -0400
+
+ limits
+
+diff --git a/pixman/pixman-utils.c b/pixman/pixman-utils.c
+index 2ec2594..de05c0e 100644
+--- a/pixman/pixman-utils.c
++++ b/pixman/pixman-utils.c
+@@ -27,6 +27,7 @@
+ #endif
+ #include <stdio.h>
+ #include <stdlib.h>
++#include <limits.h>
+
+ #include "pixman-private.h"
+
diff --git a/gfx/cairo/pixman-lowres-interp.patch b/gfx/cairo/pixman-lowres-interp.patch
new file mode 100644
index 000000000..e2572f000
--- /dev/null
+++ b/gfx/cairo/pixman-lowres-interp.patch
@@ -0,0 +1,222 @@
+summary: Bug 689707. Use lower precision bilinear interpolation. r=joe
+
+diff --git a/gfx/cairo/libpixman/src/pixman-bits-image.c b/gfx/cairo/libpixman/src/pixman-bits-image.c
+--- a/gfx/cairo/libpixman/src/pixman-bits-image.c
++++ b/gfx/cairo/libpixman/src/pixman-bits-image.c
+@@ -124,18 +124,18 @@ bits_image_fetch_pixel_bilinear (bits_im
+ int height = image->height;
+ int x1, y1, x2, y2;
+ uint32_t tl, tr, bl, br;
+ int32_t distx, disty;
+
+ x1 = x - pixman_fixed_1 / 2;
+ y1 = y - pixman_fixed_1 / 2;
+
+- distx = (x1 >> 8) & 0xff;
+- disty = (y1 >> 8) & 0xff;
++ distx = interpolation_coord(x1);
++ disty = interpolation_coord(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)
+ {
+@@ -190,17 +190,17 @@ bits_image_fetch_bilinear_no_repeat_8888
+
+ if (!pixman_transform_point_3d (bits->common.transform, &v))
+ return;
+
+ 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 = (y >> 8) & 0xff;
++ disty = interpolation_coord(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
+@@ -299,17 +299,17 @@ bits_image_fetch_bilinear_no_repeat_8888
+ 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 = (x >> 8) & 0xff;
++ distx = interpolation_coord(x);
+
+ *buffer++ = bilinear_interpolation (0, tr, 0, br, distx, disty);
+
+ x += ux;
+ x_top += ux_top;
+ x_bottom += ux_bottom;
+ mask += mask_inc;
+ }
+@@ -324,17 +324,17 @@ bits_image_fetch_bilinear_no_repeat_8888
+ 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 = (x >> 8) & 0xff;
++ distx = interpolation_coord(x);
+
+ *buffer = bilinear_interpolation (tl, tr, bl, br, distx, disty);
+ }
+
+ buffer++;
+ x += ux;
+ x_top += ux_top;
+ x_bottom += ux_bottom;
+@@ -348,17 +348,17 @@ bits_image_fetch_bilinear_no_repeat_8888
+ 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 = (x >> 8) & 0xff;
++ distx = interpolation_coord(x);
+
+ *buffer = bilinear_interpolation (tl, 0, bl, 0, distx, disty);
+ }
+
+ buffer++;
+ x += ux;
+ x_top += ux_top;
+ x_bottom += ux_bottom;
+@@ -675,18 +675,18 @@ bits_image_fetch_bilinear_affine (pixman
+ const uint8_t *row2;
+
+ if (mask && !mask[i])
+ goto next;
+
+ x1 = x - pixman_fixed_1 / 2;
+ y1 = y - pixman_fixed_1 / 2;
+
+- distx = (x1 >> 8) & 0xff;
+- disty = (y1 >> 8) & 0xff;
++ distx = interpolation_coord(x1);
++ disty = interpolation_coord(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)
+ {
+diff --git a/gfx/cairo/libpixman/src/pixman-inlines.h b/gfx/cairo/libpixman/src/pixman-inlines.h
+--- a/gfx/cairo/libpixman/src/pixman-inlines.h
++++ b/gfx/cairo/libpixman/src/pixman-inlines.h
+@@ -76,16 +76,31 @@ repeat (pixman_repeat_t repeat, int *c,
+ {
+ *c = MOD (*c, size * 2);
+ if (*c >= size)
+ *c = size * 2 - *c - 1;
+ }
+ return TRUE;
+ }
+
++#ifdef MOZ_GFX_OPTIMIZE_MOBILE
++#define LOW_QUALITY_INTERPOLATION
++#endif
++
++static force_inline int32_t
++interpolation_coord(pixman_fixed_t t)
++{
++#ifdef LOW_QUALITY_INTERPOLATION
++ return (t >> 12) & 0xf;
++#else
++ return (t >> 8) & 0xff;
++#endif
++}
++
++
+ #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;
+@@ -122,16 +137,44 @@ bilinear_interpolation (uint32_t tl, uin
+ f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy;
+ r |= ((f >> 16) & 0x000000ff00000000ull) | (f & 0xff000000ull);
+
+ return (uint32_t)(r >> 16);
+ }
+
+ #else
+
++#ifdef LOW_QUALITY_INTERPOLATION
++/* Based on Filter_32_opaque_portable from Skia */
++static force_inline uint32_t
++bilinear_interpolation(uint32_t a00, uint32_t a01,
++ uint32_t a10, uint32_t a11,
++ int x, int y)
++{
++ int xy = x * y;
++ static const uint32_t mask = 0xff00ff;
++
++ int scale = 256 - 16*y - 16*x + xy;
++ uint32_t lo = (a00 & mask) * scale;
++ uint32_t hi = ((a00 >> 8) & mask) * scale;
++
++ scale = 16*x - xy;
++ lo += (a01 & mask) * scale;
++ hi += ((a01 >> 8) & mask) * scale;
++
++ scale = 16*y - xy;
++ lo += (a10 & mask) * scale;
++ hi += ((a10 >> 8) & mask) * scale;
++
++ lo += (a11 & mask) * xy;
++ hi += ((a11 >> 8) & mask) * xy;
++
++ return ((lo >> 8) & mask) | (hi & ~mask);
++}
++#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;
+
+@@ -164,17 +207,17 @@ bilinear_interpolation (uint32_t tl, uin
+
+ /* Alpha */
+ f = (tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
+ + (bl & 0x0000ff00) * distixy + (br & 0x0000ff00) * distxy;
+ r |= f & 0xff000000;
+
+ return r;
+ }
+-
++#endif
+ #endif
+
+ /*
+ * 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'
diff --git a/gfx/cairo/pixman-rename-and-endian.patch b/gfx/cairo/pixman-rename-and-endian.patch
new file mode 100644
index 000000000..41006695f
--- /dev/null
+++ b/gfx/cairo/pixman-rename-and-endian.patch
@@ -0,0 +1,22 @@
+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,18 @@ 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__
+
++#include "cairo-platform.h"
++
+ #include <pixman-version.h>
+
+ /*
+ * Standard integers
+ */
+ #if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || defined (_sgi) || defined (__sun) || defined (sun) || defined (__digital__)
+ # include <inttypes.h>
+ #elif defined (_MSC_VER)
diff --git a/gfx/cairo/pixman-xp-dll-workaround b/gfx/cairo/pixman-xp-dll-workaround
new file mode 100644
index 000000000..e5d1fcbf8
--- /dev/null
+++ b/gfx/cairo/pixman-xp-dll-workaround
@@ -0,0 +1,27 @@
+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
+@@ -114,20 +114,22 @@
+
+ #elif defined(TLS)
+
+ # define PIXMAN_DEFINE_THREAD_LOCAL(type, name) \
+ static TLS type name
+ # define PIXMAN_GET_THREAD_LOCAL(name) \
+ (&name)
+
+-#elif defined(__MINGW32__)
++#elif defined(__MINGW32__) || defined(PIXMAN_USE_XP_DLL_TLS_WORKAROUND)
+
+ # define _NO_W32_PSEUDO_MODIFIERS
+ # include <windows.h>
++#undef IN
++#undef OUT
+
+ # 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) \
diff --git a/gfx/cairo/premultiply-alpha-solid-gradients.patch b/gfx/cairo/premultiply-alpha-solid-gradients.patch
new file mode 100644
index 000000000..665f64969
--- /dev/null
+++ b/gfx/cairo/premultiply-alpha-solid-gradients.patch
@@ -0,0 +1,46 @@
+Fix single and multi-stop solid gradients by multiplying by the alpha component. r=roc
+
+This cairo commit that caused the problem: 2d790daa957471670f4ae0d3b22da89e4ee7111f.
+It was merged into mozilla-central with 1effb72d30cf
+
+diff --git a/gfx/cairo/cairo/src/cairo-pattern.c b/gfx/cairo/cairo/src/cairo-pattern.c
+--- a/gfx/cairo/cairo/src/cairo-pattern.c
++++ b/gfx/cairo/cairo/src/cairo-pattern.c
+@@ -2262,8 +2262,17 @@ _cairo_pattern_acquire_surface (const ca
+
+ if (src->n_stops)
+ {
++ cairo_color_t color;
++
++ /* multiply by alpha */
++ _cairo_color_init_rgba (&color,
++ src->stops->color.red,
++ src->stops->color.green,
++ src->stops->color.blue,
++ src->stops->color.alpha);
++
+ _cairo_pattern_init_solid (&solid,
+- &src->stops->color,
++ &color,
+ CAIRO_CONTENT_COLOR_ALPHA);
+ }
+ else
+@@ -2295,9 +2304,17 @@ _cairo_pattern_acquire_surface (const ca
+ }
+ if (i == src->n_stops) {
+ cairo_solid_pattern_t solid;
++ cairo_color_t color;
++
++ /* multiply by alpha */
++ _cairo_color_init_rgba (&color,
++ src->stops->color.red,
++ src->stops->color.green,
++ src->stops->color.blue,
++ src->stops->color.alpha);
+
+ _cairo_pattern_init_solid (&solid,
+- &src->stops->color,
++ &color,
+ CAIRO_CONTENT_COLOR_ALPHA);
+
+ status =
diff --git a/gfx/cairo/quartz-cache-CGImageRef.patch b/gfx/cairo/quartz-cache-CGImageRef.patch
new file mode 100644
index 000000000..f27f03081
--- /dev/null
+++ b/gfx/cairo/quartz-cache-CGImageRef.patch
@@ -0,0 +1,173 @@
+changeset: 42954:7881873b2b5d
+user: Robert O'Callahan <robert@ocallahan.org>
+date: Tue Jun 01 11:19:45 2010 +1200
+summary: Bug 552537. Cache the CGImageRef that we create for a CGBitmapContext so that we can take advantage of Quartz caching optimizations. r=jrmuizel
+
+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
+@@ -49,16 +49,24 @@ typedef struct cairo_quartz_surface {
+
+ CGContextRef cgContext;
+ CGAffineTransform cgContextBaseCTM;
+
+ void *imageData;
+ cairo_surface_t *imageSurfaceEquiv;
+
+ cairo_surface_clipper_t clipper;
++
++ /**
++ * If non-null, this is a CGImage representing the contents of the surface.
++ * We clear this out before any painting into the surface, so that we
++ * don't force a copy to be created.
++ */
++ CGImageRef bitmapContextImage;
++
+ cairo_rectangle_int_t extents;
+ } cairo_quartz_surface_t;
+
+ typedef struct cairo_quartz_image_surface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t 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
+@@ -1134,19 +1134,24 @@ _cairo_surface_to_cgimage (cairo_surface
+ if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
+ if (IS_EMPTY(surface)) {
+ *image_out = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
+- *image_out = CGBitmapContextCreateImage (surface->cgContext);
+- if (*image_out)
+- return CAIRO_STATUS_SUCCESS;
++ if (!surface->bitmapContextImage) {
++ surface->bitmapContextImage =
++ CGBitmapContextCreateImage (surface->cgContext);
++ }
++ if (surface->bitmapContextImage) {
++ *image_out = CGImageRetain (surface->bitmapContextImage);
++ return CAIRO_STATUS_SUCCESS;
++ }
+ }
+ }
+
+ if (stype != CAIRO_SURFACE_TYPE_IMAGE) {
+ status = _cairo_surface_acquire_source_image (source,
+ &isurf, &image_extra);
+ if (status)
+ return status;
+@@ -1589,16 +1594,29 @@ _cairo_quartz_setup_radial_source (cairo
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+ state->action = DO_SHADING;
+ }
+
+ /**
++ * Call this before any operation that can modify the contents of a
++ * cairo_quartz_surface_t.
++ */
++static void
++_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface)
++{
++ if (surface->bitmapContextImage) {
++ CGImageRelease (surface->bitmapContextImage);
++ surface->bitmapContextImage = NULL;
++ }
++}
++
++/**
+ * Sets up internal state to be used to draw the source mask, stored in
+ * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on
+ * surface->cgContext.
+ */
+ static cairo_quartz_drawing_state_t
+ _cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
+ const cairo_pattern_t *source,
+ cairo_operator_t op,
+@@ -1609,16 +1627,18 @@ _cairo_quartz_setup_state (cairo_quartz_
+ cairo_status_t status;
+
+ state.context = context;
+ state.image = NULL;
+ state.imageSurface = NULL;
+ state.shading = NULL;
+ state.pattern = NULL;
+
++ _cairo_quartz_surface_will_change (surface);
++
+ // Save before we change the pattern, colorspace, etc. so that
+ // we can restore and make sure that quartz releases our
+ // pattern (which may be stack allocated)
+ CGContextSaveGState(context);
+
+ CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
+
+ status = _cairo_quartz_surface_set_cairo_operator (surface, op);
+@@ -1936,16 +1956,21 @@ _cairo_quartz_surface_finish (void *abst
+ /* 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->bitmapContextImage) {
++ CGImageRelease (surface->bitmapContextImage);
++ surface->bitmapContextImage = NULL;
++ }
++
+ if (surface->imageSurfaceEquiv) {
+ cairo_surface_destroy (surface->imageSurfaceEquiv);
+ surface->imageSurfaceEquiv = NULL;
+ }
+
+ if (surface->imageData) {
+ free (surface->imageData);
+ surface->imageData = NULL;
+@@ -2006,16 +2031,18 @@ _cairo_quartz_surface_acquire_dest_image
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t status;
+
+ ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
+
++ _cairo_quartz_surface_will_change (surface);
++
+ status = _cairo_quartz_get_image (surface, image_out);
+ if (status)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ *image_rect = surface->extents;
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+@@ -2939,16 +2966,17 @@ _cairo_quartz_surface_create_internal (C
+ */
+ CGContextSaveGState (cgContext);
+
+ surface->cgContext = cgContext;
+ surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
+
+ surface->imageData = NULL;
+ surface->imageSurfaceEquiv = NULL;
++ surface->bitmapContextImage = NULL;
+
+ 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
+
diff --git a/gfx/cairo/quartz-cg-layers-fix-fallback.patch b/gfx/cairo/quartz-cg-layers-fix-fallback.patch
new file mode 100644
index 000000000..bcebab350
--- /dev/null
+++ b/gfx/cairo/quartz-cg-layers-fix-fallback.patch
@@ -0,0 +1,42 @@
+Bug 572912. Fix surface type passed to cairo_quartz_surface_create during fallback for CGLayers. r=jrmuizel
+
+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
+@@ -2026,17 +2026,17 @@ _cairo_quartz_surface_acquire_image (voi
+
+ /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */
+
+ status = _cairo_quartz_get_image (surface, image_out);
+
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) {
+ /* copy the layer into a Quartz bitmap context so we can get the data */
+ cairo_surface_t *tmp =
+- cairo_quartz_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
++ cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32,
+ surface->extents.width,
+ surface->extents.height);
+ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp;
+
+ /* if surface creation failed, we won't have a Quartz surface here */
+ if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ &&
+ tmp_surface->imageSurfaceEquiv) {
+ CGContextSaveGState (tmp_surface->cgContext);
+@@ -2049,16 +2049,17 @@ _cairo_quartz_surface_acquire_image (voi
+ CGContextDrawLayerAtPoint (tmp_surface->cgContext,
+ CGPointMake (0.0, 0.0),
+ surface->cgLayer);
+ CGContextRestoreGState (tmp_surface->cgContext);
+
+ *image_out = (cairo_image_surface_t*)
+ cairo_surface_reference(tmp_surface->imageSurfaceEquiv);
+ *image_extra = tmp;
++ status = CAIRO_STATUS_SUCCESS;
+ } else {
+ cairo_surface_destroy (tmp);
+ }
+ }
+
+ if (status)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
diff --git a/gfx/cairo/quartz-cglayers.patch b/gfx/cairo/quartz-cglayers.patch
new file mode 100644
index 000000000..bb3d44d9e
--- /dev/null
+++ b/gfx/cairo/quartz-cglayers.patch
@@ -0,0 +1,715 @@
+changeset: 42959:e1964291f8ff
+user: Robert O'Callahan <robert@ocallahan.org>
+date: Tue Jun 01 11:33:23 2010 +1200
+summary: Bug 568189. Implement CGLayer-backed cairo-quartz surfaces. r=jrmuizel
+
+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
+@@ -57,16 +57,21 @@ typedef struct cairo_quartz_surface {
+
+ /**
+ * If non-null, this is a CGImage representing the contents of the surface.
+ * We clear this out before any painting into the surface, so that we
+ * don't force a copy to be created.
+ */
+ CGImageRef bitmapContextImage;
+
++ /**
++ * If non-null, this is the CGLayer for the surface.
++ */
++ CGLayerRef cgLayer;
++
+ cairo_rectangle_int_t extents;
+ } cairo_quartz_surface_t;
+
+ typedef struct cairo_quartz_image_surface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t 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
+@@ -1110,18 +1110,17 @@ CreateRepeatingRadialGradientFunction (c
+ static void
+ DataProviderReleaseCallback (void *info, const void *data, size_t size)
+ {
+ cairo_surface_t *surface = (cairo_surface_t *) info;
+ cairo_surface_destroy (surface);
+ }
+
+ static cairo_status_t
+-_cairo_surface_to_cgimage (cairo_surface_t *target,
+- cairo_surface_t *source,
++_cairo_surface_to_cgimage (cairo_surface_t *source,
+ CGImageRef *image_out)
+ {
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_surface_type_t stype = cairo_surface_get_type (source);
+ cairo_image_surface_t *isurf;
+ CGImageRef image;
+ void *image_extra;
+
+@@ -1267,17 +1266,17 @@ _cairo_quartz_cairo_repeating_surface_pa
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ spattern = (cairo_surface_pattern_t *) apattern;
+ pat_surf = spattern->surface;
+
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image);
++ status = _cairo_surface_to_cgimage (pat_surf, &image);
+ if (status)
+ return status;
+ if (image == NULL)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ info = malloc(sizeof(SurfacePatternDrawInfo));
+ if (!info)
+ return CAIRO_STATUS_NO_MEMORY;
+@@ -1339,33 +1338,39 @@ _cairo_quartz_cairo_repeating_surface_pa
+ }
+
+ typedef enum {
+ DO_SOLID,
+ DO_SHADING,
+ DO_PATTERN,
+ DO_IMAGE,
+ DO_TILED_IMAGE,
++ DO_LAYER,
+ DO_UNSUPPORTED,
+ DO_NOTHING
+ } cairo_quartz_action_t;
+
+ /* State used during a drawing operation. */
+ typedef struct {
+ CGContextRef context;
+ cairo_quartz_action_t action;
+
+- // Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE
++ // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
+ CGAffineTransform transform;
+
+ // Used with DO_IMAGE and DO_TILED_IMAGE
+ CGImageRef image;
+ cairo_surface_t *imageSurface;
++
++ // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
+ CGRect imageRect;
+
++ // Used with DO_LAYER
++ CGLayerRef layer;
++
+ // Used with DO_SHADING
+ CGShadingRef shading;
+
+ // Used with DO_PATTERN
+ CGPatternRef pattern;
+ } cairo_quartz_drawing_state_t;
+
+ static void
+@@ -1423,17 +1428,17 @@ _cairo_quartz_setup_fallback_source (cai
+ _cairo_pattern_transform (&pattern.base,
+ &fallback->device_transform_inverse);
+ status = _cairo_surface_paint (fallback,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL);
+ }
+ #endif
+
+- status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
++ status = _cairo_surface_to_cgimage (fallback, &img);
+ if (status) {
+ state->action = DO_UNSUPPORTED;
+ return;
+ }
+ if (img == NULL) {
+ state->action = DO_NOTHING;
+ return;
+ }
+@@ -1624,16 +1629,17 @@ _cairo_quartz_setup_state (cairo_quartz_
+ {
+ CGContextRef context = surface->cgContext;
+ cairo_quartz_drawing_state_t state;
+ cairo_status_t status;
+
+ state.context = context;
+ state.image = NULL;
+ state.imageSurface = NULL;
++ state.layer = NULL;
+ state.shading = NULL;
+ state.pattern = NULL;
+
+ _cairo_quartz_surface_will_change (surface);
+
+ // Save before we change the pattern, colorspace, etc. so that
+ // we can restore and make sure that quartz releases our
+ // pattern (which may be stack allocated)
+@@ -1689,33 +1695,43 @@ _cairo_quartz_setup_state (cairo_quartz_
+ 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;
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
++ cairo_matrix_invert(&m);
++ _cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform);
++
++ if (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 && source->extend == CAIRO_EXTEND_NONE) {
++ state.imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
++ state.layer = quartz_surf->cgLayer;
++ state.action = DO_LAYER;
++ return state;
++ }
++ }
++
++ status = _cairo_surface_to_cgimage (pat_surf, &img);
+ if (status) {
+ state.action = DO_UNSUPPORTED;
+ return state;
+ }
+ if (img == NULL) {
+ state.action = DO_NOTHING;
+ return state;
+ }
+
+ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+
+ state.image = img;
+
+- cairo_matrix_invert(&m);
+- _cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform);
+-
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+ if (source->extend == CAIRO_EXTEND_NONE) {
+ state.imageRect = CGRectMake (0, 0, extents.width, extents.height);
+ state.action = DO_IMAGE;
+ return state;
+ }
+@@ -1820,33 +1836,48 @@ _cairo_quartz_teardown_state (cairo_quar
+
+ CGContextRestoreGState(state->context);
+ }
+
+
+ static void
+ _cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
+ {
+- assert (state && state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE));
++ assert (state &&
++ ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) ||
++ (state->layer && state->action == DO_LAYER)));
+
+ CGContextConcatCTM (state->context, state->transform);
+ CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
+ CGContextScaleCTM (state->context, 1, -1);
+
+- if (state->action == DO_IMAGE) {
+- CGContextDrawImage (state->context, state->imageRect, state->image);
++ if (state->action == DO_TILED_IMAGE) {
++ CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
++ /* no need to worry about unbounded operators, since tiled images
++ fill the entire clip region */
++ } else {
++ if (state->action == DO_LAYER) {
++ /* Note that according to Apple docs it's completely legal
++ * to draw a CGLayer to any CGContext, even one it wasn't
++ * created for.
++ */
++ CGContextDrawLayerAtPoint (state->context, state->imageRect.origin,
++ state->layer);
++ } else {
++ CGContextDrawImage (state->context, state->imageRect, state->image);
++ }
++
+ if (!_cairo_operator_bounded_by_source (op)) {
+ CGContextBeginPath (state->context);
+ CGContextAddRect (state->context, state->imageRect);
+ CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
+ CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
+ CGContextEOFillPath (state->context);
+ }
+- } else
+- CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
++ }
+ }
+
+
+ /*
+ * get source/dest image implementation
+ */
+
+ /* Read the image from the surface's front buffer */
+@@ -1971,95 +2002,153 @@ _cairo_quartz_surface_finish (void *abst
+ surface->imageSurfaceEquiv = NULL;
+ }
+
+ if (surface->imageData) {
+ 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_acquire_image (void *abstract_surface,
++ cairo_image_surface_t **image_out,
++ void **image_extra)
+ {
+ cairo_int_status_t status;
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+
+- //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
++ *image_extra = NULL;
++
++ /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */
+
+ status = _cairo_quartz_get_image (surface, image_out);
++
++ if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) {
++ /* copy the layer into a Quartz bitmap context so we can get the data */
++ cairo_surface_t *tmp =
++ cairo_quartz_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
++ surface->extents.width,
++ surface->extents.height);
++ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp;
++
++ /* if surface creation failed, we won't have a Quartz surface here */
++ if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ &&
++ tmp_surface->imageSurfaceEquiv) {
++ CGContextSaveGState (tmp_surface->cgContext);
++ CGContextTranslateCTM (tmp_surface->cgContext, 0, surface->extents.height);
++ CGContextScaleCTM (tmp_surface->cgContext, 1, -1);
++ /* Note that according to Apple docs it's completely legal
++ * to draw a CGLayer to any CGContext, even one it wasn't
++ * created for.
++ */
++ CGContextDrawLayerAtPoint (tmp_surface->cgContext,
++ CGPointMake (0.0, 0.0),
++ surface->cgLayer);
++ CGContextRestoreGState (tmp_surface->cgContext);
++
++ *image_out = (cairo_image_surface_t*)
++ cairo_surface_reference(tmp_surface->imageSurfaceEquiv);
++ *image_extra = tmp;
++ } else {
++ cairo_surface_destroy (tmp);
++ }
++ }
++
+ if (status)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+- *image_extra = NULL;
+-
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static void
+ _cairo_quartz_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+ {
+ cairo_surface_destroy ((cairo_surface_t *) image);
++
++ if (image_extra) {
++ cairo_surface_destroy ((cairo_surface_t *) image_extra);
++ }
+ }
+
+
+ static cairo_status_t
+ _cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+- cairo_int_status_t status;
+
+ ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
+
+- _cairo_quartz_surface_will_change (surface);
+-
+- status = _cairo_quartz_get_image (surface, image_out);
+- if (status)
+- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+-
+ *image_rect = surface->extents;
+ *image_extra = NULL;
+
+- return CAIRO_STATUS_SUCCESS;
++ _cairo_quartz_surface_will_change (surface);
++
++ return _cairo_quartz_surface_acquire_image (abstract_surface,
++ image_out, image_extra);
+ }
+
+ static void
+ _cairo_quartz_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+ cairo_rectangle_int_t *image_rect,
+ void *image_extra)
+ {
+- //cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+-
+- //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
++ /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */
+
+ cairo_surface_destroy ((cairo_surface_t *) image);
++
++ if (image_extra) {
++ /* we need to write the data from the temp surface back to the layer */
++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
++ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra;
++ CGImageRef img;
++ cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img);
++ if (status) {
++ cairo_surface_destroy (&tmp_surface->base);
++ return;
++ }
++
++ CGContextSaveGState (surface->cgContext);
++ CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height);
++ CGContextScaleCTM (surface->cgContext, 1, -1);
++ CGContextDrawImage (surface->cgContext,
++ CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height),
++ img);
++ CGContextRestoreGState (surface->cgContext);
++
++ cairo_surface_destroy (&tmp_surface->base);
++ }
+ }
+
+ static cairo_surface_t *
+ _cairo_quartz_surface_create_similar (void *abstract_surface,
+ cairo_content_t content,
+ int width,
+ int height)
+ {
+- /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
+-
++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_format_t format;
+
++ if (surface->cgLayer)
++ return cairo_quartz_surface_create_cg_layer (abstract_surface, 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;
+@@ -2113,17 +2202,17 @@ _cairo_quartz_surface_clone_similar (voi
+ _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
+ qsurf->extents.width, qsurf->extents.height);
+ *clone_offset_x = 0;
+ *clone_offset_y = 0;
+ return CAIRO_STATUS_SUCCESS;
+ }
+ }
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image);
++ status = _cairo_surface_to_cgimage (src, &quartz_image);
+ if (status)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ new_format = CAIRO_FORMAT_ARGB32; /* assumed */
+ if (_cairo_surface_is_image (src)) {
+ new_format = ((cairo_image_surface_t *) src)->format;
+ }
+
+@@ -2194,17 +2283,18 @@ _cairo_quartz_surface_paint (void *abstr
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextFillRect (state.context, CGRectMake(surface->extents.x,
+ surface->extents.y,
+ surface->extents.width,
+ surface->extents.height));
+ } else if (state.action == DO_SHADING) {
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+- } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ _cairo_quartz_teardown_state (&state);
+
+ ND((stderr, "-- paint\n"));
+@@ -2291,17 +2381,18 @@ _cairo_quartz_surface_fill (void *abstra
+ // with the shading
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextClip (state.context);
+ else
+ CGContextEOClip (state.context);
+
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+- } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextClip (state.context);
+ else
+ CGContextEOClip (state.context);
+
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+@@ -2416,17 +2507,18 @@ _cairo_quartz_surface_stroke (void *abst
+ if (rv)
+ goto BAIL;
+
+ if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
+ path_for_unbounded = CGContextCopyPathPtr (state.context);
+
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextStrokePath (state.context);
+- } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
+ CGContextReplacePathWithStrokedPath (state.context);
+ CGContextClip (state.context);
+
+ CGContextSetCTM (state.context, origCTM);
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action == DO_SHADING) {
+ CGContextReplacePathWithStrokedPath (state.context);
+ CGContextClip (state.context);
+@@ -2511,17 +2603,18 @@ _cairo_quartz_surface_show_glyphs (void
+ &glyph_extents, NULL);
+ state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
+ } else {
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextSetTextDrawingMode (state.context, kCGTextFill);
+- } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || state.action == DO_SHADING) {
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_SHADING || state.action == DO_LAYER) {
+ CGContextSetTextDrawingMode (state.context, kCGTextClip);
+ isClipping = TRUE;
+ } else {
+ if (state.action != DO_NOTHING)
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto BAIL;
+ }
+
+@@ -2622,17 +2715,18 @@ _cairo_quartz_surface_show_glyphs (void
+
+ CGContextShowGlyphsWithAdvances (state.context,
+ cg_glyphs,
+ cg_advances,
+ num_glyphs);
+
+ CGContextSetCTM (state.context, ctm);
+
+- if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++ if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action == DO_SHADING) {
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+ }
+
+ BAIL:
+ if (didForceFontSmoothing)
+@@ -2679,17 +2773,17 @@ _cairo_quartz_surface_mask_with_surface
+ cairo_clip_t *clip)
+ {
+ CGRect rect;
+ CGImageRef img;
+ cairo_surface_t *pat_surf = mask->surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ CGAffineTransform ctm, mask_matrix;
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
++ status = _cairo_surface_to_cgimage (pat_surf, &img);
+ if (status)
+ return status;
+ if (img == NULL) {
+ if (!_cairo_operator_bounded_by_mask (op))
+ CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+@@ -2869,17 +2963,17 @@ _cairo_quartz_surface_clipper_intersect_
+ }
+
+ // XXXtodo implement show_page; need to figure out how to handle begin/end
+
+ static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
+ CAIRO_SURFACE_TYPE_QUARTZ,
+ _cairo_quartz_surface_create_similar,
+ _cairo_quartz_surface_finish,
+- _cairo_quartz_surface_acquire_source_image,
++ _cairo_quartz_surface_acquire_image,
+ _cairo_quartz_surface_release_source_image,
+ _cairo_quartz_surface_acquire_dest_image,
+ _cairo_quartz_surface_release_dest_image,
+ _cairo_quartz_surface_clone_similar,
+ NULL, /* composite */
+ NULL, /* fill_rectangles */
+ NULL, /* composite_trapezoids */
+ NULL, /* create_span_renderer */
+@@ -2950,16 +3044,17 @@ _cairo_quartz_surface_create_internal (C
+ CGContextSaveGState (cgContext);
+
+ surface->cgContext = cgContext;
+ surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
+
+ surface->imageData = NULL;
+ surface->imageSurfaceEquiv = NULL;
+ surface->bitmapContextImage = NULL;
++ surface->cgLayer = NULL;
+
+ 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
+@@ -3002,16 +3097,88 @@ cairo_quartz_surface_create_for_cg_conte
+ // create_internal will have set an error
+ return (cairo_surface_t*) surf;
+ }
+
+ return (cairo_surface_t *) surf;
+ }
+
+ /**
++ * cairo_quartz_cglayer_surface_create_similar
++ * @surface: The returned surface can be efficiently drawn into this
++ * destination surface (if tiling is not used)."
++ * @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
++ * with CAIRO_CONTENT_COLOR_ALPHA.
++ * 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,
++ 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, CAIRO_CONTENT_COLOR_ALPHA,
++ 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, CAIRO_CONTENT_COLOR_ALPHA,
++ 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, CAIRO_CONTENT_COLOR_ALPHA,
++ 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
+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
+@@ -45,16 +45,21 @@
+ 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_cg_layer (cairo_surface_t *surface,
++ 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 CGContextRef
+ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+
+ cairo_public CGContextRef
+
diff --git a/gfx/cairo/quartz-check-imageSurfaceEquiv.patch b/gfx/cairo/quartz-check-imageSurfaceEquiv.patch
new file mode 100644
index 000000000..1d84ab27e
--- /dev/null
+++ b/gfx/cairo/quartz-check-imageSurfaceEquiv.patch
@@ -0,0 +1,36 @@
+From: Daniel Holbert <dholbert@cs.stanford.edu>
+Bug 612662 patch 3: Drop cairo_quartz_surface_t's "imageSurfaceEquiv" member if we fail to create it. r=roc a=blocking-final+
+
+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
+@@ -3152,17 +3152,28 @@ cairo_quartz_surface_create (cairo_forma
+ if (surf->base.status) {
+ CGContextRelease (cgc);
+ free (imageData);
+ // create_internal will have set an error
+ return (cairo_surface_t*) surf;
+ }
+
+ surf->imageData = imageData;
+- surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
++
++ cairo_surface_t* tmpImageSurfaceEquiv =
++ cairo_image_surface_create_for_data (imageData, format,
++ width, height, stride);
++
++ if (cairo_surface_status (tmpImageSurfaceEquiv)) {
++ // Tried & failed to create an imageSurfaceEquiv!
++ cairo_surface_destroy (tmpImageSurfaceEquiv);
++ surf->imageSurfaceEquiv = NULL;
++ } else {
++ surf->imageSurfaceEquiv = tmpImageSurfaceEquiv;
++ }
+
+ return (cairo_surface_t *) surf;
+ }
+
+ /**
+ * cairo_quartz_surface_get_cg_context
+ * @surface: the Cairo Quartz surface
+ *
diff --git a/gfx/cairo/quartz-const-globals.patch b/gfx/cairo/quartz-const-globals.patch
new file mode 100644
index 000000000..8db32270a
--- /dev/null
+++ b/gfx/cairo/quartz-const-globals.patch
@@ -0,0 +1,134 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1249558626 -43200
+# Node ID 963b9451ad305924738d05d997a640698cd3af91
+# Parent e564f3ab4ea6e3b5dd9c4e9e6042d3a84c229dde
+Bug 508730. Clean up Quartz gradient code by moving some local variables to static const globals. r=jmuizelaar
+
+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
+@@ -684,51 +684,50 @@ ComputeGradientValue (void *info, const
+ 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 float 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
++};
++
+ static CGFunctionRef
+ CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
+ {
+ cairo_pattern_t *pat;
+ float input_value_range[2] = { 0.f, 1.f };
+- float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
+- CGFunctionCallbacks callbacks = {
+- 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
+- };
+
+ if (_cairo_pattern_create_copy (&pat, &gpat->base))
+ /* quartz doesn't deal very well with malloc failing, so there's
+ * not much point in us trying either */
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+- output_value_ranges,
+- &callbacks);
++ gradient_output_value_ranges,
++ &gradient_callbacks);
+ }
+
+ static CGFunctionRef
+ CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
+ const cairo_gradient_pattern_t *gpat,
+ CGPoint *start, CGPoint *end,
+ CGAffineTransform matrix)
+ {
+ cairo_pattern_t *pat;
+ float input_value_range[2];
+- float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
+- CGFunctionCallbacks callbacks = {
+- 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
+- };
+
+ CGPoint mstart, mend;
+
+ double dx, dy;
+ int x_rep_start = 0, x_rep_end = 0;
+ int y_rep_start = 0, y_rep_end = 0;
+
+ int rep_start, rep_end;
+@@ -787,18 +786,18 @@ CreateRepeatingLinearGradientFunction (c
+ /* quartz doesn't deal very well with malloc failing, so there's
+ * not much point in us trying either */
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+- output_value_ranges,
+- &callbacks);
++ gradient_output_value_ranges,
++ &gradient_callbacks);
+ }
+
+ static void
+ UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center,
+ double dr, double dx, double dy,
+ double x, double y)
+ {
+ /* Compute a parameter t such that a circle centered at
+@@ -847,20 +846,16 @@ CreateRepeatingRadialGradientFunction (c
+ const cairo_gradient_pattern_t *gpat,
+ CGPoint *start, double *start_radius,
+ CGPoint *end, double *end_radius)
+ {
+ CGRect clip = CGContextGetClipBoundingBox (surface->cgContext);
+ CGAffineTransform transform;
+ cairo_pattern_t *pat;
+ float input_value_range[2];
+- float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
+- CGFunctionCallbacks callbacks = {
+- 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
+- };
+ CGPoint *inner;
+ double *inner_radius;
+ CGPoint *outer;
+ double *outer_radius;
+ /* minimum and maximum t-parameter values that will make our gradient
+ cover the clipBox */
+ double t_min, t_max, t_temp;
+ /* outer minus inner */
+@@ -927,18 +922,18 @@ CreateRepeatingRadialGradientFunction (c
+ /* quartz doesn't deal very well with malloc failing, so there's
+ * not much point in us trying either */
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+- output_value_ranges,
+- &callbacks);
++ gradient_output_value_ranges,
++ &gradient_callbacks);
+ }
+
+ /* Obtain a CGImageRef from a #cairo_surface_t * */
+
+ static void
+ DataProviderReleaseCallback (void *info, const void *data, size_t size)
+ {
+ cairo_surface_t *surface = (cairo_surface_t *) info;
diff --git a/gfx/cairo/quartz-create-for-data.patch b/gfx/cairo/quartz-create-for-data.patch
new file mode 100644
index 000000000..ae374fafe
--- /dev/null
+++ b/gfx/cairo/quartz-create-for-data.patch
@@ -0,0 +1,309 @@
+diff --git a/gfx/cairo/README b/gfx/cairo/README
+--- a/gfx/cairo/README
++++ b/gfx/cairo/README
+@@ -71,16 +71,18 @@ quartz-cache-CGImageRef.patch: cache CGI
+ 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
+
+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
+@@ -63,16 +63,18 @@ typedef struct cairo_quartz_surface {
+ CGImageRef bitmapContextImage;
+
+ /**
+ * If non-null, this is the CGLayer for the surface.
+ */
+ CGLayerRef cgLayer;
+
+ cairo_rectangle_int_t extents;
++
++ cairo_bool_t ownsData;
+ } cairo_quartz_surface_t;
+
+ typedef struct cairo_quartz_image_surface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t extents;
+
+ CGImageRef image;
+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
+@@ -1880,20 +1880,21 @@ _cairo_quartz_surface_finish (void *abst
+ surface->cgContext = NULL;
+
+ if (surface->bitmapContextImage) {
+ CGImageRelease (surface->bitmapContextImage);
+ surface->bitmapContextImage = NULL;
+ }
+
+ if (surface->imageSurfaceEquiv) {
+- _cairo_image_surface_assume_ownership_of_data (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) {
++ } else if (surface->imageData && surface->ownsData) {
+ free (surface->imageData);
+ }
+
+ surface->imageData = NULL;
+
+ if (surface->cgLayer) {
+ CGLayerRelease (surface->cgLayer);
+ }
+@@ -2888,16 +2889,17 @@ _cairo_quartz_surface_create_internal (C
+
+ surface->cgContext = cgContext;
+ surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
+
+ surface->imageData = NULL;
+ surface->imageSurfaceEquiv = NULL;
+ surface->bitmapContextImage = NULL;
+ surface->cgLayer = NULL;
++ 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
+@@ -3031,23 +3033,103 @@ cairo_quartz_surface_create_cg_layer (ca
+ *
+ * Since: 1.4
+ **/
+ cairo_surface_t *
+ cairo_quartz_surface_create (cairo_format_t format,
+ unsigned int width,
+ unsigned int height)
+ {
++ int stride;
++ unsigned char *data;
++
++ 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)
++ {
++ stride = width * 4;
++ } else if (format == CAIRO_FORMAT_A8) {
++ stride = width;
++ } 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;
++
++ data = _cairo_malloc_ab (height, stride);
++ if (!data) {
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
++ }
++
++ /* zero the memory to match the image surface behaviour */
++ memset (data, 0, height * stride);
++
++ cairo_quartz_surface_t *surf;
++ surf = (cairo_quartz_surface_t *) cairo_quartz_surface_create_for_data
++ (data, format, width, height, stride);
++ if (surf->base.status) {
++ free (data);
++ return (cairo_surface_t *) surf;
++ }
++
++ // We created this data, so we can delete it.
++ surf->ownsData = TRUE;
++
++ return (cairo_surface_t *) surf;
++}
++
++/**
++ * cairo_quartz_surface_create_for_data
++ * @data: a pointer to a buffer supplied by the application in which
++ * to write contents. This pointer must be suitably aligned for any
++ * kind of variable, (for example, a pointer returned by malloc).
++ * @format: format of pixels in the surface to create
++ * @width: width of the surface, in pixels
++ * @height: height of the surface, in pixels
++ *
++ * Creates a Quartz surface backed by a CGBitmap. The surface is
++ * created using the Device RGB (or Device Gray, for A8) color space.
++ * All Cairo operations, including those that require software
++ * rendering, will succeed on this surface.
++ *
++ * Return value: the newly created surface.
++ *
++ * Since: 1.12
++ **/
++cairo_surface_t *
++cairo_quartz_surface_create_for_data (unsigned char *data,
++ cairo_format_t format,
++ unsigned int width,
++ unsigned int height,
++ unsigned int stride)
++{
+ cairo_quartz_surface_t *surf;
+ CGContextRef cgc;
+ CGColorSpaceRef cgColorspace;
+ CGBitmapInfo bitinfo;
+- void *imageData;
+- int stride;
++ 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);
+@@ -3058,47 +3140,30 @@ cairo_quartz_surface_create (cairo_forma
+ {
+ 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 (!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);
+@@ -3118,16 +3183,17 @@ cairo_quartz_surface_create (cairo_forma
+ CGContextRelease (cgc);
+ free (imageData);
+ // create_internal will have set an error
+ return (cairo_surface_t*) surf;
+ }
+
+ surf->imageData = imageData;
+ surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
++ 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
+@@ -45,16 +45,23 @@
+ 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_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,
+ 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);
+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
+@@ -176,16 +176,17 @@
+ #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_data _moz_cairo_quartz_surface_create_for_data
+ #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_rectangle _moz_cairo_rectangle
+ #define cairo_rectangle_list_destroy _moz_cairo_rectangle_list_destroy
+ #define cairo_reference _moz_cairo_reference
+ #define cairo_rel_curve_to _moz_cairo_rel_curve_to
+ #define cairo_rel_line_to _moz_cairo_rel_line_to
diff --git a/gfx/cairo/quartz-fallback.patch b/gfx/cairo/quartz-fallback.patch
new file mode 100644
index 000000000..ca41b6e1f
--- /dev/null
+++ b/gfx/cairo/quartz-fallback.patch
@@ -0,0 +1,70 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1250204857 -43200
+# Node ID cc6bebbd93bb9d8606fe06b997f890acc17996fb
+# Parent caea8b548962f0df38e8e9032e9f57ef0fd099ec
+Bug 507939 - Remove erroneous clip rect fixup which caused repainting errors with repeating radial gradients on Mac. r=jmuizelaar
+
+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
+@@ -1033,38 +1033,29 @@ typedef enum {
+ DO_TILED_IMAGE
+ } cairo_quartz_action_t;
+
+ static cairo_quartz_action_t
+ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
+ const cairo_pattern_t *source)
+ {
+ CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
+- CGAffineTransform ctm;
+ double x0, y0, w, h;
+
+ cairo_surface_t *fallback;
+ cairo_t *fallback_cr;
+ CGImageRef img;
+ cairo_pattern_t *source_copy;
+
+ cairo_status_t status;
+
+ if (clipBox.size.width == 0.0f ||
+ clipBox.size.height == 0.0f)
+ return DO_NOTHING;
+
+- // the clipBox is in userspace, so:
+- ctm = CGContextGetCTM (surface->cgContext);
+- ctm = CGAffineTransformInvert (ctm);
+- clipBox = CGRectApplyAffineTransform (clipBox, ctm);
+-
+- // get the Y flip right -- the CTM will always have a Y flip in place
+- clipBox.origin.y = surface->extents.height - (clipBox.origin.y + clipBox.size.height);
+-
+ x0 = floor(clipBox.origin.x);
+ y0 = floor(clipBox.origin.y);
+ w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
+ h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
+
+ /* Create a temporary the size of the clip surface, and position
+ * it so that the device origin coincides with the original surface */
+ fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h);
+@@ -1717,18 +1708,20 @@ _cairo_quartz_surface_paint (void *abstr
+ action = _cairo_quartz_setup_source (surface, source);
+
+ if (action == DO_SOLID || action == DO_PATTERN) {
+ CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+ surface->extents.y,
+ surface->extents.width,
+ surface->extents.height));
+ } else if (action == DO_SHADING) {
++ CGContextSaveGState (surface->cgContext);
+ CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+ CGContextDrawShading (surface->cgContext, surface->sourceShading);
++ CGContextRestoreGState (surface->cgContext);
+ } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+ CGContextSaveGState (surface->cgContext);
+
+ CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+ CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
+ CGContextScaleCTM (surface->cgContext, 1, -1);
+
+ if (action == DO_IMAGE)
diff --git a/gfx/cairo/quartz-first-stop.patch b/gfx/cairo/quartz-first-stop.patch
new file mode 100644
index 000000000..5ea4b916c
--- /dev/null
+++ b/gfx/cairo/quartz-first-stop.patch
@@ -0,0 +1,57 @@
+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
+@@ -690,31 +690,51 @@ ComputeGradientValue (void *info, const
+ }
+
+ static const float 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 will clamp input values to the input range.
++
++ Our stops are all in the range 0.0 to 1.0. However, the color before the
++ beginning of the gradient line is obtained by Quartz computing a negative
++ position on the gradient line, clamping it to the input range we specified
++ for our color function, and then calling our color function (actually it
++ pre-samples the color function into an array, but that doesn't matter just
++ here). Therefore if we set the lower bound to 0.0, a negative position
++ on the gradient line will pass 0.0 to ComputeGradientValue, which will
++ select the last color stop with position 0, although it should select
++ the first color stop (this matters when there are multiple color stops with
++ position 0).
++
++ Therefore we pass a small negative number as the lower bound of the input
++ range, so this value gets passed into ComputeGradientValue, which will
++ return the color of the first stop. The number should be small because
++ as far as I can tell, Quartz pre-samples the entire input range of the color
++ function into an array of fixed size, so if the input range is larger
++ than needed, the resolution of the gradient will be unnecessarily low.
++*/
++static const float nonrepeating_gradient_input_value_range[2] = { -0.001f, 1.f };
+
+ static CGFunctionRef
+ CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
+ {
+ cairo_pattern_t *pat;
+- float input_value_range[2] = { 0.f, 1.f };
+
+ if (_cairo_pattern_create_copy (&pat, &gpat->base))
+ /* quartz doesn't deal very well with malloc failing, so there's
+ * not much point in us trying either */
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+- input_value_range,
++ nonrepeating_gradient_input_value_range,
+ 4,
+ gradient_output_value_ranges,
+ &gradient_callbacks);
+ }
+
+ static void
+ UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start,
+ double dx, double dy,
diff --git a/gfx/cairo/quartz-fix-PAD.patch b/gfx/cairo/quartz-fix-PAD.patch
new file mode 100644
index 000000000..0e5ed3107
--- /dev/null
+++ b/gfx/cairo/quartz-fix-PAD.patch
@@ -0,0 +1,64 @@
+From: Robert O'Callahan <robert@ocallahan.org>
+Bug 593270. Part 2: Treat EXTEND_PAD like EXTEND_NONE when painting. r=jrmuizel,a=joe
+
+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
+@@ -1464,35 +1464,35 @@ static void
+ _cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface,
+ const cairo_surface_pattern_t *spat,
+ cairo_rectangle_int_t *extents,
+ cairo_quartz_drawing_state_t *state)
+ {
+ const cairo_pattern_t *source = &spat->base;
+ CGContextRef context = state->context;
+
+- if (source->extend == CAIRO_EXTEND_NONE ||
++ if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD ||
+ (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
+ {
+ 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;
++ cairo_bool_t repeat = source->extend == CAIRO_EXTEND_REPEAT;
+ cairo_status_t status;
+
+ cairo_matrix_invert(&m);
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
+
+ /* Draw nonrepeating CGLayer surface using DO_LAYER */
+- if (source->extend == CAIRO_EXTEND_NONE ||
+- (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
++ if (!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) {
+ state->imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
+ state->layer = quartz_surf->cgLayer;
+ state->action = DO_LAYER;
+ return;
+ }
+ }
+@@ -1510,17 +1510,17 @@ _cairo_quartz_setup_surface_source (cair
+ /* XXXroc what is this for? */
+ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+
+ state->image = img;
+
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+- if (source->extend == CAIRO_EXTEND_NONE) {
++ if (!repeat) {
+ state->imageRect = CGRectMake (0, 0, extents.width, extents.height);
+ state->action = DO_IMAGE;
+ return;
+ }
+
+ /* 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
diff --git a/gfx/cairo/quartz-get-image-performance.patch b/gfx/cairo/quartz-get-image-performance.patch
new file mode 100644
index 000000000..ff3618cf8
--- /dev/null
+++ b/gfx/cairo/quartz-get-image-performance.patch
@@ -0,0 +1,43 @@
+# HG changeset patch
+# User Matt Woodrow <mwoodrow@mozilla.com>
+# Date 1314162877 -43200
+# Node ID 87458c4670dcd16be5a5715d741ee2ca4cf18d0f
+# Parent 95eb700a64591cda694c284a9f8ad08c11e3dd97
+Bug 675837 - Only flush Quartz surfaces on the success paths during cairo_quartz_get_image. r=roc
+
+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
+@@ -1909,30 +1909,30 @@ _cairo_quartz_get_image (cairo_quartz_su
+ 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;
+ }
+
+- CGContextFlush(surface->cgContext);
+-
+ 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);
diff --git a/gfx/cairo/quartz-get-image.patch b/gfx/cairo/quartz-get-image.patch
new file mode 100644
index 000000000..e95d82d54
--- /dev/null
+++ b/gfx/cairo/quartz-get-image.patch
@@ -0,0 +1,127 @@
+diff --git a/gfx/cairo/README b/gfx/cairo/README
+--- a/gfx/cairo/README
++++ b/gfx/cairo/README
+@@ -69,16 +69,18 @@ quartz-state.patch: bug 522859; refactor
+ 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
++
+ 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
+
+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
+@@ -1880,24 +1880,24 @@ _cairo_quartz_surface_finish (void *abst
+ surface->cgContext = NULL;
+
+ if (surface->bitmapContextImage) {
+ CGImageRelease (surface->bitmapContextImage);
+ surface->bitmapContextImage = NULL;
+ }
+
+ if (surface->imageSurfaceEquiv) {
++ _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv);
+ cairo_surface_destroy (surface->imageSurfaceEquiv);
+ surface->imageSurfaceEquiv = NULL;
++ } else if (surface->imageData) {
++ free (surface->imageData);
+ }
+
+- if (surface->imageData) {
+- free (surface->imageData);
+- surface->imageData = NULL;
+- }
++ surface->imageData = NULL;
+
+ if (surface->cgLayer) {
+ CGLayerRelease (surface->cgLayer);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+@@ -3200,16 +3200,28 @@ cairo_quartz_finish_cg_context_with_clip
+ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
+
+ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
+ return;
+
+ CGContextRestoreGState (quartz->cgContext);
+ }
+
++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 <Movies.h>
+
+ void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest)
+ {
+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
+@@ -63,16 +63,19 @@ cairo_public CGContextRef
+ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+
+ cairo_public CGContextRef
+ cairo_quartz_get_cg_context_with_clip (cairo_t *cr);
+
+ cairo_public void
+ cairo_quartz_finish_cg_context_with_clip (cairo_t *cr);
+
++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);
+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
+@@ -178,16 +178,17 @@
+ #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_rectangle _moz_cairo_rectangle
+ #define cairo_rectangle_list_destroy _moz_cairo_rectangle_list_destroy
+ #define cairo_reference _moz_cairo_reference
+ #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_reset_clip _moz_cairo_reset_clip
+ #define cairo_restore _moz_cairo_restore
diff --git a/gfx/cairo/quartz-glyph-extents.patch b/gfx/cairo/quartz-glyph-extents.patch
new file mode 100644
index 000000000..311404292
--- /dev/null
+++ b/gfx/cairo/quartz-glyph-extents.patch
@@ -0,0 +1,19 @@
+--- a/gfx/cairo/cairo/src/cairo-quartz-font.c Wed Dec 23 14:17:44 2009 -0500
++++ b/gfx/cairo/cairo/src/cairo-quartz-font.c Wed Dec 23 20:45:00 2009 +0000
+@@ -420,6 +420,16 @@ _cairo_quartz_init_glyph_metrics (cairo_
+ !CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
+ goto FAIL;
+
++ /* 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;
++ }
++
+ status = _cairo_matrix_compute_basis_scale_factors (&font->base.scale,
+ &xscale, &yscale, 1);
+ if (status)
diff --git a/gfx/cairo/quartz-is-clear.patch b/gfx/cairo/quartz-is-clear.patch
new file mode 100644
index 000000000..714951b44
--- /dev/null
+++ b/gfx/cairo/quartz-is-clear.patch
@@ -0,0 +1,28 @@
+commit df2b22c8c6677d531194579c82a55e855adff706
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Thu Apr 8 15:43:41 2010 -0400
+
+ Propagate is_clear flag to quartz_image_surface
+
+diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c
+index 5a624eb..b8809d5 100644
+--- a/src/cairo-quartz-image-surface.c
++++ b/src/cairo-quartz-image-surface.c
+@@ -146,6 +146,8 @@ _cairo_quartz_image_surface_flush (void *asurface)
+ surface->image = newImage;
+ CGImageRelease (oldImage);
+
++ surface->base.is_clear = surface->imageSurface->base.is_clear;
++
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+@@ -267,6 +269,8 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface)
+ qisurf->image = image;
+ qisurf->imageSurface = image_surface;
+
++ qisurf->base.is_clear = image_surface->base.is_clear;
++
+ return &qisurf->base;
+ }
+
diff --git a/gfx/cairo/quartz-layers-content.patch b/gfx/cairo/quartz-layers-content.patch
new file mode 100644
index 000000000..243d1f017
--- /dev/null
+++ b/gfx/cairo/quartz-layers-content.patch
@@ -0,0 +1,125 @@
+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
+@@ -2040,17 +2040,18 @@ _cairo_quartz_surface_create_similar (vo
+ cairo_content_t content,
+ int width,
+ int height)
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_format_t format;
+
+ if (surface->cgLayer)
+- return cairo_quartz_surface_create_cg_layer (abstract_surface, width, height);
++ 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
+@@ -2960,54 +2961,55 @@ cairo_quartz_surface_create_for_cg_conte
+
+ return (cairo_surface_t *) surf;
+ }
+
+ /**
+ * cairo_quartz_cglayer_surface_create_similar
+ * @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
+- * with CAIRO_CONTENT_COLOR_ALPHA.
++ * 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, CAIRO_CONTENT_COLOR_ALPHA,
++ 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, CAIRO_CONTENT_COLOR_ALPHA,
++ _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));
+@@ -3016,18 +3018,18 @@ cairo_quartz_surface_create_cg_layer (ca
+ /* 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, CAIRO_CONTENT_COLOR_ALPHA,
+- width, height);
++ 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;
+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
+@@ -46,16 +46,17 @@ 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_cg_layer (cairo_surface_t *surface,
++ cairo_content_t content,
+ 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);
+
diff --git a/gfx/cairo/quartz-mark-dirty.patch b/gfx/cairo/quartz-mark-dirty.patch
new file mode 100644
index 000000000..ddaaf94e8
--- /dev/null
+++ b/gfx/cairo/quartz-mark-dirty.patch
@@ -0,0 +1,56 @@
+Date: Thu Jan 5 18:40:01 2012 -0500
+
+Bug 715704. Add a quartz implementation of mark_dirty_rectangle. r=roc
+
+We need to drop our CGImage cache when the surface has been changed by outside users.
+
+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
+@@ -3116,16 +3116,27 @@ _cairo_quartz_surface_clipper_intersect_
+ CGContextEOClip (surface->cgContext);
+ }
+
+ ND((stderr, "-- intersect_clip_path\n"));
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++static cairo_status_t
++_cairo_quartz_surface_mark_dirty_rectangle (void *abstract_surface,
++ int x, int y,
++ int width, int height)
++{
++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
++ _cairo_quartz_surface_will_change (surface);
++ return CAIRO_STATUS_SUCCESS;
++}
++
++
+ // XXXtodo implement show_page; need to figure out how to handle begin/end
+
+ static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
+ CAIRO_SURFACE_TYPE_QUARTZ,
+ _cairo_quartz_surface_create_similar,
+ _cairo_quartz_surface_finish,
+ _cairo_quartz_surface_acquire_image,
+ _cairo_quartz_surface_release_source_image,
+@@ -3138,17 +3149,17 @@ static const struct _cairo_surface_backe
+ NULL, /* create_span_renderer */
+ NULL, /* check_span_renderer */
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_quartz_surface_get_extents,
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+- NULL, /* mark_dirty_rectangle */
++ _cairo_quartz_surface_mark_dirty_rectangle,
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+ _cairo_quartz_surface_paint,
+ _cairo_quartz_surface_mask,
+ _cairo_quartz_surface_stroke,
+ _cairo_quartz_surface_fill,
+ _cairo_quartz_surface_show_glyphs,
diff --git a/gfx/cairo/quartz-mask-non-OVER.patch b/gfx/cairo/quartz-mask-non-OVER.patch
new file mode 100644
index 000000000..a6d94be12
--- /dev/null
+++ b/gfx/cairo/quartz-mask-non-OVER.patch
@@ -0,0 +1,80 @@
+From: Robert O'Callahan <robert@ocallahan.org>
+Bug 579985. Using CGContextSetAlpha to implement mask alpha doesn't work for some operators. r=jrmuizel,a=blocking
+
+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
+@@ -2734,17 +2734,19 @@ _cairo_quartz_surface_mask (void *abstra
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+- if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
++ /* Using CGContextSetAlpha to implement mask alpha doesn't work for all operators. */
++ if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
++ op == CAIRO_OPERATOR_OVER) {
+ /* This is easy; we just need to paint with the alpha. */
+ cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
+
+ CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
+ rv = _cairo_quartz_surface_paint (surface, op, source, clip);
+ CGContextSetAlpha (surface->cgContext, 1.0);
+
+ return rv;
+diff --git a/layout/reftests/bugs/579985-1-ref.html b/layout/reftests/bugs/579985-1-ref.html
+new file mode 100644
+--- /dev/null
++++ b/layout/reftests/bugs/579985-1-ref.html
+@@ -0,0 +1,6 @@
++<!DOCTYPE HTML>
++<html>
++<body>
++<div style="width:100px; height:100px; background:blue; opacity:0.5;">Hello</div>
++</body>
++</html>
+diff --git a/layout/reftests/bugs/579985-1.html b/layout/reftests/bugs/579985-1.html
+new file mode 100644
+--- /dev/null
++++ b/layout/reftests/bugs/579985-1.html
+@@ -0,0 +1,16 @@
++<!DOCTYPE HTML>
++<html class="reftest-wait">
++<head>
++<script>
++function doTest() {
++ var d = document.getElementById("d");
++ d.style.opacity = 0.75;
++ document.documentElement.removeAttribute("class");
++}
++window.addEventListener("MozReftestInvalidate", doTest, false);
++</script>
++</head>
++<body>
++<div id="d" style="width:100px; height:100px; background:blue;">Hello</div>
++</body>
++</html>
+diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list
+--- a/layout/reftests/bugs/reftest.list
++++ b/layout/reftests/bugs/reftest.list
+@@ -1499,16 +1499,17 @@ random-if(layersGPUAccelerated) == 56499
+ == 571347-3.html 571347-3-ref.html
+ == 572598-1.html 572598-ref.html
+ == 574898-1.html 574898-ref.html
+ == 579655-1.html 579655-1-ref.html
+ == 577838-1.html 577838-1-ref.html
+ == 577838-2.html 577838-2-ref.html
+ random-if(layersGPUAccelerated) == 579323-1.html 579323-1-ref.html
+ == 579349-1.html 579349-1-ref.html
++== 579985-1.html 579985-1-ref.html
+ == 580160-1.html 580160-1-ref.html
+ HTTP(..) == 580863-1.html 580863-1-ref.html
+ random-if(layersGPUAccelerated) == 581317-1.html 581317-1-ref.html
+ == 581579-1.html 581579-1-ref.html
+ == 582037-1a.html 582037-1-ref.html
+ == 582037-1b.html 582037-1-ref.html
+ == 582037-2a.html 582037-2-ref.html
+ == 582037-2b.html 582037-2-ref.html
diff --git a/gfx/cairo/quartz-minimize-gradient-repeat.patch b/gfx/cairo/quartz-minimize-gradient-repeat.patch
new file mode 100644
index 000000000..9782bef11
--- /dev/null
+++ b/gfx/cairo/quartz-minimize-gradient-repeat.patch
@@ -0,0 +1,561 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1249558989 -43200
+# Node ID 0bac4c903d2bb1d5c0d5426209001fc2a77cc105
+# Parent 963b9451ad305924738d05d997a640698cd3af91
+Bug 508730. Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops. r=jmuizelaar
+
+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
+@@ -710,82 +710,100 @@ CreateGradientFunction (const cairo_grad
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+ gradient_output_value_ranges,
+ &gradient_callbacks);
+ }
+
++static void
++UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start,
++ double dx, double dy,
++ double x, double y)
++{
++ /* Compute a parameter t such that a line perpendicular to the (dx,dy)
++ vector, passing through (start->x + dx*t, start->y + dy*t), also
++ passes through (x,y).
++
++ Let px = x - start->x, py = y - start->y.
++ t is given by
++ (px - dx*t)*dx + (py - dy*t)*dy = 0
++
++ Solving for t we get
++ numerator = dx*px + dy*py
++ denominator = dx^2 + dy^2
++ t = numerator/denominator
++
++ In CreateRepeatingLinearGradientFunction we know the length of (dx,dy)
++ is not zero. (This is checked in _cairo_quartz_setup_linear_source.)
++ */
++ double px = x - start->x;
++ double py = y - start->y;
++ double numerator = dx*px + dy*py;
++ double denominator = dx*dx + dy*dy;
++ double t = numerator/denominator;
++
++ if (*min_t > t) {
++ *min_t = t;
++ }
++ if (*max_t < t) {
++ *max_t = t;
++ }
++}
++
+ static CGFunctionRef
+ CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
+ const cairo_gradient_pattern_t *gpat,
+ CGPoint *start, CGPoint *end,
+- CGAffineTransform matrix)
++ cairo_rectangle_int_t *extents)
+ {
+ cairo_pattern_t *pat;
+ float input_value_range[2];
++ double t_min = 0.;
++ double t_max = 0.;
++ double dx = end->x - start->x;
++ double dy = end->y - start->y;
++ double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+
+- CGPoint mstart, mend;
++ if (!extents) {
++ extents = &surface->extents;
++ }
++ bounds_x1 = extents->x;
++ bounds_y1 = extents->y;
++ bounds_x2 = extents->x + extents->width;
++ bounds_y2 = extents->y + extents->height;
++ _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
++ &bounds_x1, &bounds_y1,
++ &bounds_x2, &bounds_y2,
++ NULL);
+
+- double dx, dy;
+- int x_rep_start = 0, x_rep_end = 0;
+- int y_rep_start = 0, y_rep_end = 0;
++ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
++ bounds_x1, bounds_y1);
++ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
++ bounds_x2, bounds_y1);
++ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
++ bounds_x2, bounds_y2);
++ UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
++ bounds_x1, bounds_y2);
+
+- int rep_start, rep_end;
+-
+- // figure out how many times we'd need to repeat the gradient pattern
+- // to cover the whole (transformed) surface area
+- mstart = CGPointApplyAffineTransform (*start, matrix);
+- mend = CGPointApplyAffineTransform (*end, matrix);
+-
+- dx = fabs (mend.x - mstart.x);
+- dy = fabs (mend.y - mstart.y);
+-
+- if (dx > 1e-6) {
+- x_rep_start = (int) ceil(MIN(mstart.x, mend.x) / dx);
+- x_rep_end = (int) ceil((surface->extents.width - MAX(mstart.x, mend.x)) / dx);
+-
+- if (mend.x < mstart.x) {
+- int swap = x_rep_end;
+- x_rep_end = x_rep_start;
+- x_rep_start = swap;
+- }
+- }
+-
+- if (dy > 1e-6) {
+- y_rep_start = (int) ceil(MIN(mstart.y, mend.y) / dy);
+- y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y)) / dy);
+-
+- if (mend.y < mstart.y) {
+- int swap = y_rep_end;
+- y_rep_end = y_rep_start;
+- y_rep_start = swap;
+- }
+- }
+-
+- rep_start = MAX(x_rep_start, y_rep_start);
+- rep_end = MAX(x_rep_end, y_rep_end);
+-
+- // extend the line between start and end by rep_start times from the start
+- // and rep_end times from the end
+-
+- dx = end->x - start->x;
+- dy = end->y - start->y;
+-
+- start->x = start->x - dx * rep_start;
+- start->y = start->y - dy * rep_start;
+-
+- end->x = end->x + dx * rep_end;
+- end->y = end->y + dy * rep_end;
++ /* Move t_min and t_max to the nearest usable integer to try to avoid
++ subtle variations due to numerical instability, especially accidentally
++ cutting off a pixel. Extending the gradient repetitions is always safe. */
++ t_min = floor (t_min);
++ t_max = ceil (t_max);
++ end->x = start->x + dx*t_max;
++ end->y = start->y + dy*t_max;
++ start->x = start->x + dx*t_min;
++ start->y = start->y + dy*t_min;
+
+ // set the input range for the function -- the function knows how to
+ // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT.
+- input_value_range[0] = 0.0 - 1.0 * rep_start;
+- input_value_range[1] = 1.0 + 1.0 * rep_end;
++ input_value_range[0] = t_min;
++ input_value_range[1] = t_max;
+
+ if (_cairo_pattern_create_copy (&pat, &gpat->base))
+ /* quartz doesn't deal very well with malloc failing, so there's
+ * not much point in us trying either */
+ return NULL;
+
+ return CGFunctionCreate (pat,
+ 1,
+@@ -840,35 +858,43 @@ UpdateRadialParameterToIncludePoint(doub
+ }
+ }
+
+ /* This must only be called when one of the circles properly contains the other */
+ static CGFunctionRef
+ CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
+ const cairo_gradient_pattern_t *gpat,
+ CGPoint *start, double *start_radius,
+- CGPoint *end, double *end_radius)
++ CGPoint *end, double *end_radius,
++ cairo_rectangle_int_t *extents)
+ {
+- CGRect clip = CGContextGetClipBoundingBox (surface->cgContext);
+- CGAffineTransform transform;
+ cairo_pattern_t *pat;
+ float input_value_range[2];
+ CGPoint *inner;
+ double *inner_radius;
+ CGPoint *outer;
+ double *outer_radius;
+ /* minimum and maximum t-parameter values that will make our gradient
+ cover the clipBox */
+ double t_min, t_max, t_temp;
+ /* outer minus inner */
+ double dr, dx, dy;
++ double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+
+- _cairo_quartz_cairo_matrix_to_quartz (&gpat->base.matrix, &transform);
+- /* clip is in cairo device coordinates; get it into cairo user space */
+- clip = CGRectApplyAffineTransform (clip, transform);
++ if (!extents) {
++ extents = &surface->extents;
++ }
++ bounds_x1 = extents->x;
++ bounds_y1 = extents->y;
++ bounds_x2 = extents->x + extents->width;
++ bounds_y2 = extents->y + extents->height;
++ _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
++ &bounds_x1, &bounds_y1,
++ &bounds_x2, &bounds_y2,
++ NULL);
+
+ if (*start_radius < *end_radius) {
+ /* end circle contains start circle */
+ inner = start;
+ outer = end;
+ inner_radius = start_radius;
+ outer_radius = end_radius;
+ } else {
+@@ -878,36 +904,37 @@ CreateRepeatingRadialGradientFunction (c
+ inner_radius = end_radius;
+ outer_radius = start_radius;
+ }
+
+ dr = *outer_radius - *inner_radius;
+ dx = outer->x - inner->x;
+ dy = outer->y - inner->y;
+
++ /* We can't round or fudge t_min here, it has to be as accurate as possible. */
+ t_min = -(*inner_radius/dr);
+ inner->x += t_min*dx;
+ inner->y += t_min*dy;
+ *inner_radius = 0.;
+
+ t_temp = 0.;
+ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
+- clip.origin.x, clip.origin.y);
++ bounds_x1, bounds_y1);
+ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
+- clip.origin.x + clip.size.width, clip.origin.y);
++ bounds_x2, bounds_y1);
+ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
+- clip.origin.x + clip.size.width, clip.origin.y + clip.size.height);
++ bounds_x2, bounds_y2);
+ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
+- clip.origin.x, clip.origin.y + clip.size.height);
++ bounds_x1, bounds_y2);
+ /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
+ But for the parameter values we use with Quartz, t_min means radius 0.
+- Also, add a small fudge factor to avoid rounding issues. Since the
+- circles are alway expanding and containing the earlier circles, this is
+- OK. */
+- t_temp += 1e-6;
++ Since the circles are alway expanding and contain the earlier circles,
++ it's safe to extend t_max/t_temp as much as we want, so round t_temp up
++ to the nearest integer. This may help us give stable results. */
++ t_temp = ceil (t_temp);
+ t_max = t_min + t_temp;
+ outer->x = inner->x + t_temp*dx;
+ outer->y = inner->y + t_temp*dy;
+ *outer_radius = t_temp*dr;
+
+ /* set the input range for the function -- the function knows how to
+ map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */
+ if (*start_radius < *end_radius) {
+@@ -1218,33 +1245,57 @@ _cairo_quartz_setup_fallback_source (cai
+ surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
+ surface->sourceImage = img;
+ surface->sourceImageSurface = fallback;
+ surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
+
+ return DO_IMAGE;
+ }
+
++/*
++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. CreateRepeatingLinearGradientFunction and
++CreateRepeatingRadialGradientFunction compute the number of repetitions needed
++based on the extents of the object (the clip region cannot be used here since
++we don't want the rasterization of the entire gradient to depend on the
++clip region).
++*/
+ static cairo_quartz_action_t
+ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
+- const cairo_linear_pattern_t *lpat)
++ const cairo_linear_pattern_t *lpat,
++ cairo_rectangle_int_t *extents)
+ {
+ const cairo_pattern_t *abspat = &lpat->base.base;
+ cairo_matrix_t mat;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+
+ if (lpat->base.n_stops == 0) {
+ CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+ CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+ return DO_SOLID;
+ }
+
++ if (lpat->p1.x == lpat->p2.x &&
++ lpat->p1.y == lpat->p2.y) {
++ /* Quartz handles cases where the vector has no length very
++ * differently from pixman.
++ * Whatever the correct behaviour is, let's at least have only pixman's
++ * implementation to worry about.
++ */
++ return _cairo_quartz_setup_fallback_source (surface, abspat);
++ }
++
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+ _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+ start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
+ _cairo_fixed_to_double (lpat->p1.y));
+@@ -1254,33 +1305,34 @@ _cairo_quartz_setup_linear_source (cairo
+ if (abspat->extend == CAIRO_EXTEND_NONE ||
+ abspat->extend == CAIRO_EXTEND_PAD)
+ {
+ gradFunc = CreateGradientFunction (&lpat->base);
+ } else {
+ gradFunc = CreateRepeatingLinearGradientFunction (surface,
+ &lpat->base,
+ &start, &end,
+- surface->sourceTransform);
++ extents);
+ }
+
+ surface->sourceShading = CGShadingCreateAxial (rgb,
+ start, end,
+ gradFunc,
+ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+ return DO_SHADING;
+ }
+
+ static cairo_quartz_action_t
+ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
+- const cairo_radial_pattern_t *rpat)
++ const cairo_radial_pattern_t *rpat,
++ cairo_rectangle_int_t *extents)
+ {
+ const cairo_pattern_t *abspat = &rpat->base.base;
+ cairo_matrix_t mat;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+ double c1x = _cairo_fixed_to_double (rpat->c1.x);
+@@ -1322,17 +1374,18 @@ _cairo_quartz_setup_radial_source (cairo
+ if (abspat->extend == CAIRO_EXTEND_NONE ||
+ abspat->extend == CAIRO_EXTEND_PAD)
+ {
+ gradFunc = CreateGradientFunction (&rpat->base);
+ } else {
+ gradFunc = CreateRepeatingRadialGradientFunction (surface,
+ &rpat->base,
+ &start, &r1,
+- &end, &r2);
++ &end, &r2,
++ extents);
+ }
+
+ surface->sourceShading = CGShadingCreateRadial (rgb,
+ start,
+ r1,
+ end,
+ r2,
+ gradFunc,
+@@ -1341,17 +1394,18 @@ _cairo_quartz_setup_radial_source (cairo
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+ return DO_SHADING;
+ }
+
+ static cairo_quartz_action_t
+ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source)
++ const cairo_pattern_t *source,
++ cairo_rectangle_int_t *extents)
+ {
+ assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
+
+ surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
+ CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+@@ -1367,24 +1421,22 @@ _cairo_quartz_setup_source (cairo_quartz
+ solid->color.blue,
+ solid->color.alpha);
+
+ return DO_SOLID;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
+- return _cairo_quartz_setup_linear_source (surface, lpat);
+-
++ return _cairo_quartz_setup_linear_source (surface, lpat, extents);
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
+- return _cairo_quartz_setup_radial_source (surface, rpat);
+-
++ return _cairo_quartz_setup_radial_source (surface, rpat, extents);
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
+ {
+ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ cairo_surface_t *pat_surf = spat->surface;
+ CGImageRef img;
+@@ -1852,17 +1904,17 @@ _cairo_quartz_surface_paint (void *abstr
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ if (op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
+
+- action = _cairo_quartz_setup_source (surface, source);
++ action = _cairo_quartz_setup_source (surface, source, NULL);
+
+ if (action == DO_SOLID || action == DO_PATTERN) {
+ CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+ surface->extents.y,
+ surface->extents.width,
+ surface->extents.height));
+ } else if (action == DO_SHADING) {
+ CGContextSaveGState (surface->cgContext);
+@@ -1886,16 +1938,35 @@ _cairo_quartz_surface_paint (void *abstr
+ }
+
+ _cairo_quartz_teardown_source (surface, source);
+
+ ND((stderr, "-- paint\n"));
+ return rv;
+ }
+
++static cairo_bool_t
++_cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
++{
++ /* For repeating gradients we need to manually extend the gradient and
++ repeat stops, since Quartz doesn't support repeating gradients natively.
++ We need to minimze the number of repeated stops, and since rasterization
++ depends on the number of repetitions we use (even if some of the
++ repetitions go beyond the extents of the object or outside the clip
++ region), it's important to use the same number of repetitions when
++ rendering an object no matter what the clip region is. So the
++ computation of the repetition count cannot depended on the clip region,
++ and should only depend on the object extents, so we need to compute
++ the object extents for repeating gradients. */
++ return (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
++ source->type == CAIRO_PATTERN_TYPE_RADIAL) &&
++ (source->extend == CAIRO_EXTEND_REPEAT ||
++ source->extend == CAIRO_EXTEND_REFLECT);
++}
++
+ static cairo_int_status_t
+ _cairo_quartz_surface_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,
+@@ -1926,17 +1997,27 @@ _cairo_quartz_surface_fill (void *abstra
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ CGContextSaveGState (surface->cgContext);
+
+ CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
+ CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
+
+- action = _cairo_quartz_setup_source (surface, source);
++ if (_cairo_quartz_source_needs_extents (source))
++ {
++ /* We don't need precise extents since these are only used to
++ compute the number of gradient reptitions needed to cover the
++ object. */
++ cairo_rectangle_int_t path_extents;
++ _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
++ action = _cairo_quartz_setup_source (surface, source, &path_extents);
++ } else {
++ action = _cairo_quartz_setup_source (surface, source, NULL);
++ }
+
+ CGContextBeginPath (surface->cgContext);
+
+ stroke.cgContext = surface->cgContext;
+ stroke.ctm_inverse = NULL;
+ rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
+ if (rv)
+ goto BAIL;
+@@ -2059,17 +2140,24 @@ _cairo_quartz_surface_stroke (void *abst
+
+ CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
+ if (fdash != sdash)
+ free (fdash);
+ }
+
+ CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
+
+- action = _cairo_quartz_setup_source (surface, source);
++ if (_cairo_quartz_source_needs_extents (source))
++ {
++ cairo_rectangle_int_t path_extents;
++ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
++ action = _cairo_quartz_setup_source (surface, source, &path_extents);
++ } else {
++ action = _cairo_quartz_setup_source (surface, source, NULL);
++ }
+
+ CGContextBeginPath (surface->cgContext);
+
+ stroke.cgContext = surface->cgContext;
+ stroke.ctm_inverse = ctm_inverse;
+ rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
+ if (rv)
+ goto BAIL;
+@@ -2180,17 +2268,26 @@ _cairo_quartz_surface_show_glyphs (void
+ if (op == CAIRO_OPERATOR_DEST)
+ return CAIRO_STATUS_SUCCESS;
+
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ CGContextSaveGState (surface->cgContext);
+
+- action = _cairo_quartz_setup_source (surface, source);
++ if (_cairo_quartz_source_needs_extents (source))
++ {
++ cairo_rectangle_int_t glyph_extents;
++ _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
++ &glyph_extents);
++ action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
++ } else {
++ action = _cairo_quartz_setup_source (surface, source, NULL);
++ }
++
+ if (action == DO_SOLID || action == DO_PATTERN) {
+ CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
+ } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
+ CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
+ isClipping = TRUE;
+ } else {
+ if (action != DO_NOTHING)
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
diff --git a/gfx/cairo/quartz-optimize-OVER.patch b/gfx/cairo/quartz-optimize-OVER.patch
new file mode 100644
index 000000000..2c587459b
--- /dev/null
+++ b/gfx/cairo/quartz-optimize-OVER.patch
@@ -0,0 +1,71 @@
+From: Robert O'Callahan <robert@ocallahan.org>
+Bug 579885. Part 4: Paint opaque surfaces using kPrivateCGCompositeCopy when possible. r=jrmuizel,a=blocking
+
+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
+@@ -122,16 +122,17 @@ static void (*CGContextClipToMaskPtr) (C
+ static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+ static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
+ static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
+ static bool (*CGContextGetShouldAntialiasFontsPtr) (CGContextRef) = NULL;
+ static bool (*CGContextGetShouldSmoothFontsPtr) (CGContextRef) = NULL;
+ static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+ static void (*CGContextReplacePathWithClipPathPtr) (CGContextRef) = NULL;
++static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
+
+ static SInt32 _cairo_quartz_osx_version = 0x0;
+
+ static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
+
+ /*
+ * Utility functions
+ */
+@@ -157,16 +158,17 @@ static void quartz_ensure_symbols(void)
+ CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
+ CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
+ CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
+ CGContextGetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldAntialiasFonts");
+ CGContextGetShouldSmoothFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldSmoothFonts");
+ CGContextReplacePathWithClipPathPtr = dlsym(RTLD_DEFAULT, "CGContextReplacePathWithClipPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
++ CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
+
+ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
+ // assume 10.4
+ _cairo_quartz_osx_version = 0x1040;
+ }
+
+ _cairo_quartz_symbol_lookup_done = TRUE;
+ }
+@@ -1698,16 +1700,28 @@ _cairo_quartz_setup_state (cairo_quartz_
+
+ if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
+ _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
+ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
++ if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (source) &&
++ CGContextGetAlphaPtr &&
++ CGContextGetAlphaPtr (surface->cgContext) == 1.0) {
++ // Quartz won't touch pixels outside the bounds of the
++ // source surface, so we can just go ahead and use Copy here
++ // to accelerate things.
++ // Quartz won't necessarily be able to do this optimization internally;
++ // for CGLayer surfaces, we can know all the pixels are opaque
++ // (because it's CONTENT_COLOR), but Quartz won't know.
++ CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy);
++ }
++
+ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ _cairo_quartz_setup_surface_source (surface, spat, extents, &state);
+ return state;
+ }
+
+ state.action = DO_UNSUPPORTED;
+ return state;
+ }
diff --git a/gfx/cairo/quartz-refactor-surface-setup.patch b/gfx/cairo/quartz-refactor-surface-setup.patch
new file mode 100644
index 000000000..22e2d0ee1
--- /dev/null
+++ b/gfx/cairo/quartz-refactor-surface-setup.patch
@@ -0,0 +1,290 @@
+From: Robert O'Callahan <robert@ocallahan.org>
+Bug 593270. Part 1: Move surface setup code to a helper function. r=jrmuizel,a=joe
+
+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
+@@ -1455,16 +1455,147 @@ _cairo_quartz_setup_radial_source (cairo
+ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+ state->action = DO_SHADING;
+ }
+
++static void
++_cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface,
++ const cairo_surface_pattern_t *spat,
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
++{
++ const cairo_pattern_t *source = &spat->base;
++ CGContextRef context = state->context;
++
++ if (source->extend == CAIRO_EXTEND_NONE ||
++ (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
++ {
++ 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;
++ cairo_status_t status;
++
++ cairo_matrix_invert(&m);
++ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
++
++ /* Draw nonrepeating CGLayer surface using DO_LAYER */
++ if (source->extend == CAIRO_EXTEND_NONE ||
++ (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
++ cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
++ if (quartz_surf->cgLayer) {
++ state->imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
++ state->layer = quartz_surf->cgLayer;
++ state->action = DO_LAYER;
++ return;
++ }
++ }
++
++ status = _cairo_surface_to_cgimage (pat_surf, &img);
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
++ }
++ if (img == NULL) {
++ state->action = DO_NOTHING;
++ return;
++ }
++
++ /* XXXroc what is this for? */
++ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
++
++ state->image = img;
++
++ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
++ assert (is_bounded);
++
++ if (source->extend == CAIRO_EXTEND_NONE) {
++ state->imageRect = CGRectMake (0, 0, extents.width, extents.height);
++ state->action = DO_IMAGE;
++ return;
++ }
++
++ /* 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 (context),
++ state->transform);
++
++ srcRect = CGRectMake (0, 0, extents.width, extents.height);
++ 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->imageRect = srcRect;
++ state->action = DO_TILED_IMAGE;
++ return;
++ }
++
++ /* Fall through to generic SURFACE case */
++ }
++
++ CGFloat patternAlpha = 1.0f;
++ CGColorSpaceRef patternSpace;
++ CGPatternRef pattern;
++ cairo_int_status_t status;
++
++ status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ state->action = DO_NOTHING;
++ return;
++ }
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
++ }
++
++ patternSpace = CGColorSpaceCreatePattern (NULL);
++ CGContextSetFillColorSpace (context, patternSpace);
++ CGContextSetFillPattern (context, pattern, &patternAlpha);
++ CGContextSetStrokeColorSpace (context, patternSpace);
++ CGContextSetStrokePattern (context, pattern, &patternAlpha);
++ CGColorSpaceRelease (patternSpace);
++
++ /* Quartz likes to munge the pattern phase (as yet unexplained
++ * why); force it to 0,0 as we've already baked in the correct
++ * pattern translation into the pattern matrix
++ */
++ CGContextSetPatternPhase (context, CGSizeMake(0,0));
++
++ state->pattern = pattern;
++ state->action = DO_PATTERN;
++ return;
++}
++
+ /**
+ * Call this before any operation that can modify the contents of a
+ * cairo_quartz_surface_t.
+ */
+ static void
+ _cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface)
+ {
+ if (surface->bitmapContextImage) {
+@@ -1566,133 +1697,19 @@ _cairo_quartz_setup_state (cairo_quartz_
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
+ _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
+ return state;
+ }
+
+- if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+- (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
+- {
++ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ 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;
+-
+- cairo_matrix_invert(&m);
+- _cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform);
+-
+- if (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 && source->extend == CAIRO_EXTEND_NONE) {
+- state.imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
+- state.layer = quartz_surf->cgLayer;
+- state.action = DO_LAYER;
+- return state;
+- }
+- }
+-
+- status = _cairo_surface_to_cgimage (pat_surf, &img);
+- if (status) {
+- state.action = DO_UNSUPPORTED;
+- return state;
+- }
+- if (img == NULL) {
+- state.action = DO_NOTHING;
+- return state;
+- }
+-
+- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+-
+- state.image = img;
+-
+- is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+- assert (is_bounded);
+-
+- if (source->extend == CAIRO_EXTEND_NONE) {
+- state.imageRect = CGRectMake (0, 0, extents.width, extents.height);
+- state.action = DO_IMAGE;
+- return state;
+- }
+-
+- /* 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 (context),
+- state.transform);
+-
+- srcRect = CGRectMake (0, 0, extents.width, extents.height);
+- 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.imageRect = srcRect;
+- state.action = DO_TILED_IMAGE;
+- return state;
+- }
+-
+- /* Fall through to generic SURFACE case */
+- }
+-
+- if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+- CGFloat patternAlpha = 1.0f;
+- CGColorSpaceRef patternSpace;
+- CGPatternRef pattern;
+- cairo_int_status_t status;
+-
+- status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
+- if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+- state.action = DO_NOTHING;
+- return state;
+- }
+- if (status) {
+- state.action = DO_UNSUPPORTED;
+- return state;
+- }
+-
+- patternSpace = CGColorSpaceCreatePattern (NULL);
+- CGContextSetFillColorSpace (context, patternSpace);
+- CGContextSetFillPattern (context, pattern, &patternAlpha);
+- CGContextSetStrokeColorSpace (context, patternSpace);
+- CGContextSetStrokePattern (context, pattern, &patternAlpha);
+- CGColorSpaceRelease (patternSpace);
+-
+- /* Quartz likes to munge the pattern phase (as yet unexplained
+- * why); force it to 0,0 as we've already baked in the correct
+- * pattern translation into the pattern matrix
+- */
+- CGContextSetPatternPhase (context, CGSizeMake(0,0));
+-
+- state.pattern = pattern;
+- state.action = DO_PATTERN;
++ _cairo_quartz_setup_surface_source (surface, spat, extents, &state);
+ return state;
+ }
+
+ state.action = DO_UNSUPPORTED;
+ return state;
+ }
+
+ /**
diff --git a/gfx/cairo/quartz-remove-snapshot.patch b/gfx/cairo/quartz-remove-snapshot.patch
new file mode 100644
index 000000000..5cc2cddb4
--- /dev/null
+++ b/gfx/cairo/quartz-remove-snapshot.patch
@@ -0,0 +1,62 @@
+changeset: 42958:dd0f1f0a96b3
+user: Robert O'Callahan <robert@ocallahan.org>
+date: Tue Jun 01 11:33:05 2010 +1200
+summary: Bug 568189. Part 3: Remove snapshot backend function since it doesn't work and we may as well just fall back. r=jrmuizel
+
+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
+@@ -1993,33 +1993,16 @@ _cairo_quartz_surface_acquire_source_ima
+ if (status)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ *image_extra = NULL;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+-static cairo_surface_t *
+-_cairo_quartz_surface_snapshot (void *abstract_surface)
+-{
+- cairo_int_status_t status;
+- cairo_quartz_surface_t *surface = abstract_surface;
+- cairo_image_surface_t *image;
+-
+- if (surface->imageSurfaceEquiv)
+- return NULL;
+-
+- status = _cairo_quartz_get_image (surface, &image);
+- if (unlikely (status))
+- return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+-
+- return &image->base;
+-}
+-
+ static void
+ _cairo_quartz_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+ {
+ cairo_surface_destroy ((cairo_surface_t *) image);
+ }
+
+@@ -2916,17 +2899,17 @@ static const struct _cairo_surface_backe
+ _cairo_quartz_surface_stroke,
+ _cairo_quartz_surface_fill,
+ #if CAIRO_HAS_QUARTZ_FONT
+ _cairo_quartz_surface_show_glyphs,
+ #else
+ NULL, /* show_glyphs */
+ #endif
+
+- _cairo_quartz_surface_snapshot,
++ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL /* fill_stroke */
+ };
+
+ cairo_quartz_surface_t *
+ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
+ cairo_content_t content,
+ unsigned int width,
+
diff --git a/gfx/cairo/quartz-repeating-radial-gradients.patch b/gfx/cairo/quartz-repeating-radial-gradients.patch
new file mode 100644
index 000000000..67fca30d0
--- /dev/null
+++ b/gfx/cairo/quartz-repeating-radial-gradients.patch
@@ -0,0 +1,305 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1249558156 -43200
+# Node ID e564f3ab4ea6e3b5dd9c4e9e6042d3a84c229dde
+# Parent 6ef9993a30bf2f983c9d64d7441d2e3b6b935de1
+Bug 508227. Don't fallback to Quartz for repeating radial gradients. r=jmuizelaar
+
+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
+@@ -708,20 +708,20 @@ CreateGradientFunction (const cairo_grad
+ 1,
+ input_value_range,
+ 4,
+ output_value_ranges,
+ &callbacks);
+ }
+
+ static CGFunctionRef
+-CreateRepeatingGradientFunction (cairo_quartz_surface_t *surface,
+- const cairo_gradient_pattern_t *gpat,
+- CGPoint *start, CGPoint *end,
+- CGAffineTransform matrix)
++CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
++ const cairo_gradient_pattern_t *gpat,
++ CGPoint *start, CGPoint *end,
++ CGAffineTransform matrix)
+ {
+ cairo_pattern_t *pat;
+ float input_value_range[2];
+ float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
+ CGFunctionCallbacks callbacks = {
+ 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
+ };
+
+@@ -791,16 +791,156 @@ CreateRepeatingGradientFunction (cairo_q
+ return CGFunctionCreate (pat,
+ 1,
+ input_value_range,
+ 4,
+ output_value_ranges,
+ &callbacks);
+ }
+
++static void
++UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center,
++ double dr, double dx, double dy,
++ double x, double y)
++{
++ /* Compute a parameter t such that a circle centered at
++ (center->x + dx*t, center->y + dy*t) with radius dr*t contains the
++ point (x,y).
++
++ Let px = x - center->x, py = y - center->y.
++ Parameter values for which t is on the circle are given by
++ (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2
++
++ Solving for t using the quadratic formula, and simplifying, we get
++ numerator = dx*px + dy*py +-
++ sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
++ denominator = dx^2 + dy^2 - dr^2
++ t = numerator/denominator
++
++ In CreateRepeatingRadialGradientFunction we know the outer circle
++ contains the inner circle. Therefore the distance between the circle
++ centers plus the radius of the inner circle is less than the radius of
++ the outer circle. (This is checked in _cairo_quartz_setup_radial_source.)
++ Therefore
++ dx^2 + dy^2 < dr^2
++ So the denominator is negative and the larger solution for t is given by
++ numerator = dx*px + dy*py -
++ sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
++ denominator = dx^2 + dy^2 - dr^2
++ t = numerator/denominator
++ dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive.
++ */
++ double px = x - center->x;
++ double py = y - center->y;
++ double dx_py_minus_dy_px = dx*py - dy*px;
++ double numerator = dx*px + dy*py -
++ sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px);
++ double denominator = dx*dx + dy*dy - dr*dr;
++ double t = numerator/denominator;
++
++ if (*max_t < t) {
++ *max_t = t;
++ }
++}
++
++/* This must only be called when one of the circles properly contains the other */
++static CGFunctionRef
++CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
++ const cairo_gradient_pattern_t *gpat,
++ CGPoint *start, double *start_radius,
++ CGPoint *end, double *end_radius)
++{
++ CGRect clip = CGContextGetClipBoundingBox (surface->cgContext);
++ CGAffineTransform transform;
++ cairo_pattern_t *pat;
++ float input_value_range[2];
++ float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
++ CGFunctionCallbacks callbacks = {
++ 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
++ };
++ CGPoint *inner;
++ double *inner_radius;
++ CGPoint *outer;
++ double *outer_radius;
++ /* minimum and maximum t-parameter values that will make our gradient
++ cover the clipBox */
++ double t_min, t_max, t_temp;
++ /* outer minus inner */
++ double dr, dx, dy;
++
++ _cairo_quartz_cairo_matrix_to_quartz (&gpat->base.matrix, &transform);
++ /* clip is in cairo device coordinates; get it into cairo user space */
++ clip = CGRectApplyAffineTransform (clip, transform);
++
++ if (*start_radius < *end_radius) {
++ /* end circle contains start circle */
++ inner = start;
++ outer = end;
++ inner_radius = start_radius;
++ outer_radius = end_radius;
++ } else {
++ /* start circle contains end circle */
++ inner = end;
++ outer = start;
++ inner_radius = end_radius;
++ outer_radius = start_radius;
++ }
++
++ dr = *outer_radius - *inner_radius;
++ dx = outer->x - inner->x;
++ dy = outer->y - inner->y;
++
++ t_min = -(*inner_radius/dr);
++ inner->x += t_min*dx;
++ inner->y += t_min*dy;
++ *inner_radius = 0.;
++
++ t_temp = 0.;
++ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
++ clip.origin.x, clip.origin.y);
++ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
++ clip.origin.x + clip.size.width, clip.origin.y);
++ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
++ clip.origin.x + clip.size.width, clip.origin.y + clip.size.height);
++ UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
++ clip.origin.x, clip.origin.y + clip.size.height);
++ /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
++ But for the parameter values we use with Quartz, t_min means radius 0.
++ Also, add a small fudge factor to avoid rounding issues. Since the
++ circles are alway expanding and containing the earlier circles, this is
++ OK. */
++ t_temp += 1e-6;
++ t_max = t_min + t_temp;
++ outer->x = inner->x + t_temp*dx;
++ outer->y = inner->y + t_temp*dy;
++ *outer_radius = t_temp*dr;
++
++ /* set the input range for the function -- the function knows how to
++ map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */
++ if (*start_radius < *end_radius) {
++ input_value_range[0] = t_min;
++ input_value_range[1] = t_max;
++ } else {
++ input_value_range[0] = -t_max;
++ input_value_range[1] = -t_min;
++ }
++
++ if (_cairo_pattern_create_copy (&pat, &gpat->base))
++ /* quartz doesn't deal very well with malloc failing, so there's
++ * not much point in us trying either */
++ return NULL;
++
++ return CGFunctionCreate (pat,
++ 1,
++ input_value_range,
++ 4,
++ output_value_ranges,
++ &callbacks);
++}
++
+ /* Obtain a CGImageRef from a #cairo_surface_t * */
+
+ static void
+ DataProviderReleaseCallback (void *info, const void *data, size_t size)
+ {
+ cairo_surface_t *surface = (cairo_surface_t *) info;
+ cairo_surface_destroy (surface);
+ }
+@@ -1112,23 +1252,24 @@ _cairo_quartz_setup_linear_source (cairo
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+ start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
+ _cairo_fixed_to_double (lpat->p1.y));
+ end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
+ _cairo_fixed_to_double (lpat->p2.y));
+
+ if (abspat->extend == CAIRO_EXTEND_NONE ||
+- abspat->extend == CAIRO_EXTEND_PAD)
++ abspat->extend == CAIRO_EXTEND_PAD)
+ {
+ gradFunc = CreateGradientFunction (&lpat->base);
+ } else {
+- gradFunc = CreateRepeatingGradientFunction (surface,
+- &lpat->base,
+- &start, &end, surface->sourceTransform);
++ gradFunc = CreateRepeatingLinearGradientFunction (surface,
++ &lpat->base,
++ &start, &end,
++ surface->sourceTransform);
+ }
+
+ surface->sourceShading = CGShadingCreateAxial (rgb,
+ start, end,
+ gradFunc,
+ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+@@ -1142,52 +1283,68 @@ _cairo_quartz_setup_radial_source (cairo
+ const cairo_radial_pattern_t *rpat)
+ {
+ const cairo_pattern_t *abspat = &rpat->base.base;
+ cairo_matrix_t mat;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
++ double c1x = _cairo_fixed_to_double (rpat->c1.x);
++ double c1y = _cairo_fixed_to_double (rpat->c1.y);
++ double c2x = _cairo_fixed_to_double (rpat->c2.x);
++ double c2y = _cairo_fixed_to_double (rpat->c2.y);
++ double r1 = _cairo_fixed_to_double (rpat->r1);
++ double r2 = _cairo_fixed_to_double (rpat->r2);
++ double dx = c1x - c2x;
++ double dy = c1y - c2y;
++ double centerDistance = sqrt (dx*dx + dy*dy);
+
+ if (rpat->base.n_stops == 0) {
+ CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+ CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+ return DO_SOLID;
+ }
+
+- if (abspat->extend == CAIRO_EXTEND_REPEAT ||
+- abspat->extend == CAIRO_EXTEND_REFLECT)
+- {
+- /* I started trying to map these to Quartz, but it's much harder
+- * then the linear case (I think it would involve doing multiple
+- * Radial shadings). So, instead, let's just render an image
+- * for pixman to draw the shading into, and use that.
++ if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
++ r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
++ /* Quartz handles cases where neither circle contains the other very
++ * differently from pixman.
++ * Whatever the correct behaviour is, let's at least have only pixman's
++ * implementation to worry about.
++ * Note that this also catches the cases where r1 == r2.
+ */
+- return _cairo_quartz_setup_fallback_source (surface, &rpat->base.base);
++ return _cairo_quartz_setup_fallback_source (surface, abspat);
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+ _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+- start = CGPointMake (_cairo_fixed_to_double (rpat->c1.x),
+- _cairo_fixed_to_double (rpat->c1.y));
+- end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x),
+- _cairo_fixed_to_double (rpat->c2.y));
++ start = CGPointMake (c1x, c1y);
++ end = CGPointMake (c2x, c2y);
+
+- gradFunc = CreateGradientFunction (&rpat->base);
++ if (abspat->extend == CAIRO_EXTEND_NONE ||
++ abspat->extend == CAIRO_EXTEND_PAD)
++ {
++ gradFunc = CreateGradientFunction (&rpat->base);
++ } else {
++ gradFunc = CreateRepeatingRadialGradientFunction (surface,
++ &rpat->base,
++ &start, &r1,
++ &end, &r2);
++ }
+
+ surface->sourceShading = CGShadingCreateRadial (rgb,
+ start,
+- _cairo_fixed_to_double (rpat->r1),
++ r1,
+ end,
+- _cairo_fixed_to_double (rpat->r2),
++ r2,
+ gradFunc,
+ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+ return DO_SHADING;
+ }
diff --git a/gfx/cairo/quartz-state.patch b/gfx/cairo/quartz-state.patch
new file mode 100644
index 000000000..d4c04f706
--- /dev/null
+++ b/gfx/cairo/quartz-state.patch
@@ -0,0 +1,1190 @@
+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
+@@ -50,30 +50,16 @@ typedef struct cairo_quartz_surface {
+ CGContextRef cgContext;
+ CGAffineTransform cgContextBaseCTM;
+
+ void *imageData;
+ cairo_surface_t *imageSurfaceEquiv;
+
+ cairo_surface_clipper_t clipper;
+ cairo_rectangle_int_t extents;
+-
+- /* These are stored while drawing operations are in place, set up
+- * by quartz_setup_source() and quartz_finish_source()
+- */
+- CGAffineTransform sourceTransform;
+-
+- CGImageRef sourceImage;
+- cairo_surface_t *sourceImageSurface;
+- CGRect sourceImageRect;
+-
+- CGShadingRef sourceShading;
+- CGPatternRef sourcePattern;
+-
+- CGInterpolationQuality oldInterpolationQuality;
+ } cairo_quartz_surface_t;
+
+ typedef struct cairo_quartz_image_surface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t extents;
+
+ CGImageRef image;
+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
+@@ -1333,36 +1333,59 @@ _cairo_quartz_cairo_repeating_surface_pa
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ typedef enum {
+ DO_SOLID,
+ DO_SHADING,
+ DO_PATTERN,
+ DO_IMAGE,
++ DO_TILED_IMAGE,
+ DO_UNSUPPORTED,
+- DO_NOTHING,
+- DO_TILED_IMAGE
++ DO_NOTHING
+ } cairo_quartz_action_t;
+
+-static cairo_quartz_action_t
++/* State used during a drawing operation. */
++typedef struct {
++ CGContextRef context;
++ cairo_quartz_action_t action;
++
++ // Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE
++ CGAffineTransform transform;
++
++ // Used with DO_IMAGE and DO_TILED_IMAGE
++ CGImageRef image;
++ cairo_surface_t *imageSurface;
++ CGRect imageRect;
++
++ // Used with DO_SHADING
++ CGShadingRef shading;
++
++ // Used with DO_PATTERN
++ CGPatternRef pattern;
++} cairo_quartz_drawing_state_t;
++
++static void
+ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source)
++ const cairo_pattern_t *source,
++ cairo_quartz_drawing_state_t *state)
+ {
+- CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
++ CGRect clipBox = CGContextGetClipBoundingBox (state->context);
+ double x0, y0, w, h;
+
+ cairo_surface_t *fallback;
+ CGImageRef img;
+
+ cairo_status_t status;
+
+ if (clipBox.size.width == 0.0f ||
+- clipBox.size.height == 0.0f)
+- return DO_NOTHING;
++ clipBox.size.height == 0.0f) {
++ state->action = DO_NOTHING;
++ return;
++ }
+
+ x0 = floor(clipBox.origin.x);
+ y0 = floor(clipBox.origin.y);
+ w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
+ h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
+
+ /* Create a temporary the size of the clip surface, and position
+ * it so that the device origin coincides with the original surface */
+@@ -1396,73 +1419,79 @@ _cairo_quartz_setup_fallback_source (cai
+ &fallback->device_transform_inverse);
+ status = _cairo_surface_paint (fallback,
+ CAIRO_OPERATOR_SOURCE,
+ &pattern.base, NULL);
+ }
+ #endif
+
+ status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
+- if (status)
+- return DO_UNSUPPORTED;
+- if (img == NULL)
+- return DO_NOTHING;
+-
+- surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
+- surface->sourceImage = img;
+- surface->sourceImageSurface = fallback;
+- surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
+-
+- return DO_IMAGE;
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
++ }
++ if (img == NULL) {
++ state->action = DO_NOTHING;
++ return;
++ }
++
++ state->imageRect = CGRectMake (0.0, 0.0, w, h);
++ state->image = img;
++ state->imageSurface = fallback;
++ state->transform = CGAffineTransformMakeTranslation (x0, y0);
++ state->action = DO_IMAGE;
+ }
+
+ /*
+ Quartz does not support repeating radients. We handle repeating gradients
+ by manually extending the gradient and repeating color stops. We need to
+ minimize the number of repetitions since Quartz seems to sample our color
+ function across the entire range, even if part of that range is not needed
+ for the visible area of the gradient, and it samples with some fixed resolution,
+ so if the gradient range is too large it samples with very low resolution and
+ the gradient is very coarse. CreateRepeatingLinearGradientFunction and
+ CreateRepeatingRadialGradientFunction compute the number of repetitions needed
+ based on the extents of the object (the clip region cannot be used here since
+ we don't want the rasterization of the entire gradient to depend on the
+ clip region).
+ */
+-static cairo_quartz_action_t
++static void
+ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
+ const cairo_linear_pattern_t *lpat,
+- cairo_rectangle_int_t *extents)
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
+ {
+ const cairo_pattern_t *abspat = &lpat->base.base;
+ cairo_matrix_t mat;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+
+ if (lpat->base.n_stops == 0) {
+- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+- return DO_SOLID;
++ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
++ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
++ state->action = DO_SOLID;
++ return;
+ }
+
+ if (lpat->p1.x == lpat->p2.x &&
+ lpat->p1.y == lpat->p2.y) {
+ /* Quartz handles cases where the vector has no length very
+ * differently from pixman.
+ * Whatever the correct behaviour is, let's at least have only pixman's
+ * implementation to worry about.
+ */
+- return _cairo_quartz_setup_fallback_source (surface, abspat);
++ _cairo_quartz_setup_fallback_source (surface, abspat, state);
++ return;
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
++ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+ start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
+ _cairo_fixed_to_double (lpat->p1.y));
+ end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
+ _cairo_fixed_to_double (lpat->p2.y));
+
+@@ -1472,31 +1501,32 @@ _cairo_quartz_setup_linear_source (cairo
+ gradFunc = CreateGradientFunction (&lpat->base);
+ } else {
+ gradFunc = CreateRepeatingLinearGradientFunction (surface,
+ &lpat->base,
+ &start, &end,
+ extents);
+ }
+
+- surface->sourceShading = CGShadingCreateAxial (rgb,
+- start, end,
+- gradFunc,
+- extend, extend);
++ state->shading = CGShadingCreateAxial (rgb,
++ start, end,
++ gradFunc,
++ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+- return DO_SHADING;
++ state->action = DO_SHADING;
+ }
+
+-static cairo_quartz_action_t
++static void
+ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
+ const cairo_radial_pattern_t *rpat,
+- cairo_rectangle_int_t *extents)
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
+ {
+ const cairo_pattern_t *abspat = &rpat->base.base;
+ cairo_matrix_t mat;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+ double c1x = _cairo_fixed_to_double (rpat->c1.x);
+@@ -1505,35 +1535,37 @@ _cairo_quartz_setup_radial_source (cairo
+ double c2y = _cairo_fixed_to_double (rpat->c2.y);
+ double r1 = _cairo_fixed_to_double (rpat->r1);
+ double r2 = _cairo_fixed_to_double (rpat->r2);
+ double dx = c1x - c2x;
+ double dy = c1y - c2y;
+ double centerDistance = sqrt (dx*dx + dy*dy);
+
+ if (rpat->base.n_stops == 0) {
+- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+- return DO_SOLID;
++ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
++ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
++ state->action = DO_SOLID;
++ return;
+ }
+
+ if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
+ r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
+ /* Quartz handles cases where neither circle contains the other very
+ * differently from pixman.
+ * Whatever the correct behaviour is, let's at least have only pixman's
+ * implementation to worry about.
+ * Note that this also catches the cases where r1 == r2.
+ */
+- return _cairo_quartz_setup_fallback_source (surface, abspat);
++ _cairo_quartz_setup_fallback_source (surface, abspat, state);
++ return;
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
++ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+ start = CGPointMake (c1x, c1y);
+ end = CGPointMake (c2x, c2y);
+
+ if (abspat->extend == CAIRO_EXTEND_NONE ||
+ abspat->extend == CAIRO_EXTEND_PAD)
+@@ -1542,111 +1574,146 @@ _cairo_quartz_setup_radial_source (cairo
+ } else {
+ gradFunc = CreateRepeatingRadialGradientFunction (surface,
+ &rpat->base,
+ &start, &r1,
+ &end, &r2,
+ extents);
+ }
+
+- surface->sourceShading = CGShadingCreateRadial (rgb,
+- start,
+- r1,
+- end,
+- r2,
+- gradFunc,
+- extend, extend);
++ state->shading = CGShadingCreateRadial (rgb,
++ start,
++ r1,
++ end,
++ r2,
++ gradFunc,
++ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+- return DO_SHADING;
++ state->action = DO_SHADING;
+ }
+
+-static cairo_quartz_action_t
+-_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source,
+- cairo_rectangle_int_t *extents)
++/**
++ * Sets up internal state to be used to draw the source mask, stored in
++ * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on
++ * surface->cgContext.
++ */
++static cairo_quartz_drawing_state_t
++_cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
++ const cairo_pattern_t *source,
++ cairo_operator_t op,
++ cairo_rectangle_int_t *extents)
+ {
+- assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
+-
+- surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
+- CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
++ CGContextRef context = surface->cgContext;
++ cairo_quartz_drawing_state_t state;
++ cairo_status_t status;
++
++ state.context = context;
++ state.image = NULL;
++ state.imageSurface = NULL;
++ state.shading = NULL;
++ state.pattern = NULL;
++
++ // Save before we change the pattern, colorspace, etc. so that
++ // we can restore and make sure that quartz releases our
++ // pattern (which may be stack allocated)
++ CGContextSaveGState(context);
++
++ CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
++
++ status = _cairo_quartz_surface_set_cairo_operator (surface, op);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ state.action = DO_NOTHING;
++ return state;
++ }
++ if (status) {
++ state.action = DO_UNSUPPORTED;
++ return state;
++ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+
+- CGContextSetRGBStrokeColor (surface->cgContext,
++ CGContextSetRGBStrokeColor (context,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+- CGContextSetRGBFillColor (surface->cgContext,
++ CGContextSetRGBFillColor (context,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+- return DO_SOLID;
++ state.action = DO_SOLID;
++ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
+- return _cairo_quartz_setup_linear_source (surface, lpat, extents);
++ _cairo_quartz_setup_linear_source (surface, lpat, extents, &state);
++ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
+- return _cairo_quartz_setup_radial_source (surface, rpat, extents);
++ _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
++ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
+ {
+ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ cairo_surface_t *pat_surf = spat->surface;
+ CGImageRef img;
+ cairo_matrix_t m = spat->base.matrix;
+ cairo_rectangle_int_t extents;
+- cairo_status_t status;
+ CGAffineTransform xform;
+ CGRect srcRect;
+ cairo_fixed_t fw, fh;
+ cairo_bool_t is_bounded;
+
+ status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
+- if (status)
+- return DO_UNSUPPORTED;
+- if (img == NULL)
+- return DO_NOTHING;
++ if (status) {
++ state.action = DO_UNSUPPORTED;
++ return state;
++ }
++ if (img == NULL) {
++ state.action = DO_NOTHING;
++ return state;
++ }
+
+ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+
+- surface->sourceImage = img;
++ state.image = img;
+
+ cairo_matrix_invert(&m);
+- _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
++ _cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform);
+
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+ if (source->extend == CAIRO_EXTEND_NONE) {
+- surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
+- return DO_IMAGE;
++ state.imageRect = CGRectMake (0, 0, extents.width, extents.height);
++ state.action = DO_IMAGE;
++ return state;
+ }
+
+ /* 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 (surface->cgContext),
+- surface->sourceTransform);
++ xform = CGAffineTransformConcat (CGContextGetCTM (context),
++ state.transform);
+
+ srcRect = CGRectMake (0, 0, extents.width, extents.height);
+ 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 &&
+@@ -1657,111 +1724,109 @@ _cairo_quartz_setup_source (cairo_quartz
+
+ srcRect.size.width = round(srcRect.size.width);
+ srcRect.size.height = round(srcRect.size.height);
+
+ xform = CGAffineTransformInvert (xform);
+
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+- surface->sourceImageRect = srcRect;
+-
+- return DO_TILED_IMAGE;
++ state.imageRect = srcRect;
++ state.action = DO_TILED_IMAGE;
++ return state;
+ }
+
+ /* Fall through to generic SURFACE case */
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ CGFloat patternAlpha = 1.0f;
+ CGColorSpaceRef patternSpace;
+ CGPatternRef pattern;
+ cairo_int_status_t status;
+
+ status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
+- if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+- return DO_NOTHING;
+- if (status)
+- return DO_UNSUPPORTED;
+-
+- // 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);
+-
+- patternSpace = CGColorSpaceCreatePattern(NULL);
+- CGContextSetFillColorSpace (surface->cgContext, patternSpace);
+- CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
+- CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
+- CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ state.action = DO_NOTHING;
++ return state;
++ }
++ if (status) {
++ state.action = DO_UNSUPPORTED;
++ return state;
++ }
++
++ patternSpace = CGColorSpaceCreatePattern (NULL);
++ CGContextSetFillColorSpace (context, patternSpace);
++ CGContextSetFillPattern (context, pattern, &patternAlpha);
++ CGContextSetStrokeColorSpace (context, patternSpace);
++ CGContextSetStrokePattern (context, pattern, &patternAlpha);
+ CGColorSpaceRelease (patternSpace);
+
+ /* Quartz likes to munge the pattern phase (as yet unexplained
+ * why); force it to 0,0 as we've already baked in the correct
+ * pattern translation into the pattern matrix
+ */
+- CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
+-
+- surface->sourcePattern = pattern;
+-
+- return DO_PATTERN;
++ CGContextSetPatternPhase (context, CGSizeMake(0,0));
++
++ state.pattern = pattern;
++ state.action = DO_PATTERN;
++ return state;
+ }
+
+- return DO_UNSUPPORTED;
++ state.action = DO_UNSUPPORTED;
++ return state;
+ }
+
++/**
++ * 1) Tears down internal state used to draw the source
++ * 2) Does CGContextRestoreGState(state->context)
++ */
+ static void
+-_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source)
++_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
+ {
+- CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
+-
+- if (surface->sourceImage) {
+- CGImageRelease(surface->sourceImage);
+- surface->sourceImage = NULL;
+-
+- cairo_surface_destroy(surface->sourceImageSurface);
+- surface->sourceImageSurface = NULL;
++ if (state->image) {
++ CGImageRelease(state->image);
+ }
+
+- if (surface->sourceShading) {
+- CGShadingRelease(surface->sourceShading);
+- surface->sourceShading = NULL;
++ if (state->imageSurface) {
++ cairo_surface_destroy(state->imageSurface);
+ }
+
+- if (surface->sourcePattern) {
+- CGPatternRelease(surface->sourcePattern);
+- // To tear down the pattern and colorspace
+- CGContextRestoreGState(surface->cgContext);
+-
+- surface->sourcePattern = NULL;
++ if (state->shading) {
++ CGShadingRelease(state->shading);
+ }
++
++ if (state->pattern) {
++ CGPatternRelease(state->pattern);
++ }
++
++ CGContextRestoreGState(state->context);
+ }
+
+
+ static void
+-_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_action_t action)
++_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
+ {
+- assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE));
+-
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
+- CGContextScaleCTM (surface->cgContext, 1, -1);
+-
+- if (action == DO_IMAGE) {
+- CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+- if (!_cairo_operator_bounded_by_source(op)) {
+- CGContextBeginPath (surface->cgContext);
+- CGContextAddRect (surface->cgContext, surface->sourceImageRect);
+- CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
+- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0);
+- CGContextEOFillPath (surface->cgContext);
++ assert (state && state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE));
++
++ CGContextConcatCTM (state->context, state->transform);
++ CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
++ CGContextScaleCTM (state->context, 1, -1);
++
++ if (state->action == DO_IMAGE) {
++ CGContextDrawImage (state->context, state->imageRect, state->image);
++ if (!_cairo_operator_bounded_by_source (op)) {
++ CGContextBeginPath (state->context);
++ CGContextAddRect (state->context, state->imageRect);
++ CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
++ CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
++ CGContextEOFillPath (state->context);
+ }
+ } else
+- CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
++ CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
+ }
+
+
+ /*
+ * get source/dest image implementation
+ */
+
+ /* Read the image from the surface's front buffer */
+@@ -2098,52 +2163,44 @@ _cairo_quartz_surface_get_extents (void
+ static cairo_int_status_t
+ _cairo_quartz_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+
+ ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+-
+- action = _cairo_quartz_setup_source (surface, source, NULL);
+-
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+- surface->extents.y,
+- surface->extents.width,
+- surface->extents.height));
+- } else if (action == DO_SHADING) {
+- CGContextSaveGState (surface->cgContext);
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- CGContextRestoreGState (surface->cgContext);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- CGContextSaveGState (surface->cgContext);
+- _cairo_quartz_draw_image (surface, op, action);
+- CGContextRestoreGState (surface->cgContext);
+- } else if (action != DO_NOTHING) {
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
++
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
++ CGContextFillRect (state.context, CGRectMake(surface->extents.x,
++ surface->extents.y,
++ surface->extents.width,
++ surface->extents.height));
++ } else if (state.action == DO_SHADING) {
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+- _cairo_quartz_teardown_source (surface, source);
++ _cairo_quartz_teardown_state (&state);
+
+ ND((stderr, "-- paint\n"));
+ return rv;
+ }
+
+ static cairo_bool_t
+ _cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
+ {
+@@ -2170,91 +2227,83 @@ _cairo_quartz_surface_fill (void *abstra
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ quartz_stroke_t stroke;
+ CGPathRef path_for_unbounded = NULL;
+
+ ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+-
+- CGContextSaveGState (surface->cgContext);
+-
+- CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
+-
+ if (_cairo_quartz_source_needs_extents (source))
+ {
+ /* We don't need precise extents since these are only used to
+ compute the number of gradient reptitions needed to cover the
+ object. */
+ cairo_rectangle_int_t path_extents;
+ _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
+- action = _cairo_quartz_setup_source (surface, source, &path_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
+ } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+- CGContextBeginPath (surface->cgContext);
+-
+- stroke.cgContext = surface->cgContext;
++ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
++
++ CGContextBeginPath (state.context);
++
++ stroke.cgContext = state.context;
+ stroke.ctm_inverse = NULL;
+ rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
+ if (rv)
+ goto BAIL;
+
+ if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
+- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+-
+- if (action == DO_SOLID || action == DO_PATTERN) {
++ path_for_unbounded = CGContextCopyPathPtr (state.context);
++
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextFillPath (surface->cgContext);
++ CGContextFillPath (state.context);
+ else
+- CGContextEOFillPath (surface->cgContext);
+- } else if (action == DO_SHADING) {
++ CGContextEOFillPath (state.context);
++ } else if (state.action == DO_SHADING) {
+
+ // we have to clip and then paint the shading; we can't fill
+ // with the shading
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextClip (surface->cgContext);
++ CGContextClip (state.context);
+ else
+- CGContextEOClip (surface->cgContext);
+-
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
++ CGContextEOClip (state.context);
++
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextClip (surface->cgContext);
++ CGContextClip (state.context);
+ else
+- CGContextEOClip (surface->cgContext);
+-
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action != DO_NOTHING) {
++ CGContextEOClip (state.context);
++
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ BAIL:
+- _cairo_quartz_teardown_source (surface, source);
+-
+- CGContextRestoreGState (surface->cgContext);
++ _cairo_quartz_teardown_state (&state);
+
+ if (path_for_unbounded) {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_STROKE_FILL;
+ ub.u.stroke_fill.cgPath = path_for_unbounded;
+ ub.u.stroke_fill.fill_rule = fill_rule;
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
+@@ -2274,44 +2323,49 @@ _cairo_quartz_surface_stroke (void *abst
+ cairo_matrix_t *ctm,
+ cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ quartz_stroke_t stroke;
+ CGAffineTransform origCTM, strokeTransform;
+ CGPathRef path_for_unbounded = NULL;
+
+ ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
++ if (_cairo_quartz_source_needs_extents (source))
++ {
++ cairo_rectangle_int_t path_extents;
++ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
++ } else {
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
++ }
+
+ // 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 (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
+- CGContextSetLineWidth (surface->cgContext, style->line_width);
+- CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
+- CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
+- CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
+-
+- origCTM = CGContextGetCTM (surface->cgContext);
++ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
++ CGContextSetLineWidth (state.context, style->line_width);
++ CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
++ CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
++ CGContextSetMiterLimit (state.context, style->miter_limit);
++
++ origCTM = CGContextGetCTM (state.context);
+
+ if (style->dash && style->num_dashes) {
+ #define STATIC_DASH 32
+ CGFloat sdash[STATIC_DASH];
+ CGFloat *fdash = sdash;
+ double offset = style->dash_offset;
+ unsigned int max_dashes = style->num_dashes;
+ unsigned int k;
+@@ -2330,90 +2384,75 @@ _cairo_quartz_surface_stroke (void *abst
+ if (max_dashes > STATIC_DASH)
+ fdash = _cairo_malloc_ab (max_dashes, sizeof (CGFloat));
+ if (fdash == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (k = 0; k < max_dashes; k++)
+ fdash[k] = (CGFloat) style->dash[k % style->num_dashes];
+ }
+- CGContextSetLineDash (surface->cgContext, offset, fdash, max_dashes);
++ CGContextSetLineDash (state.context, offset, fdash, max_dashes);
+ if (fdash != sdash)
+ free (fdash);
+ } else
+- CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
+-
+- CGContextSaveGState (surface->cgContext);
+-
+-
+- if (_cairo_quartz_source_needs_extents (source))
+- {
+- cairo_rectangle_int_t path_extents;
+- _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
+- action = _cairo_quartz_setup_source (surface, source, &path_extents);
+- } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
+- }
++ CGContextSetLineDash (state.context, 0, NULL, 0);
+
+ _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+- CGContextConcatCTM (surface->cgContext, strokeTransform);
+-
+- CGContextBeginPath (surface->cgContext);
+-
+- stroke.cgContext = surface->cgContext;
++ CGContextConcatCTM (state.context, strokeTransform);
++
++ CGContextBeginPath (state.context);
++
++ stroke.cgContext = state.context;
+ stroke.ctm_inverse = ctm_inverse;
+ rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
+ if (rv)
+ goto BAIL;
+
+ if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
+- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+-
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextStrokePath (surface->cgContext);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+- CGContextClip (surface->cgContext);
+-
+- CGContextSetCTM (surface->cgContext, origCTM);
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action == DO_SHADING) {
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+- CGContextClip (surface->cgContext);
+-
+- CGContextSetCTM (surface->cgContext, origCTM);
+-
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- } else if (action != DO_NOTHING) {
++ path_for_unbounded = CGContextCopyPathPtr (state.context);
++
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
++ CGContextStrokePath (state.context);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++ CGContextReplacePathWithStrokedPath (state.context);
++ CGContextClip (state.context);
++
++ CGContextSetCTM (state.context, origCTM);
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action == DO_SHADING) {
++ CGContextReplacePathWithStrokedPath (state.context);
++ CGContextClip (state.context);
++
++ CGContextSetCTM (state.context, origCTM);
++
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
++ goto BAIL;
+ }
+
++ if (path_for_unbounded) {
++ CGContextSetCTM (state.context, origCTM);
++ CGContextConcatCTM (state.context, strokeTransform);
++
++ CGContextBeginPath (state.context);
++ CGContextAddPath (state.context, path_for_unbounded);
++ CGPathRelease (path_for_unbounded);
++
++ CGContextReplacePathWithStrokedPath (state.context);
++
++ CGContextAddRect (state.context, CGContextGetClipBoundingBox (state.context));
++
++ CGContextSetRGBFillColor (state.context, 0., 0., 0., 0.);
++ CGContextEOFillPath (state.context);
++ }
++
+ BAIL:
+- _cairo_quartz_teardown_source (surface, source);
+-
+- CGContextRestoreGState (surface->cgContext);
+-
+- if (path_for_unbounded) {
+- CGContextSaveGState (surface->cgContext);
+- CGContextConcatCTM (surface->cgContext, strokeTransform);
+-
+- CGContextBeginPath (surface->cgContext);
+- CGContextAddPath (surface->cgContext, path_for_unbounded);
+- CGPathRelease (path_for_unbounded);
+-
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+-
+- CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
+-
+- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+- CGContextEOFillPath (surface->cgContext);
+-
+- CGContextRestoreGState (surface->cgContext);
+- }
++ _cairo_quartz_teardown_state (&state);
+
+ ND((stderr, "-- stroke\n"));
+ return rv;
+ }
+
+ #if CAIRO_HAS_QUARTZ_FONT
+ static cairo_int_status_t
+ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
+@@ -2429,17 +2468,17 @@ _cairo_quartz_surface_show_glyphs (void
+ #define STATIC_BUF_SIZE 64
+ CGGlyph glyphs_static[STATIC_BUF_SIZE];
+ CGSize cg_advances_static[STATIC_BUF_SIZE];
+ CGGlyph *cg_glyphs = &glyphs_static[0];
+ CGSize *cg_advances = &cg_advances_static[0];
+
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ float xprev, yprev;
+ int i;
+ CGFontRef cgfref = NULL;
+
+ cairo_bool_t isClipping = FALSE;
+ cairo_bool_t didForceFontSmoothing = FALSE;
+
+ if (IS_EMPTY(surface))
+@@ -2450,65 +2489,59 @@ _cairo_quartz_surface_show_glyphs (void
+
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+-
+- CGContextSaveGState (surface->cgContext);
+-
+ if (_cairo_quartz_source_needs_extents (source))
+ {
+ cairo_rectangle_int_t glyph_extents;
+ _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
+ &glyph_extents, NULL);
+- action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
+ } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
+- CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
++ CGContextSetTextDrawingMode (state.context, kCGTextFill);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || state.action == DO_SHADING) {
++ CGContextSetTextDrawingMode (state.context, kCGTextClip);
+ isClipping = TRUE;
+ } else {
+- if (action != DO_NOTHING)
++ if (state.action != DO_NOTHING)
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto BAIL;
+ }
+
+ /* this doesn't addref */
+ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
+- CGContextSetFont (surface->cgContext, cgfref);
+- CGContextSetFontSize (surface->cgContext, 1.0);
++ CGContextSetFont (state.context, cgfref);
++ CGContextSetFontSize (state.context, 1.0);
+
+ switch (scaled_font->options.antialias) {
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+- CGContextSetShouldAntialias (surface->cgContext, TRUE);
+- CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
++ CGContextSetShouldAntialias (state.context, TRUE);
++ CGContextSetShouldSmoothFonts (state.context, TRUE);
+ if (CGContextSetAllowsFontSmoothingPtr &&
+- !CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
++ !CGContextGetAllowsFontSmoothingPtr (state.context))
+ {
+ didForceFontSmoothing = TRUE;
+- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
++ CGContextSetAllowsFontSmoothingPtr (state.context, TRUE);
+ }
+ break;
+ case CAIRO_ANTIALIAS_NONE:
+- CGContextSetShouldAntialias (surface->cgContext, FALSE);
++ CGContextSetShouldAntialias (state.context, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+- CGContextSetShouldAntialias (surface->cgContext, TRUE);
+- CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
++ CGContextSetShouldAntialias (state.context, TRUE);
++ CGContextSetShouldSmoothFonts (state.context, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_DEFAULT:
+ /* Don't do anything */
+ break;
+ }
+
+ if (num_glyphs > STATIC_BUF_SIZE) {
+ cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
+@@ -2532,17 +2565,17 @@ _cairo_quartz_surface_show_glyphs (void
+ textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
+ textTransform = CGAffineTransformConcat (CGAffineTransformMake(scaled_font->ctm.xx,
+ -scaled_font->ctm.yx,
+ -scaled_font->ctm.xy,
+ scaled_font->ctm.yy,
+ 0., 0.),
+ textTransform);
+
+- CGContextSetTextMatrix (surface->cgContext, textTransform);
++ CGContextSetTextMatrix (state.context, textTransform);
+
+ /* Convert our glyph positions to glyph advances. We need n-1 advances,
+ * since the advance at index 0 is applied after glyph 0. */
+ xprev = glyphs[0].x;
+ yprev = glyphs[0].y;
+
+ cg_glyphs[0] = glyphs[0].index;
+
+@@ -2569,40 +2602,38 @@ _cairo_quartz_surface_show_glyphs (void
+
+ #if 0
+ for (i = 0; i < num_glyphs; i++) {
+ ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height));
+ }
+ #endif
+
+ /* Translate to the first glyph's position before drawing */
+- ctm = CGContextGetCTM (surface->cgContext);
+- CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
+-
+- CGContextShowGlyphsWithAdvances (surface->cgContext,
++ ctm = CGContextGetCTM (state.context);
++ CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
++
++ CGContextShowGlyphsWithAdvances (state.context,
+ cg_glyphs,
+ cg_advances,
+ num_glyphs);
+
+- CGContextSetCTM (surface->cgContext, ctm);
+-
+- if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action == DO_SHADING) {
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
++ CGContextSetCTM (state.context, ctm);
++
++ if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action == DO_SHADING) {
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
+ }
+
+ BAIL:
+- _cairo_quartz_teardown_source (surface, source);
+-
+ if (didForceFontSmoothing)
+- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
+-
+- CGContextRestoreGState (surface->cgContext);
++ CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
++
++ _cairo_quartz_teardown_state (&state);
+
+ if (rv == CAIRO_STATUS_SUCCESS &&
+ cgfref &&
+ !_cairo_operator_bounded_by_mask (op))
+ {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_SHOW_GLYPHS;
+
diff --git a/gfx/cairo/quartz-support-color-emoji-font.patch b/gfx/cairo/quartz-support-color-emoji-font.patch
new file mode 100644
index 000000000..5fb88b271
--- /dev/null
+++ b/gfx/cairo/quartz-support-color-emoji-font.patch
@@ -0,0 +1,432 @@
+From: Jonathan Kew <jkew@mozilla.com>
+bug 715798 pt 1 - support Apple Color Emoji font in cairo-quartz backend. r=jrmuizel
+
+diff --git a/gfx/cairo/cairo/src/cairo-quartz-font.c b/gfx/cairo/cairo/src/cairo-quartz-font.c
+--- a/gfx/cairo/cairo/src/cairo-quartz-font.c
++++ b/gfx/cairo/cairo/src/cairo-quartz-font.c
+@@ -85,16 +85,20 @@ typedef struct {
+ 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;
+
++/* CTFontCreateWithGraphicsFont is not public until 10.5. */
++typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
++static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
++
+ static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
+ static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
+
+ static void
+ quartz_font_ensure_symbols(void)
+ {
+ if (_cairo_quartz_font_symbol_lookup_done)
+ return;
+@@ -122,16 +126,18 @@ quartz_font_ensure_symbols(void)
+ 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");
+
++ CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
++
+ if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
+ CGFontGetGlyphBBoxesPtr &&
+ CGFontGetGlyphsForUnicharsPtr &&
+ CGFontGetUnitsPerEmPtr &&
+ CGFontGetGlyphAdvancesPtr &&
+ CGFontGetGlyphPathPtr &&
+ (CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
+ _cairo_quartz_font_symbols_present = TRUE;
+@@ -145,16 +151,17 @@ typedef struct _cairo_quartz_scaled_font
+ 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,
+@@ -229,16 +236,20 @@ static cairo_status_t
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static void
+ _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);
+ }
+
+ 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,
+@@ -353,16 +364,22 @@ cairo_quartz_font_face_create_for_cgfont
+ 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
+ */
+@@ -772,16 +789,24 @@ static const cairo_scaled_font_backend_t
+ 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;
++}
++
+ #ifndef __LP64__
+ /*
+ * compat with old ATSUI backend
+ */
+
+ /**
+ * cairo_quartz_font_face_create_for_atsu_font_id
+ * @font_id: an ATSUFontID for the font.
+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
+@@ -45,16 +45,19 @@
+ #include "cairo-surface-clipper-private.h"
+
+ #ifdef CGFLOAT_DEFINED
+ typedef CGFloat cairo_quartz_float_t;
+ #else
+ typedef float cairo_quartz_float_t;
+ #endif
+
++/* 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;
+@@ -99,15 +102,18 @@ CGImageRef
+ cairo_bool_t interpolate,
+ CGColorSpaceRef colorSpaceOverride,
+ CGDataProviderReleaseDataCallback releaseCallback,
+ void *releaseInfo);
+
+ CGFontRef
+ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
+
++CTFontRef
++_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
++
+ #else
+
+ # error Cairo was not compiled with support for the quartz backend
+
+ #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
+--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
+@@ -130,16 +130,19 @@ static void (*CGContextClipToMaskPtr) (C
+ static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+ static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
+ static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
+ static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+ static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
+ static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
+
++/* CTFontDrawGlyphs is not available until 10.7 */
++static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
++
+ static SInt32 _cairo_quartz_osx_version = 0x0;
+
+ static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
+
+ /*
+ * Utility functions
+ */
+
+@@ -167,16 +170,18 @@ static void quartz_ensure_symbols(void)
+ CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
+ CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
+ CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
+ CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+ CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
+
++ CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
++
+ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
+ // assume 10.5
+ _cairo_quartz_osx_version = 0x1050;
+ }
+
+ _cairo_quartz_symbol_lookup_done = TRUE;
+ }
+
+@@ -605,20 +610,23 @@ static inline void
+ dst->d = src->yy;
+ dst->tx = src->x0;
+ dst->ty = src->y0;
+ }
+
+ typedef struct {
+ bool isClipping;
+ CGGlyph *cg_glyphs;
+- CGSize *cg_advances;
++ union {
++ CGSize *cg_advances;
++ CGPoint *cg_positions;
++ } u;
+ size_t nglyphs;
+ CGAffineTransform textTransform;
+- CGFontRef font;
++ cairo_scaled_font_t *scaled_font;
+ CGPoint origin;
+ } unbounded_show_glyphs_t;
+
+ typedef struct {
+ CGPathRef cgPath;
+ cairo_fill_rule_t fill_rule;
+ } unbounded_stroke_fill_t;
+
+@@ -686,36 +694,43 @@ static void
+ CGContextBeginPath (cgc);
+ CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
+
+ if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
+ CGContextFillPath (cgc);
+ else
+ CGContextEOFillPath (cgc);
+ } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
+- CGContextSetFont (cgc, op->u.show_glyphs.font);
+- CGContextSetFontSize (cgc, 1.0);
+- CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+- CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
+- CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
+-
+ if (op->u.show_glyphs.isClipping) {
+ /* Note that the comment in show_glyphs about kCGTextClip
+ * and the text transform still applies here; however, the
+ * cg_advances we have were already transformed, so we
+ * don't have to do anything. */
+ CGContextSetTextDrawingMode (cgc, kCGTextClip);
+ CGContextSaveGState (cgc);
+ }
+-
+- CGContextShowGlyphsWithAdvances (cgc,
+- op->u.show_glyphs.cg_glyphs,
+- op->u.show_glyphs.cg_advances,
+- op->u.show_glyphs.nglyphs);
+-
++ CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
++ CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
++ if (CTFontDrawGlyphsPtr) {
++ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font),
++ op->u.show_glyphs.cg_glyphs,
++ op->u.show_glyphs.u.cg_positions,
++ op->u.show_glyphs.nglyphs,
++ cgc);
++ } else {
++ CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font));
++ CGContextSetFontSize (cgc, 1.0);
++ CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
++
++ CGContextShowGlyphsWithAdvances (cgc,
++ op->u.show_glyphs.cg_glyphs,
++ op->u.show_glyphs.u.cg_advances,
++ op->u.show_glyphs.nglyphs);
++
++ }
+ if (op->u.show_glyphs.isClipping) {
+ CGContextClearRect (cgc, clipBoxRound);
+ CGContextRestoreGState (cgc);
+ }
+ } else if (op->op == UNBOUNDED_MASK) {
+ CGAffineTransform ctm = CGContextGetCTM (cgc);
+ CGContextSaveGState (cgc);
+ CGContextConcatCTM (cgc, op->u.mask.maskTransform);
+@@ -2684,16 +2699,19 @@ static cairo_int_status_t
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+ {
+ CGAffineTransform textTransform, ctm, invTextTransform;
+ #define STATIC_BUF_SIZE 64
+ CGGlyph glyphs_static[STATIC_BUF_SIZE];
+ CGSize cg_advances_static[STATIC_BUF_SIZE];
+ CGGlyph *cg_glyphs = &glyphs_static[0];
++ /* We'll use the cg_advances array for either advances or positions,
++ depending which API we're using to actually draw. The types involved
++ have the same size, so this is safe. */
+ CGSize *cg_advances = &cg_advances_static[0];
+
+ cairo_rectangle_int_t glyph_extents;
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+ cairo_quartz_drawing_state_t state;
+ cairo_quartz_float_t xprev, yprev;
+ int i;
+@@ -2796,41 +2814,62 @@ static cairo_int_status_t
+ 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);
+
+ CGContextSetTextMatrix (state.context, CGAffineTransformIdentity);
+
+- /* Convert our glyph positions to glyph advances. We need n-1 advances,
+- * since the advance at index 0 is applied after glyph 0. */
+- xprev = glyphs[0].x;
+- yprev = glyphs[0].y;
+-
+- cg_glyphs[0] = glyphs[0].index;
+-
+- for (i = 1; i < num_glyphs; i++) {
+- cairo_quartz_float_t xf = glyphs[i].x;
+- cairo_quartz_float_t yf = glyphs[i].y;
+- cg_glyphs[i] = glyphs[i].index;
+- cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
+- xprev = xf;
+- yprev = yf;
+- }
+-
+ /* Translate to the first glyph's position before drawing */
+ ctm = CGContextGetCTM (state.context);
+ CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
+ CGContextConcatCTM (state.context, textTransform);
+
+- CGContextShowGlyphsWithAdvances (state.context,
+- cg_glyphs,
+- cg_advances,
+- num_glyphs);
++ if (CTFontDrawGlyphsPtr) {
++ /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use
++ * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap
++ * fonts like Apple Color Emoji will render properly.
++ * For this, we need to convert our glyph positions to Core Graphics's CGPoint.
++ * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */
++
++ CGPoint *cg_positions = (CGPoint*) cg_advances;
++ cairo_quartz_float_t origin_x = glyphs[0].x;
++ cairo_quartz_float_t origin_y = glyphs[0].y;
++
++ for (i = 0; i < num_glyphs; i++) {
++ CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y);
++ cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform);
++ cg_glyphs[i] = glyphs[i].index;
++ }
++
++ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
++ cg_glyphs, cg_positions, num_glyphs, state.context);
++ } else {
++ /* Convert our glyph positions to glyph advances. We need n-1 advances,
++ * since the advance at index 0 is applied after glyph 0. */
++ xprev = glyphs[0].x;
++ yprev = glyphs[0].y;
++
++ cg_glyphs[0] = glyphs[0].index;
++
++ for (i = 1; i < num_glyphs; i++) {
++ cairo_quartz_float_t xf = glyphs[i].x;
++ cairo_quartz_float_t yf = glyphs[i].y;
++ cg_glyphs[i] = glyphs[i].index;
++ cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
++ xprev = xf;
++ yprev = yf;
++ }
++
++ CGContextShowGlyphsWithAdvances (state.context,
++ cg_glyphs,
++ cg_advances,
++ num_glyphs);
++ }
+
+ CGContextSetCTM (state.context, ctm);
+
+ if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action == DO_SHADING) {
+ CGContextConcatCTM (state.context, state.transform);
+@@ -2847,20 +2886,27 @@ BAIL:
+ cgfref &&
+ !_cairo_operator_bounded_by_mask (op))
+ {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_SHOW_GLYPHS;
+
+ ub.u.show_glyphs.isClipping = isClipping;
+ ub.u.show_glyphs.cg_glyphs = cg_glyphs;
+- ub.u.show_glyphs.cg_advances = cg_advances;
++ if (CTFontDrawGlyphsPtr) {
++ /* we're using Core Text API: the cg_advances array was
++ reused (above) for glyph positions */
++ CGPoint *cg_positions = (CGPoint*) cg_advances;
++ ub.u.show_glyphs.u.cg_positions = cg_positions;
++ } else {
++ ub.u.show_glyphs.u.cg_advances = cg_advances;
++ }
+ ub.u.show_glyphs.nglyphs = num_glyphs;
+ ub.u.show_glyphs.textTransform = textTransform;
+- ub.u.show_glyphs.font = cgfref;
++ ub.u.show_glyphs.scaled_font = scaled_font;
+ ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
+ }
+
+
+ if (cg_advances != &cg_advances_static[0]) {
+ free (cg_advances);
diff --git a/gfx/cairo/quartz-surface-mask-patch b/gfx/cairo/quartz-surface-mask-patch
new file mode 100644
index 000000000..d5ee7d8be
--- /dev/null
+++ b/gfx/cairo/quartz-surface-mask-patch
@@ -0,0 +1,79 @@
+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
+@@ -128,20 +128,22 @@ CG_EXTERN CGImageRef CGBitmapContextCrea
+ */
+ static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+ static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+ static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
+ static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
+ static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+ static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
+ static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
+
++static SInt32 _cairo_quartz_osx_version = 0x0;
++
+ 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
+@@ -163,20 +165,25 @@ static void quartz_ensure_symbols(void)
+
+ CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask");
+ CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
+ CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
+ CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
+ CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+ CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
+
++ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
++ // assume 10.5
++ _cairo_quartz_osx_version = 0x1050;
++ }
++
+ _cairo_quartz_symbol_lookup_done = TRUE;
+ }
+
+ CGImageRef
+ _cairo_quartz_create_cgimage (cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride,
+ void *data,
+ cairo_bool_t interpolate,
+@@ -3028,22 +3035,25 @@ static cairo_int_status_t
+ CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
+ rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip);
+ CGContextSetAlpha (surface->cgContext, 1.0);
+
+ return rv;
+ }
+
+ /* If we have CGContextClipToMask, we can do more complex masks */
+ if (CGContextClipToMaskPtr) {
+ /* For these, we can skip creating a temporary surface, since we already have one */
+- if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
++ /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */
++ if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
++ mask->extend == CAIRO_EXTEND_NONE) {
+ return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip);
++ }
+
+ return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip);
+ }
+
+ /* So, CGContextClipToMask is not present in 10.3.9, so we're
+ * doomed; if we have imageData, we can do fallback, otherwise
+ * just pretend success.
+ */
+ if (surface->imageData)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
diff --git a/gfx/cairo/setlcdfilter_in_tree.patch b/gfx/cairo/setlcdfilter_in_tree.patch
new file mode 100644
index 000000000..5a9470cc2
--- /dev/null
+++ b/gfx/cairo/setlcdfilter_in_tree.patch
@@ -0,0 +1,30 @@
+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
+@@ -1361,21 +1361,25 @@ static cairo_status_t
+ case FT_RENDER_MODE_MONO:
+ case FT_RENDER_MODE_LIGHT:
+ case FT_RENDER_MODE_NORMAL:
+ case FT_RENDER_MODE_MAX:
+ default:
+ break;
+ }
+
+ if (!initialized_setLcdFilter) {
+ initialized_setLcdFilter = 1;
++#ifdef HAVE_FT_LIBRARY_SETLCDFILTER
++ setLcdFilter = &FT_Library_SetLcdFilter;
++#else
+ setLcdFilter = (setLcdFilterFunc) dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter");
++#endif
+ }
+
+ if (setLcdFilter)
+ setLcdFilter (library, lcd_filter);
+
+ fterror = FT_Render_Glyph (face->glyph, render_mode);
+
+ if (setLcdFilter)
+ setLcdFilter (library, FT_LCD_FILTER_NONE);
+
+
diff --git a/gfx/cairo/support-new-style-atomic-primitives.patch b/gfx/cairo/support-new-style-atomic-primitives.patch
new file mode 100644
index 000000000..1830a4691
--- /dev/null
+++ b/gfx/cairo/support-new-style-atomic-primitives.patch
@@ -0,0 +1,121 @@
+From 5d150ee111c222f09e78f4f88540964476327844 Mon Sep 17 00:00:00 2001
+From: Nathan Froyd <froydnj@mozilla.com>
+Date: Mon, 4 May 2015 13:38:41 -0400
+Subject: Support new-style __atomic_* primitives
+
+Recent versions of GCC/clang feature a new set of compiler intrinsics
+for performing atomic operations, motivated by the operations needed to
+support the C++11 memory model. These intrinsics are more flexible than
+the old __sync_* intrinstics and offer efficient support for atomic load
+and store operations.
+
+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.
+
+Patch from Nathan Froyd <froydnj@mozilla.com>
+
+diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h
+index 327fed1..11b2887 100644
+--- a/src/cairo-atomic-private.h
++++ b/src/cairo-atomic-private.h
+@@ -53,6 +53,96 @@
+
+ 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 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_INTEL_ATOMIC_PRIMITIVES
+
+ #define HAS_ATOMIC_OPS 1
+--
+cgit v0.10.2
+
diff --git a/gfx/cairo/surface-clipper.patch b/gfx/cairo/surface-clipper.patch
new file mode 100644
index 000000000..608841147
--- /dev/null
+++ b/gfx/cairo/surface-clipper.patch
@@ -0,0 +1,26 @@
+commit 061cc774a861f349334117203c301dee202f9f26
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+Date: Wed Apr 7 23:05:48 2010 -0400
+
+ Remove an incorrect optimization that was causing the clip
+ not to be set when it should've been.
+
+ This happens when the path is equal but the parents aren't shared.
+
+diff --git a/src/cairo-surface-clipper.c b/src/cairo-surface-clipper.c
+index d536f0c..03610d1 100644
+--- a/src/cairo-surface-clipper.c
++++ b/src/cairo-surface-clipper.c
+@@ -78,12 +78,6 @@ _cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper,
+ if (clip != NULL && clip->path == clipper->clip.path)
+ return CAIRO_STATUS_SUCCESS;
+
+- if (clip != NULL && clipper->clip.path != NULL &&
+- _cairo_path_fixed_equal (&clip->path->path, &clipper->clip.path->path))
+- {
+- return CAIRO_STATUS_SUCCESS;
+- }
+-
+ /* all clipped out state should never propagate this far */
+ assert (clip == NULL || clip->path != NULL);
+
diff --git a/gfx/cairo/tee-surfaces-pointwise.patch b/gfx/cairo/tee-surfaces-pointwise.patch
new file mode 100644
index 000000000..180005fa6
--- /dev/null
+++ b/gfx/cairo/tee-surfaces-pointwise.patch
@@ -0,0 +1,278 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1294019288 -46800
+# Node ID bacc54d452a9fddb5a0d6a1442ec7be4de81ffa7
+# Parent ccba8826be1451d0e61d0df38363dadffb20ba48
+Bug 593604. Part 2: When compositing a tee surface into another tee surface, try to compose the subsurfaces pointwise. r=jrmuizel,a=blocking
+
+diff --git a/gfx/cairo/cairo/src/cairo-tee-surface.c b/gfx/cairo/cairo/src/cairo-tee-surface.c
+--- a/gfx/cairo/cairo/src/cairo-tee-surface.c
++++ b/gfx/cairo/cairo/src/cairo-tee-surface.c
+@@ -186,35 +186,72 @@ 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 const cairo_pattern_t *
++_cairo_tee_surface_match_source (cairo_tee_surface_t *surface,
++ const cairo_pattern_t *source,
++ int index,
++ cairo_surface_wrapper_t *dest,
++ cairo_surface_pattern_t *temp)
++{
++ cairo_surface_t *s;
++ cairo_status_t status = cairo_pattern_get_surface ((cairo_pattern_t *)source, &s);
++ if (status == CAIRO_STATUS_SUCCESS &&
++ cairo_surface_get_type (s) == CAIRO_SURFACE_TYPE_TEE) {
++ cairo_surface_t *tee_surf = cairo_tee_surface_index (s, index);
++ if (tee_surf->status == CAIRO_STATUS_SUCCESS &&
++ tee_surf->backend == dest->target->backend) {
++ status = _cairo_pattern_init_copy (&temp->base, source);
++ if (status == CAIRO_STATUS_SUCCESS) {
++ cairo_surface_destroy (temp->surface);
++ temp->surface = tee_surf;
++ cairo_surface_reference (temp->surface);
++ return &temp->base;
++ }
++ }
++ }
++
++ return source;
++}
++
+ static cairo_int_status_t
+ _cairo_tee_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+ {
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
++ const cairo_pattern_t *matched_source;
++ cairo_surface_pattern_t temp;
+
+- status = _cairo_surface_wrapper_paint (&surface->master, op, source, clip);
++ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
++ status = _cairo_surface_wrapper_paint (&surface->master, op, matched_source, clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ return status;
+
+ 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);
++ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
++ status = _cairo_surface_wrapper_paint (&slaves[n], op, matched_source, clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static cairo_int_status_t
+@@ -223,27 +260,37 @@ _cairo_tee_surface_mask (void *abstrac
+ const cairo_pattern_t *source,
+ const cairo_pattern_t *mask,
+ cairo_clip_t *clip)
+ {
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
++ const cairo_pattern_t *matched_source;
++ cairo_surface_pattern_t temp;
+
++ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_mask (&surface->master,
+- op, source, mask, clip);
++ op, matched_source, mask, clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ return status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
++ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_mask (&slaves[n],
+- op, source, mask, clip);
++ op, matched_source, mask, clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static cairo_int_status_t
+@@ -257,35 +304,45 @@ _cairo_tee_surface_stroke (void *abst
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+ {
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
++ const cairo_pattern_t *matched_source;
++ cairo_surface_pattern_t temp;
+
++ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_stroke (&surface->master,
+- op, source,
++ op, matched_source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ return status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
++ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_stroke (&slaves[n],
+- op, source,
++ op, matched_source,
+ path, style,
+ ctm, ctm_inverse,
+ tolerance, antialias,
+ clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static cairo_int_status_t
+@@ -297,33 +354,43 @@ _cairo_tee_surface_fill (void *abstra
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+ {
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
++ const cairo_pattern_t *matched_source;
++ cairo_surface_pattern_t temp;
+
++ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_fill (&surface->master,
+- op, source,
++ op, matched_source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ return status;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
++ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_fill (&slaves[n],
+- op, source,
++ op, matched_source,
+ path, fill_rule,
+ tolerance, antialias,
+ clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ return status;
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static cairo_bool_t
+@@ -346,46 +413,56 @@ _cairo_tee_surface_show_text_glyphs (voi
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip)
+ {
+ cairo_tee_surface_t *surface = abstract_surface;
+ cairo_surface_wrapper_t *slaves;
+ int n, num_slaves;
+ cairo_status_t status;
+ cairo_glyph_t *glyphs_copy;
++ const cairo_pattern_t *matched_source;
++ cairo_surface_pattern_t temp;
+
+ /* XXX: This copying is ugly. */
+ glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+ if (unlikely (glyphs_copy == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
++ matched_source = _cairo_tee_surface_match_source (surface, source, 0, &surface->master, &temp);
+ status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op,
+- source,
++ matched_source,
+ utf8, utf8_len,
+ glyphs_copy, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ goto CLEANUP;
+
+ num_slaves = _cairo_array_num_elements (&surface->slaves);
+ slaves = _cairo_array_index (&surface->slaves, 0);
+ for (n = 0; n < num_slaves; n++) {
+ memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
++ matched_source = _cairo_tee_surface_match_source (surface, source, n + 1, &slaves[n], &temp);
+ status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op,
+- source,
++ matched_source,
+ utf8, utf8_len,
+ glyphs_copy, num_glyphs,
+ clusters, num_clusters,
+ cluster_flags,
+ scaled_font,
+ clip);
++ if (matched_source == &temp.base) {
++ _cairo_pattern_fini (&temp.base);
++ }
+ if (unlikely (status))
+ goto CLEANUP;
+ }
+
+ CLEANUP:
+ free (glyphs_copy);
+ return status;
+ }
diff --git a/gfx/cairo/text-path-filling-threshold.patch b/gfx/cairo/text-path-filling-threshold.patch
new file mode 100644
index 000000000..69efce093
--- /dev/null
+++ b/gfx/cairo/text-path-filling-threshold.patch
@@ -0,0 +1,90 @@
+diff --git a/gfx/cairo/cairo/src/cairo-gstate.c b/gfx/cairo/cairo/src/cairo-gstate.c
+--- a/gfx/cairo/cairo/src/cairo-gstate.c
++++ b/gfx/cairo/cairo/src/cairo-gstate.c
+@@ -1673,26 +1673,31 @@ _cairo_gstate_show_text_glyphs (cairo_gs
+
+ source_pattern = &source_pattern_stack.base;
+ status = _cairo_gstate_copy_transformed_source (gstate, &source_pattern);
+ if (unlikely (status))
+ goto CLEANUP_GLYPHS;
+
+ /* For really huge font sizes, we can just do path;fill instead of
+ * 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
++ * not all components below us correctly handle huge font sizes, and
++ * path filling can be cheaper since parts of glyphs are likely to be
++ * clipped out. 256 seems like a good limit. But alas, seems like cairo's
+ * 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.
++ * sizes. So, no win just yet when we're using cairo's rasterizer.
++ * For now, if we're using cairo's rasterizer, use path filling only
++ * for insanely-huge sizes, just to make sure we don't make anyone
++ * unhappy. When we get a really fast rasterizer in cairo, we may
++ * want to readjust this. The threshold calculation is
++ * encapsulated in _cairo_surface_get_text_path_fill_threshold.
+ *
+ * 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) {
++ _cairo_scaled_font_get_max_scale (gstate->scaled_font) <=
++ _cairo_surface_get_text_path_fill_threshold (gstate->target)) {
+ status = _cairo_surface_show_text_glyphs (gstate->target,
+ gstate->op,
+ source_pattern,
+ utf8, utf8_len,
+ transformed_glyphs, num_glyphs,
+ transformed_clusters, num_clusters,
+ cluster_flags,
+ gstate->scaled_font, NULL);
+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
+@@ -1120,16 +1120,22 @@ cairo_surface_get_fallback_resolution (c
+ 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;
+ }
+
++int
++_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface)
++{
++ return surface->backend->fill == NULL ? 10240 : 256;
++}
++
+ cairo_bool_t
+ _cairo_surface_has_device_transform (cairo_surface_t *surface)
+ {
+ return ! _cairo_matrix_is_identity (&surface->device_transform);
+ }
+
+ /**
+ * _cairo_surface_acquire_source_image:
+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
+@@ -2065,16 +2065,19 @@ _cairo_surface_composite_shape_fixup_unb
+ int dst_x,
+ int dst_y,
+ unsigned int width,
+ unsigned int height);
+
+ cairo_private cairo_bool_t
+ _cairo_surface_is_opaque (const cairo_surface_t *surface);
+
++cairo_private int
++_cairo_surface_get_text_path_fill_threshold (const cairo_surface_t *surface);
++
+ cairo_private void
+ _cairo_surface_set_device_scale (cairo_surface_t *surface,
+ double sx,
+ double sy);
+
+ cairo_private cairo_bool_t
+ _cairo_surface_has_device_transform (cairo_surface_t *surface);
+
diff --git a/gfx/cairo/unicode-printing.patch b/gfx/cairo/unicode-printing.patch
new file mode 100644
index 000000000..09b3991ea
--- /dev/null
+++ b/gfx/cairo/unicode-printing.patch
@@ -0,0 +1,333 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+@@ -1426,16 +1426,104 @@ _cairo_win32_printing_surface_fill (void
+ }
+
+ fflush(stderr);
+
+ return status;
+ }
+
+ static cairo_int_status_t
++_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_surface_t *surface,
++ cairo_operator_t op,
++ const cairo_pattern_t *source,
++ cairo_glyph_t *glyphs,
++ int num_glyphs,
++ cairo_scaled_font_t *scaled_font,
++ cairo_clip_t *clip,
++ int *remaining_glyphs)
++{
++ 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_show_glyphs_internal (
++ surface,
++ op,
++ source,
++ sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first],
++ i - first + 1,
++ scaled_font,
++ clip,
++ remaining_glyphs,
++ ! sequence_is_unicode);
++ 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,
+ cairo_clip_t *clip,
+ int *remaining_glyphs)
+@@ -1533,77 +1621,24 @@ _cairo_win32_printing_surface_show_glyph
+ }
+ }
+ #endif
+
+ #if CAIRO_HAS_WIN32_FONT
+ if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 &&
+ source->type == CAIRO_PATTERN_TYPE_SOLID)
+ {
+- cairo_matrix_t ctm;
+- cairo_glyph_t *type1_glyphs = NULL;
+- cairo_scaled_font_subsets_glyph_t subset_glyph;
+-
+- /* Calling ExtTextOutW() with ETO_GLYPH_INDEX and a Type 1
+- * font on a printer DC prints garbled text. The text displays
+- * correctly on a display DC. When using a printer
+- * DC, ExtTextOutW() only works with characters and not glyph
+- * indices.
+- *
+- * For Type 1 fonts the glyph indices are converted back to
+- * unicode characters before calling _cairo_win32_surface_show_glyphs().
+- *
+- * 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 (_cairo_win32_scaled_font_is_type1 (scaled_font)) {
+- type1_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
+- if (type1_glyphs == NULL) {
+- status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+- goto FINISH;
+- }
+- memcpy (type1_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,
+- type1_glyphs[i].index,
+- NULL, 0,
+- &subset_glyph);
+- if (status)
+- goto FINISH;
+-
+- type1_glyphs[i].index = subset_glyph.unicode;
+- }
+- glyphs = type1_glyphs;
+- }
+-
+- if (surface->has_ctm || surface->has_gdi_ctm) {
+- cairo_matrix_multiply (&ctm, &surface->ctm, &surface->gdi_ctm);
+- for (i = 0; i < num_glyphs; i++)
+- cairo_matrix_transform_point (&ctm, &glyphs[i].x, &glyphs[i].y);
+- cairo_matrix_multiply (&ctm, &scaled_font->ctm, &ctm);
+- scaled_font = cairo_scaled_font_create (scaled_font->font_face,
+- &scaled_font->font_matrix,
+- &ctm,
+- &scaled_font->options);
+- }
+- status = _cairo_win32_surface_show_glyphs (surface, op,
+- source, glyphs,
+- num_glyphs, scaled_font,
+- clip,
+- remaining_glyphs);
+- if (surface->has_ctm || surface->has_gdi_ctm)
+- cairo_scaled_font_destroy (scaled_font);
+-
+- if (type1_glyphs != NULL)
+- free (type1_glyphs);
+-
++ status = _cairo_win32_printing_surface_emit_win32_glyphs (surface,
++ op,
++ source,
++ glyphs,
++ num_glyphs,
++ scaled_font,
++ clip,
++ remaining_glyphs);
+ goto FINISH;
+ }
+ #endif
+
+ SaveDC (surface->dc);
+ old_ctm = surface->ctm;
+ old_has_ctm = surface->has_ctm;
+ surface->has_ctm = TRUE;
+diff --git a/gfx/cairo/cairo/src/cairo-win32-private.h b/gfx/cairo/cairo/src/cairo-win32-private.h
+--- a/gfx/cairo/cairo/src/cairo-win32-private.h
++++ b/gfx/cairo/cairo/src/cairo-win32-private.h
+@@ -157,16 +157,27 @@ _cairo_win32_surface_get_extents (void
+ uint32_t
+ _cairo_win32_flags_for_dc (HDC dc);
+
+ cairo_status_t
+ _cairo_win32_surface_set_clip_region (void *abstract_surface,
+ cairo_region_t *region);
+
+ cairo_int_status_t
++_cairo_win32_surface_show_glyphs_internal (void *surface,
++ cairo_operator_t op,
++ const cairo_pattern_t *source,
++ cairo_glyph_t *glyphs,
++ int num_glyphs,
++ cairo_scaled_font_t *scaled_font,
++ cairo_clip_t *clip,
++ int *remaining_glyphs,
++ cairo_bool_t glyph_indices);
++
++cairo_int_status_t
+ _cairo_win32_surface_show_glyphs (void *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_glyph_t *glyphs,
+ int num_glyphs,
+ cairo_scaled_font_t *scaled_font,
+ cairo_clip_t *clip,
+ int *remaining_glyphs);
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -1607,24 +1607,25 @@ static cairo_status_t
+ _cairo_win32_surface_flush (void *abstract_surface)
+ {
+ return _cairo_win32_surface_set_clip_region (abstract_surface, NULL);
+ }
+
+ #define STACK_GLYPH_SIZE 256
+
+ cairo_int_status_t
+-_cairo_win32_surface_show_glyphs (void *surface,
+- cairo_operator_t op,
+- const cairo_pattern_t *source,
+- cairo_glyph_t *glyphs,
+- int num_glyphs,
+- cairo_scaled_font_t *scaled_font,
+- cairo_clip_t *clip,
+- int *remaining_glyphs)
++_cairo_win32_surface_show_glyphs_internal (void *surface,
++ cairo_operator_t op,
++ const cairo_pattern_t *source,
++ cairo_glyph_t *glyphs,
++ int num_glyphs,
++ cairo_scaled_font_t *scaled_font,
++ cairo_clip_t *clip,
++ int *remaining_glyphs,
++ cairo_bool_t glyph_indexing)
+ {
+ #ifdef CAIRO_HAS_WIN32_FONT
+ if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) {
+ #ifdef CAIRO_HAS_DWRITE_FONT
+ return _cairo_dwrite_show_glyphs_on_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip);
+ #endif
+ } else {
+ cairo_win32_surface_t *dst = surface;
+@@ -1737,29 +1738,20 @@ _cairo_win32_surface_show_glyphs (void
+ dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y);
+ /* note that GDI coordinate system is inverted */
+
+ logical_x = next_logical_x;
+ logical_y = next_logical_y;
+ }
+ }
+
+- /* Using glyph indices for a Type 1 font does not work on a
+- * printer DC. The win32 printing surface will convert the the
+- * glyph indices of Type 1 fonts to the unicode values.
+- */
+- if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) &&
+- _cairo_win32_scaled_font_is_type1 (scaled_font))
+- {
++ if (glyph_indexing)
++ glyph_index_option = ETO_GLYPH_INDEX;
++ else
+ glyph_index_option = 0;
+- }
+- else
+- {
+- glyph_index_option = ETO_GLYPH_INDEX;
+- }
+
+ win_result = ExtTextOutW(dst->dc,
+ start_x,
+ start_y,
+ glyph_index_option | ETO_PDY,
+ NULL,
+ glyph_buf,
+ num_glyphs,
+@@ -1778,16 +1770,37 @@ _cairo_win32_surface_show_glyphs (void
+ }
+ #else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ #endif
+ }
+
+ #undef STACK_GLYPH_SIZE
+
++cairo_int_status_t
++_cairo_win32_surface_show_glyphs (void *surface,
++ cairo_operator_t op,
++ const cairo_pattern_t *source,
++ cairo_glyph_t *glyphs,
++ int num_glyphs,
++ cairo_scaled_font_t *scaled_font,
++ cairo_clip_t *clip,
++ int *remaining_glyphs)
++{
++ return _cairo_win32_surface_show_glyphs_internal (surface,
++ op,
++ source,
++ glyphs,
++ num_glyphs,
++ scaled_font,
++ clip,
++ remaining_glyphs,
++ TRUE);
++}
++
+ static cairo_surface_t *
+ cairo_win32_surface_create_internal (HDC hdc, cairo_format_t format)
+ {
+ cairo_win32_surface_t *surface;
+
+ RECT rect;
+
+ surface = malloc (sizeof (cairo_win32_surface_t));
diff --git a/gfx/cairo/use-show-text-glyphs-if-glyph-path-fails.patch b/gfx/cairo/use-show-text-glyphs-if-glyph-path-fails.patch
new file mode 100644
index 000000000..1670eaf73
--- /dev/null
+++ b/gfx/cairo/use-show-text-glyphs-if-glyph-path-fails.patch
@@ -0,0 +1,42 @@
+From: Jonathan Kew <jkew@mozilla.com>
+bug 715798 pt 2 - fall back to show_text_glyphs even at huge sizes if scaled_font_glyph_path didn't work. r=jrmuizel
+
+diff --git a/gfx/cairo/cairo/src/cairo-gstate.c b/gfx/cairo/cairo/src/cairo-gstate.c
+--- a/gfx/cairo/cairo/src/cairo-gstate.c
++++ b/gfx/cairo/cairo/src/cairo-gstate.c
+@@ -2002,23 +2002,34 @@ cairo_status_t
+ 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) {
++ if (status == CAIRO_STATUS_SUCCESS && !_cairo_path_fixed_fill_is_empty (&path)) {
+ status = _cairo_surface_fill (gstate->target, op, pattern,
+ &path,
+ CAIRO_FILL_RULE_WINDING,
+ gstate->tolerance,
+ gstate->scaled_font->options.antialias,
+ _gstate_get_clip (gstate, &clip));
++ } else {
++ /* if _cairo_scaled_font_glyph_path() failed, maybe the font doesn't support
++ * returning paths, so try the _cairo_surface_show_text_glyphs() option
++ */
++ status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
++ utf8, utf8_len,
++ transformed_glyphs, num_glyphs,
++ transformed_clusters, num_clusters,
++ cluster_flags,
++ gstate->scaled_font,
++ _gstate_get_clip (gstate, &clip));
+ }
+
+ _cairo_path_fixed_fini (&path);
+ }
+
+ _cairo_clip_fini (&clip);
+
+ CLEANUP_GLYPHS:
diff --git a/gfx/cairo/win32-ExtCreatePen-zero-size.patch b/gfx/cairo/win32-ExtCreatePen-zero-size.patch
new file mode 100644
index 000000000..3970015f7
--- /dev/null
+++ b/gfx/cairo/win32-ExtCreatePen-zero-size.patch
@@ -0,0 +1,85 @@
+From: Robert O'Callahan <robert@ocallahan.org>
+Bug 768348. Avoid ExtCreatePen failures by avoiding rounding widths and dash lengths down to zero. r=jrmuizel
+
+diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+@@ -1251,22 +1251,24 @@ static cairo_int_status_t
+ {
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_int_status_t status;
+ HPEN pen;
+ LOGBRUSH brush;
+ COLORREF color;
+ XFORM xform;
+ DWORD pen_style;
++ DWORD pen_width;
+ DWORD *dash_array;
+ HGDIOBJ obj;
+ unsigned int i;
+ cairo_solid_pattern_t clear;
+ cairo_matrix_t mat;
+ double scale;
++ double scaled_width;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (status)
+ return status;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ _cairo_win32_printing_surface_init_clear_color (surface, &clear);
+ source = (cairo_pattern_t*) &clear;
+@@ -1288,17 +1290,21 @@ static cairo_int_status_t
+ _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]);
++ DWORD dashes = (DWORD) (scale * style->dash[i]);
++ /* zero dash-lengths cause ExtCreatePen to fail. Make the dashes
++ * longer if necessary.
++ */
++ dash_array[i] = MAX(1, dashes);
+ }
+ } else {
+ pen_style |= PS_SOLID;
+ }
+
+ SetMiterLimit (surface->dc, (FLOAT) (style->miter_limit), NULL);
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+@@ -1310,18 +1316,29 @@ static cairo_int_status_t
+ /* 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);
++ scaled_width = scale * style->line_width;
++ if (scaled_width == 0.0)
++ return status;
++ pen_width = (DWORD)scaled_width;
++ if (pen_width == 0) {
++ /* ExtCreatePen will fail if passed zero width. We have to choose
++ * between drawing something too wide, or drawing nothing at all.
++ * Let's draw something.
++ */
++ pen_width = 1;
++ }
+ pen = ExtCreatePen(pen_style,
+- scale * style->line_width,
++ pen_width,
+ &brush,
+ style->num_dashes,
+ dash_array);
+ if (pen == NULL)
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen");
+ obj = SelectObject (surface->dc, pen);
+ if (obj == NULL)
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject");
diff --git a/gfx/cairo/win32-avoid-extend-pad-fallback.patch b/gfx/cairo/win32-avoid-extend-pad-fallback.patch
new file mode 100644
index 000000000..b04282ce9
--- /dev/null
+++ b/gfx/cairo/win32-avoid-extend-pad-fallback.patch
@@ -0,0 +1,109 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -1114,17 +1114,17 @@ static cairo_int_status_t
+ cairo_win32_surface_t *dst = abstract_dst;
+ cairo_win32_surface_t *src;
+ cairo_surface_pattern_t *src_surface_pattern;
+ int alpha;
+ double scalex, scaley;
+ cairo_fixed_t x0_fixed, y0_fixed;
+ cairo_int_status_t status;
+
+- cairo_bool_t needs_alpha, needs_scale, needs_repeat;
++ cairo_bool_t needs_alpha, needs_scale, needs_repeat, needs_pad;
+ cairo_image_surface_t *src_image = NULL;
+
+ cairo_format_t src_format;
+ cairo_rectangle_int_t src_extents;
+
+ cairo_rectangle_int_t src_r = { src_x, src_y, width, height };
+ cairo_rectangle_int_t dst_r = { dst_x, dst_y, width, height };
+
+@@ -1145,17 +1145,18 @@ static cairo_int_status_t
+ {
+ goto UNSUPPORTED;
+ }
+
+ if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+ goto UNSUPPORTED;
+
+ if (pattern->extend != CAIRO_EXTEND_NONE &&
+- pattern->extend != CAIRO_EXTEND_REPEAT)
++ pattern->extend != CAIRO_EXTEND_REPEAT &&
++ pattern->extend != CAIRO_EXTEND_PAD)
+ goto UNSUPPORTED;
+
+ if (mask_pattern) {
+ /* FIXME: When we fully support RENDER style 4-channel
+ * masks we need to check r/g/b != 1.0.
+ */
+ if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+@@ -1252,16 +1253,17 @@ static cairo_int_status_t
+
+ /* If the src rectangle doesn't wholly lie within the src extents,
+ * fudge things. We really need to do fixup on the unpainted
+ * region -- e.g. the SOURCE operator is broken for areas outside
+ * of the extents, because it won't clear that area to transparent
+ * black.
+ */
+
++ needs_pad = FALSE;
+ if (pattern->extend != CAIRO_EXTEND_REPEAT) {
+ needs_repeat = FALSE;
+
+ /* If the src rect and the extents of the source image don't overlap at all,
+ * we can't do anything useful here.
+ */
+ if (src_r.x > src_extents.width || src_r.y > src_extents.height ||
+ (src_r.x + src_r.width) < 0 || (src_r.y + src_r.height) < 0)
+@@ -1273,40 +1275,48 @@ static cairo_int_status_t
+
+ if (src_r.x < 0) {
+ src_r.width += src_r.x;
+
+ dst_r.width += src_r.x;
+ dst_r.x -= src_r.x;
+
+ src_r.x = 0;
++ needs_pad = TRUE;
+ }
+
+ if (src_r.y < 0) {
+ src_r.height += src_r.y;
+
+ dst_r.height += src_r.y;
+ dst_r.y -= src_r.y;
+
+ src_r.y = 0;
++ needs_pad = TRUE;
+ }
+
+ if (src_r.x + src_r.width > src_extents.width) {
+ src_r.width = src_extents.width - src_r.x;
+ dst_r.width = src_r.width;
++ needs_pad = TRUE;
+ }
+
+ if (src_r.y + src_r.height > src_extents.height) {
+ src_r.height = src_extents.height - src_r.y;
+ dst_r.height = src_r.height;
++ needs_pad = TRUE;
+ }
+ } else {
+ needs_repeat = TRUE;
+ }
+
++ if (pattern->extend == CAIRO_EXTEND_PAD && needs_pad) {
++ goto UNSUPPORTED;
++ }
++
+ /*
+ * Operations that we can do:
+ *
+ * AlphaBlend uses the following formula for alpha when not use the per-pixel alpha (AlphaFormat = 0)
+ * Dst.Alpha = Src.Alpha * (SCA/255.0) + Dst.Alpha * (1.0 - (SCA/255.0))
+ * This turns into Dst.Alpha = Src.Alpha when SCA = 255.
+ * (http://msdn.microsoft.com/en-us/library/aa921335.aspx)
+ *
diff --git a/gfx/cairo/win32-canvas-glyph-position.patch b/gfx/cairo/win32-canvas-glyph-position.patch
new file mode 100644
index 000000000..4ed10596c
--- /dev/null
+++ b/gfx/cairo/win32-canvas-glyph-position.patch
@@ -0,0 +1,31 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c
+--- a/gfx/cairo/cairo/src/cairo-win32-font.c
++++ b/gfx/cairo/cairo/src/cairo-win32-font.c
+@@ -1182,22 +1182,22 @@ _add_glyph (cairo_glyph_state_t *state,
+ 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;
+ }
+-
+- 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);
diff --git a/gfx/cairo/win32-cleartype-clipping.patch b/gfx/cairo/win32-cleartype-clipping.patch
new file mode 100644
index 000000000..839e94039
--- /dev/null
+++ b/gfx/cairo/win32-cleartype-clipping.patch
@@ -0,0 +1,23 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c
+--- a/gfx/cairo/cairo/src/cairo-win32-font.c
++++ b/gfx/cairo/cairo/src/cairo-win32-font.c
+@@ -986,6 +986,19 @@ _cairo_win32_scaled_font_init_glyph_metr
+ &metrics, 0, NULL, &matrix) == GDI_ERROR) {
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetGlyphOutlineW");
+ 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 (status)
diff --git a/gfx/cairo/win32-composite-src-mod.patch b/gfx/cairo/win32-composite-src-mod.patch
new file mode 100644
index 000000000..f2f99940a
--- /dev/null
+++ b/gfx/cairo/win32-composite-src-mod.patch
@@ -0,0 +1,44 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -928,16 +928,19 @@ _cairo_win32_surface_composite_inner (ca
+ return _composite_alpha_blend (dst, src, alpha,
+ src_r.x, src_r.y, src_r.width, src_r.height,
+ dst_r.x, dst_r.y, dst_r.width, dst_r.height);
+ }
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++/* from pixman-private.h */
++#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b))
++
+ static cairo_int_status_t
+ _cairo_win32_surface_composite (cairo_operator_t op,
+ cairo_pattern_t *pattern,
+ cairo_pattern_t *mask_pattern,
+ void *abstract_dst,
+ int src_x,
+ int src_y,
+ int mask_x,
+@@ -1209,18 +1212,18 @@ _cairo_win32_surface_composite (cairo_op
+ /* If we need to repeat, we turn the repeated blit into
+ * a bunch of piece-by-piece blits.
+ */
+ if (needs_repeat) {
+ cairo_rectangle_int_t piece_src_r, piece_dst_r;
+ uint32_t rendered_width = 0, rendered_height = 0;
+ uint32_t to_render_height, to_render_width;
+ int32_t piece_x, piece_y;
+- int32_t src_start_x = src_r.x % src_extents.width;
+- int32_t src_start_y = src_r.y % src_extents.height;
++ int32_t src_start_x = MOD(src_r.x, src_extents.width);
++ int32_t src_start_y = MOD(src_r.y, src_extents.height);
+
+ if (needs_scale)
+ goto UNSUPPORTED;
+
+ /* If both the src and dest have an image, we may as well fall
+ * back, because it will be faster than our separate blits.
+ * Our blit code will be fastest when the src is a DDB and the
+ * destination is a DDB.
diff --git a/gfx/cairo/win32-d3dsurface9.patch b/gfx/cairo/win32-d3dsurface9.patch
new file mode 100644
index 000000000..b0e80252f
--- /dev/null
+++ b/gfx/cairo/win32-d3dsurface9.patch
@@ -0,0 +1,465 @@
+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
+@@ -335,16 +335,17 @@
+ #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_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_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
+diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+@@ -1852,16 +1852,17 @@ cairo_win32_printing_surface_create (HDC
+ }
+
+ _cairo_surface_clipper_init (&surface->clipper,
+ _cairo_win32_printing_surface_clipper_intersect_clip_path);
+
+ surface->image = NULL;
+ surface->format = CAIRO_FORMAT_RGB24;
+ surface->content = CAIRO_CONTENT_COLOR_ALPHA;
++ surface->d3d9surface = NULL;
+
+ surface->dc = hdc;
+ surface->bitmap = NULL;
+ surface->is_dib = FALSE;
+ surface->saved_dc_bitmap = NULL;
+ surface->brush = NULL;
+ surface->old_brush = NULL;
+ surface->font_subsets = _cairo_scaled_font_subsets_create_scaled ();
+diff --git a/gfx/cairo/cairo/src/cairo-win32-private.h b/gfx/cairo/cairo/src/cairo-win32-private.h
+--- a/gfx/cairo/cairo/src/cairo-win32-private.h
++++ b/gfx/cairo/cairo/src/cairo-win32-private.h
+@@ -54,16 +54,18 @@ CAIRO_BEGIN_DECLS
+
+ typedef struct _cairo_win32_surface {
+ cairo_surface_t base;
+
+ cairo_format_t format;
+
+ HDC dc;
+
++ struct IDirect3DSurface9 *d3d9surface;
++
+ /* We create off-screen surfaces as DIBs or DDBs, based on what we created
+ * originally*/
+ HBITMAP bitmap;
+ cairo_bool_t is_dib;
+
+ /* Used to save the initial 1x1 monochrome bitmap for the DC to
+ * select back into the DC before deleting the DC and our
+ * bitmap. For Windows XP, this doesn't seem to be necessary
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -54,16 +54,17 @@
+ #include "cairo-win32-private.h"
+ #include "cairo-scaled-font-subsets-private.h"
+ #include "cairo-surface-fallback-private.h"
+ #include "cairo-surface-clipper-private.h"
+ #include "cairo-gstate-private.h"
+ #include "cairo-private.h"
+ #include <wchar.h>
+ #include <windows.h>
++#include <d3d9.h>
+
+ #if defined(__MINGW32__) && !defined(ETO_PDY)
+ # define ETO_PDY 0x2000
+ #endif
+
+ #undef DEBUG_COMPOSITE
+
+ /* for older SDKs */
+@@ -384,16 +385,17 @@ static cairo_surface_t *
+
+ surface->image = cairo_image_surface_create_for_data (bits, format,
+ width, height, rowstride);
+ status = surface->image->status;
+ if (status)
+ goto FAIL;
+
+ surface->format = format;
++ surface->d3d9surface = NULL;
+
+ surface->clip_rect.x = 0;
+ surface->clip_rect.y = 0;
+ surface->clip_rect.width = width;
+ surface->clip_rect.height = height;
+
+ surface->initial_clip_rgn = NULL;
+ surface->had_simple_clip = FALSE;
+@@ -481,26 +483,73 @@ cairo_status_t
+ if (surface->bitmap) {
+ SelectObject (surface->dc, surface->saved_dc_bitmap);
+ DeleteObject (surface->bitmap);
+ DeleteDC (surface->dc);
+ } else {
+ _cairo_win32_restore_initial_clip (surface);
+ }
+
++ if (surface->d3d9surface) {
++ IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc);
++ IDirect3DSurface9_Release (surface->d3d9surface);
++ }
++
+ if (surface->initial_clip_rgn)
+ DeleteObject (surface->initial_clip_rgn);
+
+ if (surface->font_subsets != NULL)
+ _cairo_scaled_font_subsets_destroy (surface->font_subsets);
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static cairo_status_t
++_cairo_win32_surface_d3d9_lock_rect (cairo_win32_surface_t *surface,
++ int x,
++ int y,
++ int width,
++ int height,
++ cairo_image_surface_t **local_out)
++{
++ cairo_image_surface_t *local;
++ cairo_int_status_t status;
++
++ RECT rectin = { x, y, x+width, y+height };
++ D3DLOCKED_RECT rectout;
++ HRESULT hr;
++ hr = IDirect3DSurface9_ReleaseDC (surface->d3d9surface, surface->dc);
++ hr = IDirect3DSurface9_LockRect (surface->d3d9surface,
++ &rectout, &rectin, 0);
++ surface->dc = 0; // Don't use the DC when this is locked!
++ if (hr) {
++ IDirect3DSurface9_GetDC (surface->d3d9surface, &surface->dc);
++ return CAIRO_INT_STATUS_UNSUPPORTED;
++ }
++ local = cairo_image_surface_create_for_data (rectout.pBits,
++ surface->format,
++ width, height,
++ rectout.Pitch);
++ if (local == NULL) {
++ IDirect3DSurface9_UnlockRect (surface->d3d9surface);
++ IDirect3DSurface9_GetDC (surface->d3d9surface, &surface->dc);
++ return CAIRO_INT_STATUS_UNSUPPORTED;
++ }
++ if (local->base.status) {
++ IDirect3DSurface9_UnlockRect (surface->d3d9surface);
++ IDirect3DSurface9_GetDC (surface->d3d9surface, &surface->dc);
++ return local->base.status;
++ }
++
++ *local_out = local;
++
++ return CAIRO_STATUS_SUCCESS;
++}
++
++static cairo_status_t
+ _cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface,
+ int x,
+ int y,
+ int width,
+ int height,
+ cairo_win32_surface_t **local_out)
+ {
+ cairo_win32_surface_t *local;
+@@ -599,17 +648,16 @@ static void
+ }
+
+ static cairo_status_t
+ _cairo_win32_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
+ {
+ cairo_win32_surface_t *surface = abstract_surface;
+- cairo_win32_surface_t *local;
+ cairo_status_t status;
+
+ if (!surface->image && !surface->is_dib && surface->bitmap &&
+ (surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0)
+ {
+ /* This is a DDB, and we're being asked to use it as a source for
+ * something that we couldn't support natively. So turn it into
+ * a DIB, so that we have an equivalent image surface, as long
+@@ -619,69 +667,109 @@ static cairo_status_t
+ }
+
+ if (surface->image) {
+ *image_out = (cairo_image_surface_t *)surface->image;
+ *image_extra = NULL;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+- status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0,
+- surface->extents.width,
+- surface->extents.height, &local);
+- if (status)
+- return status;
+-
+- *image_out = (cairo_image_surface_t *)local->image;
+- *image_extra = local;
++ if (surface->d3d9surface) {
++ cairo_image_surface_t *local;
++ status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface, 0, 0,
++ surface->extents.width,
++ surface->extents.height, &local);
++ if (status)
++ return status;
++
++ *image_out = local;
++ *image_extra = surface;
++ } else {
++ cairo_win32_surface_t *local;
++ status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0,
++ surface->extents.width,
++ surface->extents.height, &local);
++ if (status)
++ return status;
++
++ *image_out = (cairo_image_surface_t *)local->image;
++ *image_extra = local;
++ }
++ // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points
++ // to the original surface to get back the d3d9surface and properly unlock.
++
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static void
+ _cairo_win32_surface_release_source_image (void *abstract_surface,
+ cairo_image_surface_t *image,
+ void *image_extra)
+ {
++ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_win32_surface_t *local = image_extra;
+
+- if (local)
++ if (local && local->d3d9surface) {
++ IDirect3DSurface9_UnlockRect (local->d3d9surface);
++ IDirect3DSurface9_GetDC (local->d3d9surface, &local->dc);
++ cairo_surface_destroy ((cairo_surface_t *)image);
++ } else {
+ cairo_surface_destroy ((cairo_surface_t *)local);
++ }
+ }
+
+ static cairo_status_t
+ _cairo_win32_surface_acquire_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t **image_out,
+ cairo_rectangle_int_t *image_rect,
+ void **image_extra)
+ {
+ cairo_win32_surface_t *surface = abstract_surface;
+- cairo_win32_surface_t *local = NULL;
+ cairo_status_t status;
+
+ if (surface->image) {
+ GdiFlush();
+
+ *image_out = (cairo_image_surface_t *) surface->image;
+ *image_extra = NULL;
+ *image_rect = surface->extents;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+- status = _cairo_win32_surface_get_subimage (abstract_surface,
++ if (surface->d3d9surface) {
++ cairo_image_surface_t *local = NULL;
++ status = _cairo_win32_surface_d3d9_lock_rect (abstract_surface,
+ interest_rect->x,
+ interest_rect->y,
+ interest_rect->width,
+- interest_rect->height,
+- &local);
+- if (status)
+- return status;
+-
+- *image_out = (cairo_image_surface_t *) local->image;
+- *image_extra = local;
++ interest_rect->height, &local);
++
++ if (status)
++ return status;
++
++ *image_out = local;
++ *image_extra = surface;
++ } else {
++ cairo_win32_surface_t *local = NULL;
++ status = _cairo_win32_surface_get_subimage (abstract_surface,
++ interest_rect->x,
++ interest_rect->y,
++ interest_rect->width,
++ interest_rect->height, &local);
++
++ if (status)
++ return status;
++
++ *image_out = (cairo_image_surface_t *) local->image;
++ *image_extra = local;
++ }
++ // image_extra is always of type cairo_win32_surface_t. For d3d9surface it points
++ // to the original surface to get back the d3d9surface and properly unlock.
++
+ *image_rect = *interest_rect;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static void
+ _cairo_win32_surface_release_dest_image (void *abstract_surface,
+ cairo_rectangle_int_t *interest_rect,
+ cairo_image_surface_t *image,
+@@ -689,29 +777,37 @@ static void
+ void *image_extra)
+ {
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_win32_surface_t *local = image_extra;
+
+ if (!local)
+ return;
+
+- /* clear any clip that's currently set on the surface
+- so that we can blit uninhibited. */
+- _cairo_win32_surface_set_clip_region (surface, NULL);
+-
+- if (!BitBlt (surface->dc,
+- image_rect->x, image_rect->y,
+- image_rect->width, image_rect->height,
+- local->dc,
+- 0, 0,
+- SRCCOPY))
+- _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image");
+-
+- cairo_surface_destroy ((cairo_surface_t *)local);
++ if (local->d3d9surface) {
++ IDirect3DSurface9_UnlockRect (local->d3d9surface);
++ IDirect3DSurface9_GetDC (local->d3d9surface, &local->dc);
++ cairo_surface_destroy ((cairo_surface_t *)image);
++ } else {
++
++ /* clear any clip that's currently set on the surface
++ so that we can blit uninhibited. */
++ _cairo_win32_surface_set_clip_region (surface, NULL);
++
++ if (!BitBlt (surface->dc,
++ image_rect->x, image_rect->y,
++ image_rect->width, image_rect->height,
++ local->dc,
++ 0, 0,
++ SRCCOPY))
++ _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image");
++
++ cairo_surface_destroy ((cairo_surface_t *)local);
++ }
++
+ }
+
+ cairo_status_t
+ _cairo_win32_surface_set_clip_region (void *abstract_surface,
+ cairo_region_t *region)
+ {
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_status_t status = CAIRO_STATUS_SUCCESS;
+@@ -1849,16 +1945,17 @@ cairo_win32_surface_create_internal (HDC
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ surface->clip_region = NULL;
+ surface->image = NULL;
+ surface->format = format;
+
++ surface->d3d9surface = NULL;
+ surface->dc = hdc;
+ surface->bitmap = NULL;
+ surface->is_dib = FALSE;
+ surface->saved_dc_bitmap = NULL;
+ surface->brush = NULL;
+ surface->old_brush = NULL;
+ surface->font_subsets = NULL;
+
+@@ -2009,16 +2106,29 @@ cairo_win32_surface_create_with_ddb (HDC
+
+ FINISH:
+ if (screen_dc)
+ ReleaseDC (NULL, screen_dc);
+
+ return (cairo_surface_t*) new_surf;
+ }
+
++cairo_public cairo_surface_t *
++cairo_win32_surface_create_with_d3dsurface9 (IDirect3DSurface9 *surface)
++{
++ HDC dc;
++ cairo_win32_surface_t *win_surface;
++
++ IDirect3DSurface9_AddRef (surface);
++ IDirect3DSurface9_GetDC (surface, &dc);
++ win_surface = cairo_win32_surface_create_internal(dc, CAIRO_FORMAT_RGB24);
++ win_surface->d3d9surface = surface;
++ return (cairo_surface_t*) win_surface;
++
++}
+ /**
+ * _cairo_surface_is_win32:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is a win32 surface. This will
+ * return False if this is a win32 printing surface; use
+ * _cairo_surface_is_win32_printing() to check for that.
+ *
+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
+@@ -59,17 +59,16 @@ 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);
+@@ -143,16 +142,21 @@ cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scale
+ void
+ cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, int geometry, int mode);
+
+ int
+ cairo_dwrite_get_cleartype_rendering_mode();
+
+ #endif /* CAIRO_HAS_DWRITE_FONT */
+
++struct IDirect3DSurface9;
++cairo_public cairo_surface_t *
++cairo_win32_surface_create_with_d3dsurface9 (struct IDirect3DSurface9 *surface);
++
++
+ #if CAIRO_HAS_D2D_SURFACE
+
+ struct _cairo_device
+ {
+ int type;
+ int refcount;
+ };
+
diff --git a/gfx/cairo/win32-ddb-dib.patch b/gfx/cairo/win32-ddb-dib.patch
new file mode 100644
index 000000000..a520d6e10
--- /dev/null
+++ b/gfx/cairo/win32-ddb-dib.patch
@@ -0,0 +1,181 @@
+b=455513; add optional flag to allow converting a DDB to a DIB internally, if the surface is every used as a source; r=jmuizelaar
+
+If a DDB is used as a source for an operation that can't be handled
+natively by GDI, we end up needing to take a really slow path (creating a
+temporary surface for acquire_source) for each operation. If we convert
+the DDB to a DIB, we then end up having a real image buffer and can hand
+things off to pixman directly.
+
+This isn't the default mode because I'm not sure if there are cases where a
+DDB is explicitly needed (e.g. for printing), and it would change
+current cairo behaviour. It might become the default at some point in the
+future.
+
+diff --git a/gfx/cairo/cairo/src/cairo-win32-private.h b/gfx/cairo/cairo/src/cairo-win32-private.h
+--- a/gfx/cairo/cairo/src/cairo-win32-private.h
++++ b/gfx/cairo/cairo/src/cairo-win32-private.h
+@@ -117,6 +117,9 @@
+
+ /* Whether we can use the CHECKJPEGFORMAT escape function */
+ CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8),
++
++ /* if this DDB surface can be converted to a DIB if necessary */
++ CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<9),
+ };
+
+ cairo_status_t
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -560,6 +560,56 @@
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++static void
++_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface)
++{
++ cairo_win32_surface_t *new_surface;
++ int width = surface->extents.width;
++ int height = surface->extents.height;
++
++ BOOL ok;
++ HBITMAP oldbitmap;
++
++ new_surface = (cairo_win32_surface_t*)
++ _cairo_win32_surface_create_for_dc (surface->dc,
++ surface->format,
++ width,
++ height);
++
++ if (new_surface->base.status)
++ return;
++
++ /* DDB can't be 32bpp, so BitBlt is safe */
++ ok = BitBlt (new_surface->dc,
++ 0, 0, width, height,
++ surface->dc,
++ 0, 0, SRCCOPY);
++
++ if (!ok)
++ goto out;
++
++ /* Now swap around new_surface and surface's internal bitmap
++ * pointers. */
++ DeleteDC (new_surface->dc);
++ new_surface->dc = NULL;
++
++ oldbitmap = SelectObject (surface->dc, new_surface->bitmap);
++ DeleteObject (oldbitmap);
++
++ surface->image = new_surface->image;
++ surface->is_dib = new_surface->is_dib;
++ surface->bitmap = new_surface->bitmap;
++
++ new_surface->bitmap = NULL;
++ new_surface->image = NULL;
++
++ /* Finally update flags */
++ surface->flags = _cairo_win32_flags_for_dc (surface->dc);
++
++ out:
++ cairo_surface_destroy ((cairo_surface_t*)new_surface);
++}
++
+ static cairo_status_t
+ _cairo_win32_surface_acquire_source_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+@@ -568,6 +618,17 @@
+ cairo_win32_surface_t *surface = abstract_surface;
+ cairo_win32_surface_t *local = NULL;
+ cairo_status_t status;
++
++ if (!surface->image && !surface->is_dib && surface->bitmap &&
++ (surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0)
++ {
++ /* This is a DDB, and we're being asked to use it as a source for
++ * something that we couldn't support natively. So turn it into
++ * a DIB, so that we have an equivalent image surface, as long
++ * as we're allowed to via flags.
++ */
++ _cairo_win32_convert_ddb_to_dib (surface);
++ }
+
+ if (surface->image) {
+ *image_out = (cairo_image_surface_t *)surface->image;
+@@ -2133,3 +2194,61 @@
+ free(rd);
+ fflush (stderr);
+ }
++
++/**
++ * cairo_win32_surface_set_can_convert_to_dib
++ * @surface: a #cairo_surface_t
++ * @can_convert: a #cairo_bool_t indicating whether this surface can
++ * be coverted to a DIB if necessary
++ *
++ * A DDB surface with this flag set can be converted to a DIB if it's
++ * used as a source in a way that GDI can't natively handle; for
++ * example, drawing a RGB24 DDB onto an ARGB32 DIB. Doing this
++ * conversion results in a significant speed optimization, because we
++ * can call on pixman to perform the operation natively, instead of
++ * reading the data from the DC each time.
++ *
++ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
++ * changed, or an error otherwise.
++ *
++ */
++cairo_status_t
++cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert)
++{
++ cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
++ if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
++ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
++
++ if (surface->bitmap) {
++ if (can_convert)
++ surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
++ else
++ surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
++ }
++
++ return CAIRO_STATUS_SUCCESS;
++}
++
++/**
++ * cairo_win32_surface_get_can_convert_to_dib
++ * @surface: a #cairo_surface_t
++ * @can_convert: a #cairo_bool_t* that receives the return value
++ *
++ * Returns the value of the flag indicating whether the surface can be
++ * converted to a DIB if necessary, as set by
++ * cairo_win32_surface_set_can_convert_to_dib.
++ *
++ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
++ * retreived, or an error otherwise.
++ *
++ */
++cairo_status_t
++cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert)
++{
++ cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
++ if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
++ return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
++
++ *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0);
++ return CAIRO_STATUS_SUCCESS;
++}
+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
+@@ -68,6 +68,12 @@ cairo_win32_surface_get_dc (cairo_surface_t *surface);
+ cairo_public cairo_surface_t *
+ cairo_win32_surface_get_image (cairo_surface_t *surface);
+
++cairo_public cairo_status_t
++cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert);
++
++cairo_public cairo_status_t
++cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert);
++
+ #if CAIRO_HAS_WIN32_FONT
+
diff --git a/gfx/cairo/win32-ffs-gcc.patch b/gfx/cairo/win32-ffs-gcc.patch
new file mode 100644
index 000000000..e7cc1d1f6
--- /dev/null
+++ b/gfx/cairo/win32-ffs-gcc.patch
@@ -0,0 +1,25 @@
+diff --git a/gfx/cairo/cairo/src/cairo-compiler-private.h b/gfx/cairo/cairo/src/cairo-compiler-private.h
+index ffac9ce..9a05831 100644
+--- a/gfx/cairo/cairo/src/cairo-compiler-private.h
++++ b/gfx/cairo/cairo/src/cairo-compiler-private.h
+@@ -229,16 +229,20 @@ ffs (int x)
+
+ if (_BitScanForward(&i, x) != 0)
+ return i + 1;
+
+ return 0;
+ }
+ #endif
+
++#elif defined(__WIN32__) && defined(__GNUC__)
++
++#define ffs(x) __builtin_ffs(x)
++
+ #endif
+
+ #if defined(_MSC_VER) && defined(_M_IX86)
+ /* 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 \
diff --git a/gfx/cairo/win32-gdi-font-cache-no-HFONT.patch b/gfx/cairo/win32-gdi-font-cache-no-HFONT.patch
new file mode 100644
index 000000000..fe93095ae
--- /dev/null
+++ b/gfx/cairo/win32-gdi-font-cache-no-HFONT.patch
@@ -0,0 +1,145 @@
+# HG changeset patch
+# User Robert O'Callahan <robert@ocallahan.org>
+# Date 1357107533 -46800
+# Node ID ed54dfdd2facb11a4d4158138b460a31de45e9f7
+# Parent ab6457cc16ec14ea07386dcfc57cad6b8a9ac55d
+Bug 717178. Part 3 alternative: don't put Win32 cairo_font_face_ts into the font-face cache if they were created with an explicit HFONT. r=jrmuizel
+
+diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c
+--- a/gfx/cairo/cairo/src/cairo-win32-font.c
++++ b/gfx/cairo/cairo/src/cairo-win32-font.c
+@@ -1941,16 +1942,21 @@ const cairo_font_face_backend_t _cairo_w
+ * 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.
++ *
++ * Only #cairo_font_face_t values with null 'hfont' (no
++ * HFONT preallocated by caller) are stored in this table. We rely
++ * on callers to manage the lifetime of the HFONT, and they can't
++ * do that if we share #cairo_font_face_t values with other callers.
+ */
+
+ static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL;
+
+ static int
+ _cairo_win32_font_face_keys_equal (const void *key_a,
+ const void *key_b);
+
+@@ -2036,22 +2042,24 @@ static int
+ }
+
+ static void
+ _cairo_win32_font_face_destroy (void *abstract_face)
+ {
+ cairo_hash_table_t *hash_table;
+ cairo_win32_font_face_t *font_face = abstract_face;
+
+- hash_table = _cairo_win32_font_face_hash_table_lock ();
+- if (unlikely (hash_table == NULL)) {
+- return;
++ if (!font_face->hfont) {
++ hash_table = _cairo_win32_font_face_hash_table_lock ();
++ if (unlikely (hash_table == NULL)) {
++ return;
++ }
++ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
++ _cairo_win32_font_face_hash_table_unlock ();
+ }
+- _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
+- _cairo_win32_font_face_hash_table_unlock ();
+ }
+
+ /**
+ * 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.
+@@ -2070,55 +2078,63 @@ static void
+ **/
+ 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;
+- }
++ if (!font) {
++ hash_table = _cairo_win32_font_face_hash_table_lock ();
++ if (unlikely (hash_table == NULL)) {
++ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
++ return (cairo_font_face_t *)&_cairo_font_face_nil;
++ }
+
+- _cairo_win32_font_face_init_key (&key, logfont, font);
++ _cairo_win32_font_face_init_key (&key, logfont, font);
+
+- /* Return existing unscaled font if it exists in the hash table. */
+- font_face = _cairo_hash_table_lookup (hash_table,
+- &key.base.hash_entry);
+- if (font_face != NULL) {
+- cairo_font_face_reference (&font_face->base);
+- goto DONE;
++ /* 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) {
++ cairo_font_face_reference (&font_face->base);
++ goto DONE;
++ }
+ }
+
+ /* Otherwise create it and insert into hash table. */
+ font_face = 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);
+
+- 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;
++ if (!font) {
++ status = _cairo_hash_table_insert (hash_table,
++ &font_face->base.hash_entry);
++ if (unlikely (status))
++ goto FAIL;
++ }
+
+ DONE:
+- _cairo_win32_font_face_hash_table_unlock ();
++ if (!font) {
++ _cairo_win32_font_face_hash_table_unlock ();
++ }
+
+ return &font_face->base;
+
+ FAIL:
+- _cairo_win32_font_face_hash_table_unlock ();
++ if (!font) {
++ _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
diff --git a/gfx/cairo/win32-gdi-font-cache.patch b/gfx/cairo/win32-gdi-font-cache.patch
new file mode 100644
index 000000000..e082a2e4d
--- /dev/null
+++ b/gfx/cairo/win32-gdi-font-cache.patch
@@ -0,0 +1,375 @@
+# HG changeset patch
+# User Andrea Canciani <ranma42@gmail.com>, Adrian Johnson <ajohnson@redneon.com>
+# Date 1354838294 -46800
+# Node ID 390df735b9d5c5ba07a4d3fe9ca2ebc9e7626a78
+# Parent e30a5b6a5a003b85fc1ca8b76719a56ef59d976e
+Bug 717178. Part 2: Import changesets eb29a25d, 6e3e3291 and 101fab7c from upstream.
+======
+
+From 101fab7cd8a90f7cf3d8113c792b3f8c2a9afb7d Mon Sep 17 00:00:00 2001
+From: Andrea Canciani <ranma42@gmail.com>
+Date: Wed, 15 Jun 2011 09:37:36 +0000
+Subject: win32-font: Improve static data reset function
+
+The hashtable is guaranteed to only contain font faces which are
+currently referenced, hence there is no need to remove any font face
+when it is reset (just like for toy-font).
+
+This makes the function simpler and fixes the assertion
+
+Assertion failed: predicate != NULL, file cairo-hash.c, line 373
+
+hit by multiple tests (the first one being "clear").
+
+See https://bugs.freedesktop.org/show_bug.cgi?id=38049
+
+======
+
+From eb29a25dd6dddc511388bf883c9b95843ecdb823 Mon Sep 17 00:00:00 2001
+From: Adrian Johnson <ajohnson@redneon.com>
+Date: Tue, 16 Nov 2010 13:18:39 +0000
+Subject: win32: Use a font_face hash table to provide unique font faces
+
+Similar to the freetype and toy font backends, use a hash table
+to map logfont,hfont to font faces.
+
+This fixes the multiple embedding of the same font in PDF.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=24849
+
+======
+
+From 6e3e329170ab4b96bc0d587c8071e869e228e758 Mon Sep 17 00:00:00 2001
+From: Adrian Johnson <ajohnson@redneon.com>
+Date: Thu, 18 Nov 2010 12:37:45 +0000
+Subject: win32: fix font_face hashing
+
+some bugs were discovered while testing with firefox
+
+======
+
+diff --git a/gfx/cairo/cairo/src/cairo-debug.c b/gfx/cairo/cairo/src/cairo-debug.c
+--- a/gfx/cairo/cairo/src/cairo-debug.c
++++ b/gfx/cairo/cairo/src/cairo-debug.c
+@@ -64,16 +64,20 @@ cairo_debug_reset_static_data (void)
+ _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 ();
+
+diff --git a/gfx/cairo/cairo/src/cairo-mutex-list-private.h b/gfx/cairo/cairo/src/cairo-mutex-list-private.h
+--- a/gfx/cairo/cairo/src/cairo-mutex-list-private.h
++++ b/gfx/cairo/cairo/src/cairo-mutex-list-private.h
+@@ -46,16 +46,20 @@ CAIRO_MUTEX_DECLARE (_cairo_intern_strin
+ CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex)
+ CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex)
+ CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex)
+
+ #if CAIRO_HAS_FT_FONT
+ CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex)
+ #endif
+
++#if CAIRO_HAS_WIN32_FONT
++CAIRO_MUTEX_DECLARE (_cairo_win32_font_face_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
+
+diff --git a/gfx/cairo/cairo/src/cairo-win32-font.c b/gfx/cairo/cairo/src/cairo-win32-font.c
+--- a/gfx/cairo/cairo/src/cairo-win32-font.c
++++ b/gfx/cairo/cairo/src/cairo-win32-font.c
+@@ -42,16 +42,18 @@
+ # define _WIN32_WINNT 0x0500
+ #endif
+
+ #include "cairoint.h"
+
+ #include "cairo-win32-private.h"
+ #include "cairo-error-private.h"
+
++#include <wchar.h>
++
+ #ifndef SPI_GETFONTSMOOTHINGTYPE
+ #define SPI_GETFONTSMOOTHINGTYPE 0x200a
+ #endif
+ #ifndef FE_FONTSMOOTHINGCLEARTYPE
+ #define FE_FONTSMOOTHINGCLEARTYPE 2
+ #endif
+ #ifndef CLEARTYPE_QUALITY
+ #define CLEARTYPE_QUALITY 5
+@@ -1887,19 +1889,17 @@ struct _cairo_win32_font_face {
+ cairo_font_face_t base;
+ LOGFONTW logfont;
+ HFONT hfont;
+ };
+
+ /* implement the platform-specific interface */
+
+ static void
+-_cairo_win32_font_face_destroy (void *abstract_face)
+-{
+-}
++_cairo_win32_font_face_destroy (void *abstract_face);
+
+ static cairo_bool_t
+ _is_scale (const cairo_matrix_t *matrix, double scale)
+ {
+ return matrix->xx == scale && matrix->yy == scale &&
+ matrix->xy == 0. && matrix->yx == 0. &&
+ matrix->x0 == 0. && matrix->y0 == 0.;
+ }
+@@ -1932,16 +1932,128 @@ static cairo_status_t
+
+ const cairo_font_face_backend_t _cairo_win32_font_face_backend = {
+ CAIRO_FONT_TYPE_WIN32,
+ _cairo_win32_font_face_create_for_toy,
+ _cairo_win32_font_face_destroy,
+ _cairo_win32_font_face_scaled_font_create
+ };
+
++/* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t.
++ * The primary purpose of this mapping is to provide unique
++ * #cairo_font_face_t values so that our cache and mapping from
++ * #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 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;
++}
++
++static void
++_cairo_win32_font_face_destroy (void *abstract_face)
++{
++ cairo_hash_table_t *hash_table;
++ cairo_win32_font_face_t *font_face = abstract_face;
++
++ hash_table = _cairo_win32_font_face_hash_table_lock ();
++ if (unlikely (hash_table == NULL)) {
++ return;
++ }
++ _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
++ _cairo_win32_font_face_hash_table_unlock ();
++}
++
+ /**
+ * 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.
+@@ -1954,30 +2066,61 @@ const cairo_font_face_backend_t _cairo_w
+ * 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.
+ **/
+ cairo_font_face_t *
+ cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font)
+ {
+- cairo_win32_font_face_t *font_face;
++ 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) {
++ cairo_font_face_reference (&font_face->base);
++ goto DONE;
++ }
++
++ /* Otherwise create it and insert into hash table. */
+ font_face = malloc (sizeof (cairo_win32_font_face_t));
+ if (!font_face) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+- return (cairo_font_face_t *)&_cairo_font_face_nil;
++ goto FAIL;
+ }
+
+- font_face->logfont = *logfont;
+- font_face->hfont = font;
+-
++ _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;
++
++DONE:
++ _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.
+ *
+@@ -2176,8 +2319,14 @@ cairo_win32_scaled_font_get_device_to_lo
+ 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/cairoint.h b/gfx/cairo/cairo/src/cairoint.h
+--- a/gfx/cairo/cairo/src/cairoint.h
++++ b/gfx/cairo/cairo/src/cairoint.h
+@@ -403,16 +403,19 @@ cairo_private void
+ _cairo_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);
++
+ /* the font backend interface */
+
+ struct _cairo_unscaled_font_backend {
+ void (*destroy) (void *unscaled_font);
+ };
+
+ /* #cairo_toy_font_face_t - simple family/slant/weight font faces used for
+ * the built-in font API
diff --git a/gfx/cairo/win32-inline-cpp-keyword.patch b/gfx/cairo/win32-inline-cpp-keyword.patch
new file mode 100644
index 000000000..4b6f542d7
--- /dev/null
+++ b/gfx/cairo/win32-inline-cpp-keyword.patch
@@ -0,0 +1,24 @@
+diff --git a/gfx/cairo/cairo/src/cairo-compiler-private.h b/gfx/cairo/cairo/src/cairo-compiler-private.h
+--- a/gfx/cairo/cairo/src/cairo-compiler-private.h
++++ b/gfx/cairo/cairo/src/cairo-compiler-private.h
+@@ -205,18 +205,20 @@
+ #if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
+ #define snprintf _snprintf
+ #define popen _popen
+ #define pclose _pclose
+ #define hypot _hypot
+ #endif
+
+ #ifdef _MSC_VER
++#ifndef __cplusplus
+ #undef inline
+ #define inline __inline
++#endif
+
+ /* there are currently linkage problems that arise when trying to include intrin.h in c++:
+ * D:\sdks\v7.0\include\winnt.h(3674) : error C2733: second C linkage of overloaded function '_interlockedbittestandset' not allowed
+ * so avoid defining ffs in c++ code for now */
+ #ifndef __cplusplus
+ /* Add a definition of ffs */
+ #include <intrin.h>
+ #pragma intrinsic(_BitScanForward)
diff --git a/gfx/cairo/win32-logical-font-scale.patch b/gfx/cairo/win32-logical-font-scale.patch
new file mode 100644
index 000000000..02d812be3
--- /dev/null
+++ b/gfx/cairo/win32-logical-font-scale.patch
@@ -0,0 +1,12 @@
+diff -r e10a8066a62c gfx/cairo/cairo/src/cairo-win32-private.h
+--- a/gfx/cairo/cairo/src/cairo-win32-private.h Fri Jun 08 17:39:38 2007 -0700
++++ b/gfx/cairo/cairo/src/cairo-win32-private.h Fri Jun 29 09:14:35 2007 +0200
+@@ -46,7 +46,7 @@
+ #define SB_NONE 0
+ #endif
+
+-#define WIN32_FONT_LOGICAL_SCALE 32
++#define WIN32_FONT_LOGICAL_SCALE 1
+
+ typedef struct _cairo_win32_surface {
+ cairo_surface_t base;
diff --git a/gfx/cairo/win32-printing-axis-swap.patch b/gfx/cairo/win32-printing-axis-swap.patch
new file mode 100644
index 000000000..87a1a91e4
--- /dev/null
+++ b/gfx/cairo/win32-printing-axis-swap.patch
@@ -0,0 +1,292 @@
+# HG changeset patch
+# User Lee Salzman <lsalzman@mozilla.com>
+# Date 1445463645 14400
+# Wed Oct 21 17:40:45 2015 -0400
+# Node ID 9e84563cbd73c5b0993dfd018ca25b660b667e94
+# Parent 2d3fd51c4182c253a2f102655e8e9e466032853f
+workaround for Windows printer drivers that can't handle swapped X and Y axes
+
+diff --git a/gfx/cairo/cairo/src/cairo-matrix.c b/gfx/cairo/cairo/src/cairo-matrix.c
+--- a/gfx/cairo/cairo/src/cairo-matrix.c
++++ b/gfx/cairo/cairo/src/cairo-matrix.c
+@@ -873,42 +873,56 @@ cairo_bool_t
+ (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)
++/* determine the length of the major and minor axes of a circle of the given
++ radius after applying the transformation matrix. */
++void
++_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
++ double radius,
++ double *major,
++ double *minor)
+ {
+- double a, b, c, d, f, g, h, i, j;
++ double a, b, c, d, f, g, h, i, j, k;
+
+ _cairo_matrix_get_affine (matrix,
+ &a, &b,
+ &c, &d,
+ NULL, NULL);
+
+ i = a*a + b*b;
+ j = c*c + d*d;
++ k = a*c + b*d;
+
+ f = 0.5 * (i + j);
+ g = 0.5 * (i - j);
+- h = a*c + b*d;
++ h = hypot (g, k);
+
+- return radius * sqrt (f + hypot (g, h));
++ if (major)
++ *major = radius * sqrt (f + h);
++ if (minor)
++ *minor = radius * sqrt (f - h);
++}
+
+- /*
+- * we don't need the minor axis length, which is
+- * double min = radius * sqrt (f - sqrt (g*g+h*h));
+- */
++/* determine the length of the major axis of a circle of the given radius
++ after applying the transformation matrix. */
++double
++_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
++ double radius)
++{
++ double major;
++
++ _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL);
++
++ return major;
+ }
+
+ void
+ _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
+ pixman_transform_t *pixman_transform,
+ double xc,
+ double yc)
+ {
+diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+@@ -610,16 +610,17 @@ static cairo_status_t
+ int x_tile, y_tile, left, right, top, bottom;
+ 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;
++ cairo_bool_t axis_swap;
+
+ /* If we can't use StretchDIBits with this surface, we can't do anything
+ * here.
+ */
+ if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+@@ -658,39 +659,65 @@ static cairo_status_t
+ &mime_size,
+ &mime_info);
+ }
+ if (_cairo_status_is_error (status))
+ return status;
+
+ use_mime = (status == CAIRO_STATUS_SUCCESS);
+
+- if (!use_mime && image->format != CAIRO_FORMAT_RGB24) {
++ m = pattern->base.matrix;
++ status = cairo_matrix_invert (&m);
++ /* _cairo_pattern_set_matrix guarantees invertibility */
++ assert (status == CAIRO_STATUS_SUCCESS);
++ cairo_matrix_multiply (&m, &m, &surface->ctm);
++ cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
++ /* Check if the matrix swaps the X and Y axes by checking if the diagonal
++ * is effectively zero. This can happen, for example, if it was composed
++ * with a rotation such as a landscape transform. Some printing devices
++ * don't support such transforms in StretchDIBits.
++ */
++ axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1;
++
++ if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) {
+ cairo_surface_t *opaque_surface;
+ cairo_surface_pattern_t image_pattern;
+ cairo_solid_pattern_t background_pattern;
++ int width = image->width, height = image->height;
+
++ if (axis_swap) {
++ width = image->height;
++ height = image->width;
++ }
+ opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+- image->width,
+- image->height);
++ width,
++ 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;
++ if (image->format != CAIRO_FORMAT_RGB24) {
++ _cairo_pattern_init_solid (&background_pattern,
++ background_color);
++ status = _cairo_surface_paint (opaque_surface,
++ CAIRO_OPERATOR_SOURCE,
++ &background_pattern.base,
++ NULL);
++ if (status)
++ goto CLEANUP_OPAQUE_IMAGE;
++ }
+
+ _cairo_pattern_init_for_surface (&image_pattern, &image->base);
++ if (axis_swap) {
++ /* swap the X and Y axes to undo the axis swap in the matrix */
++ cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
++ cairo_pattern_set_matrix (&image_pattern.base, &swap_xy);
++ cairo_matrix_multiply (&m, &swap_xy, &m);
++ }
+ status = _cairo_surface_paint (opaque_surface,
+ CAIRO_OPERATOR_OVER,
+ &image_pattern.base,
+ NULL);
+ _cairo_pattern_fini (&image_pattern.base);
+ if (status)
+ goto CLEANUP_OPAQUE_IMAGE;
+
+@@ -706,23 +733,16 @@ static cairo_status_t
+ 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 = pattern->base.matrix;
+- status = cairo_matrix_invert (&m);
+- /* _cairo_pattern_set_matrix guarantees invertibility */
+- assert (status == CAIRO_STATUS_SUCCESS);
+-
+- cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
+- cairo_matrix_multiply(&m, &m, &surface->ctm);
+ SaveDC (surface->dc);
+ _cairo_matrix_to_win32_xform (&m, &xform);
+
+ if (! SetWorldTransform (surface->dc, &xform)) {
+ status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern");
+ goto CLEANUP_OPAQUE_IMAGE;
+ }
+
+@@ -1260,16 +1280,17 @@ static cairo_int_status_t
+ DWORD pen_width;
+ DWORD *dash_array;
+ HGDIOBJ obj;
+ unsigned int i;
+ cairo_solid_pattern_t clear;
+ cairo_matrix_t mat;
+ double scale;
+ double scaled_width;
++ double major, minor;
+
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (status)
+ return status;
+
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ _cairo_win32_printing_surface_init_clear_color (surface, &clear);
+ source = (cairo_pattern_t*) &clear;
+@@ -1350,22 +1371,40 @@ static cairo_int_status_t
+ if (status)
+ return status;
+
+ /*
+ * Switch to user space to set line parameters
+ */
+ SaveDC (surface->dc);
+
+- _cairo_matrix_to_win32_xform (&mat, &xform);
+- xform.eDx = 0.0f;
+- xform.eDy = 0.0f;
++ /* Some printers don't handle transformed strokes. Avoid the transform
++ * if not required for the pen shape. Use the SVD here to find the major
++ * and minor scales then check if they differ by more than 1 device unit.
++ * If the difference is smaller, then just treat the scaling as uniform.
++ * This check ignores rotations as the pen shape is symmetric before
++ * transformation.
++ */
++ _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor);
++ if (fabs (major - minor) > 1) {
++ /* Check if the matrix swaps the X and Y axes such that the diagonal
++ * is nearly zero. This was observed to cause problems with XPS export.
++ */
++ if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) {
++ /* swap the X and Y axes to undo the axis swap in the matrix */
++ cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
++ cairo_matrix_multiply (&mat, &swap_xy, &mat);
++ }
++ _cairo_matrix_to_win32_xform (&mat, &xform);
++ xform.eDx = 0.0f;
++ xform.eDy = 0.0f;
+
+- if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
+- return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
++ if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
++ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
++ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ StrokePath (surface->dc);
+ } else {
+ if (!WidenPath (surface->dc))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath");
+ if (!SelectClipPath (surface->dc, RGN_AND))
+ return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath");
+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
+@@ -2115,16 +2115,22 @@ cairo_private cairo_bool_t
+ 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 void
++_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
++ double radius,
++ double *major,
++ double *minor);
++
+ cairo_private double
+ _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+ double radius) cairo_pure;
+
+ cairo_private void
+ _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
+ pixman_transform_t *pixman_transform,
+ double xc,
diff --git a/gfx/cairo/win32-raster.patch b/gfx/cairo/win32-raster.patch
new file mode 100644
index 000000000..080873121
--- /dev/null
+++ b/gfx/cairo/win32-raster.patch
@@ -0,0 +1,262 @@
+changeset: 29338:f2a10f325734
+tag: qtip
+tag: tip
+tag: win32-raster-mask2.patch
+tag: qbase
+user: Jeff Muizelaar <jmuizelaar@mozilla.com>
+date: Mon Jun 22 14:26:07 2009 -0400
+summary: imported patch win32-raster-mask2.patch
+
+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
+@@ -1232,27 +1232,27 @@ typedef struct _cairo_image_surface_span
+ cairo_composite_rectangles_t composite_rectangles;
+ } cairo_image_surface_span_renderer_t;
+
+-static cairo_status_t
+-_cairo_image_surface_span_renderer_render_row (
+- void *abstract_renderer,
++void
++_cairo_image_surface_span_render_row (
+ int y,
+ const cairo_half_open_span_t *spans,
+- unsigned num_spans)
++ unsigned num_spans,
++ cairo_image_surface_t *mask,
++ const cairo_composite_rectangles_t *rects)
+ {
+- cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
+- int xmin = renderer->composite_rectangles.mask.x;
+- int xmax = xmin + renderer->composite_rectangles.width;
++ int xmin = rects->mask.x;
++ int xmax = xmin + rects->width;
+ uint8_t *row;
+ int prev_x = xmin;
+ int prev_alpha = 0;
+ unsigned i;
+
+ /* Make sure we're within y-range. */
+- y -= renderer->composite_rectangles.mask.y;
+- if (y < 0 || y >= renderer->composite_rectangles.height)
++ y -= rects->mask.y;
++ if (y < 0 || y >= rects->height)
+ return CAIRO_STATUS_SUCCESS;
+
+- row = (uint8_t*)(renderer->mask->data) + y*(size_t)renderer->mask->stride - xmin;
++ row = (uint8_t*)(mask->data) + y*(size_t)mask->stride - xmin;
+
+ /* Find the first span within x-range. */
+ for (i=0; i < num_spans && spans[i].x < xmin; i++) {}
+@@ -1286,7 +1286,17 @@ _cairo_image_surface_span_renderer_rende
+ if (prev_alpha != 0 && prev_x < xmax) {
+ memset(row + prev_x, prev_alpha, xmax - prev_x);
+ }
++}
+
++static cairo_status_t
++_cairo_image_surface_span_renderer_render_row (
++ void *abstract_renderer,
++ int y,
++ const cairo_half_open_span_t *spans,
++ unsigned num_spans)
++{
++ cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
++ _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles);
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+diff --git a/gfx/cairo/cairo/src/cairo-tor-scan-converter.c b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c
+--- a/gfx/cairo/cairo/src/cairo-tor-scan-converter.c
++++ b/gfx/cairo/cairo/src/cairo-tor-scan-converter.c
+@@ -295,9 +295,9 @@ typedef int grid_area_t;
+ #elif GRID_XY == 15
+ # define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c))
+ #elif GRID_XY == 2*256*15
+-# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4)) >> 9)
++# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9)
+ #else
+-# define GRID_AREA_TO_ALPHA(c) ((c)*255 / GRID_XY) /* tweak me for rounding */
++# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY)
+ #endif
+
+ #define UNROLL3(x) x x x
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -2048,6 +2048,148 @@ _cairo_win32_surface_reset (void *abstra
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++typedef struct _cairo_win32_surface_span_renderer {
++ cairo_span_renderer_t base;
++
++ cairo_operator_t op;
++ const cairo_pattern_t *pattern;
++ cairo_antialias_t antialias;
++
++ cairo_image_surface_t *mask;
++ cairo_win32_surface_t *dst;
++
++ cairo_composite_rectangles_t composite_rectangles;
++} cairo_win32_surface_span_renderer_t;
++
++static cairo_status_t
++_cairo_win32_surface_span_renderer_render_row (
++ void *abstract_renderer,
++ int y,
++ const cairo_half_open_span_t *spans,
++ unsigned num_spans)
++{
++ cairo_win32_surface_span_renderer_t *renderer = abstract_renderer;
++ _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles);
++ return CAIRO_STATUS_SUCCESS;
++}
++
++static void
++_cairo_win32_surface_span_renderer_destroy (void *abstract_renderer)
++{
++ cairo_win32_surface_span_renderer_t *renderer = abstract_renderer;
++ if (!renderer) return;
++
++ if (renderer->mask != NULL)
++ cairo_surface_destroy (&renderer->mask->base);
++
++ free (renderer);
++}
++
++static cairo_status_t
++_cairo_win32_surface_span_renderer_finish (void *abstract_renderer)
++{
++ cairo_win32_surface_span_renderer_t *renderer = abstract_renderer;
++ cairo_status_t status = CAIRO_STATUS_SUCCESS;
++
++ if (renderer->pattern == NULL || renderer->mask == NULL)
++ return CAIRO_STATUS_SUCCESS;
++
++ status = cairo_surface_status (&renderer->mask->base);
++ if (status == CAIRO_STATUS_SUCCESS) {
++ cairo_composite_rectangles_t *rects = &renderer->composite_rectangles;
++ cairo_win32_surface_t *dst = renderer->dst;
++ cairo_pattern_t *mask_pattern = cairo_pattern_create_for_surface (&renderer->mask->base);
++ /* composite onto the image surface directly if we can */
++ if (dst->image) {
++ GdiFlush();
++
++ status = dst->image->backend->composite (renderer->op,
++ renderer->pattern, mask_pattern, dst->image,
++ rects->src.x,
++ rects->src.y,
++ 0, 0, /* mask.x, mask.y */
++ rects->dst.x, rects->dst.y,
++ rects->width, rects->height);
++ } else {
++ /* otherwise go through the fallback_composite path which
++ * will do the appropriate surface acquisition */
++ status = _cairo_surface_fallback_composite (
++ renderer->op,
++ renderer->pattern, mask_pattern, dst,
++ rects->src.x,
++ rects->src.y,
++ 0, 0, /* mask.x, mask.y */
++ rects->dst.x, rects->dst.y,
++ rects->width, rects->height);
++ }
++ cairo_pattern_destroy (mask_pattern);
++
++ }
++ if (status != CAIRO_STATUS_SUCCESS)
++ return _cairo_span_renderer_set_error (abstract_renderer,
++ status);
++ return CAIRO_STATUS_SUCCESS;
++}
++
++static cairo_bool_t
++_cairo_win32_surface_check_span_renderer (cairo_operator_t op,
++ const cairo_pattern_t *pattern,
++ void *abstract_dst,
++ cairo_antialias_t antialias,
++ const cairo_composite_rectangles_t *rects)
++{
++ (void) op;
++ (void) pattern;
++ (void) abstract_dst;
++ (void) antialias;
++ (void) rects;
++ return TRUE;
++}
++
++static cairo_span_renderer_t *
++_cairo_win32_surface_create_span_renderer (cairo_operator_t op,
++ const cairo_pattern_t *pattern,
++ void *abstract_dst,
++ cairo_antialias_t antialias,
++ const cairo_composite_rectangles_t *rects)
++{
++ cairo_win32_surface_t *dst = abstract_dst;
++ cairo_win32_surface_span_renderer_t *renderer
++ = calloc(1, sizeof(*renderer));
++ cairo_status_t status;
++ int width = rects->width;
++ int height = rects->height;
++
++ if (renderer == NULL)
++ return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
++
++ renderer->base.destroy = _cairo_win32_surface_span_renderer_destroy;
++ renderer->base.finish = _cairo_win32_surface_span_renderer_finish;
++ renderer->base.render_row =
++ _cairo_win32_surface_span_renderer_render_row;
++ renderer->op = op;
++ renderer->pattern = pattern;
++ renderer->antialias = antialias;
++ renderer->dst = dst;
++
++ renderer->composite_rectangles = *rects;
++
++ /* TODO: support rendering to A1 surfaces (or: go add span
++ * compositing to pixman.) */
++ renderer->mask = (cairo_image_surface_t *)
++ cairo_image_surface_create (CAIRO_FORMAT_A8,
++ width, height);
++
++ status = cairo_surface_status (&renderer->mask->base);
++
++ if (status != CAIRO_STATUS_SUCCESS) {
++ _cairo_win32_surface_span_renderer_destroy (renderer);
++ return _cairo_span_renderer_create_in_error (status);
++ }
++ return &renderer->base;
++}
++
++
+ static const cairo_surface_backend_t cairo_win32_surface_backend = {
+ CAIRO_SURFACE_TYPE_WIN32,
+ _cairo_win32_surface_create_similar,
+@@ -2060,8 +2202,8 @@ static const cairo_surface_backend_t cai
+ _cairo_win32_surface_composite,
+ _cairo_win32_surface_fill_rectangles,
+ NULL, /* composite_trapezoids */
+- NULL, /* create_span_renderer */
+- NULL, /* check_span_renderer */
++ _cairo_win32_surface_create_span_renderer,
++ _cairo_win32_surface_check_span_renderer,
+ NULL, /* copy_page */
+ NULL, /* show_page */
+ _cairo_win32_surface_set_clip_region,
+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
+@@ -2193,6 +2193,12 @@ _cairo_image_surface_set_clip_region (vo
+ cairo_private cairo_image_surface_t *
+ _cairo_image_surface_coerce (cairo_image_surface_t *surface,
+ cairo_format_t format);
++cairo_private void
++_cairo_image_surface_span_render_row (int y,
++ const cairo_half_open_span_t *spans,
++ unsigned num_spans,
++ cairo_image_surface_t *mask,
++ const cairo_composite_rectangles_t *rects);
+
+ cairo_private cairo_image_transparency_t
+ _cairo_image_analyze_transparency (cairo_image_surface_t *image);
diff --git a/gfx/cairo/win32-transparent-surface.patch b/gfx/cairo/win32-transparent-surface.patch
new file mode 100644
index 000000000..c8765cf28
--- /dev/null
+++ b/gfx/cairo/win32-transparent-surface.patch
@@ -0,0 +1,129 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -1709,40 +1709,23 @@ _cairo_win32_surface_show_glyphs (void
+ }
+ #else
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ #endif
+ }
+
+ #undef STACK_GLYPH_SIZE
+
+-/**
+- * cairo_win32_surface_create:
+- * @hdc: the DC to create a surface for
+- *
+- * Creates a cairo surface that targets the given DC. The DC will be
+- * queried for its initial clip extents, and this will be used as the
+- * size of the cairo surface. The resulting surface will always be of
+- * format %CAIRO_FORMAT_RGB24; should you need another surface format,
+- * you will need to create one through
+- * cairo_win32_surface_create_with_dib().
+- *
+- * Return value: the newly created surface
+- **/
+-cairo_surface_t *
+-cairo_win32_surface_create (HDC hdc)
++static cairo_surface_t *
++cairo_win32_surface_create_internal (HDC hdc, cairo_format_t format)
+ {
+ cairo_win32_surface_t *surface;
+
+- cairo_format_t format;
+ RECT rect;
+
+- /* Assume that everything coming in as a HDC is RGB24 */
+- format = CAIRO_FORMAT_RGB24;
+-
+ surface = malloc (sizeof (cairo_win32_surface_t));
+ if (surface == NULL)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) {
+ free (surface);
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+@@ -1765,17 +1748,58 @@ cairo_win32_surface_create (HDC hdc)
+ surface->extents.width = rect.right - rect.left;
+ surface->extents.height = rect.bottom - rect.top;
+
+ surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
+ _cairo_surface_init (&surface->base, &cairo_win32_surface_backend,
+ _cairo_content_from_format (format));
+
+- return (cairo_surface_t *)surface;
++ return &surface->base;
++}
++
++/**
++ * cairo_win32_surface_create:
++ * @hdc: the DC to create a surface for
++ *
++ * Creates a cairo surface that targets the given DC. The DC will be
++ * queried for its initial clip extents, and this will be used as the
++ * size of the cairo surface. The resulting surface will always be of
++ * format %CAIRO_FORMAT_RGB24; should you need another surface format,
++ * you will need to create one through
++ * cairo_win32_surface_create_with_dib() or call
++ * cairo_win32_surface_create_with_alpha.
++ *
++ * Return value: the newly created surface
++ **/
++cairo_surface_t *
++cairo_win32_surface_create (HDC hdc)
++{
++ /* Assume everything comes in as RGB24 */
++ return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_RGB24);
++}
++
++/**
++ * cairo_win32_surface_create_with_alpha:
++ * @hdc: the DC to create a surface for
++ *
++ * Creates a cairo surface that targets the given DC. The DC will be
++ * queried for its initial clip extents, and this will be used as the
++ * size of the cairo surface. The resulting surface will always be of
++ * format %CAIRO_FORMAT_ARGB32; this format is used when drawing into
++ * transparent windows.
++ *
++ * Return value: the newly created surface
++ *
++ * Since: 1.10
++ **/
++cairo_surface_t *
++cairo_win32_surface_create_with_alpha (HDC hdc)
++{
++ return cairo_win32_surface_create_internal(hdc, CAIRO_FORMAT_ARGB32);
+ }
+
+ /**
+ * cairo_win32_surface_create_with_dib:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+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
+@@ -44,16 +44,19 @@
+ #include <windows.h>
+
+ CAIRO_BEGIN_DECLS
+
+ cairo_public cairo_surface_t *
+ cairo_win32_surface_create (HDC hdc);
+
+ cairo_public cairo_surface_t *
++cairo_win32_surface_create_with_alpha (HDC hdc);
++
++cairo_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);
+
diff --git a/gfx/cairo/win32-vertically-offset-glyph.patch b/gfx/cairo/win32-vertically-offset-glyph.patch
new file mode 100644
index 000000000..ffdf63b4a
--- /dev/null
+++ b/gfx/cairo/win32-vertically-offset-glyph.patch
@@ -0,0 +1,23 @@
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -1742,17 +1742,18 @@ _cairo_win32_surface_show_glyphs (void
+
+ 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);
++ dxy_buf[j+1] = _cairo_lround (logical_y - next_logical_y);
++ /* note that GDI coordinate system is inverted */
+
+ logical_x = next_logical_x;
+ logical_y = next_logical_y;
+ }
+ }
+
+ /* Using glyph indices for a Type 1 font does not work on a
+ * printer DC. The win32 printing surface will convert the the
diff --git a/gfx/cairo/wrap-source_image.patch b/gfx/cairo/wrap-source_image.patch
new file mode 100644
index 000000000..89da5a08d
--- /dev/null
+++ b/gfx/cairo/wrap-source_image.patch
@@ -0,0 +1,105 @@
+Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
+diff --git a/src/cairo-surface.c b/src/cairo-surface.c
+index 8278694..12f6242 100644
+--- a/src/cairo-surface.c
++++ b/src/cairo-surface.c
+@@ -1530,6 +1530,70 @@ _cairo_recording_surface_clone_similar (cairo_surface_t *surface,
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++struct acquire_source_image_data
++{
++ cairo_surface_t *src;
++ cairo_image_surface_t *image;
++ void *image_extra;
++};
++
++static void
++_wrap_release_source_image (void *data)
++{
++ struct acquire_source_image_data *acquire_data = data;
++ _cairo_surface_release_source_image (acquire_data->src,
++ acquire_data->image,
++ acquire_data->image_extra);
++ free(data);
++}
++
++static cairo_status_t
++_wrap_image (cairo_surface_t *src,
++ cairo_image_surface_t *image,
++ void *image_extra,
++ cairo_image_surface_t **out)
++{
++ static cairo_user_data_key_t wrap_image_key;
++ cairo_image_surface_t *surface;
++ cairo_status_t status;
++
++ struct acquire_source_image_data *data = malloc (sizeof (*data));
++ if (unlikely (data == NULL))
++ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
++ data->src = src;
++ data->image = image;
++ data->image_extra = image_extra;
++
++ surface = (cairo_image_surface_t *)
++ _cairo_image_surface_create_with_pixman_format (image->data,
++ image->pixman_format,
++ image->width,
++ image->height,
++ image->stride);
++ status = surface->base.status;
++ if (status) {
++ free (data);
++ return status;
++ }
++
++ status = _cairo_user_data_array_set_data (&surface->base.user_data,
++ &wrap_image_key,
++ data,
++ _wrap_release_source_image);
++ if (status) {
++ cairo_surface_destroy (&surface->base);
++ free (data);
++ return status;
++ }
++
++ pixman_image_set_component_alpha (
++ surface->pixman_image,
++ pixman_image_get_component_alpha (surface->pixman_image));
++
++ *out = surface;
++ return CAIRO_STATUS_SUCCESS;
++}
++
+ /**
+ * _cairo_surface_clone_similar:
+ * @surface: a #cairo_surface_t
+@@ -1606,15 +1670,19 @@ _cairo_surface_clone_similar (cairo_surface_t *surface,
+ /* If we failed, try again with an image surface */
+ status = _cairo_surface_acquire_source_image (src, &image, &image_extra);
+ if (status == CAIRO_STATUS_SUCCESS) {
+- status =
+- surface->backend->clone_similar (surface, &image->base,
+- src_x, src_y,
+- width, height,
+- clone_offset_x,
+- clone_offset_y,
+- clone_out);
+-
+- _cairo_surface_release_source_image (src, image, image_extra);
++ status = _wrap_image(src, image, image_extra, &image);
++ if (status != CAIRO_STATUS_SUCCESS) {
++ _cairo_surface_release_source_image (src, image, image_extra);
++ } else {
++ status =
++ surface->backend->clone_similar (surface, &image->base,
++ src_x, src_y,
++ width, height,
++ clone_offset_x,
++ clone_offset_y,
++ clone_out);
++ cairo_surface_destroy(&image->base);
++ }
+ }
+ }
+ }
diff --git a/gfx/cairo/xlib-flush-glyphs.patch b/gfx/cairo/xlib-flush-glyphs.patch
new file mode 100644
index 000000000..78a19d0dd
--- /dev/null
+++ b/gfx/cairo/xlib-flush-glyphs.patch
@@ -0,0 +1,66 @@
+diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+index f0de3c7..e24c962 100644
+--- a/gfx/cairo/cairo/src/cairo-xlib-surface.c
++++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+@@ -50,35 +50,36 @@
+ #include "cairo-xlib-private.h"
+ #include "cairo-xlib-surface-private.h"
+ #include "cairo-clip-private.h"
+ #include "cairo-error-private.h"
+ #include "cairo-scaled-font-private.h"
+ #include "cairo-surface-snapshot-private.h"
+ #include "cairo-surface-subsurface-private.h"
+ #include "cairo-region-private.h"
++#include "cairo-xlib-xrender-private.h"
+
+ #include <X11/Xutil.h> /* for XDestroyImage */
++#include <X11/Xlibint.h> /* for access to XDisplay's innards */
+
+ #define XLIB_COORD_MAX 32767
+
+ #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 <X11/Xlibint.h>
+ 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;
+@@ -4313,16 +4314,23 @@ _cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
+ }
+ break;
+ default:
+ ASSERT_NOT_REACHED;
+ break;
+ }
+ /* XXX assume X server wants pixman padding. Xft assumes this as well */
+
++ struct _XDisplay *dpy = (struct _XDisplay *) display->display;
++ int req_length = sz_xRenderAddGlyphsReq + 4;
++ if (req_length & 3)
++ req_length += 4 - (req_length & 3);
++ if (dpy->bufptr + req_length > dpy->bufmax)
++ XFlush (display->display);
++
+ XRenderAddGlyphs (display->display, glyphset_info->glyphset,
+ &glyph_index, &glyph_info, 1,
+ (char *) data,
+ glyph_surface->stride * glyph_surface->height);
+
+ if (data != glyph_surface->data)
+ free (data);
+
diff --git a/gfx/cairo/xlib-glyph-clip-region.patch b/gfx/cairo/xlib-glyph-clip-region.patch
new file mode 100644
index 000000000..50ff7f951
--- /dev/null
+++ b/gfx/cairo/xlib-glyph-clip-region.patch
@@ -0,0 +1,40 @@
+diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+--- a/gfx/cairo/cairo/src/cairo-xlib-surface.c
++++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c
+@@ -4806,30 +4806,30 @@ static cairo_int_status_t
+ }
+
+ X_DEBUG ((display->display, "show_glyphs (dst=%x)", (unsigned int) dst->drawable));
+
+ if (clip_region != NULL &&
+ cairo_region_num_rectangles (clip_region) == 1)
+ {
+ cairo_rectangle_int_t glyph_extents;
+- const cairo_rectangle_int_t *clip_extents;
++ cairo_rectangle_int_t clip_extents;
+
+ /* Can we do without the clip?
+ * Around 50% of the time the clip is redundant (firefox).
+ */
+ _cairo_scaled_font_glyph_approximate_extents (scaled_font,
+ glyphs, num_glyphs,
+ &glyph_extents);
+
+- clip_extents = &clip->path->extents;
+- if (clip_extents->x <= glyph_extents.x &&
+- clip_extents->y <= glyph_extents.y &&
+- clip_extents->x + clip_extents->width >= glyph_extents.x + glyph_extents.width &&
+- clip_extents->y + clip_extents->height >= glyph_extents.y + glyph_extents.height)
++ cairo_region_get_extents(clip_region, &clip_extents);
++ if (clip_extents.x <= glyph_extents.x &&
++ clip_extents.y <= glyph_extents.y &&
++ clip_extents.x + clip_extents.width >= glyph_extents.x + glyph_extents.width &&
++ clip_extents.y + clip_extents.height >= glyph_extents.y + glyph_extents.height)
+ {
+ clip_region = NULL;
+ }
+ }
+
+ status = _cairo_xlib_surface_set_clip_region (dst, clip_region);
+ if (unlikely (status))
+ goto BAIL0;
diff --git a/gfx/cairo/xlib-initialize-members.patch b/gfx/cairo/xlib-initialize-members.patch
new file mode 100644
index 000000000..0066ceb34
--- /dev/null
+++ b/gfx/cairo/xlib-initialize-members.patch
@@ -0,0 +1,19 @@
+diff -r 059e9961a122 gfx/cairo/cairo/src/cairo-xlib-display.c
+--- a/gfx/cairo/cairo/src/cairo-xlib-display.c Thu Feb 25 03:59:05 2010 -0800
++++ b/gfx/cairo/cairo/src/cairo-xlib-display.c Fri Feb 26 16:15:29 2010 +0100
+@@ -259,7 +259,14 @@
+ * add our hook. For now, that means Render, so we call into its
+ * QueryVersion function to ensure it gets initialized.
+ */
+- XRenderQueryVersion (dpy, &render_major, &render_minor);
++ Status s = XRenderQueryVersion (dpy, &render_major, &render_minor);
++ if (s == 0) {
++ /* XRenderQueryVersion failed, possibly because the server
++ * doesn't have the RENDER extension. Don't leave the version
++ * numbers uninitialised. See #548793.
++ */
++ render_major = render_minor = 0;
++ }
+
+ codes = XAddExtension (dpy);
+ if (unlikely (codes == NULL)) {
diff --git a/gfx/cairo/zero-sized.patch b/gfx/cairo/zero-sized.patch
new file mode 100644
index 000000000..bdd6ca798
--- /dev/null
+++ b/gfx/cairo/zero-sized.patch
@@ -0,0 +1,39 @@
+diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
+index e9e544d..cde68a1 100644
+--- a/src/cairo-image-surface.c
++++ b/src/cairo-image-surface.c
+@@ -324,8 +324,8 @@ _cairo_image_surface_create_with_pixman_format (unsigned char *data,
+ cairo_surface_t *surface;
+ pixman_image_t *pixman_image;
+
+- pixman_image = pixman_image_create_bits (pixman_format, width, height,
+- (uint32_t *) data, stride);
++ pixman_image = pixman_image_create_bits (pixman_format, width ? width : 1, height ? height : 1,
++ (uint32_t *) data, stride ? stride : 4);
+
+ if (unlikely (pixman_image == NULL))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
+index f86a133..ddcb600 100644
+--- a/src/cairo-xlib-surface.c
++++ b/src/cairo-xlib-surface.c
+@@ -675,7 +675,8 @@ _get_image_surface (cairo_xlib_surface_t *surface,
+
+ pixmap = XCreatePixmap (surface->dpy,
+ surface->drawable,
+- extents.width, extents.height,
++ extents.width <= 0 ? 1 : extents.width,
++ extents.height <= 0 ? 1 : extents.height,
+ surface->depth);
+ if (pixmap) {
+ XCopyArea (surface->dpy, surface->drawable, pixmap, surface->gc,
+@@ -686,7 +687,8 @@ _get_image_surface (cairo_xlib_surface_t *surface,
+ ximage = XGetImage (surface->dpy,
+ pixmap,
+ 0, 0,
+- extents.width, extents.height,
++ extents.width <= 0 ? 1 : extents.width,
++ extents.height <= 0 ? 1 : extents.height,
+ AllPlanes, ZPixmap);
+
+ XFreePixmap (surface->dpy, pixmap);
diff --git a/gfx/cairo/zombie-face.patch b/gfx/cairo/zombie-face.patch
new file mode 100644
index 000000000..a4175fecc
--- /dev/null
+++ b/gfx/cairo/zombie-face.patch
@@ -0,0 +1,119 @@
+From 0238fe2cafea2e1ed19bb222117bd73ee6898d4d Mon Sep 17 00:00:00 2001
+From: Karl Tomlinson <karlt+@karlt.net>
+Date: Thu, 14 May 2009 10:46:29 +0000
+Subject: [ft] Resolve mutual referencing problems with zombie faces
+
+Bug 21706 -- zombie ft_font_face / ft_unscaled_font mutual
+ referencing problems
+[http://bugs.freedesktop.org/show_bug.cgi?id=21706]
+
+There can be more than one zombie font_face belonging to an unscaled_font,
+but only the first is destroyed. This leaks the client's FT_Face
+(and associated font data) as release of the FT_Face depends on release
+of the font_face.
+
+(The reason why Firefox ends up with two different font_faces for one
+unscaled_font is that load_flags for faces with artificial oblique have
+FT_LOAD_NO_BITMAP set.
+https://bugzilla.mozilla.org/show_bug.cgi?id=486974)
+
+Also it's possible for _cairo_ft_font_face_create to pull out a zombie
+font_face from the unscaled_font, which would crash
+_cairo_ft_font_face_scaled_font_create, as that expects non-null
+font_face->unscaled (if !font-face->pattern).
+---
+diff --git a/AUTHORS b/AUTHORS
+index 289fecb..8c06174 100644
+--- a/AUTHORS
++++ b/AUTHORS
+@@ -86,7 +86,7 @@ Travis Spencer <tspencer@cs.pdx.edu> XCB backend fix
+ Bill Spitzak <spitzak@d2.com> Build fix to find Xrender.h without xrender.pc
+ Zhe Su <james.su@gmail.com> Add support for fontconfig's embeddedbitmap option
+ Owen Taylor <otaylor@redhat.com> Font rewrite, documentation, win32 backend
+-Karl Tomlinson <karlt+@karlt.net>
++Karl Tomlinson <karlt+@karlt.net> Optimisation and obscure bug fixes (mozilla)
+ Alp Toker <alp@atoker.com> Fix several code/comment typos
+ Malcolm Tredinnick <malcolm@commsecure.com.au> Documentation fixes
+ David Turner <david@freetype.org> Optimize gradient calculations
+diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
+index 1e2a18e..f9ff0b1 100644
+--- a/src/cairo-ft-font.c
++++ b/src/cairo-ft-font.c
+@@ -543,8 +543,10 @@ _cairo_ft_unscaled_font_destroy (void *abstract_font)
+ /* See comments in _ft_font_face_destroy about the "zombie" state
+ * for a _ft_font_face.
+ */
+- if (unscaled->faces && !unscaled->faces->unscaled)
++ if (unscaled->faces && unscaled->faces->unscaled == NULL) {
++ assert (unscaled->faces->next == NULL);
+ cairo_font_face_destroy (&unscaled->faces->base);
++ }
+ } else {
+ _font_map_release_face_lock_held (font_map, unscaled);
+ }
+@@ -2233,9 +2235,10 @@ _cairo_ft_font_face_destroy (void *abstract_face)
+ if (font_face == NULL)
+ return;
+
+- /* When destroying the face created by cairo_ft_font_face_create_for_ft_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 public references to the font face.
++ * is still alive but there are no other references to a font face with
++ * the same FT_Face.
+ *
+ * We go from:
+ *
+@@ -2249,6 +2252,8 @@ _cairo_ft_font_face_destroy (void *abstract_face)
+
+ 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_font_face_reference (&font_face->base);
+@@ -2394,12 +2399,21 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled,
+ font_face->ft_options.extra_flags == ft_options->extra_flags &&
+ cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base))
+ {
+- if (font_face->base.status == CAIRO_STATUS_SUCCESS)
+- return cairo_font_face_reference (&font_face->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;
++ }
+
+- /* 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);
++ return &font_face->base;
++ } else
++ return cairo_font_face_reference (&font_face->base);
+ }
+ }
+
+@@ -2415,6 +2429,14 @@ _cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled,
+
+ 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;
+
+--
+cgit v0.8.2